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:
@@ -49,9 +49,9 @@ async def bans_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
|
||||
@@ -87,7 +87,7 @@ class TestGetActiveBans:
|
||||
"app.routers.bans.ban_service.get_active_bans",
|
||||
AsyncMock(return_value=mock_response),
|
||||
):
|
||||
resp = await bans_client.get("/api/bans/active")
|
||||
resp = await bans_client.get("/api/v1/bans/active")
|
||||
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
@@ -100,7 +100,7 @@ class TestGetActiveBans:
|
||||
resp = await AsyncClient(
|
||||
transport=ASGITransport(app=bans_client._transport.app), # type: ignore[attr-defined]
|
||||
base_url="http://test",
|
||||
).get("/api/bans/active")
|
||||
).get("/api/v1/bans/active")
|
||||
assert resp.status_code == 401
|
||||
|
||||
async def test_empty_when_no_bans(self, bans_client: AsyncClient) -> None:
|
||||
@@ -110,7 +110,7 @@ class TestGetActiveBans:
|
||||
"app.routers.bans.ban_service.get_active_bans",
|
||||
AsyncMock(return_value=mock_response),
|
||||
):
|
||||
resp = await bans_client.get("/api/bans/active")
|
||||
resp = await bans_client.get("/api/v1/bans/active")
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["total"] == 0
|
||||
@@ -135,7 +135,7 @@ class TestGetActiveBans:
|
||||
"app.routers.bans.ban_service.get_active_bans",
|
||||
AsyncMock(return_value=mock_response),
|
||||
):
|
||||
resp = await bans_client.get("/api/bans/active")
|
||||
resp = await bans_client.get("/api/v1/bans/active")
|
||||
|
||||
ban = resp.json()["bans"][0]
|
||||
assert "ip" in ban
|
||||
@@ -160,7 +160,7 @@ class TestBanIp:
|
||||
AsyncMock(return_value=None),
|
||||
):
|
||||
resp = await bans_client.post(
|
||||
"/api/bans",
|
||||
"/api/v1/bans",
|
||||
json={"ip": "1.2.3.4", "jail": "sshd"},
|
||||
)
|
||||
|
||||
@@ -174,7 +174,7 @@ class TestBanIp:
|
||||
AsyncMock(side_effect=ValueError("Invalid IP address: 'bad'")),
|
||||
):
|
||||
resp = await bans_client.post(
|
||||
"/api/bans",
|
||||
"/api/v1/bans",
|
||||
json={"ip": "bad", "jail": "sshd"},
|
||||
)
|
||||
|
||||
@@ -189,7 +189,7 @@ class TestBanIp:
|
||||
AsyncMock(side_effect=JailNotFoundError("ghost")),
|
||||
):
|
||||
resp = await bans_client.post(
|
||||
"/api/bans",
|
||||
"/api/v1/bans",
|
||||
json={"ip": "1.2.3.4", "jail": "ghost"},
|
||||
)
|
||||
|
||||
@@ -200,7 +200,7 @@ class TestBanIp:
|
||||
resp = await AsyncClient(
|
||||
transport=ASGITransport(app=bans_client._transport.app), # type: ignore[attr-defined]
|
||||
base_url="http://test",
|
||||
).post("/api/bans", json={"ip": "1.2.3.4", "jail": "sshd"})
|
||||
).post("/api/v1/bans", json={"ip": "1.2.3.4", "jail": "sshd"})
|
||||
assert resp.status_code == 401
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ class TestUnbanIp:
|
||||
):
|
||||
resp = await bans_client.request(
|
||||
"DELETE",
|
||||
"/api/bans",
|
||||
"/api/v1/bans",
|
||||
json={"ip": "1.2.3.4", "unban_all": True},
|
||||
)
|
||||
|
||||
@@ -235,7 +235,7 @@ class TestUnbanIp:
|
||||
):
|
||||
resp = await bans_client.request(
|
||||
"DELETE",
|
||||
"/api/bans",
|
||||
"/api/v1/bans",
|
||||
json={"ip": "1.2.3.4", "jail": "sshd"},
|
||||
)
|
||||
|
||||
@@ -250,7 +250,7 @@ class TestUnbanIp:
|
||||
):
|
||||
resp = await bans_client.request(
|
||||
"DELETE",
|
||||
"/api/bans",
|
||||
"/api/v1/bans",
|
||||
json={"ip": "bad", "unban_all": True},
|
||||
)
|
||||
|
||||
@@ -266,7 +266,7 @@ class TestUnbanIp:
|
||||
):
|
||||
resp = await bans_client.request(
|
||||
"DELETE",
|
||||
"/api/bans",
|
||||
"/api/v1/bans",
|
||||
json={"ip": "1.2.3.4", "jail": "ghost"},
|
||||
)
|
||||
|
||||
@@ -287,7 +287,7 @@ class TestUnbanAll:
|
||||
"app.routers.bans.jail_service.unban_all_ips",
|
||||
AsyncMock(return_value=3),
|
||||
):
|
||||
resp = await bans_client.request("DELETE", "/api/bans/all")
|
||||
resp = await bans_client.request("DELETE", "/api/v1/bans/all")
|
||||
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
@@ -300,7 +300,7 @@ class TestUnbanAll:
|
||||
"app.routers.bans.jail_service.unban_all_ips",
|
||||
AsyncMock(return_value=0),
|
||||
):
|
||||
resp = await bans_client.request("DELETE", "/api/bans/all")
|
||||
resp = await bans_client.request("DELETE", "/api/v1/bans/all")
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["count"] == 0
|
||||
@@ -318,7 +318,7 @@ class TestUnbanAll:
|
||||
)
|
||||
),
|
||||
):
|
||||
resp = await bans_client.request("DELETE", "/api/bans/all")
|
||||
resp = await bans_client.request("DELETE", "/api/v1/bans/all")
|
||||
|
||||
assert resp.status_code == 502
|
||||
|
||||
@@ -327,5 +327,5 @@ class TestUnbanAll:
|
||||
resp = await AsyncClient(
|
||||
transport=ASGITransport(app=bans_client._transport.app), # type: ignore[attr-defined]
|
||||
base_url="http://test",
|
||||
).request("DELETE", "/api/bans/all")
|
||||
).request("DELETE", "/api/v1/bans/all")
|
||||
assert resp.status_code == 401
|
||||
|
||||
Reference in New Issue
Block a user