216 lines
5.7 KiB
Markdown
216 lines
5.7 KiB
Markdown
# 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 in `backend/tests/conftest.py`
|
|
- heavy monkeypatching of router dependencies
|
|
- almost no real lifecycle/integration confidence
|
|
- no system perf or throughput measurements
|
|
|
|
## 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-asyncio`
|
|
- `httpx`
|
|
- `asgi-lifespan`
|
|
- `testcontainers[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`
|
|
- `lifespan_app`
|
|
- app with startup/shutdown active
|
|
- `http_client`
|
|
- async `httpx.AsyncClient`
|
|
- `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
|
|
- `unit`
|
|
- `integration`
|
|
- `api`
|
|
- `websocket`
|
|
- `slow`
|
|
- `requires_docker`
|
|
- `perf`
|
|
|
|
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
|
|
1. Read-heavy API latency
|
|
- `GET /proxy`
|
|
- `GET /proxy/{port}/health/history`
|
|
- measure latency percentiles and RPS
|
|
|
|
2. Control-plane mutation loop
|
|
- create/update/start/stop/delete proxy in repeated cycles
|
|
- measure failure rate and latency
|
|
|
|
3. WebSocket fan-out
|
|
- concurrent clients on logs and health streams
|
|
- verify stability under multiple listeners
|
|
|
|
4. 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 `k6` perf 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.
|
|
|