Add filter discovery endpoints with active/inactive status (Task 2.1)
- Add list_filters() and get_filter() to config_file_service.py:
scans filter.d/, parses [Definition] + [Init] sections, merges .local
overrides, and cross-references running jails to set active/used_by_jails
- Add FilterConfig.active, used_by_jails, source_file, has_local_override
fields to the Pydantic model; add FilterListResponse and FilterNotFoundError
- Add GET /api/config/filters and GET /api/config/filters/{name} to config.py
- Remove the shadowed GET /api/config/filters list route from file_config.py;
rename GET /api/config/filters/{name} raw variant to /filters/{name}/raw
- Update frontend: fetchFilterFiles() adapts FilterListResponse -> ConfFilesResponse;
add fetchFilters() and fetchFilter() to api/config.ts; remove unused
fetchFilterFiles/fetchActionFiles calls from useConfigActiveStatus
- Fix ConfigPageLogPath test mock to include fetchInactiveJails and related
exports introduced by Stage 1
- Backend: 169 tests pass, mypy --strict clean, ruff clean
- Frontend: 63 tests pass, tsc --noEmit clean, eslint clean
This commit is contained in:
@@ -218,45 +218,24 @@ class TestSetJailConfigEnabled:
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# GET /api/config/filters
|
||||
# GET /api/config/filters/{name}/raw
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestListFilterFiles:
|
||||
async def test_200_returns_files(self, file_config_client: AsyncClient) -> None:
|
||||
with patch(
|
||||
"app.routers.file_config.file_config_service.list_filter_files",
|
||||
AsyncMock(return_value=_conf_files_resp()),
|
||||
):
|
||||
resp = await file_config_client.get("/api/config/filters")
|
||||
class TestGetFilterFileRaw:
|
||||
"""Tests for the renamed ``GET /api/config/filters/{name}/raw`` endpoint.
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["total"] == 1
|
||||
The simple list (``GET /api/config/filters``) and the structured detail
|
||||
(``GET /api/config/filters/{name}``) are now served by the config router.
|
||||
This endpoint returns the raw file content only.
|
||||
"""
|
||||
|
||||
async def test_503_on_config_dir_error(
|
||||
self, file_config_client: AsyncClient
|
||||
) -> None:
|
||||
with patch(
|
||||
"app.routers.file_config.file_config_service.list_filter_files",
|
||||
AsyncMock(side_effect=ConfigDirError("x")),
|
||||
):
|
||||
resp = await file_config_client.get("/api/config/filters")
|
||||
|
||||
assert resp.status_code == 503
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# GET /api/config/filters/{name}
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestGetFilterFile:
|
||||
async def test_200_returns_content(self, file_config_client: AsyncClient) -> None:
|
||||
with patch(
|
||||
"app.routers.file_config.file_config_service.get_filter_file",
|
||||
AsyncMock(return_value=_conf_file_content("nginx")),
|
||||
):
|
||||
resp = await file_config_client.get("/api/config/filters/nginx")
|
||||
resp = await file_config_client.get("/api/config/filters/nginx/raw")
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["name"] == "nginx"
|
||||
@@ -266,7 +245,7 @@ class TestGetFilterFile:
|
||||
"app.routers.file_config.file_config_service.get_filter_file",
|
||||
AsyncMock(side_effect=ConfigFileNotFoundError("missing")),
|
||||
):
|
||||
resp = await file_config_client.get("/api/config/filters/missing")
|
||||
resp = await file_config_client.get("/api/config/filters/missing/raw")
|
||||
|
||||
assert resp.status_code == 404
|
||||
|
||||
|
||||
Reference in New Issue
Block a user