# E2E Test Failure Tasks > Generated from Robot Framework test run on 2026-06-21. > **Overall Result:** 123 tests, 0 passed, 122 failed, 1 skipped. > **Root Cause:** Backend health check returns HTTP 503 because fail2ban is not installed/running (`ModuleNotFoundError: No module named 'fail2ban'`). All suites fail at `Suite Setup` → `Wait For Backend Health`. --- ## Task: Fix E2E Suite Setup — Backend Health Check **Problem:** Every test suite fails during `Suite Setup` because `Wait For Backend Health` in `e2e/resources/common.resource` polls `GET /api/v1/health` and expects HTTP 200. The backend returns HTTP 503 with body `{"status":"unavailable","fail2ban":"offline","database":"ok","scheduler":"running","cache":"initialised"}`. **Step That Fails:** `Suite Setup` → `Wait For Backend Health` keyword (line 24 in `common.resource`). **Files to Check:** - `e2e/resources/common.resource` — `Wait For Backend Health` keyword - `backend/app/routers/health.py` — health endpoint logic - `backend/app/utils/fail2ban_client.py` — fail2ban module import - `Docker/compose.debug.yml` — how fail2ban is wired in the Docker stack **Reference Docs:** - `e2e/Instructions.md` — "Requires: stack up (make up)" - `Docs/Deployment.md` — fail2ban dependency setup - `Makefile` — `make e2e` target **Expected Behavior:** Either: 1. The health check accepts 503 as "backend is up but degraded" and proceeds with tests, OR 2. fail2ban is installed and running so health returns 200. --- ## Task: 01 Setup And Auth — Setup Page Renders All Form Fields **Test:** `Setup Page Renders All Form Fields` **Suite:** `01_setup_and_auth.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`) — backend never becomes healthy within 120s timeout. **Files to Check:** - `e2e/tests/01_setup_and_auth.robot` - `e2e/resources/common.resource` - `frontend/src/pages/SetupPage.tsx` (or equivalent setup page component) **Reference Docs:** - `e2e/Instructions.md` — Setup wizard feature coverage - `Docs/Features.md` — Setup wizard form fields, password strength, validation **Expected Behavior:** Setup page loads with all required form fields visible (Master Password, Confirm Password, Database Path, fail2ban Socket Path, Timezone, Session Duration, Submit button). --- ## Task: 01 Setup And Auth — Password Strength Indicator Updates On Input **Test:** `Password Strength Indicator Updates On Input` **Suite:** `01_setup_and_auth.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/01_setup_and_auth.robot` - `frontend/src/components/PasswordStrengthIndicator.tsx` (or equivalent) **Reference Docs:** - `Docs/Features.md` — Password strength indicator behavior **Expected Behavior:** Four-segment strength bar updates dynamically as user types password. Weak password shows 1 segment, strong password shows 4 segments. --- ## Task: 01 Setup And Auth — Password Mismatch Shows Validation Error **Test:** `Password Mismatch Shows Validation Error` **Suite:** `01_setup_and_auth.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/01_setup_and_auth.robot` - `frontend/src/pages/SetupPage.tsx` **Reference Docs:** - `Docs/Features.md` — Setup form validation **Expected Behavior:** Submitting with non-matching passwords surfaces an error on the Confirm Password field. --- ## Task: 01 Setup And Auth — Empty Required Fields Show Validation Errors **Test:** `Empty Required Fields Show Validation Errors` **Suite:** `01_setup_and_auth.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/01_setup_and_auth.robot` - `frontend/src/pages/SetupPage.tsx` **Reference Docs:** - `Docs/Features.md` — Required field validation **Expected Behavior:** Submitting with blank required fields shows field-level error messages for each empty required input. --- ## Task: 01 Setup And Auth — Invalid Session Duration Shows Validation Error **Test:** `Invalid Session Duration Shows Validation Error` **Suite:** `01_setup_and_auth.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/01_setup_and_auth.robot` - `frontend/src/pages/SetupPage.tsx` - `backend/app/models/request.py` — session duration validation **Reference Docs:** - `Docs/Features.md` — Session duration validation rules **Expected Behavior:** Submitting with an invalid session duration (e.g., negative, zero, or non-numeric) shows a validation error. --- ## Task: 01 Setup And Auth — Incomplete Password Shows Complexity Error **Test:** `Incomplete Password Shows Complexity Error` **Suite:** `01_setup_and_auth.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/01_setup_and_auth.robot` - `frontend/src/pages/SetupPage.tsx` - `frontend/src/utils/passwordValidation.ts` (or equivalent) **Reference Docs:** - `Docs/Features.md` — Password complexity requirements **Expected Behavior:** Submitting a password that does not meet complexity rules (length, special chars, etc.) shows a complexity error message. --- ## Task: 01 Setup And Auth — Setup Completes Successfully And Redirects To Login **Test:** `Setup Completes Successfully And Redirects To Login` **Suite:** `01_setup_and_auth.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/01_setup_and_auth.robot` - `frontend/src/pages/SetupPage.tsx` - `backend/app/routers/setup.py` — setup completion endpoint - `backend/app/services/setup_service.py` **Reference Docs:** - `Docs/Features.md` — Setup completion flow - `e2e/Instructions.md` — Setup wizard full submit **Expected Behavior:** Filling all required fields with valid data and submitting completes setup, creates the admin user, initializes the database, and redirects to `/login`. --- ## Task: 02 Ban Records — Simulated Failed Logins Appear As Ban Records **Test:** `Simulated Failed Logins Appear As Ban Records` **Suite:** `02_ban_records.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Specific Error:** `'' does not contain '192.168.100.99'` (from earlier partial run before backend was fully down). **Files to Check:** - `e2e/tests/02_ban_records.robot` - `backend/app/routers/bans.py` — ban records endpoint - `backend/app/services/ban_service.py` - `Docker/simulate_failed_logins.sh` — simulation script **Reference Docs:** - `e2e/Instructions.md` — "end-to-end ban pipeline: fail2ban log → history" - `Docs/Features.md` — Ban pipeline flow **Expected Behavior:** Simulating failed SSH logins triggers fail2ban to ban the IP, and the banned IP appears in the dashboard/history within a reasonable time. --- ## Task: 02 Login — Login Page Renders Password Input **Test:** `Login Page Renders Password Input` **Suite:** `02_login.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/02_login.robot` - `frontend/src/pages/LoginPage.tsx` **Reference Docs:** - `e2e/Instructions.md` — Login page feature coverage - `Docs/Features.md` — Login page UI **Expected Behavior:** Login page shows a single password input field (no username field) and a submit button. --- ## Task: 02 Login — Login Page Has No Username Field **Test:** `Login Page Has No Username Field` **Suite:** `02_login.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/02_login.robot` - `frontend/src/pages/LoginPage.tsx` **Reference Docs:** - `e2e/Instructions.md` — "Login page must NOT ask for a username" **Expected Behavior:** The login page does NOT contain a username/email input field. Only password is required. --- ## Task: 02 Login — Login With Wrong Password Shows Error **Test:** `Login With Wrong Password Shows Error` **Suite:** `02_login.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/02_login.robot` - `frontend/src/pages/LoginPage.tsx` - `backend/app/routers/auth.py` — login endpoint - `backend/app/services/auth_service.py` **Reference Docs:** - `e2e/Instructions.md` — "wrong password" test case **Expected Behavior:** Submitting an incorrect password displays an error message (e.g., "Invalid credentials") without revealing whether the user exists. --- ## Task: 02 Login — Login Rate Limits After Multiple Failures **Test:** `Login Rate Limits After Multiple Failures` **Suite:** `02_login.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/02_login.robot` - `backend/app/routers/auth.py` — rate limiting - `backend/app/middleware/rate_limit.py` (or equivalent) **Reference Docs:** - `e2e/Instructions.md` — "Per-IP rate limit trial" - `Docs/Security.md` — Rate limiting rules **Expected Behavior:** After 5 failed login attempts from the same IP within 60 seconds, subsequent attempts receive HTTP 429 Too Many Requests. --- ## Task: 02 Login — Session Endpoint Returns 401 Without Cookie **Test:** `Session Endpoint Returns 401 Without Cookie` **Suite:** `02_login.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/02_login.robot` - `backend/app/routers/auth.py` — session validation endpoint - `backend/app/middleware/session.py` (or equivalent) **Reference Docs:** - `e2e/Instructions.md` — "session validation 401" **Expected Behavior:** Calling `GET /api/v1/auth/session` without a valid session cookie returns HTTP 401 Unauthorized. --- ## Task: 02 Login — Direct Access To Protected Route Redirects To Login **Test:** `Direct Access To Protected Route Redirects To Login` **Suite:** `02_login.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/02_login.robot` - `frontend/src/App.tsx` — route guards - `frontend/src/components/ProtectedRoute.tsx` (or equivalent) **Reference Docs:** - `Docs/Features.md` — Route protection **Expected Behavior:** Navigating directly to a protected route (e.g., `/dashboard`) without being logged in redirects to `/login`. --- ## Task: 02 Login — Session Validation 401 On Mount Redirects To Login **Test:** `Session Validation 401 On Mount Redirects To Login` **Suite:** `02_login.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/02_login.robot` - `frontend/src/hooks/useSession.ts` (or equivalent) - `frontend/src/components/ProtectedRoute.tsx` **Reference Docs:** - `e2e/Instructions.md` — "When the backend session check returns 401, the UI redirects" **Expected Behavior:** When the frontend mounts and the session validation API returns 401, the user is redirected to the login page. --- ## Task: 02 Login — Logout Clears Session **Test:** `Logout Clears Session` **Suite:** `02_login.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/02_login.robot` - `frontend/src/components/Sidebar.tsx` — logout button - `backend/app/routers/auth.py` — logout endpoint **Reference Docs:** - `e2e/Instructions.md` — "Clicking the Sign Out button clears the session" **Expected Behavior:** Clicking "Sign Out" clears the session cookie and invalidates the session on the backend. --- ## Task: 02 Login — After Logout Protected Pages Redirect To Login **Test:** `After Logout Protected Pages Redirect To Login` **Suite:** `02_login.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/02_login.robot` - `frontend/src/App.tsx` — route guards **Reference Docs:** - `e2e/Instructions.md` — Post-logout behavior **Expected Behavior:** After logging out, attempting to navigate to any protected page redirects to `/login`. --- ## Task: 02 Login — Login Preserves Originally Requested Page Via Next Parameter **Test:** `Login Preserves Originally Requested Page Via Next Parameter` **Suite:** `02_login.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/02_login.robot` - `frontend/src/pages/LoginPage.tsx` — `next` query param handling **Reference Docs:** - `e2e/Instructions.md` — "After successful login, user is redirected to the originally requested page" **Expected Behavior:** If an unauthenticated user tries to access `/jails`, they are redirected to `/login?next=/jails`. After successful login, they are redirected to `/jails`. --- ## Task: 03 Blocklist Import — Manual Blocklist Import Completes Without Error **Test:** `Manual Blocklist Import Completes Without Error` **Suite:** `03_blocklist_import.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Specific Error:** `Url: http://localhost:8000/api/v1/blocklists Expected status: 401 != 200` **Files to Check:** - `e2e/tests/03_blocklist_import.robot` - `backend/app/routers/blocklists.py` — blocklist import endpoint - `backend/app/services/blocklist_service.py` **Reference Docs:** - `e2e/Instructions.md` — "blocklist manual import via UI" - `Docs/Features.md` — Blocklist importer **Expected Behavior:** A logged-in admin can trigger a manual blocklist import via the UI/API, and it completes without errors. The endpoint should return 200 (not 401, which implies auth/session issue). --- ## Task: 03 Dashboard — Dashboard Page Renders Status Bar **Test:** `Dashboard Page Renders Status Bar` **Suite:** `03_dashboard.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/03_dashboard.robot` - `frontend/src/pages/DashboardPage.tsx` - `backend/app/routers/dashboard.py` — status endpoint **Reference Docs:** - `e2e/Instructions.md` — "status bar, time-range presets, data-source badges" - `Docs/Features.md` — Dashboard overview **Expected Behavior:** The dashboard page renders a status bar showing fail2ban server status (online/offline), version, and other key metrics. --- ## Task: 03 Dashboard — Dashboard Ban List Renders Columns **Test:** `Dashboard Ban List Renders Columns` **Suite:** `03_dashboard.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/03_dashboard.robot` - `frontend/src/components/BanList.tsx` (or equivalent) - `backend/app/routers/dashboard.py` — bans endpoint **Reference Docs:** - `Docs/Features.md` — Dashboard ban list table **Expected Behavior:** The ban list table contains expected columns: IP Address, Country, Banned At, Expires At, Jail, Actions. --- ## Task: 03 Dashboard — Dashboard Time Range 24h Shows Live Source **Test:** `Dashboard Time Range 24h Shows Live Source` **Suite:** `03_dashboard.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/03_dashboard.robot` - `frontend/src/pages/DashboardPage.tsx` — time range selector - `backend/app/routers/dashboard.py` — bans endpoint with range filter **Reference Docs:** - `e2e/Instructions.md` — "Selecting Last 24 hours shows Live source badge" **Expected Behavior:** Selecting "Last 24 hours" from the time range dropdown shows a "Live" data source badge, indicating data comes directly from fail2ban. --- ## Task: 03 Dashboard — Dashboard Time Range 7d Shows Archive Source **Test:** `Dashboard Time Range 7d Shows Archive Source` **Suite:** `03_dashboard.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/03_dashboard.robot` - `frontend/src/pages/DashboardPage.tsx` - `backend/app/routers/dashboard.py` **Reference Docs:** - `e2e/Instructions.md` — "Selecting Last 7 days shows Archive source badge" **Expected Behavior:** Selecting "Last 7 days" shows an "Archive" data source badge, indicating data comes from the SQLite archive. --- ## Task: 03 Dashboard — Dashboard Time Range 30d Shows Archive Source **Test:** `Dashboard Time Range 30d Shows Archive Source` **Suite:** `03_dashboard.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/03_dashboard.robot` - `frontend/src/pages/DashboardPage.tsx` **Reference Docs:** - `e2e/Instructions.md` — "Selecting Last 30 days shows Archive source badge" **Expected Behavior:** Selecting "Last 30 days" shows an "Archive" data source badge. --- ## Task: 03 Dashboard — Dashboard Time Range 365d Shows Archive Source **Test:** `Dashboard Time Range 365d Shows Archive Source` **Suite:** `03_dashboard.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/03_dashboard.robot` - `frontend/src/pages/DashboardPage.tsx` **Reference Docs:** - `e2e/Instructions.md` — "Selecting Last 365 days shows Archive source badge" **Expected Behavior:** Selecting "Last 365 days" shows an "Archive" data source badge. --- ## Task: 03 Dashboard — Dashboard Bans Endpoint Returns Expected Shape **Test:** `Dashboard Bans Endpoint Returns Expected Shape` **Suite:** `03_dashboard.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/03_dashboard.robot` - `backend/app/routers/dashboard.py` — `GET /api/v1/dashboard/bans` - `backend/app/models/response.py` — ban response schema **Reference Docs:** - `Docs/API-Reference.md` — Dashboard bans endpoint contract **Expected Behavior:** `GET /api/v1/dashboard/bans?range=24h&page=1&page_size=100` returns a paginated response with `items`, `total`, `page`, `page_size` fields, and each item has `ip`, `country`, `banned_at`, `expires_at`, `jail`. --- ## Task: 03 Dashboard — Dashboard Status Endpoint Returns Version **Test:** `Dashboard Status Endpoint Returns Version` **Suite:** `03_dashboard.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/03_dashboard.robot` - `backend/app/routers/dashboard.py` — `GET /api/v1/dashboard/status` **Reference Docs:** - `Docs/API-Reference.md` — Dashboard status endpoint **Expected Behavior:** `GET /api/v1/dashboard/status` returns JSON containing `version`, `online`, `jails_count`, and other status fields. --- ## Task: 03 Dashboard — Dashboard Bans By Country Endpoint **Test:** `Dashboard Bans By Country Endpoint` **Suite:** `03_dashboard.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/03_dashboard.robot` - `backend/app/routers/dashboard.py` — `GET /api/v1/dashboard/bans/by-country` **Reference Docs:** - `Docs/API-Reference.md` — Geo aggregation endpoint **Expected Behavior:** `GET /api/v1/dashboard/bans/by-country?range=24h` returns a list of `{country_code, country_name, count}` objects. --- ## Task: 03 Dashboard — Dashboard Bans Trend Endpoint **Test:** `Dashboard Bans Trend Endpoint` **Suite:** `03_dashboard.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/03_dashboard.robot` - `backend/app/routers/dashboard.py` — `GET /api/v1/dashboard/bans/trend` **Reference Docs:** - `Docs/API-Reference.md` — Trend endpoint **Expected Behavior:** `GET /api/v1/dashboard/bans/trend?range=24h` returns time-series data for chart rendering. --- ## Task: 03 Dashboard — Dashboard Bans By Jail Endpoint **Test:** `Dashboard Bans By Jail Endpoint` **Suite:** `03_dashboard.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/03_dashboard.robot` - `backend/app/routers/dashboard.py` — jail aggregation endpoint **Reference Docs:** - `Docs/API-Reference.md` — Jail aggregation endpoint **Expected Behavior:** Returns bans grouped by jail name with counts. --- ## Task: 04 Map — Map Page Renders World Map And Companion Table **Test:** `Map Page Renders World Map And Companion Table` **Suite:** `04_map.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/04_map.robot` - `frontend/src/pages/MapPage.tsx` - `frontend/src/components/WorldMap.tsx` (or equivalent) **Reference Docs:** - `e2e/Instructions.md` — "World Map View — country fills, click-to-filter" - `Docs/Features.md` — Map view **Expected Behavior:** The map page renders an interactive world map and a companion data table below it. --- ## Task: 04 Map — Map Page Renders Time Range Selector **Test:** `Map Page Renders Time Range Selector` **Suite:** `04_map.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/04_map.robot` - `frontend/src/pages/MapPage.tsx` **Reference Docs:** - `e2e/Instructions.md` — Map time range presets **Expected Behavior:** The map page includes a time range selector (24h, 7d, 30d, 365d). --- ## Task: 04 Map — Map Page 24h Preset Shows Live Source Badge **Test:** `Map Page 24h Preset Shows Live Source Badge` **Suite:** `04_map.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/04_map.robot` - `frontend/src/pages/MapPage.tsx` **Reference Docs:** - `e2e/Instructions.md` — "Selecting Last 24 hours shows Live source badge" **Expected Behavior:** Selecting 24h on the map page shows a "Live" data source badge. --- ## Task: 04 Map — Map Page 7d Preset Shows Archive Source Badge **Test:** `Map Page 7d Preset Shows Archive Source Badge` **Suite:** `04_map.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/04_map.robot` - `frontend/src/pages/MapPage.tsx` **Reference Docs:** - `e2e/Instructions.md` — "Selecting Last 7 days shows Archive source badge" **Expected Behavior:** Selecting 7d on the map page shows an "Archive" data source badge. --- ## Task: 04 Map — Map Companion Table Is Sticky Header **Test:** `Map Companion Table Is Sticky Header` **Suite:** `04_map.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/04_map.robot` - `frontend/src/components/MapCompanionTable.tsx` (or equivalent) **Reference Docs:** - `e2e/Instructions.md` — "sticky table header/footer" **Expected Behavior:** The companion table below the map has a sticky header that remains visible while scrolling. --- ## Task: 04 Map — Map Page Has Zoom Controls **Test:** `Map Page Has Zoom Controls` **Suite:** `04_map.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/04_map.robot` - `frontend/src/components/WorldMap.tsx` **Reference Docs:** - `e2e/Instructions.md` — "zoom controls" **Expected Behavior:** The map includes zoom in/out controls (buttons or mouse wheel support). --- ## Task: 04 Map — Map Bans By Country API Endpoint **Test:** `Map Bans By Country API Endpoint` **Suite:** `04_map.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/04_map.robot` - `backend/app/routers/dashboard.py` — geo endpoint **Reference Docs:** - `Docs/API-Reference.md` — Geo/country endpoint **Expected Behavior:** `GET /api/v1/dashboard/bans/by-country` returns country-level ban counts for map coloring. --- ## Task: 04 Map — Map Threshold Config Endpoint Exists **Test:** `Map Threshold Config Endpoint Exists` **Suite:** `04_map.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/04_map.robot` - `backend/app/routers/config_misc.py` (or equivalent) **Reference Docs:** - `Docs/API-Reference.md` — Map threshold configuration **Expected Behavior:** A configuration endpoint exists for setting map color thresholds (e.g., low/medium/high ban count ranges). --- ## Task: 04 Map — Map Threshold Config Returns Thresholds **Test:** `Map Threshold Config Returns Thresholds` **Suite:** `04_map.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/04_map.robot` - `backend/app/routers/config_misc.py` **Reference Docs:** - `Docs/API-Reference.md` — Map threshold configuration **Expected Behavior:** The threshold config endpoint returns the currently configured threshold values. --- ## Task: 04 Map — Map Filter Clears And Resets Companion Table **Test:** `Map Filter Clears And Resets Companion Table` **Suite:** `04_map.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/04_map.robot` - `frontend/src/pages/MapPage.tsx` - `frontend/src/components/MapCompanionTable.tsx` **Reference Docs:** - `e2e/Instructions.md` — "click-to-filter, Clear filter" **Expected Behavior:** Clicking a country on the map filters the companion table to that country. Clicking "Clear filter" resets the table to show all countries. --- ## Task: 05 Jails — Jails Page Lists Active Jails **Test:** `Jails Page Lists Active Jails` **Suite:** `05_jails.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/05_jails.robot` - `frontend/src/pages/JailsPage.tsx` - `backend/app/routers/jails.py` — jails list endpoint **Reference Docs:** - `e2e/Instructions.md` — "Jail Management — list" - `Docs/Features.md` — Jail management **Expected Behavior:** The jails page displays a table of all active jails with status, backend, banned count, failed count, find time, ban time, and max retry. --- ## Task: 05 Jails — Jails API Returns Active Jails **Test:** `Jails API Returns Active Jails` **Suite:** `05_jails.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/05_jails.robot` - `backend/app/routers/jails.py` — `GET /api/v1/jails` **Reference Docs:** - `Docs/API-Reference.md` — Jails endpoint **Expected Behavior:** `GET /api/v1/jails` returns a list of active jail configurations and their current status. --- ## Task: 05 Jails — Jail Detail Page Loads For First Active Jail **Test:** `Jail Detail Page Loads For First Active Jail` **Suite:** `05_jails.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/05_jails.robot` - `frontend/src/pages/JailDetailPage.tsx` - `backend/app/routers/jails.py` — jail detail endpoint **Reference Docs:** - `e2e/Instructions.md` — "Jail Management — list, ban/unban API, IP lookup" **Expected Behavior:** Navigating to `/jails/{jail-name}` loads the detail page for that jail, showing banned IPs, ignore list, and jail controls. --- ## Task: 05 Jails — Ban An IP Via API **Test:** `Ban An IP Via API` **Suite:** `05_jails.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/05_jails.robot` - `backend/app/routers/jails.py` — `POST /api/v1/bans` - `backend/app/services/ban_service.py` **Reference Docs:** - `Docs/API-Reference.md` — Ban endpoint - `e2e/Instructions.md` — "Ban / Unban IP" **Expected Behavior:** `POST /api/v1/bans` with `{ip, jail}` bans the specified IP in the specified jail. --- ## Task: 05 Jails — Unban The IP We Just Banned **Test:** `Unban The IP We Just Banned` **Suite:** `05_jails.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/05_jails.robot` - `backend/app/routers/jails.py` — `DELETE /api/v1/bans/{ip}` **Reference Docs:** - `Docs/API-Reference.md` — Unban endpoint **Expected Behavior:** `DELETE /api/v1/bans/{ip}` (or equivalent unban endpoint) removes the ban for the specified IP. --- ## Task: 05 Jails — Unban All Endpoint Accepts Request **Test:** `Unban All Endpoint Accepts Request` **Suite:** `05_jails.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/05_jails.robot` - `backend/app/routers/jails.py` — bulk unban endpoint **Reference Docs:** - `Docs/API-Reference.md` — Bulk unban endpoint **Expected Behavior:** An endpoint exists to unban all IPs (either globally or per-jail) and returns success. --- ## Task: 05 Jails — Active Bans Endpoint Returns List **Test:** `Active Bans Endpoint Returns List` **Suite:** `05_jails.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/05_jails.robot` - `backend/app/routers/jails.py` — active bans endpoint **Reference Docs:** - `Docs/API-Reference.md` — Active bans endpoint **Expected Behavior:** `GET /api/v1/jails/{name}/bans` returns a paginated list of currently banned IPs for the specified jail. --- ## Task: 05 Jails — IP Lookup Endpoint Returns Geo **Test:** `IP Lookup Endpoint Returns Geo` **Suite:** `05_jails.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/05_jails.robot` - `backend/app/routers/geo.py` — geo lookup endpoint - `backend/app/services/geo_service.py` **Reference Docs:** - `Docs/API-Reference.md` — Geo lookup endpoint - `Docs/Features.md` — IP geolocation **Expected Behavior:** `GET /api/v1/geo/{ip}` returns geolocation data (country, city, ASN, org) for the given IP. --- ## Task: 05 Jails — Ignore List Add And Remove Via API **Test:** `Ignore List Add And Remove Via API` **Suite:** `05_jails.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/05_jails.robot` - `backend/app/routers/jails.py` — ignore list endpoints **Reference Docs:** - `Docs/API-Reference.md` — Ignore list endpoints **Expected Behavior:** `POST /api/v1/jails/{name}/ignore` adds an IP to the ignore list. `DELETE /api/v1/jails/{name}/ignore/{ip}` removes it. --- ## Task: 05 Jails — Ignore Self Toggle Via API **Test:** `Ignore Self Toggle Via API` **Suite:** `05_jails.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/05_jails.robot` - `backend/app/routers/jails.py` — ignore self toggle **Reference Docs:** - `Docs/API-Reference.md` — Ignore self toggle **Expected Behavior:** An endpoint exists to toggle "ignore self" (exclude the server's own IP from banning) for a jail. --- ## Task: 05 Jails — Jail Reload Endpoint Works **Test:** `Jail Reload Endpoint Works` **Suite:** `05_jails.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/05_jails.robot` - `backend/app/routers/jails.py` — reload endpoint **Reference Docs:** - `Docs/API-Reference.md` — Jail control endpoints **Expected Behavior:** `POST /api/v1/jails/{name}/reload` reloads the jail configuration and returns success. --- ## Task: 05 Jails — Jail Stop Endpoint Works **Test:** `Jail Stop Endpoint Works` **Suite:** `05_jails.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/05_jails.robot` - `backend/app/routers/jails.py` — stop endpoint **Reference Docs:** - `Docs/API-Reference.md` — Jail control endpoints **Expected Behavior:** `POST /api/v1/jails/{name}/stop` stops the jail and returns success. --- ## Task: 05 Jails — Jail Start Endpoint Works **Test:** `Jail Start Endpoint Works` **Suite:** `05_jails.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/05_jails.robot` - `backend/app/routers/jails.py` — start endpoint **Reference Docs:** - `Docs/API-Reference.md` — Jail control endpoints **Expected Behavior:** `POST /api/v1/jails/{name}/start` starts the jail and returns success. --- ## Task: 05 Jails — Jail Idle Endpoint Works **Test:** `Jail Idle Endpoint Works` **Suite:** `05_jails.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/05_jails.robot` - `backend/app/routers/jails.py` — idle endpoint **Reference Docs:** - `Docs/API-Reference.md` — Jail control endpoints **Expected Behavior:** `POST /api/v1/jails/{name}/idle` sets the jail to idle mode (monitoring without banning) and returns success. --- ## Task: 05 Jails — Reload All Jails Endpoint Works **Test:** `Reload All Jails Endpoint Works` **Suite:** `05_jails.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/05_jails.robot` - `backend/app/routers/jails.py` — reload all endpoint **Reference Docs:** - `Docs/API-Reference.md` — Jail control endpoints **Expected Behavior:** `POST /api/v1/jails/reload` reloads all jails and returns success. --- ## Task: 05 Jails — Geo Stats Endpoint Returns Counters **Test:** `Geo Stats Endpoint Returns Counters` **Suite:** `05_jails.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/05_jails.robot` - `backend/app/routers/geo.py` — geo stats endpoint **Reference Docs:** - `Docs/API-Reference.md` — Geo stats endpoint **Expected Behavior:** `GET /api/v1/geo/stats` returns aggregated geolocation statistics (bans by country, etc.). --- ## Task: 06 Config Jails Filters Actions — Config Page Renders All Required Tabs **Test:** `Config Page Renders All Required Tabs` **Suite:** `06_config_jails_filters_actions.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/06_config_jails_filters_actions.robot` - `frontend/src/pages/ConfigPage.tsx` **Reference Docs:** - `e2e/Instructions.md` — "Configuration View — Jails/Filters/Actions tabs" - `Docs/Features.md` — Configuration view **Expected Behavior:** The configuration page renders tabs for Jails, Filters, Actions, Server, and Regex Tester. --- ## Task: 06 Config Jails Filters Actions — Config Jails Tab Defaults To Active **Test:** `Config Jails Tab Defaults To Active` **Suite:** `06_config_jails_filters_actions.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/06_config_jails_filters_actions.robot` - `frontend/src/pages/ConfigPage.tsx` **Reference Docs:** - `e2e/Instructions.md` — "Jails/Filters/Actions tabs" **Expected Behavior:** When navigating to `/config`, the "Jails" tab is active by default. --- ## Task: 06 Config Jails Filters Actions — Config Filters Tab Loads **Test:** `Config Filters Tab Loads` **Suite:** `06_config_jails_filters_actions.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/06_config_jails_filters_actions.robot` - `frontend/src/pages/ConfigPage.tsx` - `backend/app/routers/config_misc.py` — filters endpoint **Reference Docs:** - `e2e/Instructions.md` — "Filters tab" **Expected Behavior:** Clicking the "Filters" tab loads and displays the list of fail2ban filter configurations. --- ## Task: 06 Config Jails Filters Actions — Config Actions Tab Loads **Test:** `Config Actions Tab Loads` **Suite:** `06_config_jails_filters_actions.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/06_config_jails_filters_actions.robot` - `frontend/src/pages/ConfigPage.tsx` - `backend/app/routers/config_misc.py` — actions endpoint **Reference Docs:** - `e2e/Instructions.md` — "Actions tab" **Expected Behavior:** Clicking the "Actions" tab loads and displays the list of fail2ban action configurations. --- ## Task: 06 Config Jails Filters Actions — Config Server Tab Loads **Test:** `Config Server Tab Loads` **Suite:** `06_config_jails_filters_actions.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/06_config_jails_filters_actions.robot` - `frontend/src/pages/ConfigPage.tsx` **Reference Docs:** - `e2e/Instructions.md` — "Server settings + log viewer" **Expected Behavior:** The Server tab loads and displays server-wide settings. --- ## Task: 06 Config Jails Filters Actions — Config Regex Tester Tab Loads **Test:** `Config Regex Tester Tab Loads` **Suite:** `06_config_jails_filters_actions.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/06_config_jails_filters_actions.robot` - `frontend/src/pages/ConfigPage.tsx` - `frontend/src/components/RegexTester.tsx` (or equivalent) **Reference Docs:** - `e2e/Instructions.md` — "regex tester" **Expected Behavior:** The Regex Tester tab loads with input fields for regex pattern and test log lines. --- ## Task: 06 Config Jails Filters Actions — Config Regex Tester API Endpoint Validates Pattern **Test:** `Config Regex Tester API Endpoint Validates Pattern` **Suite:** `06_config_jails_filters_actions.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/06_config_jails_filters_actions.robot` - `backend/app/routers/config_misc.py` — regex tester endpoint **Reference Docs:** - `Docs/API-Reference.md` — Regex tester endpoint **Expected Behavior:** `POST /api/v1/config/regex-test` (or equivalent) accepts a regex pattern and test lines, returns matches/non-matches. --- ## Task: 06 Config Jails Filters Actions — Config Jails Endpoint Lists Jail Configs **Test:** `Config Jails Endpoint Lists Jail Configs` **Suite:** `06_config_jails_filters_actions.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/06_config_jails_filters_actions.robot` - `backend/app/routers/jail_config.py` — jail configs endpoint **Reference Docs:** - `Docs/API-Reference.md` — Jail config endpoint **Expected Behavior:** `GET /api/v1/config/jails` returns a list of jail configuration files and their parsed settings. --- ## Task: 06 Config Jails Filters Actions — Config Filters Endpoint Lists Filter Configs **Test:** `Config Filters Endpoint Lists Filter Configs` **Suite:** `06_config_jails_filters_actions.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/06_config_jails_filters_actions.robot` - `backend/app/routers/jail_config.py` — filter configs endpoint **Reference Docs:** - `Docs/API-Reference.md` — Filter config endpoint **Expected Behavior:** `GET /api/v1/config/filters` returns a list of filter configuration files. --- ## Task: 06 Config Jails Filters Actions — Config Actions Endpoint Lists Action Configs **Test:** `Config Actions Endpoint Lists Action Configs` **Suite:** `06_config_jails_filters_actions.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/06_config_jails_filters_actions.robot` - `backend/app/routers/jail_config.py` — action configs endpoint **Reference Docs:** - `Docs/API-Reference.md` — Action config endpoint **Expected Behavior:** `GET /api/v1/config/actions` returns a list of action configuration files. --- ## Task: 06 Config Jails Filters Actions — Config Global Settings Endpoint **Test:** `Config Global Settings Endpoint` **Suite:** `06_config_jails_filters_actions.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/06_config_jails_filters_actions.robot` - `backend/app/routers/jail_config.py` — global settings endpoint **Reference Docs:** - `Docs/API-Reference.md` — Global settings endpoint **Expected Behavior:** `GET /api/v1/config/global` returns the global fail2ban configuration (e.g., `fail2ban.conf`). --- ## Task: 06 Config Jails Filters Actions — Config Service Status Endpoint **Test:** `Config Service Status Endpoint` **Suite:** `06_config_jails_filters_actions.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/06_config_jails_filters_actions.robot` - `backend/app/routers/config_misc.py` — service status endpoint **Reference Docs:** - `Docs/API-Reference.md` — Service status endpoint **Expected Behavior:** `GET /api/v1/config/service-status` returns fail2ban service status and log configuration. --- ## Task: 06 Config Jails Filters Actions — Config Security Headers Endpoint **Test:** `Config Security Headers Endpoint` **Suite:** `06_config_jails_filters_actions.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/06_config_jails_filters_actions.robot` - `backend/app/routers/config_misc.py` — security headers endpoint **Reference Docs:** - `Docs/Security.md` — Security headers **Expected Behavior:** An endpoint returns the currently configured security headers (CSP, X-Frame-Options, etc.). --- ## Task: 06 Config Jails Filters Actions — Config Inline Edit Round Trip For First Jail **Test:** `Config Inline Edit Round Trip For First Jail` **Suite:** `06_config_jails_filters_actions.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/06_config_jails_filters_actions.robot` - `frontend/src/pages/ConfigPage.tsx` - `backend/app/routers/jail_config.py` — update endpoint **Reference Docs:** - `e2e/Instructions.md` — "inline edit" - `Docs/Features.md` — Config inline editing **Expected Behavior:** Editing a jail config field inline, saving, and refreshing the page persists the change. --- ## Task: 06 Config Jails Filters Actions — Config Raw Section Lazy Load **Test:** `Config Raw Section Lazy Load` **Suite:** `06_config_jails_filters_actions.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/06_config_jails_filters_actions.robot` - `frontend/src/pages/ConfigPage.tsx` **Reference Docs:** - `e2e/Instructions.md` — "raw config" **Expected Behavior:** The raw config section is lazily loaded (not fetched until expanded) to improve initial page load performance. --- ## Task: 06 Config Jails Filters Actions — Config Raw Action File Endpoint **Test:** `Config Raw Action File Endpoint` **Suite:** `06_config_jails_filters_actions.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/06_config_jails_filters_actions.robot` - `backend/app/routers/jail_config.py` — raw file endpoint **Reference Docs:** - `Docs/API-Reference.md` — Raw config endpoint **Expected Behavior:** `GET /api/v1/config/actions/{name}/raw` returns the raw contents of an action config file. --- ## Task: 06 Config Jails Filters Actions — Config Jail Files Endpoint **Test:** `Config Jail Files Endpoint` **Suite:** `06_config_jails_filters_actions.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/06_config_jails_filters_actions.robot` - `backend/app/routers/jail_config.py` — jail files endpoint **Reference Docs:** - `Docs/API-Reference.md` — Jail files endpoint **Expected Behavior:** `GET /api/v1/config/jails/files` returns a list of available jail configuration files. --- ## Task: 06 Config Jails Filters Actions — Config Invalid Regex Returns 4xx **Test:** `Config Invalid Regex Returns 4xx` **Suite:** `06_config_jails_filters_actions.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/06_config_jails_filters_actions.robot` - `backend/app/routers/config_misc.py` — regex tester endpoint **Reference Docs:** - `Docs/API-Reference.md` — Regex tester validation **Expected Behavior:** Submitting an invalid regex pattern to the regex tester endpoint returns HTTP 400 (or other 4xx) with a descriptive error message. --- ## Task: 07 Config Log And Serversettings — Server Settings GET Returns Expected Keys **Test:** `Server Settings GET Returns Expected Keys` **Suite:** `07_config_log_and_serversettings.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/07_config_log_and_serversettings.robot` - `backend/app/routers/config_misc.py` — server settings endpoint **Reference Docs:** - `e2e/Instructions.md` — "Server settings" - `Docs/API-Reference.md` — Server settings endpoint **Expected Behavior:** `GET /api/v1/config/server` returns JSON with expected keys: `log_level`, `db_purge_age`, `max_matches`, etc. --- ## Task: 07 Config Log And Serversettings — Server Settings Update Log Level **Test:** `Server Settings Update Log Level` **Suite:** `07_config_log_and_serversettings.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/07_config_log_and_serversettings.robot` - `backend/app/routers/config_misc.py` — server settings update endpoint **Reference Docs:** - `Docs/API-Reference.md` — Server settings update **Expected Behavior:** `PUT /api/v1/config/server` with `{log_level: "DEBUG"}` updates the log level and returns the updated config. --- ## Task: 07 Config Log And Serversettings — Server Settings Reject Invalid Log Level **Test:** `Server Settings Reject Invalid Log Level` **Suite:** `07_config_log_and_serversettings.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/07_config_log_and_serversettings.robot` - `backend/app/routers/config_misc.py` — validation **Reference Docs:** - `Docs/API-Reference.md` — Server settings validation **Expected Behavior:** Submitting an invalid log level (e.g., `"INVALID"`) returns HTTP 400 with a validation error. --- ## Task: 07 Config Log And Serversettings — Server Settings Update DB Purge Age **Test:** `Server Settings Update DB Purge Age` **Suite:** `07_config_log_and_serversettings.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/07_config_log_and_serversettings.robot` - `backend/app/routers/config_misc.py` **Reference Docs:** - `Docs/API-Reference.md` — Server settings update **Expected Behavior:** Updating `db_purge_age` persists the new value and applies it to the archive cleanup schedule. --- ## Task: 07 Config Log And Serversettings — Server Settings Update Max Matches **Test:** `Server Settings Update Max Matches` **Suite:** `07_config_log_and_serversettings.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/07_config_log_and_serversettings.robot` - `backend/app/routers/config_misc.py` **Reference Docs:** - `Docs/API-Reference.md` — Server settings update **Expected Behavior:** Updating `max_matches` persists the new value for log parsing. --- ## Task: 07 Config Log And Serversettings — Server Settings Reject Path Outside Allowlist **Test:** `Server Settings Reject Path Outside Allowlist` **Suite:** `07_config_log_and_serversettings.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/07_config_log_and_serversettings.robot` - `backend/app/routers/config_misc.py` — path validation **Reference Docs:** - `Docs/Security.md` — Path traversal prevention **Expected Behavior:** Submitting a log file path outside the allowed directory (e.g., `"/etc/passwd"`) returns HTTP 400. --- ## Task: 07 Config Log And Serversettings — Server Settings Accept Stdout Special Target **Test:** `Server Settings Accept Stdout Special Target` **Suite:** `07_config_log_and_serversettings.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/07_config_log_and_serversettings.robot` - `backend/app/routers/config_misc.py` **Reference Docs:** - `Docs/API-Reference.md` — Server settings special targets **Expected Behavior:** `"stdout"` is accepted as a special log target value. --- ## Task: 07 Config Log And Serversettings — Server Settings Accept Syslog Special Target **Test:** `Server Settings Accept Syslog Special Target` **Suite:** `07_config_log_and_serversettings.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/07_config_log_and_serversettings.robot` - `backend/app/routers/config_misc.py` **Reference Docs:** - `Docs/API-Reference.md` — Server settings special targets **Expected Behavior:** `"syslog"` is accepted as a special log target value. --- ## Task: 07 Config Log And Serversettings — Server Settings Accept Safe File Path **Test:** `Server Settings Accept Safe File Path` **Suite:** `07_config_log_and_serversettings.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/07_config_log_and_serversettings.robot` - `backend/app/routers/config_misc.py` **Reference Docs:** - `Docs/API-Reference.md` — Server settings path validation **Expected Behavior:** A safe, allowlisted file path (e.g., `"/var/log/auth.log"`) is accepted. --- ## Task: 07 Config Log And Serversettings — Flush Logs Endpoint Works **Test:** `Flush Logs Endpoint Works` **Suite:** `07_config_log_and_serversettings.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/07_config_log_and_serversettings.robot` - `backend/app/routers/config_misc.py` — flush logs endpoint **Reference Docs:** - `Docs/API-Reference.md` — Log management endpoints **Expected Behavior:** `POST /api/v1/config/logs/flush` flushes buffered logs and returns success. --- ## Task: 07 Config Log And Serversettings — Log Preview Endpoint Returns Content **Test:** `Log Preview Endpoint Returns Content` **Suite:** `07_config_log_and_serversettings.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/07_config_log_and_serversettings.robot` - `backend/app/routers/config_misc.py` — log preview endpoint **Reference Docs:** - `Docs/API-Reference.md` — Log preview endpoint **Expected Behavior:** `GET /api/v1/config/logs/preview` returns the most recent log lines as text or JSON. --- ## Task: 07 Config Log And Serversettings — Log Endpoint Returns Content Or 404 **Test:** `Log Endpoint Returns Content Or 404` **Suite:** `07_config_log_and_serversettings.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/07_config_log_and_serversettings.robot` - `backend/app/routers/config_misc.py` — log endpoint **Reference Docs:** - `Docs/API-Reference.md` — Log endpoint **Expected Behavior:** `GET /api/v1/config/logs` returns log content if available, or HTTP 404 if no log file is configured/found. --- ## Task: 07 Config Log And Serversettings — Log Observation Add Rejects Path Outside Allowlist **Test:** `Log Observation Add Rejects Path Outside Allowlist` **Suite:** `07_config_log_and_serversettings.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/07_config_log_and_serversettings.robot` - `backend/app/routers/config_misc.py` — log observation endpoint **Reference Docs:** - `Docs/Security.md` — Path traversal prevention - `e2e/Instructions.md` — "log observation allowlist" **Expected Behavior:** Adding a log observation path outside the allowlist returns HTTP 400 with a security error. --- ## Task: 07 Config Log And Serversettings — Log Observation Add Endpoint Exists **Test:** `Log Observation Add Endpoint Exists` **Suite:** `07_config_log_and_serversettings.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/07_config_log_and_serversettings.robot` - `backend/app/routers/config_misc.py` **Reference Docs:** - `Docs/API-Reference.md` — Log observation endpoints **Expected Behavior:** `POST /api/v1/config/logs/observation` exists and accepts `{path: "/var/log/auth.log"}`. --- ## Task: 08 History — History Page Renders **Test:** `History Page Renders` **Suite:** `08_history.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/08_history.robot` - `frontend/src/pages/HistoryPage.tsx` **Reference Docs:** - `e2e/Instructions.md` — "Ban History — table, filters, per-IP timeline" - `Docs/Features.md` — History view **Expected Behavior:** The history page renders with a table, filters, and pagination controls. --- ## Task: 08 History — History Page Shows Archive Source Badge By Default **Test:** `History Page Shows Archive Source Badge By Default` **Suite:** `08_history.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/08_history.robot` - `frontend/src/pages/HistoryPage.tsx` **Reference Docs:** - `e2e/Instructions.md` — "archive vs fail2ban source" **Expected Behavior:** The history page shows an "Archive" data source badge by default (since history comes from the SQLite archive, not live fail2ban). --- ## Task: 08 History — History Page Default 7d Range **Test:** `History Page Default 7d Range` **Suite:** `08_history.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/08_history.robot` - `frontend/src/pages/HistoryPage.tsx` **Reference Docs:** - `e2e/Instructions.md` — History time range defaults **Expected Behavior:** The history page defaults to showing the last 7 days of ban history. --- ## Task: 08 History — History Endpoint Returns Paginated Data **Test:** `History Endpoint Returns Paginated Data` **Suite:** `08_history.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/08_history.robot` - `backend/app/routers/history.py` — history endpoint **Reference Docs:** - `Docs/API-Reference.md` — History endpoint **Expected Behavior:** `GET /api/v1/history?page=1&page_size=50` returns paginated ban history with `items`, `total`, `page`, `page_size`. --- ## Task: 08 History — History Archive Endpoint Returns Data **Test:** `History Archive Endpoint Returns Data` **Suite:** `08_history.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/08_history.robot` - `backend/app/routers/history.py` — archive endpoint **Reference Docs:** - `Docs/API-Reference.md` — History archive endpoint **Expected Behavior:** `GET /api/v1/history/archive` returns archived ban records. --- ## Task: 08 History — History Per IP Endpoint Returns Data **Test:** `History Per IP Endpoint Returns Data` **Suite:** `08_history.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/08_history.robot` - `backend/app/routers/history.py` — per-IP endpoint **Reference Docs:** - `Docs/API-Reference.md` — Per-IP history endpoint **Expected Behavior:** `GET /api/v1/history/ip/{ip}` returns the ban timeline for a specific IP address. --- ## Task: 08 History — History Filter By Jail Returns Data **Test:** `History Filter By Jail Returns Data` **Suite:** `08_history.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/08_history.robot` - `backend/app/routers/history.py` **Reference Docs:** - `Docs/API-Reference.md` — History filtering **Expected Behavior:** `GET /api/v1/history?jail=ssh` returns only bans from the specified jail. --- ## Task: 08 History — History Filter By Source Fail2ban **Test:** `History Filter By Source Fail2ban` **Suite:** `08_history.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/08_history.robot` - `backend/app/routers/history.py` **Reference Docs:** - `Docs/API-Reference.md` — History source filtering **Expected Behavior:** Filtering by `source=fail2ban` returns only live fail2ban-sourced records. --- ## Task: 08 History — History Filter By Source Archive **Test:** `History Filter By Source Archive` **Suite:** `08_history.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/08_history.robot` - `backend/app/routers/history.py` **Reference Docs:** - `Docs/API-Reference.md` — History source filtering **Expected Behavior:** Filtering by `source=archive` returns only archived records. --- ## Task: 08 History — History URL Params Honored **Test:** `History URL Params Honored` **Suite:** `08_history.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/08_history.robot` - `frontend/src/pages/HistoryPage.tsx` — URL param parsing **Reference Docs:** - `Docs/Features.md` — Deep-linking with URL params **Expected Behavior:** Navigating to `/history?range=30d&jail=ssh` pre-selects the 30-day range and ssh jail filter. --- ## Task: 09 Blocklists — Blocklists Page Renders **Test:** `Blocklists Page Renders` **Suite:** `09_blocklists.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/09_blocklists.robot` - `frontend/src/pages/BlocklistsPage.tsx` **Reference Docs:** - `e2e/Instructions.md` — "External Blocklist Importer — CRUD" - `Docs/Features.md` — Blocklist management **Expected Behavior:** The blocklists page renders with a table of configured sources, import controls, and schedule settings. --- ## Task: 09 Blocklists — Blocklists Sources List Endpoint **Test:** `Blocklists Sources List Endpoint` **Suite:** `09_blocklists.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/09_blocklists.robot` - `backend/app/routers/blocklists.py` — sources list endpoint **Reference Docs:** - `Docs/API-Reference.md` — Blocklist sources endpoint **Expected Behavior:** `GET /api/v1/blocklists` returns a list of configured blocklist sources. --- ## Task: 09 Blocklists — Blocklist Source Create Rejects Invalid Scheme **Test:** `Blocklist Source Create Rejects Invalid Scheme` **Suite:** `09_blocklists.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/09_blocklists.robot` - `backend/app/routers/blocklists.py` — create endpoint validation **Reference Docs:** - `Docs/Security.md` — SSRF prevention - `e2e/Instructions.md` — "SSRF validation" **Expected Behavior:** Creating a blocklist source with an invalid URL scheme (e.g., `ftp://`, `file://`) returns HTTP 400. --- ## Task: 09 Blocklists — Blocklist Source Create Rejects Loopback URL **Test:** `Blocklist Source Create Rejects Loopback URL` **Suite:** `09_blocklists.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/09_blocklists.robot` - `backend/app/routers/blocklists.py` — SSRF validation **Reference Docs:** - `Docs/Security.md` — SSRF prevention **Expected Behavior:** Creating a blocklist source with a loopback URL (`http://127.0.0.1/...`, `http://localhost/...`) returns HTTP 400. --- ## Task: 09 Blocklists — Blocklist Source Create Rejects Private IP URL **Test:** `Blocklist Source Create Rejects Private IP URL` **Suite:** `09_blocklists.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/09_blocklists.robot` - `backend/app/routers/blocklists.py` — SSRF validation **Reference Docs:** - `Docs/Security.md` — SSRF prevention **Expected Behavior:** Creating a blocklist source with a private IP URL (`http://192.168.1.1/...`, `http://10.0.0.1/...`) returns HTTP 400. --- ## Task: 09 Blocklists — Blocklist Source Create Rejects Link Local URL **Test:** `Blocklist Source Create Rejects Link Local URL` **Suite:** `09_blocklists.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/09_blocklists.robot` - `backend/app/routers/blocklists.py` — SSRF validation **Reference Docs:** - `Docs/Security.md` — SSRF prevention **Expected Behavior:** Creating a blocklist source with a link-local URL (`http://169.254.1.1/...`) returns HTTP 400. --- ## Task: 09 Blocklists — Blocklist Schedule Endpoint Returns Config **Test:** `Blocklist Schedule Endpoint Returns Config` **Suite:** `09_blocklists.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/09_blocklists.robot` - `backend/app/routers/blocklists.py` — schedule endpoint **Reference Docs:** - `Docs/API-Reference.md` — Blocklist schedule endpoint **Expected Behavior:** `GET /api/v1/blocklists/schedule` returns the current import schedule configuration (cron expression, enabled status). --- ## Task: 09 Blocklists — Blocklist Schedule Update Works **Test:** `Blocklist Schedule Update Works` **Suite:** `09_blocklists.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/09_blocklists.robot` - `backend/app/routers/blocklists.py` — schedule update endpoint **Reference Docs:** - `Docs/API-Reference.md` — Blocklist schedule update **Expected Behavior:** `PUT /api/v1/blocklists/schedule` updates the import schedule and returns the updated config. --- ## Task: 09 Blocklists — Blocklist Manual Import Endpoint Reachable **Test:** `Blocklist Manual Import Endpoint Reachable` **Suite:** `09_blocklists.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/09_blocklists.robot` - `backend/app/routers/blocklists.py` — manual import endpoint **Reference Docs:** - `Docs/API-Reference.md` — Blocklist manual import **Expected Behavior:** `POST /api/v1/blocklists/import` triggers a manual import and returns a job ID or success status. --- ## Task: 09 Blocklists — Blocklist Import Log Endpoint Returns Paginated Data **Test:** `Blocklist Import Log Endpoint Returns Paginated Data` **Suite:** `09_blocklists.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/09_blocklists.robot` - `backend/app/routers/blocklists.py` — import log endpoint **Reference Docs:** - `Docs/API-Reference.md` — Blocklist import log **Expected Behavior:** `GET /api/v1/blocklists/log` returns paginated import log entries with timestamps, source, status, and error counts. --- ## Task: 09 Blocklists — Blocklist Delete Non Existent Returns 404 **Test:** `Blocklist Delete Non Existent Returns 404` **Suite:** `09_blocklists.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/09_blocklists.robot` - `backend/app/routers/blocklists.py` — delete endpoint **Reference Docs:** - `Docs/API-Reference.md` — Blocklist delete endpoint **Expected Behavior:** `DELETE /api/v1/blocklists/{id}` for a non-existent source returns HTTP 404. --- ## Task: 09 Blocklists — Blocklist Create And Delete Cycle **Test:** `Blocklist Create And Delete Cycle` **Suite:** `09_blocklists.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/09_blocklists.robot` - `backend/app/routers/blocklists.py` — create and delete endpoints **Reference Docs:** - `e2e/Instructions.md` — "CRUD" **Expected Behavior:** Creating a valid blocklist source, verifying it appears in the list, and then deleting it removes it from the list. --- ## Task: 10 General Layout — Sidebar Is Visible On Dashboard **Test:** `Sidebar Is Visible On Dashboard` **Suite:** `10_general_layout.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/10_general_layout.robot` - `frontend/src/components/Sidebar.tsx` (or `Layout.tsx`) **Reference Docs:** - `e2e/Instructions.md` — "sidebar nav" - `Docs/Features.md` — Layout **Expected Behavior:** After login, the sidebar navigation is visible on the dashboard page. --- ## Task: 10 General Layout — Sidebar Lists All Required Pages **Test:** `Sidebar Lists All Required Pages` **Suite:** `10_general_layout.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/10_general_layout.robot` - `frontend/src/components/Sidebar.tsx` **Reference Docs:** - `e2e/Instructions.md` — "sidebar nav" **Expected Behavior:** The sidebar contains links to: Dashboard, World Map, Jails, History, Blocklists, Configuration. --- ## Task: 10 General Layout — Sidebar Sign Out Logs User Out **Test:** `Sidebar Sign Out Logs User Out` **Suite:** `10_general_layout.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/10_general_layout.robot` - `frontend/src/components/Sidebar.tsx` - `backend/app/routers/auth.py` — logout endpoint **Reference Docs:** - `e2e/Instructions.md` — "Sign Out button" **Expected Behavior:** Clicking "Sign Out" in the sidebar clears the session and redirects to `/login`. --- ## Task: 10 General Layout — Theme Toggle Is Present In Sidebar **Test:** `Theme Toggle Is Present In Sidebar` **Suite:** `10_general_layout.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/10_general_layout.robot` - `frontend/src/components/Sidebar.tsx` - `frontend/src/hooks/useTheme.ts` (or equivalent) **Reference Docs:** - `e2e/Instructions.md` — "theme toggle" **Expected Behavior:** The sidebar contains a theme toggle button (light/dark mode). --- ## Task: 10 General Layout — Active Page Highlighted In Sidebar **Test:** `Active Page Highlighted In Sidebar` **Suite:** `10_general_layout.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/10_general_layout.robot` - `frontend/src/components/Sidebar.tsx` **Reference Docs:** - `e2e/Instructions.md` — "active link highlighting" **Expected Behavior:** The current page's sidebar link is visually highlighted (different background/text color). --- ## Task: 10 General Layout — Session Persists Across Page Reload **Test:** `Session Persists Across Page Reload` **Suite:** `10_general_layout.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/10_general_layout.robot` - `frontend/src/hooks/useSession.ts` - `backend/app/routers/auth.py` — session validation **Reference Docs:** - `e2e/Instructions.md` — "session persistence" **Expected Behavior:** Reloading the page while logged in does NOT redirect to login; the session cookie is still valid. --- ## Task: 10 General Layout — Theme Toggle Changes Color Mode **Test:** `Theme Toggle Changes Color Mode` **Suite:** `10_general_layout.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/10_general_layout.robot` - `frontend/src/components/Sidebar.tsx` - `frontend/src/hooks/useTheme.ts` **Reference Docs:** - `e2e/Instructions.md` — "theme toggle" **Expected Behavior:** Clicking the theme toggle switches the UI between light and dark mode, and the preference persists in `localStorage`. --- ## Task: 10 General Layout — Health Endpoint Returns Component Status **Test:** `Health Endpoint Returns Component Status` **Suite:** `10_general_layout.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/10_general_layout.robot` - `backend/app/routers/health.py` — health endpoint **Reference Docs:** - `e2e/Instructions.md` — "health endpoints" - `Docs/API-Reference.md` — Health endpoint **Expected Behavior:** `GET /api/v1/health` returns JSON with component statuses: `database`, `fail2ban`, `scheduler`, `cache`, etc. --- ## Task: 10 General Layout — Liveness Endpoint Returns 200 **Test:** `Liveness Endpoint Returns 200` **Suite:** `10_general_layout.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/10_general_layout.robot` - `backend/app/routers/health.py` — liveness endpoint **Reference Docs:** - `Docs/API-Reference.md` — Liveness probe - `Docs/Deployment.md` — Kubernetes probes **Expected Behavior:** `GET /api/v1/health/live` always returns HTTP 200 (even if fail2ban is down), indicating the Python process is alive. --- ## Task: 10 General Layout — Metrics Endpoint Returns Prometheus Text **Test:** `Metrics Endpoint Returns Prometheus Text` **Suite:** `10_general_layout.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/10_general_layout.robot` - `backend/app/routers/metrics.py` (or equivalent) **Reference Docs:** - `Docs/Observability.md` — Prometheus metrics - `Docs/API-Reference.md` — Metrics endpoint **Expected Behavior:** `GET /api/v1/metrics` returns Prometheus-formatted text with application metrics (request counts, ban counts, etc.). --- ## Task: 10 General Layout — Setup Timezone Endpoint Returns IANA String **Test:** `Setup Timezone Endpoint Returns IANA String` **Suite:** `10_general_layout.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/10_general_layout.robot` - `backend/app/routers/setup.py` — timezone endpoint **Reference Docs:** - `Docs/API-Reference.md` — Setup timezone endpoint **Expected Behavior:** `GET /api/v1/setup/timezone` returns a valid IANA timezone string (e.g., `"UTC"`, `"Europe/Berlin"`). --- ## Task: 10 General Layout — Setup Status Endpoint Returns Completed Flag **Test:** `Setup Status Endpoint Returns Completed Flag` **Suite:** `10_general_layout.robot` **Step That Fails:** Parent suite setup (`Wait For Backend Health`). **Files to Check:** - `e2e/tests/10_general_layout.robot` - `backend/app/routers/setup.py` — setup status endpoint **Reference Docs:** - `Docs/API-Reference.md` — Setup status endpoint **Expected Behavior:** `GET /api/v1/setup` returns `{completed: true|false}` indicating whether initial setup has been completed.