# Fixture Builders — Synthetic Injectors (outlier, blackout-spoof, multi-segment)

**Task**: AZ-408_fixture_builders_synth_injectors
**Name**: Runtime synthetic-injection fixture builders
**Description**: Implement runtime-generated synthetic fixtures: `outlier-injection-derkachi` (light/medium/heavy densities), `blackout-spoof-derkachi` (5 s / 15 s / 35 s windows + paired FC GPS spoof), `multi-segment-derkachi` (3+ blackout windows without spoof).
**Complexity**: 3 points
**Dependencies**: AZ-406, AZ-407 (tile-cache-fixture for Derkachi route bbox)
**Component**: Blackbox Tests / Fixture builders (epic AZ-262 / E-BBT)
**Tracker**: AZ-408
**Epic**: AZ-262 (E-BBT)

## Problem

The negative-path scenarios FT-N-01, FT-N-04, FT-P-08, NFT-RES-04 all rely on programmatically derived overlays of the Derkachi fixture (visual outliers, blackout windows, simultaneous FC GPS spoof). These overlays must be deterministic, runtime-generated into per-test tmpfs (per `test-data.md` § Data Isolation), and — for the spoof case — coordinate with FC inbound stream patching.

## Outcome

- `tests/fixtures/injectors/outlier.py` overlays `derkachi-fixture` with random crops from far-away tiles (>350 m offset per AC-3.1) at three densities — `light` (1 in 100 frames), `medium` (1 in 10), `heavy` (1 in 3). Each density is a CLI flag; same `--seed` produces identical overlays.
- `tests/fixtures/injectors/blackout_spoof.py` produces three sub-scenarios — 5 s, 15 s, 35 s pure-black-frame windows on the video stream AND simultaneous spoofed-GPS injection on the FC inbound stream. Spoof pattern: realistic-looking GPS that jumps 200-500 m in a `north_east_random_direction`. The injector is a coordinated pair (video overlay + FC inbound proxy patch) so both fire at the exact same wall-clock instant.
- `tests/fixtures/injectors/multi_segment.py` generates 3+ blackout segments distributed across the Derkachi flight (positions configurable; default = at 25 %, 50 %, 75 % of replay) WITHOUT spoof injection. Used to exercise satellite-reference re-localization without a security failsafe path.
- All injectors emit to per-test tmpfs (`/tmp/<run-id>/<scenario>/`) and are auto-cleared at teardown.

## Scope

### Included
- The three injector scripts + a shared library for deterministic random-seed handling.
- A small FC-inbound proxy patch for blackout_spoof — sits between the IMU/GPS replay and the SUT's FC inbound port; passes through everything except the spoofed-GPS bursts during the configured window(s).
- pytest fixtures wrapping each injector for use in the per-scenario test files.

### Excluded
- The static fixtures (tile-cache, age-injector, cold-boot, mavlink-passkey, cve-jpeg) — owned by AZ-407.
- Per-scenario test logic (FT-N-01, FT-N-04, FT-P-08, NFT-RES-04) — those tasks consume the injector outputs.
- Persistent storage of generated fixtures — explicitly per-test tmpfs; never written to a persistent volume.

## Acceptance Criteria

**AC-1: outlier injector is seed-deterministic**
Given the same `--seed` value
When `outlier.py --density medium` runs twice
Then both runs produce overlays with identical frame indices replaced and identical replacement crop selection.

**AC-2: outlier offsets exceed 350 m (AC-3.1 envelope)**
Given an `outlier-injection-derkachi` `medium`-density overlay
When the per-frame replacement crop is geo-located via the tile-cache GT
Then ≥99 % of replacement crops are >350 m from the original frame's GT centre.

**AC-3: blackout_spoof produces synchronized video + FC events**
Given `blackout_spoof.py --window 15s`
When the test runs
Then within ≤40 ms wall-clock of the video stream's first all-black frame, the FC inbound proxy starts emitting spoofed GPS frames; both stop within ≤40 ms at the window's end.

**AC-4: blackout_spoof spoof pattern is realistic-looking**
Given the spoof injector emits GPS during the blackout
Then the spoofed `lat`/`lon`/`alt`/`fix_type`/`hdop` fields are within typical-flight ranges (no NaN, no obvious sentinel values, fix_type in {3, 4}, hdop in [0.5, 2.5]); the position deltas between consecutive spoofed frames are in [200 m, 500 m] per AC-NEW-8.

**AC-5: multi_segment produces ≥3 disjoint blackout windows**
Given `multi_segment.py`
Then the output contains ≥3 blackout windows; consecutive windows are separated by ≥30 s of normal frames; total blackout coverage ≤ 25 % of the source duration (so the rest of the flight remains exercising satellite-anchor recovery).

**AC-6: tmpfs auto-cleared at teardown**
Given a test using any injector completes (PASS or FAIL)
Then the injector's tmpfs scratch directory is removed within ≤2 s of teardown; subsequent tests start with empty tmpfs.

## System Under Test Boundary

The injectors only produce input data; they never replace, stub, or fake any SUT module. The blackout_spoof FC-inbound proxy is a pure pass-through with timed splice — it does not implement any SUT logic.

- No internal SUT module is imported by any injector.
- The FC-inbound proxy operates at the protocol level (MAVLink frame routing); it does not interpret SUT output.
- Geo-location of replacement crops uses the tile-cache fixture's manifest only (a public artifact); it does not query the SUT's tile lookup.

## Constraints

- Determinism: same `--seed` → identical overlay across runs (this is required for the regression detector in 40_csv_reporter_refinements).
- Tmpfs-only: injectors never write to persistent volumes.
- Coordinated timing for blackout_spoof: video-overlay and FC-inbound spoof must fire within ≤40 ms of each other (AC-NEW-8 / FT-N-04 pass criterion is "within ≤1 frame OR ≤400 ms").

## Document Dependencies

- `_docs/02_document/tests/test-data.md` § Seed Data Sets (synthetic-injection rows)
- `_docs/02_document/tests/blackbox-tests.md` (FT-N-01, FT-N-04, FT-P-08)
- `_docs/02_document/tests/resilience-tests.md` (NFT-RES-04 reuses blackout_spoof)
