5.7 KiB
5.7 KiB
Backend Testing Strategy
Goal
Replace the current mostly mocked backend suite with a professional layered strategy that:
- keeps fast unit tests
- adds realistic integration coverage with real app wiring
- adds an external performance/load harness
- minimizes monkeypatch use in behavior-level tests
This strategy is backend-only. Frontend automation is not part of this phase.
Current State
- Tooling:
- pytest only
- no async test helpers
- no realistic HTTP/WebSocket client beyond
TestClient - no containerized test infra
- no benchmark/load tooling
- Problems:
- custom
FastAPI()test app inbackend/tests/conftest.py - heavy monkeypatching of router dependencies
- almost no real lifecycle/integration confidence
- no system perf or throughput measurements
- custom
Target Test Pyramid
1. Unit tests
Purpose:
- pure logic only
- parser/validator/serialization helpers
- deterministic error mapping
Rules:
- monkeypatch allowed only at narrow technical seams
- no network, no Mongo, no Docker, no filesystem mutation beyond temp dirs
Examples:
- proxy code validation
- mode/protocol validation
- template metadata normalization
- health result shaping helpers
2. Integration tests
Purpose:
- real FastAPI app
- real startup/shutdown
- real Mongo-backed repositories
- realistic temp runtime directories
- real HTTP and WebSocket behavior
Recommended tooling:
pytest-asynciohttpxasgi-lifespantestcontainers[mongodb]
Rules:
- use the actual app from
app.main, not a bespoke test app - override as little as possible
- prefer seeded fixtures over monkeypatching repositories
- temp runtime dirs are acceptable; fake repositories are not
Coverage to add:
- auth login/logout with real repository behavior
- proxy create/update/delete against real Mongo
- health history endpoints with persisted samples
- logs and health websocket auth and streaming
- startup reconciliation with persisted runtime state
- failure-path integration for runtime validation and cleanup
3. System tests
Purpose:
- realistic end-to-end control plane on a running stack
- optional and slower than integration tests
Scope:
- backend process
- Mongo container
- optional playground/docker service fixture
- real HTTP calls and websocket clients
Use for:
- reverse-mode service remap
- mitmdump orphan recovery
- runtime artifact cleanup
- template apply/save behavior at the API level
Fixture Architecture
Core fixtures
mongo_container- real Mongo container started once per session
app_settings- isolated env/config for test DB, auth credentials, runtime dirs
real_app- imported from
app.main
- imported from
lifespan_app- app with startup/shutdown active
http_client- async
httpx.AsyncClient
- async
auth_token- produced through real login endpoint
runtime_dir- temp dir for scripts/config/logs
seed_proxy- repository-backed fixture that inserts a real proxy document
Markers
unitintegrationapiwebsocketslowrequires_dockerperf
These should drive selective local and CI runs.
Monkeypatch Policy
Allowed:
- forcing subprocess failure
- forcing clock/timeouts
- fault injection into low-level boundaries
- temp path redirection where isolation is needed
Not the default:
- patching repository functions in router tests
- patching service functions in API behavior tests
- bypassing authentication for general API coverage
Rule of thumb:
- if the test is about endpoint behavior, use the real endpoint stack
- if the test is about pure logic, isolate it completely
Performance and Load Harness
Tool choice
Use k6 as the primary external load tool.
Reason:
- good HTTP load support
- scriptable scenarios
- usable for websocket-related smoke scenarios
- clearer reporting than ad-hoc shell loops
Perf layout
Add a dedicated non-default perf layer, for example under:
backend/perf/
Include:
- startup script or instructions
- scenario scripts
- seeded data setup
- report capture format
Baseline scenarios
- Read-heavy API latency
GET /proxyGET /proxy/{port}/health/history- measure latency percentiles and RPS
- Control-plane mutation loop
- create/update/start/stop/delete proxy in repeated cycles
- measure failure rate and latency
- WebSocket fan-out
- concurrent clients on logs and health streams
- verify stability under multiple listeners
- Health monitor cost
- N running proxies with scheduled checks
- measure API latency degradation and monitor lag
Perf outputs
For each scenario record:
- concurrency
- duration
- warmup
- RPS
- p50/p95/p99 latency
- error rate
- environment notes
These should not be strict CI gates initially; they should establish baselines first.
Migration Plan
Phase 1
- Add pytest markers and split directories by layer.
- Introduce async test dependencies and real app client fixtures.
- Keep existing suite running.
Phase 2
- Convert router tests away from broad monkeypatching.
- Replace bespoke app fixture with real lifespan-backed app in integration tests.
- Add Mongo testcontainer fixture.
Phase 3
- Add websocket integration tests.
- Add startup/recovery/runtime integration tests.
- Shrink old unit/API tests where they duplicate new integration coverage.
Phase 4
- Introduce
k6perf scripts and documentation. - Add baseline measurement workflow for local runs and CI nightly/optional jobs.
Acceptance Criteria
- Fast local unit suite remains available.
- Integration suite uses the real app and real Mongo.
- WebSocket paths are tested without bypassing core auth behavior.
- Router-level monkeypatching is no longer the dominant pattern.
- Reverse-mode lifecycle and recovery get realistic coverage.
- Perf/load scripts exist and produce repeatable latency/RPS baselines.