feat: implement API versioning /api/v1/
- All backend routers moved to /api/v1/ prefix
- Frontend BASE_URL updated to /api/v1
- Setup redirect middleware updated to redirect to /api/v1/setup
- Health router path fixed: prefix=/api/v1/health, @router.get('')
- conftest.py: set server_status=online for test fixture
- Created Docs/API_VERSIONING.md with deprecation policy
- Updated Docs/Backend-Development.md with versioning section
- Updated Instructions.md curl examples
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -114,11 +114,11 @@ async def history_client(tmp_path: Path) -> AsyncClient: # type: ignore[misc]
|
||||
|
||||
transport = ASGITransport(app=app)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as ac:
|
||||
resp = await ac.post("/api/setup", json=_SETUP_PAYLOAD)
|
||||
resp = await ac.post("/api/v1/setup", json=_SETUP_PAYLOAD)
|
||||
assert resp.status_code == 201
|
||||
|
||||
login_resp = await ac.post(
|
||||
"/api/auth/login",
|
||||
"/api/v1/auth/login",
|
||||
json={"password": _SETUP_PAYLOAD["master_password"]},
|
||||
)
|
||||
assert login_resp.status_code == 200
|
||||
@@ -144,15 +144,15 @@ class TestHistoryList:
|
||||
"app.routers.history.history_service.list_history",
|
||||
new=AsyncMock(return_value=_make_history_list()),
|
||||
):
|
||||
response = await history_client.get("/api/history")
|
||||
response = await history_client.get("/api/v1/history")
|
||||
assert response.status_code == 200
|
||||
|
||||
async def test_returns_401_when_unauthenticated(
|
||||
self, client: AsyncClient
|
||||
) -> None:
|
||||
"""Unauthenticated request returns HTTP 401."""
|
||||
await client.post("/api/setup", json=_SETUP_PAYLOAD)
|
||||
response = await client.get("/api/history")
|
||||
await client.post("/api/v1/setup", json=_SETUP_PAYLOAD)
|
||||
response = await client.get("/api/v1/history")
|
||||
assert response.status_code == 401
|
||||
|
||||
async def test_response_shape(self, history_client: AsyncClient) -> None:
|
||||
@@ -162,7 +162,7 @@ class TestHistoryList:
|
||||
"app.routers.history.history_service.list_history",
|
||||
new=AsyncMock(return_value=mock_response),
|
||||
):
|
||||
response = await history_client.get("/api/history")
|
||||
response = await history_client.get("/api/v1/history")
|
||||
|
||||
body = response.json()
|
||||
assert "items" in body
|
||||
@@ -192,7 +192,7 @@ class TestHistoryList:
|
||||
"app.routers.history.history_service.list_history",
|
||||
new=mock_fn,
|
||||
):
|
||||
await history_client.get("/api/history?jail=nginx")
|
||||
await history_client.get("/api/v1/history?jail=nginx")
|
||||
|
||||
_args, kwargs = mock_fn.call_args
|
||||
assert kwargs.get("jail") == "nginx"
|
||||
@@ -204,7 +204,7 @@ class TestHistoryList:
|
||||
"app.routers.history.history_service.list_history",
|
||||
new=mock_fn,
|
||||
):
|
||||
await history_client.get("/api/history?ip=192.168")
|
||||
await history_client.get("/api/v1/history?ip=192.168")
|
||||
|
||||
_args, kwargs = mock_fn.call_args
|
||||
assert kwargs.get("ip_filter") == "192.168"
|
||||
@@ -216,7 +216,7 @@ class TestHistoryList:
|
||||
"app.routers.history.history_service.list_history",
|
||||
new=mock_fn,
|
||||
):
|
||||
await history_client.get("/api/history?range=7d")
|
||||
await history_client.get("/api/v1/history?range=7d")
|
||||
|
||||
_args, kwargs = mock_fn.call_args
|
||||
assert kwargs.get("range_") == "7d"
|
||||
@@ -228,7 +228,7 @@ class TestHistoryList:
|
||||
"app.routers.history.history_service.list_history",
|
||||
new=mock_fn,
|
||||
):
|
||||
await history_client.get("/api/history?origin=blocklist")
|
||||
await history_client.get("/api/v1/history?origin=blocklist")
|
||||
|
||||
_args, kwargs = mock_fn.call_args
|
||||
assert kwargs.get("origin") == "blocklist"
|
||||
@@ -240,7 +240,7 @@ class TestHistoryList:
|
||||
"app.routers.history.history_service.list_history",
|
||||
new=mock_fn,
|
||||
):
|
||||
await history_client.get("/api/history?source=archive")
|
||||
await history_client.get("/api/v1/history?source=archive")
|
||||
|
||||
_args, kwargs = mock_fn.call_args
|
||||
assert kwargs.get("source") == "archive"
|
||||
@@ -254,7 +254,7 @@ class TestHistoryList:
|
||||
"app.routers.history.history_service.list_history",
|
||||
new=mock_fn,
|
||||
):
|
||||
await history_client.get("/api/history/archive")
|
||||
await history_client.get("/api/v1/history/archive")
|
||||
|
||||
_args, kwargs = mock_fn.call_args
|
||||
assert kwargs.get("source") == "archive"
|
||||
@@ -272,7 +272,7 @@ class TestHistoryList:
|
||||
)
|
||||
),
|
||||
):
|
||||
response = await history_client.get("/api/history")
|
||||
response = await history_client.get("/api/v1/history")
|
||||
|
||||
body = response.json()
|
||||
assert body["items"] == []
|
||||
@@ -295,15 +295,15 @@ class TestIpHistory:
|
||||
"app.routers.history.history_service.get_ip_detail",
|
||||
new=AsyncMock(return_value=_make_ip_detail("1.2.3.4")),
|
||||
):
|
||||
response = await history_client.get("/api/history/1.2.3.4")
|
||||
response = await history_client.get("/api/v1/history/1.2.3.4")
|
||||
assert response.status_code == 200
|
||||
|
||||
async def test_returns_401_when_unauthenticated(
|
||||
self, client: AsyncClient
|
||||
) -> None:
|
||||
"""Unauthenticated request returns HTTP 401."""
|
||||
await client.post("/api/setup", json=_SETUP_PAYLOAD)
|
||||
response = await client.get("/api/history/1.2.3.4")
|
||||
await client.post("/api/v1/setup", json=_SETUP_PAYLOAD)
|
||||
response = await client.get("/api/v1/history/1.2.3.4")
|
||||
assert response.status_code == 401
|
||||
|
||||
async def test_returns_404_for_unknown_ip(
|
||||
@@ -314,7 +314,7 @@ class TestIpHistory:
|
||||
"app.routers.history.history_service.get_ip_detail",
|
||||
new=AsyncMock(return_value=None),
|
||||
):
|
||||
response = await history_client.get("/api/history/9.9.9.9")
|
||||
response = await history_client.get("/api/v1/history/9.9.9.9")
|
||||
assert response.status_code == 404
|
||||
|
||||
async def test_response_shape(self, history_client: AsyncClient) -> None:
|
||||
@@ -324,7 +324,7 @@ class TestIpHistory:
|
||||
"app.routers.history.history_service.get_ip_detail",
|
||||
new=AsyncMock(return_value=mock_detail),
|
||||
):
|
||||
response = await history_client.get("/api/history/1.2.3.4")
|
||||
response = await history_client.get("/api/v1/history/1.2.3.4")
|
||||
|
||||
body = response.json()
|
||||
assert body["ip"] == "1.2.3.4"
|
||||
@@ -376,7 +376,7 @@ class TestIpHistory:
|
||||
"app.routers.history.history_service.get_ip_detail",
|
||||
new=AsyncMock(return_value=mock_detail),
|
||||
):
|
||||
response = await history_client.get("/api/history/10.0.0.1")
|
||||
response = await history_client.get("/api/v1/history/10.0.0.1")
|
||||
|
||||
assert response.status_code == 200
|
||||
body = response.json()
|
||||
|
||||
Reference in New Issue
Block a user