2026-06-19 20:13:43 +08:00
|
|
|
# Test Fixtures
|
|
|
|
|
|
2026-06-20 06:10:45 +08:00
|
|
|
Shared test fixtures for the SnapOtter test suite. Organized by modality in two tiers.
|
2026-06-19 20:13:43 +08:00
|
|
|
|
|
|
|
|
## Two-Tier Structure
|
|
|
|
|
|
|
|
|
|
**Tier 1: Tiny Synthetics (matrices/format coverage)**
|
|
|
|
|
Small, project-generated files for format-compatibility matrices and fast unit tests.
|
2026-06-20 06:10:45 +08:00
|
|
|
Located in `*/formats/`, `*/edge/`, `*/hostile/`, and `security/`.
|
2026-06-19 20:13:43 +08:00
|
|
|
|
|
|
|
|
**Tier 2: Real Heroes (depth/fidelity testing)**
|
|
|
|
|
Larger, content-representative files for integration and fidelity tests.
|
2026-06-20 06:10:45 +08:00
|
|
|
Located in `*/valid/`. Each must be tracked in `manifest.json` with sha256 + provenance.
|
2026-06-19 20:13:43 +08:00
|
|
|
|
2026-06-20 06:10:45 +08:00
|
|
|
## Modality-First Layout
|
2026-06-19 20:13:43 +08:00
|
|
|
|
|
|
|
|
```
|
|
|
|
|
tests/fixtures/
|
|
|
|
|
index.ts # Typed registry (import paths from here, not raw strings)
|
2026-06-20 06:10:45 +08:00
|
|
|
manifest.json # sha256 + bytes + provenance for all valid/ assets
|
2026-06-19 20:13:43 +08:00
|
|
|
gen-manifest.mjs # Script to re-stamp manifest hashes
|
2026-06-20 06:10:45 +08:00
|
|
|
gen-synthetic-content.mjs # Regenerate CC0 synthetics (QR, barcode, OCR, etc.)
|
2026-06-19 20:13:43 +08:00
|
|
|
LICENSES.md # Provenance audit trail
|
2026-06-20 06:10:45 +08:00
|
|
|
image/
|
|
|
|
|
valid/ # Base test images, portraits, OCR, barcodes, QR codes
|
|
|
|
|
formats/ # 35 format samples (sample.png, sample.arw, sample.psd, ...)
|
|
|
|
|
edge/ # Edge cases (1x1, blank, extreme aspect, fake transparency)
|
|
|
|
|
hostile/ # Truncated, zero-byte, garbage, bomb, extension-mismatch
|
|
|
|
|
video/
|
|
|
|
|
valid/ # BBB heroes (mov/webm/mkv/avi), TTS speech, metadata MP4
|
|
|
|
|
formats/ # tiny.{mp4,webm,avi,...}, subtitle files (srt/vtt/ass)
|
|
|
|
|
hostile/ # Truncated MP4
|
|
|
|
|
audio/
|
|
|
|
|
valid/ # TTS speech (wav/flac/ogg/m4a/aac/opus), tagged MP3
|
|
|
|
|
formats/ # tiny.{mp3,wav,flac,...}, tone-stereo, tone-gap
|
|
|
|
|
hostile/ # Zero-byte WAV
|
|
|
|
|
document/
|
|
|
|
|
valid/ # PDFs (3-page, 6-page, encrypted, OCR-scanned)
|
|
|
|
|
formats/ # tiny.{docx,xlsx,epub,html,md}
|
|
|
|
|
edge/ # remote-img.html (SSRF test vector)
|
|
|
|
|
hostile/ # Truncated DOCX, garbage PDF
|
|
|
|
|
data/
|
|
|
|
|
valid/ # tiny.{csv,json,xml,yaml,tsv,zip}
|
|
|
|
|
security/ # SVG XXE test vectors (file-read, SSRF)
|
2026-06-19 20:13:43 +08:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Using the Registry
|
|
|
|
|
|
|
|
|
|
Always import paths from `tests/fixtures/index.ts` rather than hard-coding paths:
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
import { fixtures, readFixture } from "../../fixtures/index.js";
|
|
|
|
|
|
|
|
|
|
// Direct path access
|
|
|
|
|
const pngPath = fixtures.image.base.png200;
|
|
|
|
|
|
|
|
|
|
// Read bytes
|
|
|
|
|
const buffer = readFixture(fixtures.image.base.png200);
|
|
|
|
|
|
|
|
|
|
// Format accessor (for matrix iteration)
|
|
|
|
|
const arwPath = fixtures.image.formats("arw");
|
|
|
|
|
const mp4Path = fixtures.video.tiny("mp4");
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Adding a Fixture
|
|
|
|
|
|
2026-06-20 06:10:45 +08:00
|
|
|
1. Add the file to the appropriate modality directory
|
2026-06-19 20:13:43 +08:00
|
|
|
2. Add a key in `tests/fixtures/index.ts` pointing to it
|
2026-06-20 06:10:45 +08:00
|
|
|
3. Run `node tests/fixtures/gen-manifest.mjs` (for `valid/` files)
|
|
|
|
|
4. Fill in `sourceUrl` and `license` in `manifest.json` (required)
|
2026-06-19 20:13:43 +08:00
|
|
|
5. Update `LICENSES.md` with the provenance record
|
2026-06-20 06:10:45 +08:00
|
|
|
6. Run the guards: `pnpm vitest run tests/unit/fixtures/`
|
2026-06-19 20:13:43 +08:00
|
|
|
|
|
|
|
|
## Licensing Gate
|
|
|
|
|
|
2026-06-20 06:10:45 +08:00
|
|
|
Every `valid/` hero must have a verified license (CC0, CC-BY, CC-BY-SA, or
|
|
|
|
|
public-domain). Assets with `UNVERIFIED-REVIEW` need license review before
|
|
|
|
|
release. The manifest guard test reports the current count.
|
2026-06-19 20:13:43 +08:00
|
|
|
|
|
|
|
|
## Guard Tests
|
|
|
|
|
|
|
|
|
|
Four guard tests protect fixture integrity:
|
|
|
|
|
|
|
|
|
|
- **fixture-resolve** (unit): every registry path exists and is non-empty
|
|
|
|
|
- **fixture-budget** (unit): per-extension size caps prevent bloat
|
|
|
|
|
- **fixture-manifest** (unit): sha256/bytes in manifest.json match disk
|
|
|
|
|
- **fixture-integrity** (integration): real heroes decode through Sharp/ffprobe/qpdf
|
2026-06-20 06:10:45 +08:00
|
|
|
|
|
|
|
|
## Generator Scripts
|
|
|
|
|
|
|
|
|
|
Three generators produce deterministic synthetics. All are idempotent and skip
|
|
|
|
|
existing files to avoid breaking manifest hashes. See `tests/README.md` for details.
|