from __future__ import annotations import asyncio import sys import tempfile import unittest from pathlib import Path import httpx sys.path.insert(0, str(Path(__file__).resolve().parents[3])) from backend.app.dependencies import get_browse_service from backend.app.fs.filesystem_adapter import FilesystemAdapter from backend.app.main import app from backend.app.security.path_guard import PathGuard from backend.app.services.browse_service import BrowseService class BrowseApiErrorsGoldenTest(unittest.TestCase): def setUp(self) -> None: self.temp_dir = tempfile.TemporaryDirectory() self.root = Path(self.temp_dir.name) / "root" self.root.mkdir(parents=True, exist_ok=True) (self.root / "a.txt").write_text("a", encoding="utf-8") service = BrowseService( path_guard=PathGuard({"storage1": str(self.root)}), filesystem=FilesystemAdapter(), ) async def _override_browse_service() -> BrowseService: return service app.dependency_overrides[get_browse_service] = _override_browse_service def tearDown(self) -> None: app.dependency_overrides.clear() self.temp_dir.cleanup() def _get(self, path: str) -> httpx.Response: async def _run() -> httpx.Response: transport = httpx.ASGITransport(app=app) async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client: return await client.get("/api/browse", params={"path": path}) return asyncio.run(_run()) def test_invalid_root_alias_error_shape(self) -> None: response = self._get("unknown/path") self.assertEqual(response.status_code, 403) self.assertEqual( response.json(), { "error": { "code": "invalid_root_alias", "message": "Unknown root alias", "details": {"path": "unknown/path"}, } }, ) def test_traversal_error_shape(self) -> None: response = self._get("storage1/../etc") self.assertEqual(response.status_code, 403) self.assertEqual( response.json(), { "error": { "code": "path_traversal_detected", "message": "Path traversal is not allowed", "details": {"path": "storage1/../etc"}, } }, ) def test_not_found_error_shape(self) -> None: response = self._get("storage1/missing") self.assertEqual(response.status_code, 404) self.assertEqual( response.json(), { "error": { "code": "path_not_found", "message": "Requested path was not found", "details": {"path": "storage1/missing"}, } }, ) def test_type_conflict_error_shape(self) -> None: response = self._get("storage1/a.txt") self.assertEqual(response.status_code, 409) self.assertEqual( response.json(), { "error": { "code": "path_type_conflict", "message": "Requested path is not a directory", "details": {"path": "storage1/a.txt"}, } }, ) if __name__ == "__main__": unittest.main()