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:
@@ -48,9 +48,9 @@ async def server_client(tmp_path: Path) -> AsyncClient: # type: ignore[misc]
|
||||
|
||||
transport = ASGITransport(app=app)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as ac:
|
||||
await ac.post("/api/setup", json=_SETUP_PAYLOAD)
|
||||
await ac.post("/api/v1/setup", json=_SETUP_PAYLOAD)
|
||||
login = await ac.post(
|
||||
"/api/auth/login",
|
||||
"/api/v1/auth/login",
|
||||
json={"password": _SETUP_PAYLOAD["master_password"]},
|
||||
)
|
||||
assert login.status_code == 200
|
||||
@@ -88,7 +88,7 @@ class TestGetServerSettings:
|
||||
"app.routers.server.server_service.get_settings",
|
||||
AsyncMock(return_value=mock_response),
|
||||
):
|
||||
resp = await server_client.get("/api/server/settings")
|
||||
resp = await server_client.get("/api/v1/server/settings")
|
||||
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
@@ -101,7 +101,7 @@ class TestGetServerSettings:
|
||||
resp = await AsyncClient(
|
||||
transport=ASGITransport(app=server_client._transport.app), # type: ignore[attr-defined]
|
||||
base_url="http://test",
|
||||
).get("/api/server/settings")
|
||||
).get("/api/v1/server/settings")
|
||||
assert resp.status_code == 401
|
||||
|
||||
async def test_502_on_connection_error(self, server_client: AsyncClient) -> None:
|
||||
@@ -112,7 +112,7 @@ class TestGetServerSettings:
|
||||
"app.routers.server.server_service.get_settings",
|
||||
AsyncMock(side_effect=Fail2BanConnectionError("down", "/tmp/fake.sock")),
|
||||
):
|
||||
resp = await server_client.get("/api/server/settings")
|
||||
resp = await server_client.get("/api/v1/server/settings")
|
||||
|
||||
assert resp.status_code == 502
|
||||
|
||||
@@ -132,7 +132,7 @@ class TestUpdateServerSettings:
|
||||
AsyncMock(return_value=None),
|
||||
):
|
||||
resp = await server_client.put(
|
||||
"/api/server/settings",
|
||||
"/api/v1/server/settings",
|
||||
json={"log_level": "DEBUG"},
|
||||
)
|
||||
|
||||
@@ -147,7 +147,7 @@ class TestUpdateServerSettings:
|
||||
AsyncMock(side_effect=ServerOperationError("set failed")),
|
||||
):
|
||||
resp = await server_client.put(
|
||||
"/api/server/settings",
|
||||
"/api/v1/server/settings",
|
||||
json={"log_level": "DEBUG"},
|
||||
)
|
||||
|
||||
@@ -158,7 +158,7 @@ class TestUpdateServerSettings:
|
||||
resp = await AsyncClient(
|
||||
transport=ASGITransport(app=server_client._transport.app), # type: ignore[attr-defined]
|
||||
base_url="http://test",
|
||||
).put("/api/server/settings", json={"log_level": "DEBUG"})
|
||||
).put("/api/v1/server/settings", json={"log_level": "DEBUG"})
|
||||
assert resp.status_code == 401
|
||||
|
||||
async def test_502_on_connection_error(self, server_client: AsyncClient) -> None:
|
||||
@@ -170,7 +170,7 @@ class TestUpdateServerSettings:
|
||||
AsyncMock(side_effect=Fail2BanConnectionError("down", "/tmp/fake.sock")),
|
||||
):
|
||||
resp = await server_client.put(
|
||||
"/api/server/settings",
|
||||
"/api/v1/server/settings",
|
||||
json={"log_level": "INFO"},
|
||||
)
|
||||
|
||||
@@ -191,7 +191,7 @@ class TestFlushLogs:
|
||||
"app.routers.server.server_service.flush_logs",
|
||||
AsyncMock(return_value="OK"),
|
||||
):
|
||||
resp = await server_client.post("/api/server/flush-logs")
|
||||
resp = await server_client.post("/api/v1/server/flush-logs")
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["message"] == "OK"
|
||||
@@ -204,7 +204,7 @@ class TestFlushLogs:
|
||||
"app.routers.server.server_service.flush_logs",
|
||||
AsyncMock(side_effect=ServerOperationError("flushlogs failed")),
|
||||
):
|
||||
resp = await server_client.post("/api/server/flush-logs")
|
||||
resp = await server_client.post("/api/v1/server/flush-logs")
|
||||
|
||||
assert resp.status_code == 400
|
||||
|
||||
@@ -213,7 +213,7 @@ class TestFlushLogs:
|
||||
resp = await AsyncClient(
|
||||
transport=ASGITransport(app=server_client._transport.app), # type: ignore[attr-defined]
|
||||
base_url="http://test",
|
||||
).post("/api/server/flush-logs")
|
||||
).post("/api/v1/server/flush-logs")
|
||||
assert resp.status_code == 401
|
||||
|
||||
async def test_502_on_connection_error(self, server_client: AsyncClient) -> None:
|
||||
@@ -224,6 +224,6 @@ class TestFlushLogs:
|
||||
"app.routers.server.server_service.flush_logs",
|
||||
AsyncMock(side_effect=Fail2BanConnectionError("down", "/tmp/fake.sock")),
|
||||
):
|
||||
resp = await server_client.post("/api/server/flush-logs")
|
||||
resp = await server_client.post("/api/v1/server/flush-logs")
|
||||
|
||||
assert resp.status_code == 502
|
||||
|
||||
Reference in New Issue
Block a user