mirror of
https://github.com/azaion/missions.git
synced 2026-06-22 23:21:08 +00:00
[AZ-575] Add 11 blackbox test task specs from decompose Step 5
Decompose Step 5 (tests-only mode) produced the test-task ladder for the Blackbox Tests epic. Test infrastructure (AZ-576) blocks the rest; all 10 blackbox child tasks fan out from it. Tasks (epic AZ-575): - AZ-576 test_infrastructure (5 SP) - AZ-577 test_vehicles_positive (5 SP) - AZ-578 test_missions_positive (5 SP) - AZ-579 test_waypoints_health_positive (5 SP) - AZ-580 test_validation_authz_negative (3 SP) - AZ-581 test_security_auth_claims (5 SP) - AZ-582 test_security_alg_rotation_cors (5 SP) - AZ-583 test_resilience_cascade_migrator (3 SP) - AZ-584 test_resilience_config_db_rotation_race (5 SP) - AZ-585 test_resource_limits (3 SP) - AZ-586 test_performance (3 SP) Total: 45 SP across 11 tasks. Coverage verified against blackbox/security/resilience/resource-limit/performance test specs (56 scenarios). _docs/_autodev_state.md advanced to Step 6 (Implement Tests). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
# Waypoints + Health Positive Flow Tests
|
||||
|
||||
**Task**: AZ-579_test_waypoints_health_positive
|
||||
**Name**: Waypoints + Health positive tests (FT-P-13..18)
|
||||
**Description**: Implement xUnit blackbox tests for the 6 happy-path Waypoint + Health scenarios — waypoint list ordered by OrderNum ASC, waypoint create echoes geo fields (no auto-conversion), waypoint update is full overwrite, health 200 anonymous, health 200 with Postgres stopped (no DB ping), and waypoint cascade delete scoped to one waypoint (sibling chain intact).
|
||||
**Complexity**: 5 points
|
||||
**Dependencies**: AZ-576_test_infrastructure
|
||||
**Component**: Blackbox Tests
|
||||
**Tracker**: AZ-579
|
||||
**Epic**: AZ-575
|
||||
|
||||
## Problem
|
||||
|
||||
Waypoints carry two non-obvious behaviors: (1) the list endpoint orders by `OrderNum` ASC regardless of insert order (AC-4.3), and (2) `PUT /missions/{id}/waypoints/{wpId}` is a FULL overwrite even though the DTO looks "partial" (non-nullable enums + numerics) — passing `Height:0` overwrites the previous `Height:120` (AC-4.4). The waypoint cascade delete (AC-4.5) is the tighter sibling of the mission cascade — it must remove the target waypoint's chain (`media → annotations → detection`) without touching a sibling waypoint's chain. The health endpoint (AC-7.1, AC-7.2) is the suite's probe contract: it MUST return 200 anonymously AND MUST NOT ping the database, because the suite reverse proxy uses `/health` to decide whether to route traffic — a DB outage must not depool a healthy process.
|
||||
|
||||
## Outcome
|
||||
|
||||
- All six FT-P-13..18 scenarios run against the dockerised `missions` service and pass.
|
||||
- Each test produces a CSV row with `Category=Blackbox`, `Traces=AC-4.x` or `AC-7.x`, `Result=pass`, within the documented `Max execution time` (2s for FT-P-13..16, 5s for FT-P-17 to allow PG stop, 10s for FT-P-18 cascade).
|
||||
- The list test asserts both shape (JSON array) and ordering (`[1,2,3,4,5]` ASC from a `[3,1,2,5,4]` insert order).
|
||||
- The update test asserts the FULL overwrite by passing `Height:0` and checking the new value is 0 (not the preserved 120).
|
||||
- The "PG stopped" health test asserts the process answers `200` even with `postgres-test` stopped — proving the probe does not ping the DB.
|
||||
- The cascade test (F4) asserts target-waypoint chain deleted AND sibling-waypoint chain preserved, with per-table counts compared against `cascade_F4_walk.json`.
|
||||
|
||||
## Scope
|
||||
|
||||
### Included
|
||||
|
||||
- FT-P-13 Waypoint list ordered by `OrderNum` ASC — `seed_5_waypoints_unordered` inserts in `[3,1,2,5,4]` order.
|
||||
- FT-P-14 Waypoint create echoes `GeoPoint` fields (no auto lat/lon ↔ MGRS conversion today — preserves the documented divergence from spec).
|
||||
- FT-P-15 Waypoint update is full overwrite — `Height:0` overwrites `Height:120`, `OrderNum` changes, `GeoPoint:null` clears.
|
||||
- FT-P-16 Health 200 anonymous — no `Authorization` header, exact JSON `{ "status": "healthy" }`.
|
||||
- FT-P-17 Health 200 with PG stopped — proves process-liveness only, no DB ping.
|
||||
- FT-P-18 Waypoint cascade delete (F4) — `DELETE /missions/{mid}/waypoints/{wp1}`; per-table counts on `wp1` chain go to 0; sibling `wp2` chain intact.
|
||||
|
||||
### Excluded
|
||||
|
||||
- FT-N-07 "waypoint operation against missing mission returns 404" lives in Task 13.
|
||||
- Waypoint nested existence check (single composite-FK predicate per `state.json` drift entry) is implementation detail; the blackbox test only asserts the observable 404 in FT-N-07.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
**AC-1: FT-P-13 waypoint list is ordered by OrderNum ASC**
|
||||
Given `seed_5_waypoints_unordered` under one mission, with `order_num` values `[3,1,2,5,4]` inserted in that order
|
||||
When `GET /missions/{id}/waypoints` is issued with a valid JWT
|
||||
Then response is `200`, body parses as JSON array, `body.length == 5`, AND `[w.OrderNum for w in body] == [1,2,3,4,5]`
|
||||
|
||||
**AC-2: FT-P-14 waypoint create echoes geo fields, no MGRS conversion**
|
||||
Given one mission row
|
||||
When `POST /missions/{id}/waypoints { GeoPoint:{Lat:50.45, Lon:30.52, Mgrs:null}, WaypointSource:0, WaypointObjective:0, OrderNum:1, Height:120 }` is issued
|
||||
Then response is `201`, `body.GeoPoint.Lat == 50.45`, `body.GeoPoint.Lon == 30.52`, AND `body.GeoPoint.Mgrs == null` (NO auto-conversion)
|
||||
|
||||
**AC-3: FT-P-15 waypoint update is full overwrite**
|
||||
Given one waypoint with `Height=120, OrderNum=1, GeoPoint=(Lat:50.45, …)`
|
||||
When `PUT /missions/{id}/waypoints/{wpId} { GeoPoint:null, WaypointSource:1, WaypointObjective:1, OrderNum:2, Height:0 }` is issued
|
||||
Then response is `200`, `body.Height == 0` (overwritten from 120), `body.OrderNum == 2`, AND `body.GeoPoint == null`
|
||||
|
||||
**AC-4: FT-P-16 health is 200 anonymous**
|
||||
Given a running `missions` container
|
||||
When `GET /health` is issued with NO `Authorization` header
|
||||
Then response is `200`, body is exactly `{ "status": "healthy" }` with case-sensitive key
|
||||
|
||||
**AC-5: FT-P-17 health is 200 with PG stopped**
|
||||
Given `missions` is running AND `docker compose stop postgres-test` has succeeded
|
||||
When `GET /health` is issued
|
||||
Then response is `200`, body is exactly `{ "status": "healthy" }` — proving the probe does NOT ping the DB
|
||||
|
||||
**AC-6: FT-P-18 waypoint cascade scope is one waypoint**
|
||||
Given `fixture_cascade_F4` (target waypoint `wp1` with chain `media → annotations → detection`; sibling waypoint `wp2` with its own chain)
|
||||
When `DELETE /missions/{mid}/waypoints/{wp1}` is issued
|
||||
Then response is `204`, AND side-channel `SELECT COUNT(*)` returns 0 for the `wp1` chain rows in `detection`, `annotations`, `media`, AND for `wp1` itself in `waypoints`
|
||||
And side-channel returns `1` for `wp2` in `waypoints` AND `> 0` for the `wp2` chain rows in `media, annotations, detection`
|
||||
And the per-table counts after deletion match `_docs/00_problem/input_data/expected_results/cascade_F4_walk.json` via deep JSON diff
|
||||
|
||||
## Non-Functional Requirements
|
||||
|
||||
**Performance**
|
||||
- FT-P-13..16: ≤ 2s each. FT-P-17: ≤ 5s (allow PG stop time). FT-P-18: ≤ 10s (cascade through 4 tables).
|
||||
|
||||
**Reliability**
|
||||
- FT-P-17 must restore `postgres-test` to `Up` before exiting (try/finally with `docker compose start postgres-test` in the fixture teardown) — otherwise subsequent tests fail with `ConnectionRefused`.
|
||||
- FT-P-18 uses `IClassFixture<DbResetFixture>` with the F4 fixture recreated per scenario.
|
||||
|
||||
## Blackbox Tests
|
||||
|
||||
| AC Ref | Initial Data/Conditions | What to Test | Expected Behavior | NFR References |
|
||||
|--------|------------------------|-------------|-------------------|----------------|
|
||||
| AC-1 | `seed_5_waypoints_unordered` ([3,1,2,5,4]) | `GET /missions/{id}/waypoints` | `200` + array + OrderNum ASC | AC-4.3 |
|
||||
| AC-2 | One mission row | `POST /missions/{id}/waypoints { GeoPoint:{Lat,Lon,Mgrs:null} }` | `201` + GeoPoint echoed + Mgrs null (no conversion) | AC-4 (data_parameters § 2.3) |
|
||||
| AC-3 | One waypoint Height=120 | `PUT … { Height:0, GeoPoint:null }` | `200` + Height=0 + GeoPoint=null (full overwrite) | AC-4.4 |
|
||||
| AC-4 | Running container | `GET /health` no auth | `200` + exact `{"status":"healthy"}` | AC-7.1 |
|
||||
| AC-5 | PG stopped | `GET /health` | `200` + exact `{"status":"healthy"}` | AC-7.2, AC-7.3 |
|
||||
| AC-6 | `fixture_cascade_F4` | `DELETE /missions/{mid}/waypoints/{wp1}` | `204` + wp1 chain 0 + wp2 chain intact + `cascade_F4_walk.json` match | AC-4.5 |
|
||||
|
||||
## Constraints
|
||||
|
||||
- HTTP only against `http://missions:8080`; bearer token via `https://jwks-mock:8443/sign` with `permissions=FL` (for waypoint endpoints); FT-P-16 and FT-P-17 explicitly send no `Authorization` header.
|
||||
- FT-P-17 uses `ComposeRestartFixture`-style helper that runs `docker compose -f docker-compose.test.yml stop postgres-test` then `docker compose -f docker-compose.test.yml start postgres-test` in teardown.
|
||||
- FT-P-18 fixture uses `_docs/00_problem/input_data/expected_results/fixture_cascade_F4.sql` (NOT a hand-rolled INSERT).
|
||||
- AAA pattern with `// Arrange` / `// Act` / `// Assert` per test.
|
||||
|
||||
## Risks & Mitigation
|
||||
|
||||
**Risk 1: FT-P-15 silently passes if SUT exposes a "partial" update path**
|
||||
- *Risk*: If a future refactor adds a JSON-merge update mode, sending `Height:0` might be interpreted as "leave Height unchanged" rather than overwrite.
|
||||
- *Mitigation*: The test ALSO sets `GeoPoint:null` and asserts the value is null after — proving the path is full-overwrite, not patch.
|
||||
|
||||
**Risk 2: FT-P-17 PG-stop leaks to other tests**
|
||||
- *Risk*: If the test fails before teardown, subsequent tests run against a dead DB.
|
||||
- *Mitigation*: The fixture uses `try/finally`; the teardown waits for `postgres-test` to reach `healthy` (poll `pg_isready`) before yielding control back to xUnit.
|
||||
|
||||
**Risk 3: FT-P-18 sibling-intact assertion gives false-pass if F4 fixture is empty**
|
||||
- *Risk*: If `fixture_cascade_F4.sql` failed to insert `wp2`'s chain, the post-delete assertion `wp2 chain > 0` fails trivially — but with a misleading message.
|
||||
- *Mitigation*: The test asserts pre-delete counts FIRST (`wp1` chain > 0 AND `wp2` chain > 0); fixture failure is caught in the Arrange phase, not the Assert phase.
|
||||
|
||||
## System Under Test Boundary
|
||||
|
||||
- Tests drive the product through the public HTTP surface (`http://missions:8080/missions/{id}/waypoints*` and `http://missions:8080/health`) plus the documented DB side-channel for fixture seeding and post-call assertions. Expected outputs are compared against `_docs/00_problem/input_data/expected_results/results_report.md` rows AC-4 4.2, 4.3, 4.4, 4.5 and AC-7 rows 7.1, 7.2, and against the machine-readable file `_docs/00_problem/input_data/expected_results/cascade_F4_walk.json`.
|
||||
- Stubs are allowed ONLY for: the external `admin` JWT issuer (`jwks-mock` container) and the DB-only stub tables for `media`, `annotations`, `detection` (seeded via side-channel SQL).
|
||||
- Stubs, fakes, deterministic fallbacks, monkeypatches, or direct imports are NOT allowed for any internal product module — including `WaypointService`, `MissionsController` (health route), `AppDataConnection`, or `Program.cs`'s health middleware. If any of these is not implemented, the test MUST fail/block as missing product implementation — it must not pass by replacing the module with a test stub.
|
||||
Reference in New Issue
Block a user