refactoring-backend #3

Merged
lukas.pupkalipinski merged 403 commits from refactoring-backend into main 2026-05-20 20:23:46 +02:00
11 changed files with 161 additions and 2 deletions
Showing only changes of commit 23c3a0d9e6 - Show all commits

3
.gitignore vendored
View File

@@ -112,3 +112,6 @@ Docker/fail2ban-dev-config/**
*.tmp
*.bak
*.orig
# ── E2E test results ───────────────────────────
e2e/results/

View File

@@ -3010,6 +3010,7 @@ atomic_write(path, updated_content) # Atomic write, auto-cleanup on error
- Every merge request must pass: ruff, type checker, all tests.
- Do not merge with failing CI.
- Keep pull requests small and focused — one feature or fix per PR.
- **E2E test results** (`e2e/results/`) are gitignored — never commit test outputs or HTML reports.
---

View File

@@ -36,4 +36,27 @@ Requires codecov.io integration with repository.
- Follow pattern: `test_<unit>_<scenario>_<expected>`
- Mock external dependencies (fail2ban socket, aiohttp calls)
- Test happy path AND error/edge cases
- See `Docs/Backend-Development.md §9` for detailed testing guide
- See `Docs/Backend-Development.md §9` for detailed testing guide
## E2E Testing
An end-to-end test suite using **Robot Framework** with the Browser library (Playwright-backed) exercises the full running stack: frontend → backend → fail2ban → database.
### Running E2E Tests
```bash
make e2e
```
Requires:
- `BANGUI_SESSION_SECRET` env var must be set (see [Backend-Development.md](Backend-Development.md) for setup)
- Stack must be startable via `make up` (Docker/Podman + compose installed)
- `rfbrowser init` is run automatically by the `e2e` target (Playwright browsers downloaded on first run; re-run after `robotframework-browser` version changes)
### HTML Report
After a run, open `e2e/results/report.html` in a browser to view the detailed HTML report with screenshots on failure.
### Writing New E2E Tests
Place new `.robot` files in `e2e/tests/`. Use `e2e/resources/common.resource` for shared variables and setup/teardown, and `e2e/resources/auth.resource` for the `Login As Admin` keyword.

View File

@@ -12,6 +12,7 @@
# make logs — tail logs for all debug services
# make restart — restart the debug stack
# make dev-ban-test — one-command smoke test of the ban pipeline
# make e2e — run the Robot Framework E2E test suite
# ──────────────────────────────────────────────────────────────
COMPOSE_FILE := Docker/compose.debug.yml
@@ -39,7 +40,7 @@ COMPOSE := $(shell command -v podman-compose 2>/dev/null \
# Detect available container runtime (podman or docker).
RUNTIME := $(shell command -v podman 2>/dev/null || echo "docker")
.PHONY: up down build restart logs clean dev-ban-test
.PHONY: up down build restart logs clean dev-ban-test e2e
## Start the debug stack (detached).
## Ensures log stub files exist so fail2ban can open them on first start.
@@ -71,6 +72,20 @@ clean:
$(RUNTIME) rmi $(DEV_IMAGES) 2>/dev/null || true
@echo "All debug volumes and local images removed. Run 'make up' to rebuild and start fresh."
## Run the Robot Framework E2E test suite.
## Requires: stack up (make up), BANGUI_SESSION_SECRET env var set.
## Installs: pip install -r e2e/requirements.txt && rfbrowser init
e2e: up
@echo "Waiting for stack to be healthy..."
@timeout=120; \
until curl -sf http://localhost:8000/api/health > /dev/null 2>&1; do \
sleep 5; timeout=$$((timeout-5)); \
if [ $$timeout -le 0 ]; then echo "Backend not healthy after 120s"; exit 1; fi; \
done
pip install -r e2e/requirements.txt -q
rfbrowser init --quiet
robot --outputdir e2e/results e2e/tests/
## One-command smoke test for the ban pipeline:
## 1. Start fail2ban, 2. write failure lines, 3. check ban status.
dev-ban-test:

2
e2e/requirements.txt Normal file
View File

@@ -0,0 +1,2 @@
robotframework>=7
robotframework-browser>=18

View File

@@ -0,0 +1,31 @@
*** Settings ***
Resource ${CURDIR}/common.resource
*** Keywords ***
Login As Admin
# Check setup status.
${response}= GET ${BACKEND_URL}/api/setup/status
${body}= Set Variable ${response.json()}
IF ${body}[setup_complete] == ${false}
# Complete the setup wizard with the dev master password ("Hallo123!").
${password}= Set Variable Hallo123!
${hashed}= Evaluate "sha256('${password}'.encode()).hexdigest()" modules=hashlib
${setup_payload}= Create Dictionary password=${hashed}
POST ${BACKEND_URL}/api/setup/complete json=${setup_payload}
# Retry login after setup.
${response}= GET ${BACKEND_URL}/api/auth/login
END
# Perform login.
${password}= Set Variable Hallo123!
${hashed}= Evaluate "sha256('${password}'.encode()).hexdigest()" modules=hashlib
${login_payload}= Create Dictionary password=${hashed}
${response}= POST ${BACKEND_URL}/api/auth/login json=${login_payload}
# Store session cookie for subsequent requests.
${cookies}= Get Cookies
${session_cookie}= Get Cookie Value bangui_session
Set Browser Variables session_cookie=${session_cookie}
Log Logged in as admin.

View File

@@ -0,0 +1,25 @@
*** Settings ***
Library Browser
Library HttpLibrary
Variables ${CURDIR}/../../.env
# Health check timeout for suite setup (120 s poll interval).
Suite Setup Wait For Backend Health timeout=120 interval=5
*** Variables ***
${FRONTEND_URL} http://localhost:5173
${BACKEND_URL} http://localhost:8000
*** Keywords ***
Wait For Backend Health
[Arguments] ${timeout}=120 ${interval}=5
${deadline}= Evaluate time.time() + ${timeout}
WHILE True
${now}= Evaluate time.time()
IF ${now} >= ${deadline} FAIL Backend did not become healthy within ${timeout} seconds
${response}= GET ${BACKEND_URL}/api/health expected_status=200
IF ${response.status} == 200 BREAK
Sleep ${interval}
END
Log Backend is healthy.

View File

@@ -0,0 +1,13 @@
*** Settings ***
Resource ${CURDIR}/../../resources/common.resource
*** Test Cases ***
Page Loads And Shows Navigation
New Browser chromium headless=${TRUE}
New Page ${FRONTEND_URL}
# Confirm the page title or root element is present.
${title}= Get Title
Should Not Be Empty ${title}
Close Browser

View File

@@ -0,0 +1,16 @@
*** Settings ***
Resource ${CURDIR}/../../resources/common.resource
Resource ${CURDIR}/../../resources/auth.resource
*** Test Cases ***
Ban Records Are Visible
New Browser chromium headless=${TRUE}
Login As Admin
Go To ${FRONTEND_URL}/bans
# Basic presence check — the ban table or empty state should be present.
${content}= Get Page Source
Should Not Be Empty ${content}
Close Browser

View File

@@ -0,0 +1,15 @@
*** Settings ***
Resource ${CURDIR}/../../resources/common.resource
Resource ${CURDIR}/../../resources/auth.resource
*** Test Cases ***
Blocklist Import Page Opens
New Browser chromium headless=${TRUE}
Login As Admin
Go To ${FRONTEND_URL}/blocklists
${content}= Get Page Source
Should Not Be Empty ${content}
Close Browser

View File

@@ -0,0 +1,15 @@
*** Settings ***
Resource ${CURDIR}/../../resources/common.resource
Resource ${CURDIR}/../../resources/auth.resource
*** Test Cases ***
Config Edit Page Opens
New Browser chromium headless=${TRUE}
Login As Admin
Go To ${FRONTEND_URL}/config
${content}= Get Page Source
Should Not Be Empty ${content}
Close Browser