refactor: enhance JWT authentication and CORS configuration

Updated JWT authentication to use configuration values instead of hardcoded secrets, improving security and flexibility. Enhanced CORS policy to conditionally allow origins based on configuration settings, with logging for permissive defaults. Updated README to reflect project renaming and clarify service context.
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-14 19:48:25 +03:00
parent 2fe394d732
commit 7025f4d075
74 changed files with 8494 additions and 19 deletions
+128
View File
@@ -0,0 +1,128 @@
# Test Environment
> **Status**: produced by autodev `/test-spec` Phase 2 (2026-05-14).
> **Naming**: post-rename target — `missions` service, `Azaion.Missions.*` namespace, `/vehicles` + `/missions` + `/missions/{id}/waypoints` routes. Until B5B8 land, the `missions` service image is built from the existing `Azaion.Flights.csproj` source — tests will be RED until the rename converges. This is the autodev-aligned path: Step 8 (Refactor) closes the gap.
> **Hardware Assessment** section is filled by `phases/hardware-assessment.md` between Phase 3 and Phase 4.
## Overview
**System under test**: the `missions` .NET 10 REST service exposed on `http://missions:8080` inside the test network. Public surface = the HTTP endpoints documented in `_docs/00_problem/input_data/data_parameters.md` § 7.
**Consumer app purpose**: a standalone xUnit test project (`tests/Azaion.Missions.E2E.Tests.csproj`) that exercises the running service through HTTP only. No `Azaion.Missions.*` types are referenced; the consumer never opens a `DataConnection` to the system-under-test's runtime DB except via a side-channel `postgres-test` connection used to (a) seed fixtures and (b) assert DB side-effects (cascade row counts, default-vehicle invariants).
The side-channel DB access is allowed because the AC catalogue (AC-1.2, AC-1.4, AC-3.1, AC-3.3, AC-10.2) explicitly defines DB state as the verifiable observable. It is NEVER used to mutate state under-test that the API would normally own — only to (1) seed fixtures and (2) assert.
## Docker Environment
### Services
| Service | Image / Build | Purpose | Ports (host:container) |
|---------|--------------|---------|-------|
| `missions` | build context `./` (`Dockerfile`); image tag `azaion/missions:test` | System under test | `5002:8080` |
| `postgres-test` | `postgres:16-alpine` | Owned PostgreSQL for test isolation. Started fresh per test class via Testcontainers OR via `docker compose down -v && docker compose up -d` between scenarios that mutate startup-sensitive state (AC-6.5 legacy drop, AC-6.6 idempotency) | `5433:5432` |
| `e2e-consumer` | build context `tests/Azaion.Missions.E2E.Tests/`; runs `dotnet test` | xUnit test runner; produces `report.csv` | — |
| `pg-side` (optional) | reused `postgres-test` connection on a side port | Side-channel DB connection for fixture seeding + post-call assertions | shares `postgres-test` |
No external mock services are required:
- `admin` (JWT issuer): the test runner mints HS256 tokens itself using a known `JWT_SECRET=test-secret-32-chars-min!!!!!!!!!`.
- `annotations`, `detection`, `autopilot`: their tables (`media`, `annotations`, `detection`, `map_objects`) are seeded directly by the side-channel for cascade tests; the services themselves are not running.
- `flight-gate`, Watchtower, suite reverse proxy: not required for service-level e2e.
### Networks
| Network | Services | Purpose |
|---------|----------|---------|
| `e2e-net` | `missions`, `postgres-test`, `e2e-consumer` | Isolated bridge network; no host network access |
### Volumes
| Volume | Mounted to | Purpose |
|--------|-----------|---------|
| `pg-test-data` | `postgres-test:/var/lib/postgresql/data` | Ephemeral; recreated per scenario class (`docker compose down -v` between class boundaries when the test asserts startup behavior) |
| `e2e-results` | `e2e-consumer:/app/results` and host `./e2e-results/` | Output of `report.csv` |
### docker-compose structure
```yaml
# Outline only — not runnable code (the actual scripts/run-tests.sh wires this up)
services:
postgres-test:
image: postgres:16-alpine
environment:
POSTGRES_DB: azaion
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres-test
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d azaion"]
interval: 1s
timeout: 1s
retries: 30
missions:
build:
context: .
environment:
DATABASE_URL: postgresql://postgres:postgres-test@postgres-test:5432/azaion
JWT_SECRET: test-secret-32-chars-min!!!!!!!!!
depends_on:
postgres-test:
condition: service_healthy
e2e-consumer:
build:
context: tests/Azaion.Missions.E2E.Tests
environment:
MISSIONS_BASE_URL: http://missions:8080
DB_SIDE_CHANNEL: Host=postgres-test;Port=5432;Database=azaion;Username=postgres;Password=postgres-test
JWT_SECRET: test-secret-32-chars-min!!!!!!!!!
depends_on:
missions:
condition: service_started
volumes:
- ./e2e-results:/app/results
```
## Consumer Application
**Tech stack**: xUnit 2.x + `Microsoft.AspNetCore.Mvc.Testing` (HttpClient via `IClassFixture`) OR plain `HttpClient` against the dockerized service. Bogus 35.x for synthetic data. JWT minting via `System.IdentityModel.Tokens.Jwt`. PostgreSQL side-channel via Npgsql (NOT linq2db — keep the consumer free of system-under-test runtime libs).
**Entry point**: `dotnet test tests/Azaion.Missions.E2E.Tests/Azaion.Missions.E2E.Tests.csproj --logger "trx;LogFileName=results.trx"` followed by a small post-processor that converts trx → `report.csv`.
### Communication with system under test
| Interface | Protocol | Endpoint | Authentication |
|-----------|----------|----------|----------------|
| Vehicle API | HTTP/1.1 JSON | `http://missions:8080/vehicles[?name=&isDefault=]` and `/vehicles/{id}[/setDefault]` | `Authorization: Bearer <HS256, permissions=FL>` |
| Mission API | HTTP/1.1 JSON | `http://missions:8080/missions[?name=&fromDate=&toDate=&page=&pageSize=]` | same |
| Waypoint API | HTTP/1.1 JSON | `http://missions:8080/missions/{id}/waypoints[/{wpId}]` | same |
| Health | HTTP/1.1 JSON | `http://missions:8080/health` | anonymous |
| DB side-channel (assertions only) | TCP/Postgres wire | `postgres-test:5432` | `postgres:postgres-test` |
### What the consumer does NOT have access to
- No `using Azaion.Missions.*;` — the consumer is a separate csproj with no project reference to the system under test.
- No `AppDataConnection` instantiation; the side-channel uses raw Npgsql `NpgsqlCommand` only.
- No file-system overlap; `e2e-consumer` is a separate container.
- No environment variable shared in process; the system-under-test's `JWT_SECRET` is supplied through compose env, the consumer mints with the same value via its own env.
## CI/CD Integration
**When to run**: on every push to `dev` (Woodpecker pipeline `.woodpecker/test-arm.yml` and `.woodpecker/test-amd.yml` after the existing `build-arm.yml` job). Currently the repo has only `build-arm.yml` (per O4); the test runner pipeline is a follow-up artifact produced by Step 6 (Implement Tests) — see `scripts/run-tests.sh` (Phase 4).
**Pipeline stage**: post-build, pre-push (the test runner pulls the just-built `azaion/missions:test` tag).
**Gate behavior**: blocking on `dev` branch. Per O4, today's pipeline has no test stage; this gate is added by Step 6 implementation.
**Timeout**: max 15 minutes total wall-clock. Cascade-delete fixtures and the bootstrap-failure scenarios (AC-6.6, AC-6.7) dominate.
## Reporting
**Format**: CSV
**Columns**: `TestId, TestName, Category, Traces, ExecutionTimeMs, Result, ErrorMessage`
**Output path**: `./e2e-results/report.csv`
Categories: `BLACKBOX`, `PERF`, `RES`, `SEC`, `RES_LIM`. `Traces` is a comma-separated list of AC and restriction IDs from `traceability-matrix.md`.
## Hardware Assessment
To be filled by `phases/hardware-assessment.md` between Phase 3 and Phase 4. Today's expected outcome: no GPU, no specialised hardware, no model inference — this is a CRUD service. Test execution requires only a Postgres-capable container and the .NET 10 SDK image. AMD64 + ARM64 both supported (matches H2). Resource ceiling: 2 GB RAM total for `missions + postgres-test + e2e-consumer` is sufficient.