-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
283 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
"""Storage API""" | ||
|
||
import shutil | ||
from pathlib import Path | ||
|
||
from fastapi import APIRouter, HTTPException, Request | ||
from fastapi.responses import JSONResponse | ||
|
||
from cbir.api.utils.models import Storage | ||
|
||
router = APIRouter() | ||
|
||
|
||
@router.get("/storages") | ||
async def get_storages(request: Request) -> JSONResponse: | ||
"""Get all storages.""" | ||
|
||
settings = request.app.state.database.settings | ||
base_path = Path(settings.data_path) | ||
|
||
if not base_path.is_dir(): | ||
raise HTTPException( | ||
status_code=404, | ||
detail="Base path not found or is not a directory", | ||
) | ||
|
||
folder_names = [f.name for f in base_path.iterdir() if f.is_dir()] | ||
|
||
return JSONResponse(content=folder_names) | ||
|
||
|
||
@router.post("/storages") | ||
async def create_storage(request: Request, body: Storage) -> JSONResponse: | ||
"""Create a new storage.""" | ||
|
||
settings = request.app.state.database.settings | ||
base_path = Path(settings.data_path) | ||
storage_path = base_path / body.name | ||
|
||
if storage_path.exists(): | ||
raise HTTPException( | ||
status_code=409, | ||
detail=f"Storage with name '{body.name}' already exists.", | ||
) | ||
|
||
try: | ||
storage_path.mkdir() | ||
except Exception as e: | ||
raise HTTPException( | ||
status_code=500, | ||
detail=f"Failed to create storage: {str(e)}", | ||
) from e | ||
|
||
return JSONResponse(content={"message": f"Created storage with name: {body.name}"}) | ||
|
||
|
||
@router.get("/storages/{name}") | ||
async def get_storage(request: Request, name: str) -> JSONResponse: | ||
"""Get a specific storage.""" | ||
|
||
settings = request.app.state.database.settings | ||
base_path = Path(settings.data_path) | ||
storage_path = base_path / name | ||
|
||
if not storage_path.is_dir(): | ||
raise HTTPException( | ||
status_code=404, | ||
detail=f"Storage with name '{name}' not found.", | ||
) | ||
|
||
return JSONResponse(content={"name": name}) | ||
|
||
|
||
@router.delete("/storages/{name}") | ||
async def delete_storage(request: Request, name: str) -> JSONResponse: | ||
"""Delete a specific storage and its content.""" | ||
|
||
settings = request.app.state.database.settings | ||
base_path = Path(settings.data_path) | ||
storage_path = base_path / name | ||
|
||
if not storage_path.is_dir(): | ||
raise HTTPException( | ||
status_code=404, | ||
detail=f"Storage with name '{name}' not found.", | ||
) | ||
|
||
try: | ||
shutil.rmtree(storage_path) | ||
except Exception as e: | ||
raise HTTPException( | ||
status_code=500, | ||
detail=f"Failed to delete storage: {str(e)}", | ||
) from e | ||
|
||
return JSONResponse(content={"message": f"Deleted storage with name: {name}"}) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
"""DTO Models""" | ||
|
||
from pydantic import BaseModel | ||
|
||
|
||
class Storage(BaseModel): | ||
""" | ||
Storage model. | ||
""" | ||
|
||
name: str |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
"""Storage tests""" | ||
|
||
import shutil | ||
import tempfile | ||
from typing import Any, Generator | ||
|
||
import pytest | ||
from fastapi import FastAPI | ||
from fastapi.testclient import TestClient | ||
|
||
from cbir.api.storages import router | ||
|
||
|
||
@pytest.fixture | ||
def app() -> Generator[Any, Any, Any]: | ||
""" | ||
Create and provide a FastAPI application instance for testing. | ||
The application is configured with: | ||
- A router included in the FastAPI instance. | ||
- A mock database with temporary settings stored in a temporary directory. | ||
Yields: | ||
local_app: The FastAPI app instance with router and mock database. | ||
""" | ||
|
||
local_app = FastAPI() | ||
local_app.include_router(router) | ||
|
||
settings = type("Settings", (object,), {"data_path": tempfile.mkdtemp()}) | ||
local_app.state.database = type("Database", (object,), {"settings": settings}) | ||
|
||
yield local_app | ||
|
||
# Cleanup after tests | ||
shutil.rmtree(local_app.state.database.settings.data_path) # type: ignore | ||
|
||
|
||
@pytest.fixture | ||
def client(app: FastAPI) -> TestClient: | ||
""" | ||
Provide a test client for the FastAPI application. | ||
Args: | ||
app (FastAPI): The FastAPI application instance to be tested. | ||
Returns: | ||
TestClient: An instance of `TestClient` that can be used to send | ||
requests to the FastAPI application and inspect the responses. | ||
""" | ||
return TestClient(app) | ||
|
||
|
||
def test_get_storages(client: TestClient) -> None: | ||
""" | ||
Test the GET /storages endpoint. | ||
Args: | ||
client: A test client instance used to send requests to the application. | ||
""" | ||
|
||
response = client.get("/storages") | ||
assert response.status_code == 200 | ||
assert response.json() == [] | ||
|
||
|
||
def test_create_storage(client: TestClient) -> None: | ||
""" | ||
Test the POST /storages endpoint. | ||
Args: | ||
client: A test client instance used to send requests to the application. | ||
""" | ||
|
||
storage_name = "test_storage" | ||
|
||
response = client.post("/storages", json={"name": storage_name}) | ||
assert response.status_code == 200 | ||
assert response.json() == {"message": f"Created storage with name: {storage_name}"} | ||
|
||
response = client.get("/storages") | ||
assert response.status_code == 200 | ||
assert storage_name in response.json() | ||
|
||
|
||
def test_get_storage(client: TestClient) -> None: | ||
""" | ||
Test the GET /storages/{name} endpoint. | ||
Args: | ||
client: A test client instance used to send requests to the application. | ||
""" | ||
|
||
storage_name = "test_storage" | ||
client.post("/storages", json={"name": storage_name}) | ||
|
||
response = client.get(f"/storages/{storage_name}") | ||
assert response.status_code == 200 | ||
assert response.json() == {"name": storage_name} | ||
|
||
|
||
def test_get_storage_not_found(client: TestClient) -> None: | ||
""" | ||
Test the GET /storages/{name} endpoint for non-existent storage. | ||
Args: | ||
client: A test client instance used to send requests to the application. | ||
""" | ||
|
||
storage_name = "non_existent_storage" | ||
|
||
response = client.get(f"/storages/{storage_name}") | ||
assert response.status_code == 404 | ||
assert response.json() == { | ||
"detail": f"Storage with name '{storage_name}' not found." | ||
} | ||
|
||
|
||
def test_create_existing_storage(client: TestClient) -> None: | ||
""" | ||
Test the POST /storages/{name} endpoint for an existing storage. | ||
Args: | ||
client: A test client instance used to send requests to the application. | ||
""" | ||
|
||
storage_name = "test_storage" | ||
client.post("/storages", json={"name": storage_name}) | ||
|
||
response = client.post("/storages", json={"name": storage_name}) | ||
assert response.status_code == 409 | ||
assert response.json() == { | ||
"detail": f"Storage with name '{storage_name}' already exists." | ||
} | ||
|
||
|
||
def test_delete_storage(client: TestClient) -> None: | ||
""" | ||
Test the DELETE /storages/{name} endpoint. | ||
Args: | ||
client: A test client instance used to send requests to the application. | ||
""" | ||
|
||
storage_name = "test_storage" | ||
client.post("/storages", json={"name": storage_name}) | ||
|
||
response = client.delete(f"/storages/{storage_name}") | ||
assert response.status_code == 200 | ||
assert response.json() == {"message": f"Deleted storage with name: {storage_name}"} | ||
|
||
response = client.get(f"/storages/{storage_name}") | ||
assert response.status_code == 404 | ||
|
||
|
||
def test_delete_storage_not_found(client: TestClient) -> None: | ||
""" | ||
Test the DELETE /storages/{name} endpoint when the storage does not exist. | ||
Args: | ||
client: A test client instance used to send requests to the application. | ||
""" | ||
|
||
storage_name = "non_existent_storage" | ||
|
||
response = client.delete(f"/storages/{storage_name}") | ||
assert response.status_code == 404 | ||
assert response.json() == { | ||
"detail": f"Storage with name '{storage_name}' not found." | ||
} |