# E2E Tests — Running Robot Framework Tests ## Test File Structure The E2E suite is organized **one `.robot` file per feature area** defined in `Docs/Features.md`. Each file is independently runnable. | File | Feature | |---|---| | `01_setup_and_auth.robot` | Setup wizard (formerly `05_setup.robot`) — form fields, password strength, validation, full submit | | `02_login.robot` | Login page — wrong password, rate limit (429), session validation 401, logout | | `03_dashboard.robot` | Ban Overview (Dashboard) — status bar, time-range presets, data-source badges, API endpoints | | `04_map.robot` | World Map View — country fills, click-to-filter, zoom controls, sticky table header/footer | | `05_jails.robot` | Jail Management — list, ban/unban API, IP lookup, ignore list, jail controls | | `06_config_jails_filters_actions.robot` | Configuration View — Jails/Filters/Actions tabs, inline edit, raw config, regex tester | | `07_config_log_and_serversettings.robot` | Server settings + log viewer + log observation allowlist | | `08_history.robot` | Ban History — table, filters, per-IP timeline, archive vs fail2ban source | | `09_blocklists.robot` | External Blocklist Importer — CRUD, SSRF validation, schedule, import log, delete restriction | | `10_general_layout.robot` | General UI/layout — sidebar nav, theme toggle, session persistence, health endpoints | | `02_ban_records.robot` | (pre-existing) end-to-end ban pipeline: fail2ban log → history | | `03_blocklist_import.robot` | (pre-existing) blocklist manual import via UI | | `04_config_edit.robot` | (pre-existing) config field auto-save round trip | ## Resource Files Shared keywords live in `resources/`: | File | Purpose | |---|---| | `common.resource` | `Wait For Backend Health`, `Wait For Frontend`, `Page Should Contain` wrapper, `XFF` helpers, IP/jail name generators | | `auth.resource` | `Login As Admin`, `Login Via HTTP`, `Logout`, `Verify Session Invalid`, `Login With Wrong Password`, `Login Exceeds Rate Limit` | | `api.resource` | `Api Get/Post/Put/Delete` wrappers that auto-inject CSRF + X-Forwarded-For headers | | `data.resource` | Unique IP / jail name / blocklist name generators (RFC5737 ranges) | ## Setup ```bash pip install -r requirements.txt rfbrowser init ``` ## Run All Tests ```bash robot --outputdir results --log log.html --report report.html tests/ ``` Or via the Makefile from the repo root: ```bash make e2e ``` ## Run Specific Test File ```bash robot --outputdir results tests/02_login.robot robot --outputdir results tests/08_history.robot ``` ## Run with Browser Visible ```bash robot --outputdir results --variable BROWSER:chromium tests/ ``` ## Rate-Limit Workaround BanGUI rate-limits several endpoints per source IP: | Bucket | Default Limit | Window | |---|---|---| | `POST /api/v1/auth/login` | 5 / IP | 60 s | | `POST /api/v1/blocklists/import` | 10 / IP | 3600 s | | `POST /api/v1/bans` | 10 000 / IP | 60 s | | `PUT /api/v1/config/jails/{name}` | 10 000 / IP | 60 s | Tests bypass these by sending a fresh `X-Forwarded-For: 192.0.2.` header per test. The `Set Random Xff Header` keyword in `common.resource` rotates the IP. The `auth.resource` `Login Via HTTP` and the `api.resource` `Api Get/Post/Put/Delete` wrappers all accept and propagate `${XFF_HEADER}` automatically. ## Test-IP Convention All test data uses RFC5737 documentation-only ranges to avoid colliding with real internet addresses: | Range | Purpose | |---|---| | `192.0.2.0/24` (TEST-NET-1) | X-Forwarded-For headers | | `198.51.100.0/24` (TEST-NET-2) | Geo-lookup test IPs | | `203.0.113.0/24` (TEST-NET-3) | Ban / unban test IPs | ## View Results Open `results/log.html` or `results/report.html` in a browser. ## Failure Protocol Per project policy, **test failures are NOT fixed by editing app code**. If a test fails: 1. Stop. 2. Report the failure with: test name, expected vs actual, log excerpt, API request/response. 3. Do not edit the test to weaken assertions. 4. Do not edit frontend / backend / fail2ban config to make the test pass. 5. The failure is a finding — separate from any bug-fix task. --- # AI Agent — General Instructions You are an autonomous coding agent working on **BanGUI**, a web application for monitoring, managing, and configuring fail2ban through a clean web interface. This document defines how you operate, what rules you follow, and which workflow you repeat for every task. Read this document completely before starting any work. --- ## 1. Project Context BanGUI consists of two main parts: | Layer | Stack | Docs | |---|---|---| | **Backend** | Python 3.12+, FastAPI, Pydantic v2, aiosqlite, structlog | [Backend-Development.md](Backend-Development.md) | | **Frontend** | TypeScript, React, Fluent UI v9, Vite | [Web-Development.md](Web-Development.md) | Supporting documentation you must know and respect: | Document | Purpose | |---|---| | [Features.md](Features.md) | Complete feature list and expected behaviour | | [Architekture.md](Architekture.md) | System architecture, component relationships, data flow | | [Web-Design.md](Web-Design.md) | Visual design rules, theming, layout, spacing, motion | | [Backend-Development.md](Backend-Development.md) | Backend coding rules, project structure, conventions | | [Web-Development.md](Web-Development.md) | Frontend coding rules, project structure, conventions | **Always** consult the relevant document before writing code. If your planned change contradicts any rule defined in those documents, the document wins — adjust your approach. --- ## 2. General Rules ### 2.1 Follow the Docs - Every coding convention, naming rule, project structure decision, and library choice is defined in the development docs. Do not deviate. - Backend code follows [Backend-Development.md](Backend-Development.md) — strict typing, async only, structlog, Pydantic models, layered architecture (routers → services → repositories). - Frontend code follows [Web-Development.md](Web-Development.md) — strict TypeScript, Fluent UI v9 only, `makeStyles` for styling, typed API calls, hooks for state. - Visual decisions follow [Web-Design.md](Web-Design.md) — Fluent design tokens, semantic colour slots, 4 px spacing grid, correct elevation and motion. ### 2.2 Write Production-Quality Code - Write code as if it ships today. No TODOs, no placeholders, no half-implementations. - Every function has explicit type annotations (Python) or type signatures (TypeScript). - Every public function has a docstring (Python — Google style) or JSDoc comment (TypeScript). - No `any` in TypeScript. No `Any` in Python (unless justified with a comment). - No magic numbers or strings — use named constants. - No dead code, no commented-out blocks, no unused imports. ### 2.3 Keep It Small and Focused - One function does one thing. - One component per file. - One service per domain. - If a file grows beyond ~150 lines (components) or ~200 lines (services), split it. ### 2.4 Never Break Existing Code - Before changing any file, understand what it does and who depends on it. - Run the existing test suite before and after your changes. If tests fail after your change, fix them before moving on. - Do not remove or rename public APIs without updating all callers. ### 2.5 Think Before You Code - Read the task description carefully. If it is ambiguous, check [Features.md](Features.md) and [Architekture.md](Architekture.md) for clarification. - Plan your changes before writing code. Identify which files are affected, which layers are involved, and what tests are needed. - Prefer the simplest correct solution. Do not over-engineer. --- ## 3. Task Workflow Repeat the following cycle for every task. Do not skip steps. ### Step 1 — Plan Your Steps - Break the task into concrete implementation steps. - Identify which files need to be created, modified, or deleted. - Identify which layers are affected (router, service, repository, model, component, hook, page, type, etc.). - Identify edge cases and error scenarios. - Write down your plan before touching any code. ### Step 2 — Write Code - Implement the feature or fix following the plan. - Follow all rules from the relevant development docs: - Backend → [Backend-Development.md](Backend-Development.md) - Frontend → [Web-Development.md](Web-Development.md) - Design → [Web-Design.md](Web-Design.md) - Architecture → [Architekture.md](Architekture.md) - Write clean, well-structured, fully typed code. - Keep commits atomic — one logical change per commit. ### Step 3 — Add Logging - Add structured log statements at key points in new or modified code. - Backend: use **structlog** with contextual key-value pairs — never `print()`. - Log at appropriate levels: `info` for operational events, `warning` for recoverable issues, `error` for failures. - Never log sensitive data (passwords, tokens, session IDs). ### Step 4 — Write Tests - Write tests for every new or changed piece of functionality. - Backend: use `pytest` + `pytest-asyncio` + `httpx.AsyncClient`. See [Backend-Development.md § 9](Backend-Development.md). - Frontend: test components and hooks according to the frontend test setup. - Test the happy path **and** error/edge cases. - Mock external dependencies — tests must never touch real infrastructure. - Follow the naming pattern: `test___`. ### Step 5 — Review Your Code Run a thorough self-review before considering the task done. Check **all** of the following: #### 5.1 — Warnings and Errors - Backend: run `ruff check` and `mypy --strict` (or `pyright --strict`). Fix every warning and error. - Frontend: run `tsc --noEmit` and `eslint`. Fix every warning and error. - Zero warnings, zero errors — no exceptions. #### 5.2 — Test Coverage - Run the test suite with coverage enabled. - Aim for **>80 % line coverage** overall. - Critical paths (auth, banning, scheduling, API endpoints) must be **100 %** covered. - If coverage is below the threshold, write additional tests before proceeding. #### 5.3 — Coding Principles Verify your code against the coding principles defined in [Backend-Development.md § 13](Backend-Development.md) and [Web-Development.md](Web-Development.md): - [ ] **Clean Code** — Meaningful names, small functions, no magic values, guard clauses over deep nesting. - [ ] **Separation of Concerns** — Each module has a single, well-defined responsibility. Layers are not mixed. - [ ] **Single Responsibility Principle** — Every class and function has one reason to change. - [ ] **DRY** — No duplicated logic. Shared behaviour is extracted. - [ ] **KISS** — The simplest correct solution is used. No over-engineering. - [ ] **Type Safety** — All types are explicit. No `any` / `Any`. No `# type: ignore` without justification. #### 5.4 — Architecture Compliance Verify against [Architekture.md](Architekture.md) and the project structure rules: - [ ] Files are in the correct directories (routers in `routers/`, services in `services/`, components in `components/`, etc.). - [ ] Dependencies flow in the right direction (routers → services → repositories; pages → components → hooks). - [ ] No circular imports. - [ ] No business logic in routers or components. - [ ] No HTTP/framework concerns in services or repositories. - [ ] Pydantic models separate request, response, and domain shapes. - [ ] Frontend types live in `types/`, not scattered across components. ### Step 6 — Update Documentation - If your change introduces new features, new endpoints, new components, or changes existing behaviour, update the relevant docs: - [Features.md](Features.md) — if feature behaviour changed. - [Architekture.md](Architekture.md) — if new modules, services, or data flows were added. - [Backend-Development.md](Backend-Development.md) or [Web-Development.md](Web-Development.md) — if new conventions were established. - Keep documentation accurate and in sync with the code. Outdated docs are worse than no docs. ## 5. When You Are Stuck - Re-read the task description and the relevant docs. - Search the existing codebase for similar patterns — follow established conventions. - Check the fail2ban source code in `fail2ban-master/` if you need to understand how fail2ban works internally. - If a decision is genuinely ambiguous and no document covers it, choose the simplest option that is consistent with existing code and document your reasoning in a code comment. --- ## 6. What You Must Never Do - **Never** commit code that does not compile or has type errors. - **Never** commit code without tests. - **Never** use libraries that are explicitly forbidden in the development docs. - **Never** bypass the linter or type checker with blanket ignores. - **Never** hard-code secrets, passwords, or tokens. - **Never** push directly to `main` — always use feature branches. - **Never** skip the review step — sloppy code compounds over time. - **Never** leave a task half-done — finish it or revert it. --- ## 7. First-Run Setup ### Initialize the Development Environment Before starting the stack for the first time, set up the required environment variables: 1. **Copy the example environment file:** ```bash cp .env.example .env ``` 2. **Generate a session secret:** ```bash python -c 'import secrets; print(secrets.token_hex(32))' ``` Copy the output and paste it as the value for `BANGUI_SESSION_SECRET` in your `.env` file. 3. **Optional: Customize other settings** - Edit `.env` to adjust timezone, port numbers, or other settings - Default values are sensible for development (UTC, ports 8000/5173) 4. **Start the stack:** ```bash make up ``` **Note:** The session secret is critical for security. Do not commit `.env` to version control — it is already in `.gitignore`. Each environment (dev, staging, production) must have its own unique secret. --- ## 8. Dev Quick-Reference ### Start / stop the stack ```bash make up # start all containers (from repo root) make down # stop all containers make logs # tail logs ``` Backend: `http://127.0.0.1:8000` · Frontend (Vite proxy): `http://127.0.0.1:5173` ### API login (dev) The backend accepts **plaintext** passwords (no frontend hashing). The session cookie is named `bangui_session`. ```bash # Dev master password: Hallo123! TOKEN=$(curl -s -X POST http://127.0.0.1:8000/api/v1/auth/login \ -H 'Content-Type: application/json' \ -d '{"password":"Hallo123!"}' \ | python3 -c 'import sys,json; print(json.load(sys.stdin)["expires_at"])') # Login sets bangui_session cookie automatically. # Use the cookie in subsequent requests: curl -b "bangui_session=$(cat ~/.bangui_session)" http://127.0.0.1:8000/api/v1/dashboard/status ``` --- ## E2E Testing Notes Debugging failures: open `results/log.html` (not output.xml) for full request/response traces. ### Auth (RequestsLibrary vs Browser) `Login As Admin` uses browser JavaScript (`Evaluate JavaScript`) — session cookie lives in the **browser context only**. Any `RequestsLibrary` keyword (GET/POST to API) in the same test needs its own auth session. Use `Login Via HTTP` from `auth.resource` to get a session cookie for RequestsLibrary calls. RequestsLibrary and Browser library share no state. ### CSRF Protection Backend enforces CSRF on all POST/PUT/DELETE via `X-BanGUI-Request: 1` header. RequestsLibrary sessions must include this header on creation: ``` Create Session bangsess ${BACKEND_URL} headers=${headers} # headers = {X-BanGUI-Request: 1} ``` Then all requests on that session inherit it. ### Robot Variable Type Rules Bare values in `Create Dictionary` are **strings**, not Python types: - `enabled=true` → `"true"` (string) — backend Pydantic expects boolean → validation fails - Fix: `enabled=${TRUE}` for booleans, `${60}` for integers - `${NONE}` is Robot's null/None equivalent - `${len(sources)}` is invalid — use `Get Length ${sources}` keyword instead ### Network Mode (podman-compose) With `network_mode: host`, containers share the host network namespace. - Backend can reach host's `127.0.0.1:PORT` — needed for mock HTTP servers - Frontend must set `VITE_BACKEND_URL=http://localhost:8000` since `localhost` now resolves to host - Vite proxy works when frontend also uses `network_mode: host` (same namespace) ### SSRF Protection Blocklist source URLs are validated: hostname must resolve to a **public** IP. - `127.0.0.1` and other loopback addresses are rejected - In dev mode (`BANGUI_LOG_LEVEL=debug`), loopback is allowed for e2e testing - For real e2e tests, use a public blocklist URL or ensure mock server is reachable ### API Response Keys Always verify response shape in `results/log.html` — field names differ from expectations: - Blocklist import log: `items` not `entries` - Error responses: `{"code": "...", "detail": "..."}` ### Process Library Teardown `Terminate Process` fails if the process alias doesn't exist (mock server not started). Wrap in `Run Keyword And Return Status` to avoid teardown cascading failures.