# Test Specification — C2.5 Re-rank

Component-scoped. Suite-level coverage in `_docs/02_document/tests/*.md`.

## Acceptance Criteria Traceability

| AC ID | Acceptance Criterion (one-line) | Test IDs | Coverage |
|-------|---------------------------------|----------|----------|
| AC-2.1b | Satellite-anchor registration | FT-P-05, **C2.5-IT-01** | Covered |
| AC-4.1 | E2E latency <400 ms p95 | NFT-PERF-01, **C2.5-PT-01** | Covered |
| AC-NEW-7 | Cache poisoning (rerank-side filter) | NFT-SEC-01, **C2.5-IT-02** | Covered (relaxed) |

---

## Component-Internal Tests

### C2.5-IT-01: Top-K=10 → Top-N=3 promotion stability

**Summary**: when C2's top-1 is the ground-truth tile, C2.5's top-1 stays the ground-truth tile.

**Traces to**: AC-2.1b

**Description**: for the Derkachi normal segment where C2 already picks the correct top-1 (per C2-IT-01), assert that C2.5's `RerankResult.candidates[0].tile_id` matches C2's top-1 in ≥98% of frames. The remaining ≤2% are accepted (rerank can legitimately demote a top-1 candidate when a top-K=2..10 candidate has more inliers — this is the design intent).

**Input data**: shared with C2-IT-01.

**Expected result**: top-1 promotion rate ≥ 0.98 (i.e., rerank rarely overrides a correct C2 top-1).

**Max execution time**: 2 min (10 LightGlue passes per frame on Tier-1 with the LightGlue runtime).

---

### C2.5-IT-02: drop-and-continue on per-candidate failure

**Summary**: if one of the K=10 candidates raises `RerankBackboneError`, C2.5 returns N=2 candidates instead of zero — never crashes.

**Traces to**: AC-NEW-7 (defensive — keeps the pipeline alive on the resilience path)

**Description**: monkey-patch the LightGlue runtime to raise `RerankBackboneError` on the 5th candidate of every frame; run 100 frames; assert (a) C2.5 emits a `RerankResult` for every frame, (b) `len(candidates) ∈ {2, 3}`, (c) error logged at WARN level.

**Input data**: `synthetic_vpr/diverse_100f/`.

**Expected result**: 100/100 frames produce a result; counts match assertion.

**Max execution time**: 2 min.

---

### C2.5-IT-03: shared LightGlue runtime serial-access invariant

**Summary**: concurrent calls to the shared `LightGlueRuntime` from C2.5 and C3 must serialize without deadlock or corrupt output.

**Traces to**: helper invariant (no AC trace; backstops the helper-ownership decision per R14)

**Description**: spawn two threads — one running C2.5 rerank, the other running C3 match — sharing the same `LightGlueRuntime`. Run 50 iterations each; assert no exceptions, all outputs produced, and the result determinism holds (compare against single-threaded baseline; bit-identical match).

**Input data**: synthetic batch.

**Expected result**: no deadlock; outputs bit-identical to single-threaded run.

**Max execution time**: 2 min.

---

## Performance Tests

### C2.5-PT-01: 10 LightGlue passes per frame budget on Tier-2

**Traces to**: AC-4.1

**Load scenario**: 3 Hz, K=10 single-pair LightGlue per frame, 10 min replay.

**Expected results**:

| Metric | Target | Failure Threshold |
|--------|--------|-------------------|
| `rerank` p95 | ≤ 80 ms (10 single-pair LightGlue) | 150 ms |
| Inference engine reuse | 1 engine across all 10 calls | regression on engine-reuse causes test failure |

**Resource limits**:
- GPU memory: ≤ 300 MB for the shared LightGlue engine (counted once, not 10×).

---

## Security Tests

C2.5 has no externally-reachable surface; defensive coverage flows through the helper-ownership invariant (C2.5-IT-03).

---

## Acceptance Tests

C2.5 has no operator-facing behaviour; covered transitively via FT-P-05 / FT-P-06.

---

## Test Data Management

| Data Set | Source | Size |
|----------|--------|------|
| `synthetic_vpr/diverse_100f/` | shared with C2 | shared |
| `flight_derkachi/normal_segment_60_stills/` | shared with C1/C2 | shared |
| Derkachi corpus + LightGlue engine | C10/C7 build artifacts | shared |

**Setup**: same as C2.
**Teardown**: corpus + engines are read-only.
**Data isolation**: per-test temp dirs under `tests/tmp/c2_5/<test-id>/`.
