Files
Dominik 1f9cb007cd Port API tests from BATS to pytest and fix ResponseVerifyer
Move all API-related tests (HTTP endpoints, config validation, auth,
search, history, lists, Lua pages, OpenAPI spec validation) from BATS
shell tests to pytest. BATS retains DNS, regex, CLI, and system-level
tests. The auth test suite now removes the password at the end, leaving
no net state change.

Fix ResponseVerifyer to respect the OpenAPI "required" field: optional
properties absent from FTL's response are silently skipped instead of
flagged as errors. The required list is propagated through all recursive
calls (top-level objects, allOf, nested objects, array items).

Add a set_config() helper that changes FTL configuration via the API
instead of shelling out to the CLI. The API PATCH is synchronous, so no
log polling or sleeps are needed.

Add build.sh test-api target to run only the pytest API tests (skips
BATS and perf tests).

Test migration mapping (25 BATS tests removed, all covered in pytest):

| # | Removed BATS test | Pytest counterpart |
|---|---|---|
| 1 | HTTP server responds with JSON error 404 to unknown API path | TestHTTPErrors::test_api_404_returns_json |
| 2 | HTTP server responds with error 404 to path outside /admin | TestHTTPErrors::test_non_admin_path_returns_404 |
| 3 | Config validation working on the API (type-based checking) | TestConfigValidationAPIType (2 tests) |
| 4 | Config validation working on the API (validator-based checking) | TestConfigValidationAPIValidator (4 tests) |
| 5 | Changing a config option set forced by ENVVAR is not possible via the API | TestEnvvarProtectedConfig::test_api_rejects_envvar_override |
| 6 | API domain search: Non-existing domain | TestDomainSearch::test_nonexistent_domain |
| 7 | API domain search: antigravity.ftl | TestDomainSearch::test_antigravity_domain |
| 8 | API domain search: Internationalized/partially capital domain | TestDomainSearch::test_punycode_normalization |
| 9 | API history: Returns full 24 hours | TestHistory::test_history_returns_24h |
| 10 | API history/clients: Returns full 24 hours | TestHistory::test_history_clients_returns_24h |
| 11 | Check /api/lists?type=block | TestLists::test_block_lists_only |
| 12 | Check /api/lists?type=allow | TestLists::test_allow_lists_only |
| 13 | Check /api/lists without type parameter | TestLists::test_all_lists_includes_both_types |
| 14 | API: No UNKNOWN reply in API | TestQueries::test_no_unknown_reply |
| 15 | API: No UNKNOWN status in API | TestQueries::test_no_unknown_status |
| 16 | Lua server page outside /admin is not served by default | TestLuaServerPages::test_lua_page_outside_admin_not_served_by_default |
| 17 | Lua server page is generating proper backtrace | TestLuaServerPages::test_lua_page_generates_proper_backtrace |
| 18 | Lua server page outside of webhome is served without login | TestLuaServerPages::test_lua_page_outside_webhome_served_without_login |
| 19 | API validation (checkAPI.py) | TestEndpointCoverage (3 tests) + TestEndpointResponses + TestTeleporter |
| 20 | API authorization (without password): No login required | TestAuthWorkflow::test_01_no_password_means_session_valid |
| 21 | Create, set, and use application password | TestAuthWorkflow::test_02 + test_03 + test_04 |
| 22 | CLI password file is as expected | TestAuthWorkflow::test_04b_cli_password_file |
| 23 | API authorization: Setting password | TestAuthWorkflow::test_05_set_password |
| 24 | API authorization (with password): Incorrect password is rejected | TestAuthWorkflow::test_06_incorrect_password_rejected |
| 25 | API authorization (with password): Correct password is accepted | TestAuthWorkflow::test_07_correct_password_accepted |

New tests without a BATS predecessor:
- TestAuthWorkflow::test_08_rate_limiting_enforced
- TestAuthWorkflow::test_09_remove_password
- TestAuthWorkflow::test_10_no_password_after_removal

Final count: 192 BATS + 38 pytest = 230 total (was 216 BATS). No tests
lost, 14 net new.

Signed-off-by: Dominik <dl6er@dl6er.de>
2026-03-25 08:51:04 +01:00
..