test(fixtures): add Tier-2 depth tests for real heroes (phase 2)

This commit is contained in:
SnapOtter
2026-06-20 01:40:28 +08:00
parent 09b26718e1
commit d09823bf7d
6 changed files with 1121 additions and 2 deletions
+122 -2
View File
@@ -1999,16 +1999,49 @@
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/find-duplicates.test.ts > Find Duplicates uses threshold 1 for very strict duplicate matching",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/find-duplicates.test.ts > Find Duplicates uses threshold at max boundary (20)",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/find-duplicates.test.ts > Find Duplicates uses threshold via settings JSON field",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/ai-depth.test.ts > AI depth: OCR on clean text image recognizes text from ocr-clean.png (200 with text, or 501 skip)",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/ai-depth.test.ts > AI depth: blur-faces on multi-face hero processes multi-face image (202 with job, or 501 skip)",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/ai-depth.test.ts > AI depth: transcribe-audio on speech hero transcribes speech WAV (200 with transcript, or 501 skip)",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/audio-depth.test.ts > Audio depth: convert-audio real speech converts FLAC speech to WAV, output has audio stream",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/audio-depth.test.ts > Audio depth: convert-audio real speech converts OGG speech to FLAC, output is valid FLAC",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/audio-depth.test.ts > Audio depth: convert-audio real speech converts WAV speech to MP3, output has audio stream with duration > 0",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/audio-depth.test.ts > Audio depth: normalize-audio real speech normalizes WAV speech, output is valid audio with duration > 0",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/document-depth.test.ts > Document depth: compress-pdf multipage hero compresses 6-page PDF, output is a valid PDF",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/document-depth.test.ts > Document depth: merge-pdf real heroes merges 6-page + 2-page PDF into 8-page document",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/document-depth.test.ts > Document depth: pdf-to-image multipage hero converts page 1 of 6-page PDF to PNG, output is a valid image",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/document-depth.test.ts > Document depth: pdf-to-image multipage hero reports correct page count for 6-page PDF via /info",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/document-depth.test.ts > Document depth: split-pdf multipage hero extracts pages 1-3 from 6-page PDF, output has 3 pages",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools Sharp decodes /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/multi-face.webp with sane dimensions",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools Sharp decodes /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/ocr-clean.png with sane dimensions",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools Sharp decodes /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/ocr-japanese.png with sane dimensions",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools Sharp decodes /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/portrait-color.jpg with sane dimensions",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools Sharp decodes /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/red-eye.jpg with sane dimensions",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/audio-with-tags.mp3",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.avi",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.mkv",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.mov",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.webm",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/media-30s.mp4",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech-10s.wav",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.aac",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.flac",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.m4a",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.ogg",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.opus",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools qpdf reports pages for /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/multipage-6.pdf",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools qpdf reports pages for /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/ocr-scanned.pdf",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/image-depth.test.ts > Image depth: compress real heroes compresses PNG, output is a valid decodable image",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/image-depth.test.ts > Image depth: compress real heroes compresses portrait JPG, output is valid and smaller",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/image-depth.test.ts > Image depth: convert HEIC hero converts HEIC portrait to JPG, output is a valid JPEG",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/image-depth.test.ts > Image depth: convert HEIC hero converts HEIC portrait to PNG, output is a valid PNG",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/image-depth.test.ts > Image depth: extract metadata from EXIF hero extracts EXIF data from test-with-exif.jpg",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/image-depth.test.ts > Image depth: extract metadata from EXIF hero returns dimensions for portrait JPG hero",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/image-depth.test.ts > Image depth: resize real heroes resizes PNG to 50x50 cover, output is exactly 50x50",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/image-depth.test.ts > Image depth: resize real heroes resizes portrait JPG to 300px wide, output re-decodes at exact dims",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/video-depth.test.ts > Video depth: convert-video hero converts hero mp4 to mp4 (re-encode), output has h264 video stream",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/video-depth.test.ts > Video depth: convert-video hero converts hero mp4 to webm, output ffprobes to vp8/vp9 with duration > 0",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/video-depth.test.ts > Video depth: extract-audio hero extracts audio from hero mp4 as mp3, output has an audio stream",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/video-depth.test.ts > Video depth: trim-video hero trims hero to 0-5s, output duration is shorter than input",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/flatten-pdf.test.ts > flatten-pdf (requires PyMuPDF) flattens a pdf and produces a valid output",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/format-matrix-ai.test.ts > AI tool cross-format matrix AVIF input (sample.avif) AI Canvas Expand",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/format-matrix-ai.test.ts > AI tool cross-format matrix AVIF input (sample.avif) Blur Faces",
@@ -11184,6 +11217,10 @@
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/barcode.avif",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/barcode.png",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/cross-format-chat.webp",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.avi",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.mkv",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.mov",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.webm",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/media-30s.mp4",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/media-30s.wav",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/motorcycle.heif",
@@ -11204,6 +11241,11 @@
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/red-eye.jpg",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech-10s.mp4",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech-10s.wav",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.aac",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.flac",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.m4a",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.ogg",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.opus",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/stress-large.jpg",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/svg-logo.svg",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/video-with-meta.mp4",
@@ -11334,6 +11376,10 @@
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/barcode.avif' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/barcode.png' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/cross-format-chat.webp' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/hero.avi' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/hero.mkv' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/hero.mov' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/hero.webm' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/media-30s.mp4' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/media-30s.wav' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/motorcycle.heif' matches sha256 + bytes and declares a license",
@@ -11354,6 +11400,11 @@
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/red-eye.jpg' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/speech-10s.mp4' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/speech-10s.wav' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/speech.aac' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/speech.flac' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/speech.m4a' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/speech.ogg' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/speech.opus' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/stress-large.jpg' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/svg-logo.svg' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/video-with-meta.mp4' matches sha256 + bytes and declares a license",
@@ -11366,6 +11417,10 @@
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/animated-simpsons.gif",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/audio-with-tags.mp3",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/barcode.png",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.avi",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.mkv",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.mov",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.webm",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/media-30s.mp4",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/media-30s.wav",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/multi-face.webp",
@@ -11383,6 +11438,11 @@
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/red-eye.jpg",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech-10s.mp4",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech-10s.wav",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.aac",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.flac",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.m4a",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.ogg",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.opus",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/stress-large.jpg",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/video-with-meta.mp4",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/watermark.jpg",
@@ -15331,16 +15391,49 @@
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/find-duplicates.test.ts > Find Duplicates uses threshold 1 for very strict duplicate matching",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/find-duplicates.test.ts > Find Duplicates uses threshold at max boundary (20)",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/find-duplicates.test.ts > Find Duplicates uses threshold via settings JSON field",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/ai-depth.test.ts > AI depth: OCR on clean text image recognizes text from ocr-clean.png (200 with text, or 501 skip)",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/ai-depth.test.ts > AI depth: blur-faces on multi-face hero processes multi-face image (202 with job, or 501 skip)",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/ai-depth.test.ts > AI depth: transcribe-audio on speech hero transcribes speech WAV (200 with transcript, or 501 skip)",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/audio-depth.test.ts > Audio depth: convert-audio real speech converts FLAC speech to WAV, output has audio stream",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/audio-depth.test.ts > Audio depth: convert-audio real speech converts OGG speech to FLAC, output is valid FLAC",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/audio-depth.test.ts > Audio depth: convert-audio real speech converts WAV speech to MP3, output has audio stream with duration > 0",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/audio-depth.test.ts > Audio depth: normalize-audio real speech normalizes WAV speech, output is valid audio with duration > 0",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/document-depth.test.ts > Document depth: compress-pdf multipage hero compresses 6-page PDF, output is a valid PDF",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/document-depth.test.ts > Document depth: merge-pdf real heroes merges 6-page + 2-page PDF into 8-page document",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/document-depth.test.ts > Document depth: pdf-to-image multipage hero converts page 1 of 6-page PDF to PNG, output is a valid image",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/document-depth.test.ts > Document depth: pdf-to-image multipage hero reports correct page count for 6-page PDF via /info",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/document-depth.test.ts > Document depth: split-pdf multipage hero extracts pages 1-3 from 6-page PDF, output has 3 pages",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools Sharp decodes /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/multi-face.webp with sane dimensions",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools Sharp decodes /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/ocr-clean.png with sane dimensions",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools Sharp decodes /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/ocr-japanese.png with sane dimensions",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools Sharp decodes /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/portrait-color.jpg with sane dimensions",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools Sharp decodes /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/red-eye.jpg with sane dimensions",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/audio-with-tags.mp3",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.avi",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.mkv",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.mov",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.webm",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/media-30s.mp4",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech-10s.wav",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.aac",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.flac",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.m4a",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.ogg",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools ffprobe reads a positive duration from /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.opus",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools qpdf reports pages for /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/multipage-6.pdf",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/fixture-integrity.test.ts > real fixtures decode through their real tools qpdf reports pages for /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/ocr-scanned.pdf",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/image-depth.test.ts > Image depth: compress real heroes compresses PNG, output is a valid decodable image",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/image-depth.test.ts > Image depth: compress real heroes compresses portrait JPG, output is valid and smaller",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/image-depth.test.ts > Image depth: convert HEIC hero converts HEIC portrait to JPG, output is a valid JPEG",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/image-depth.test.ts > Image depth: convert HEIC hero converts HEIC portrait to PNG, output is a valid PNG",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/image-depth.test.ts > Image depth: extract metadata from EXIF hero extracts EXIF data from test-with-exif.jpg",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/image-depth.test.ts > Image depth: extract metadata from EXIF hero returns dimensions for portrait JPG hero",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/image-depth.test.ts > Image depth: resize real heroes resizes PNG to 50x50 cover, output is exactly 50x50",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/image-depth.test.ts > Image depth: resize real heroes resizes portrait JPG to 300px wide, output re-decodes at exact dims",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/video-depth.test.ts > Video depth: convert-video hero converts hero mp4 to mp4 (re-encode), output has h264 video stream",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/video-depth.test.ts > Video depth: convert-video hero converts hero mp4 to webm, output ffprobes to vp8/vp9 with duration > 0",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/video-depth.test.ts > Video depth: extract-audio hero extracts audio from hero mp4 as mp3, output has an audio stream",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/fixtures/video-depth.test.ts > Video depth: trim-video hero trims hero to 0-5s, output duration is shorter than input",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/format-matrix-ai.test.ts > AI tool cross-format matrix AVIF input (sample.avif) AI Canvas Expand",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/format-matrix-ai.test.ts > AI tool cross-format matrix AVIF input (sample.avif) Blur Faces",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/integration/format-matrix-ai.test.ts > AI tool cross-format matrix AVIF input (sample.avif) Colorize",
@@ -24327,6 +24420,10 @@
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/barcode.avif",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/barcode.png",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/cross-format-chat.webp",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.avi",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.mkv",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.mov",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.webm",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/media-30s.mp4",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/media-30s.wav",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/motorcycle.heif",
@@ -24347,6 +24444,11 @@
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/red-eye.jpg",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech-10s.mp4",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech-10s.wav",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.aac",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.flac",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.m4a",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.ogg",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.opus",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/stress-large.jpg",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/svg-logo.svg",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-budget.test.ts > fixture size budget within budget: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/video-with-meta.mp4",
@@ -24477,6 +24579,10 @@
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/barcode.avif' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/barcode.png' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/cross-format-chat.webp' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/hero.avi' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/hero.mkv' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/hero.mov' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/hero.webm' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/media-30s.mp4' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/media-30s.wav' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/motorcycle.heif' matches sha256 + bytes and declares a license",
@@ -24497,6 +24603,11 @@
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/red-eye.jpg' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/speech-10s.mp4' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/speech-10s.wav' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/speech.aac' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/speech.flac' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/speech.m4a' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/speech.ogg' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/speech.opus' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/stress-large.jpg' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/svg-logo.svg' matches sha256 + bytes and declares a license",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-manifest.test.ts > fixture manifest is consistent with disk 'content/video-with-meta.mp4' matches sha256 + bytes and declares a license",
@@ -24509,6 +24620,10 @@
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/animated-simpsons.gif",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/audio-with-tags.mp3",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/barcode.png",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.avi",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.mkv",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.mov",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/hero.webm",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/media-30s.mp4",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/media-30s.wav",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/multi-face.webp",
@@ -24526,6 +24641,11 @@
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/red-eye.jpg",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech-10s.mp4",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech-10s.wav",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.aac",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.flac",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.m4a",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.ogg",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/speech.opus",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/stress-large.jpg",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/video-with-meta.mp4",
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/fixtures/fixture-resolve.test.ts > fixture registry resolves exists and is a non-empty file: /Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/fixtures/content/watermark.jpg",
@@ -26504,7 +26624,7 @@
"/Users/sidd/Desktop/Personal/Projects/SnapOtter/.claude/worktrees/test+suite-overhaul-and-real-fixtures/tests/unit/web/zustand-stores.test.ts > useThemeStore system theme resolves to dark when matchMedia prefers dark"
],
"counts": {
"total": 13359,
"passed": 13141
"total": 13419,
"passed": 13201
}
}
+182
View File
@@ -0,0 +1,182 @@
/**
* Tier-2 depth tests: AI tools with real content heroes.
*
* Each describe is wrapped in `describe.skipIf` using the AI tool's 501 gate:
* the route returns 501 FEATURE_NOT_INSTALLED when the bundle is absent.
* These tests skip cleanly in CI (no bundles) but exercise real AI processing
* when bundles are installed.
*
* To check whether bundles are installed, we probe the route with a real file.
* If 501, the describe is skipped. If 200/202, we proceed with assertions.
*/
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { fixtures, readFixture } from "../../fixtures/index.js";
import {
buildTestApp,
createMultipartPayload,
loginAsAdmin,
type TestApp,
} from "../test-server.js";
const OCR_CLEAN = readFixture(fixtures.image.ocr.clean);
const MULTI_FACE = readFixture(fixtures.image.multiFace);
const SPEECH_WAV = readFixture(fixtures.audio.speech.wav);
let testApp: TestApp;
let app: TestApp["app"];
let adminToken: string;
/**
* Probe whether an AI tool's bundle is installed by sending a minimal request.
* Returns true if the route responds with anything other than 501.
*/
async function isBundleInstalled(
toolId: string,
file: Buffer,
filename: string,
ct: string,
): Promise<boolean> {
const { body, contentType } = createMultipartPayload([
{ name: "file", filename, contentType: ct, content: file },
{ name: "settings", content: JSON.stringify({}) },
]);
const res = await app.inject({
method: "POST",
url: `/api/v1/tools/${toolId}`,
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body,
});
return res.statusCode !== 501;
}
// We cannot run `isBundleInstalled` at module scope (app not built yet).
// Instead, each AI test accepts 501 as a valid "not installed" outcome and
// only asserts on content when the response is 200/202.
beforeAll(async () => {
testApp = await buildTestApp();
app = testApp.app;
adminToken = await loginAsAdmin(app);
}, 30_000);
afterAll(async () => {
await testApp.cleanup();
}, 10_000);
/** POST a file to an AI tool route. */
async function postTool(
toolId: string,
settings: Record<string, unknown>,
file: Buffer,
filename: string,
ct: string,
) {
const { body, contentType } = createMultipartPayload([
{ name: "file", filename, contentType: ct, content: file },
{ name: "settings", content: JSON.stringify(settings) },
]);
return app.inject({
method: "POST",
url: `/api/v1/tools/${toolId}`,
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body,
});
}
// -----------------------------------------------------------------------
// OCR depth test
// -----------------------------------------------------------------------
describe("AI depth: OCR on clean text image", () => {
it("recognizes text from ocr-clean.png (200 with text, or 501 skip)", async () => {
const res = await postTool("ocr", {}, OCR_CLEAN, "ocr-clean.png", "image/png");
// 501 = bundle not installed, skip gracefully
if (res.statusCode === 501) {
const json = JSON.parse(res.body);
expect(json.code).toBe("FEATURE_NOT_INSTALLED");
return;
}
expect(res.statusCode).toBe(200);
const json = JSON.parse(res.body);
expect(json.text).toBeDefined();
expect(typeof json.text).toBe("string");
// The OCR clean fixture has readable English text
expect(json.text.length).toBeGreaterThan(0);
expect(json.engine).toBeDefined();
}, 120_000);
});
// -----------------------------------------------------------------------
// Face detection (blur-faces) depth test
// -----------------------------------------------------------------------
describe("AI depth: blur-faces on multi-face hero", () => {
it("processes multi-face image (202 with job, or 501 skip)", async () => {
const res = await postTool("blur-faces", {}, MULTI_FACE, "multi-face.webp", "image/webp");
// 501 = bundle not installed, skip gracefully
if (res.statusCode === 501) {
const json = JSON.parse(res.body);
expect(json.code).toBe("FEATURE_NOT_INSTALLED");
return;
}
// blur-faces is an async AI tool: 202 with jobId
expect(res.statusCode).toBe(202);
const json = JSON.parse(res.body);
expect(json.jobId).toBeDefined();
expect(json.async).toBe(true);
// Poll the job to completion
const { db, schema } = await import("../../../apps/api/src/db/index.js");
const { eq } = await import("drizzle-orm");
let row: { status: string; outputRefs: unknown } | undefined;
for (let i = 0; i < 120; i++) {
[row] = await db.select().from(schema.jobs).where(eq(schema.jobs.id, json.jobId));
if (row && ["completed", "failed", "canceled"].includes(row.status)) break;
await new Promise((r) => setTimeout(r, 500));
}
expect(row?.status).toBe("completed");
// Output should have at least one ref (the blurred image)
expect(Array.isArray(row?.outputRefs)).toBe(true);
expect((row?.outputRefs as string[]).length).toBeGreaterThanOrEqual(1);
}, 120_000);
});
// -----------------------------------------------------------------------
// Transcribe audio depth test
// -----------------------------------------------------------------------
describe("AI depth: transcribe-audio on speech hero", () => {
it("transcribes speech WAV (200 with transcript, or 501 skip)", async () => {
const res = await postTool("transcribe-audio", {}, SPEECH_WAV, "speech-10s.wav", "audio/wav");
// 501 = bundle not installed, skip gracefully
if (res.statusCode === 501) {
const json = JSON.parse(res.body);
expect(json.code).toBe("FEATURE_NOT_INSTALLED");
return;
}
// Transcription may be sync (200) or async (202)
expect([200, 202]).toContain(res.statusCode);
if (res.statusCode === 200) {
const json = JSON.parse(res.body);
// Transcript text should be non-empty
expect(json.text || json.transcript).toBeDefined();
const text = json.text || json.transcript;
expect(typeof text).toBe("string");
expect(text.length).toBeGreaterThan(0);
} else {
// 202: just verify job was accepted
const json = JSON.parse(res.body);
expect(json.jobId).toBeDefined();
}
}, 120_000);
});
@@ -0,0 +1,162 @@
/**
* Tier-2 depth tests: real TTS speech heroes through real audio tool routes.
*
* Exercises convert-audio and normalize-audio with the typed fixture registry,
* verifying output validity via ffprobe (codec, duration, stream presence).
*/
import { mkdtempSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { ffmpegAvailable, probeMedia } from "@snapotter/media-engine";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { fixtures, readFixture } from "../../fixtures/index.js";
import {
buildTestApp,
createMultipartPayload,
loginAsAdmin,
type TestApp,
} from "../test-server.js";
const SPEECH_WAV = readFixture(fixtures.audio.speech.wav);
const SPEECH_FLAC = readFixture(fixtures.audio.speech.flac);
const SPEECH_OGG = readFixture(fixtures.audio.speech.ogg);
let testApp: TestApp;
let app: TestApp["app"];
let adminToken: string;
beforeAll(async () => {
testApp = await buildTestApp();
app = testApp.app;
adminToken = await loginAsAdmin(app);
}, 30_000);
afterAll(async () => {
await testApp.cleanup();
}, 10_000);
/** POST a file to a tool route. */
async function postTool(
toolId: string,
settings: Record<string, unknown>,
file: Buffer,
filename: string,
ct: string,
) {
const { body, contentType } = createMultipartPayload([
{ name: "file", filename, contentType: ct, content: file },
{ name: "settings", content: JSON.stringify(settings) },
]);
return app.inject({
method: "POST",
url: `/api/v1/tools/${toolId}`,
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body,
});
}
/** Download a result and return the raw payload. */
async function download(downloadUrl: string): Promise<Buffer> {
const dl = await app.inject({
method: "GET",
url: downloadUrl,
headers: { authorization: `Bearer ${adminToken}` },
});
expect(dl.statusCode).toBe(200);
return dl.rawPayload;
}
/** Write a buffer to a temp file and probe it. */
async function probeBuffer(buf: Buffer, ext: string) {
const dir = mkdtempSync(join(tmpdir(), "audio-depth-"));
const filePath = join(dir, `output.${ext}`);
writeFileSync(filePath, buf);
return probeMedia(filePath);
}
// -----------------------------------------------------------------------
// Convert audio depth tests
// -----------------------------------------------------------------------
describe.skipIf(!ffmpegAvailable())("Audio depth: convert-audio real speech", () => {
it("converts WAV speech to MP3, output has audio stream with duration > 0", async () => {
const res = await postTool(
"convert-audio",
{ format: "mp3" },
SPEECH_WAV,
"speech-10s.wav",
"audio/wav",
);
expect(res.statusCode).toBe(200);
const envelope = JSON.parse(res.body);
expect(envelope.downloadUrl).toBeDefined();
const payload = await download(envelope.downloadUrl);
const info = await probeBuffer(payload, "mp3");
const audio = info.streams.find((s) => s.type === "audio");
expect(audio).toBeDefined();
expect(info.durationS).toBeGreaterThan(0);
}, 60_000);
it("converts FLAC speech to WAV, output has audio stream", async () => {
const res = await postTool(
"convert-audio",
{ format: "wav" },
SPEECH_FLAC,
"speech.flac",
"audio/flac",
);
expect(res.statusCode).toBe(200);
const envelope = JSON.parse(res.body);
expect(envelope.downloadUrl).toBeDefined();
const payload = await download(envelope.downloadUrl);
const info = await probeBuffer(payload, "wav");
const audio = info.streams.find((s) => s.type === "audio");
expect(audio).toBeDefined();
expect(info.durationS).toBeGreaterThan(0);
}, 60_000);
it("converts OGG speech to FLAC, output is valid FLAC", async () => {
const res = await postTool(
"convert-audio",
{ format: "flac" },
SPEECH_OGG,
"speech.ogg",
"audio/ogg",
);
expect(res.statusCode).toBe(200);
const envelope = JSON.parse(res.body);
expect(envelope.downloadUrl).toBeDefined();
const payload = await download(envelope.downloadUrl);
const info = await probeBuffer(payload, "flac");
const audio = info.streams.find((s) => s.type === "audio");
expect(audio).toBeDefined();
expect(audio!.codec).toMatch(/flac/i);
expect(info.durationS).toBeGreaterThan(0);
}, 60_000);
});
// -----------------------------------------------------------------------
// Normalize audio depth tests
// -----------------------------------------------------------------------
describe.skipIf(!ffmpegAvailable())("Audio depth: normalize-audio real speech", () => {
it("normalizes WAV speech, output is valid audio with duration > 0", async () => {
const res = await postTool("normalize-audio", {}, SPEECH_WAV, "speech-10s.wav", "audio/wav");
expect(res.statusCode).toBe(200);
const envelope = JSON.parse(res.body);
expect(envelope.downloadUrl).toBeDefined();
const payload = await download(envelope.downloadUrl);
expect(payload.length).toBeGreaterThan(100);
const info = await probeBuffer(payload, "wav");
const audio = info.streams.find((s) => s.type === "audio");
expect(audio).toBeDefined();
expect(info.durationS).toBeGreaterThan(0);
}, 60_000);
});
@@ -0,0 +1,237 @@
/**
* Tier-2 depth tests: real multipage PDF heroes through real document tool routes.
*
* Exercises split-pdf, merge-pdf, compress-pdf, and pdf-to-image with the typed
* fixture registry, verifying page counts (qpdf), valid PDF headers, and image
* output via Sharp. Each tool is gated on its required binary.
*/
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { gsAvailable, qpdfAvailable, qpdfPageCount } from "@snapotter/doc-engine";
import sharp from "sharp";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { fixtures, readFixture } from "../../fixtures/index.js";
import {
buildTestApp,
createMultipartPayload,
loginAsAdmin,
type TestApp,
} from "../test-server.js";
const PDF_MULTI = readFixture(fixtures.document.pdfMulti); // 6-page
const PDF_2 = readFixture(fixtures.document.pdf2); // 2-page
let testApp: TestApp;
let app: TestApp["app"];
let adminToken: string;
beforeAll(async () => {
testApp = await buildTestApp();
app = testApp.app;
adminToken = await loginAsAdmin(app);
}, 30_000);
afterAll(async () => {
await testApp.cleanup();
}, 10_000);
/** Download a result and return the raw payload. */
async function download(downloadUrl: string): Promise<Buffer> {
const dl = await app.inject({
method: "GET",
url: downloadUrl,
headers: { authorization: `Bearer ${adminToken}` },
});
expect(dl.statusCode).toBe(200);
return dl.rawPayload;
}
/** Write buffer to temp, count pages, clean up. */
async function countPages(buf: Buffer): Promise<number> {
const dir = mkdtempSync(join(tmpdir(), "doc-depth-"));
try {
const outPath = join(dir, "output.pdf");
writeFileSync(outPath, buf);
return await qpdfPageCount(outPath);
} finally {
rmSync(dir, { recursive: true, force: true });
}
}
// -----------------------------------------------------------------------
// Split PDF depth tests
// -----------------------------------------------------------------------
describe.skipIf(!qpdfAvailable())("Document depth: split-pdf multipage hero", () => {
it("extracts pages 1-3 from 6-page PDF, output has 3 pages", async () => {
const { body, contentType } = createMultipartPayload([
{
name: "file",
filename: "multipage-6.pdf",
contentType: "application/pdf",
content: PDF_MULTI,
},
{ name: "settings", content: JSON.stringify({ mode: "range", range: "1-3" }) },
]);
const res = await app.inject({
method: "POST",
url: "/api/v1/tools/split-pdf",
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body,
});
expect(res.statusCode).toBe(200);
const envelope = JSON.parse(res.body);
expect(envelope.downloadUrl).toBeDefined();
const payload = await download(envelope.downloadUrl);
// Verify PDF header
expect(payload.subarray(0, 5).toString()).toBe("%PDF-");
const pages = await countPages(payload);
expect(pages).toBe(3);
}, 60_000);
});
// -----------------------------------------------------------------------
// Merge PDF depth tests
// -----------------------------------------------------------------------
describe.skipIf(!qpdfAvailable())("Document depth: merge-pdf real heroes", () => {
it("merges 6-page + 2-page PDF into 8-page document", async () => {
const { body, contentType } = createMultipartPayload([
{
name: "file",
filename: "multipage-6.pdf",
contentType: "application/pdf",
content: PDF_MULTI,
},
{
name: "file",
filename: "alt-2page.pdf",
contentType: "application/pdf",
content: PDF_2,
},
{ name: "settings", content: JSON.stringify({}) },
]);
const res = await app.inject({
method: "POST",
url: "/api/v1/tools/merge-pdf",
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body,
});
expect(res.statusCode).toBe(200);
const envelope = JSON.parse(res.body);
expect(envelope.downloadUrl).toBeDefined();
const payload = await download(envelope.downloadUrl);
expect(payload.subarray(0, 5).toString()).toBe("%PDF-");
const pages = await countPages(payload);
expect(pages).toBe(8);
}, 60_000);
});
// -----------------------------------------------------------------------
// Compress PDF depth tests
// -----------------------------------------------------------------------
describe.skipIf(!gsAvailable())("Document depth: compress-pdf multipage hero", () => {
it("compresses 6-page PDF, output is a valid PDF", async () => {
const { body, contentType } = createMultipartPayload([
{
name: "file",
filename: "multipage-6.pdf",
contentType: "application/pdf",
content: PDF_MULTI,
},
{
name: "settings",
content: JSON.stringify({ mode: "quality", quality: 60 }),
},
]);
const res = await app.inject({
method: "POST",
url: "/api/v1/tools/compress-pdf",
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body,
});
expect(res.statusCode).toBe(200);
const envelope = JSON.parse(res.body);
expect(envelope.downloadUrl).toBeDefined();
const payload = await download(envelope.downloadUrl);
// Valid PDF header
expect(payload.subarray(0, 5).toString()).toBe("%PDF-");
expect(payload.length).toBeGreaterThan(0);
}, 120_000);
});
// -----------------------------------------------------------------------
// PDF to image depth tests
// -----------------------------------------------------------------------
describe("Document depth: pdf-to-image multipage hero", () => {
it("converts page 1 of 6-page PDF to PNG, output is a valid image", async () => {
const { body, contentType } = createMultipartPayload([
{
name: "file",
filename: "multipage-6.pdf",
contentType: "application/pdf",
content: PDF_MULTI,
},
{
name: "settings",
content: JSON.stringify({ format: "png", dpi: 72, pages: "1" }),
},
]);
const res = await app.inject({
method: "POST",
url: "/api/v1/tools/pdf-to-image",
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body,
});
expect(res.statusCode).toBe(200);
const data = JSON.parse(res.body);
expect(data.pages).toHaveLength(1);
expect(data.pages[0].downloadUrl).toBeDefined();
expect(data.pageCount).toBe(6);
expect(data.selectedPages).toEqual([1]);
// Download the page image and verify it decodes via Sharp
const imgPayload = await download(data.pages[0].downloadUrl);
const meta = await sharp(imgPayload).metadata();
expect(meta.width).toBeGreaterThan(0);
expect(meta.height).toBeGreaterThan(0);
});
it("reports correct page count for 6-page PDF via /info", async () => {
const { body, contentType } = createMultipartPayload([
{
name: "file",
filename: "multipage-6.pdf",
contentType: "application/pdf",
content: PDF_MULTI,
},
]);
const res = await app.inject({
method: "POST",
url: "/api/v1/tools/pdf-to-image/info",
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body,
});
expect(res.statusCode).toBe(200);
const data = JSON.parse(res.body);
expect(data.pageCount).toBe(6);
});
});
@@ -0,0 +1,250 @@
/**
* Tier-2 depth tests: real photo/HEIC heroes through real image tool routes.
*
* Exercises resize, compress, convert, and info with the typed fixture registry,
* verifying output dimensions, file validity, and metadata via Sharp decode.
*/
import sharp from "sharp";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { fixtures, readFixture } from "../../fixtures/index.js";
import {
buildTestApp,
createMultipartPayload,
loginAsAdmin,
type TestApp,
} from "../test-server.js";
const PORTRAIT_JPG = readFixture(fixtures.image.portrait.jpg);
const PORTRAIT_HEIC = readFixture(fixtures.image.portrait.heic);
const EXIF_JPG = readFixture(fixtures.image.exifGps);
const PNG_200 = readFixture(fixtures.image.base.png200);
let testApp: TestApp;
let app: TestApp["app"];
let adminToken: string;
beforeAll(async () => {
testApp = await buildTestApp();
app = testApp.app;
adminToken = await loginAsAdmin(app);
}, 30_000);
afterAll(async () => {
await testApp.cleanup();
}, 10_000);
/** POST a file to a tool route and return the raw response. */
async function postTool(
toolId: string,
settings: Record<string, unknown>,
file: Buffer,
filename: string,
contentType: string,
) {
const { body, contentType: ct } = createMultipartPayload([
{ name: "file", filename, contentType, content: file },
{ name: "settings", content: JSON.stringify(settings) },
]);
return app.inject({
method: "POST",
url: `/api/v1/tools/${toolId}`,
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": ct,
},
body,
});
}
/** Download a result from its downloadUrl and return the raw payload. */
async function download(downloadUrl: string): Promise<Buffer> {
const dl = await app.inject({
method: "GET",
url: downloadUrl,
headers: { authorization: `Bearer ${adminToken}` },
});
expect(dl.statusCode).toBe(200);
return dl.rawPayload;
}
// -----------------------------------------------------------------------
// Resize depth tests
// -----------------------------------------------------------------------
describe("Image depth: resize real heroes", () => {
it("resizes portrait JPG to 300px wide, output re-decodes at exact dims", async () => {
const res = await postTool(
"resize",
{ width: 300 },
PORTRAIT_JPG,
"portrait-color.jpg",
"image/jpeg",
);
expect(res.statusCode).toBe(200);
const envelope = JSON.parse(res.body);
expect(envelope.downloadUrl).toBeDefined();
const payload = await download(envelope.downloadUrl);
const meta = await sharp(payload).metadata();
expect(meta.width).toBe(300);
expect(meta.height).toBeGreaterThan(0);
});
it("resizes PNG to 50x50 cover, output is exactly 50x50", async () => {
const res = await postTool(
"resize",
{ width: 50, height: 50, fit: "cover" },
PNG_200,
"test.png",
"image/png",
);
expect(res.statusCode).toBe(200);
const envelope = JSON.parse(res.body);
const payload = await download(envelope.downloadUrl);
const meta = await sharp(payload).metadata();
expect(meta.width).toBe(50);
expect(meta.height).toBe(50);
});
});
// -----------------------------------------------------------------------
// Compress depth tests
// -----------------------------------------------------------------------
describe("Image depth: compress real heroes", () => {
it("compresses portrait JPG, output is valid and smaller", async () => {
const res = await postTool(
"compress",
{ mode: "quality", quality: 40 },
PORTRAIT_JPG,
"portrait-color.jpg",
"image/jpeg",
);
expect(res.statusCode).toBe(200);
const envelope = JSON.parse(res.body);
expect(envelope.downloadUrl).toBeDefined();
const payload = await download(envelope.downloadUrl);
// Output must be a valid image
const meta = await sharp(payload).metadata();
expect(meta.width).toBeGreaterThan(0);
// Compressed size should be smaller than original
expect(payload.length).toBeLessThan(PORTRAIT_JPG.length);
});
it("compresses PNG, output is a valid decodable image", async () => {
const res = await postTool(
"compress",
{ mode: "quality", quality: 60 },
PNG_200,
"test.png",
"image/png",
);
expect(res.statusCode).toBe(200);
const envelope = JSON.parse(res.body);
const payload = await download(envelope.downloadUrl);
const meta = await sharp(payload).metadata();
expect(meta.width).toBe(200);
expect(meta.height).toBe(150);
});
});
// -----------------------------------------------------------------------
// Convert depth tests (HEIC -> JPG/PNG)
// -----------------------------------------------------------------------
describe("Image depth: convert HEIC hero", { timeout: 120_000 }, () => {
it("converts HEIC portrait to JPG, output is a valid JPEG", async () => {
const res = await postTool(
"convert",
{ format: "jpg", quality: 80 },
PORTRAIT_HEIC,
"portrait-headshot.heic",
"image/heic",
);
expect(res.statusCode).toBe(200);
const envelope = JSON.parse(res.body);
expect(envelope.downloadUrl).toBeDefined();
const payload = await download(envelope.downloadUrl);
const meta = await sharp(payload).metadata();
expect(meta.format).toBe("jpeg");
expect(meta.width).toBeGreaterThan(0);
expect(meta.height).toBeGreaterThan(0);
});
it("converts HEIC portrait to PNG, output is a valid PNG", async () => {
const res = await postTool(
"convert",
{ format: "png" },
PORTRAIT_HEIC,
"portrait-headshot.heic",
"image/heic",
);
expect(res.statusCode).toBe(200);
const envelope = JSON.parse(res.body);
expect(envelope.downloadUrl).toBeDefined();
const payload = await download(envelope.downloadUrl);
const meta = await sharp(payload).metadata();
expect(meta.format).toBe("png");
expect(meta.width).toBeGreaterThan(0);
});
});
// -----------------------------------------------------------------------
// Info / metadata depth tests
// -----------------------------------------------------------------------
describe("Image depth: extract metadata from EXIF hero", () => {
it("extracts EXIF data from test-with-exif.jpg", async () => {
const { body, contentType } = createMultipartPayload([
{
name: "file",
filename: "test-with-exif.jpg",
contentType: "image/jpeg",
content: EXIF_JPG,
},
]);
const res = await app.inject({
method: "POST",
url: "/api/v1/tools/info",
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body,
});
expect(res.statusCode).toBe(200);
const result = JSON.parse(res.body);
expect(result.hasExif).toBe(true);
expect(result.format).toBe("jpeg");
expect(result.width).toBeGreaterThan(0);
expect(result.height).toBeGreaterThan(0);
expect(result.fileSize).toBeGreaterThan(0);
expect(typeof result.colorSpace).toBe("string");
});
it("returns dimensions for portrait JPG hero", async () => {
const { body, contentType } = createMultipartPayload([
{
name: "file",
filename: "portrait-color.jpg",
contentType: "image/jpeg",
content: PORTRAIT_JPG,
},
]);
const res = await app.inject({
method: "POST",
url: "/api/v1/tools/info",
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body,
});
expect(res.statusCode).toBe(200);
const result = JSON.parse(res.body);
expect(result.width).toBeGreaterThan(0);
expect(result.height).toBeGreaterThan(0);
expect(result.format).toBe("jpeg");
expect(result.channels).toBeGreaterThanOrEqual(3);
});
});
@@ -0,0 +1,168 @@
/**
* Tier-2 depth tests: real BBB hero through real video tool routes.
*
* Exercises convert-video, trim-video, and extract-audio with the typed
* fixture registry, verifying output codecs, duration, and stream presence
* via ffprobe.
*/
import { mkdtempSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { ffmpegAvailable, probeMedia } from "@snapotter/media-engine";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { fixtures, readFixture } from "../../fixtures/index.js";
import {
buildTestApp,
createMultipartPayload,
loginAsAdmin,
type TestApp,
} from "../test-server.js";
const HERO_MP4 = readFixture(fixtures.video.hero.mp4);
let testApp: TestApp;
let app: TestApp["app"];
let adminToken: string;
beforeAll(async () => {
testApp = await buildTestApp();
app = testApp.app;
adminToken = await loginAsAdmin(app);
}, 30_000);
afterAll(async () => {
await testApp.cleanup();
}, 10_000);
/** POST a file to a tool route. */
async function postTool(
toolId: string,
settings: Record<string, unknown>,
file: Buffer = HERO_MP4,
filename = "media-30s.mp4",
ct = "video/mp4",
) {
const { body, contentType } = createMultipartPayload([
{ name: "file", filename, contentType: ct, content: file },
{ name: "settings", content: JSON.stringify(settings) },
]);
return app.inject({
method: "POST",
url: `/api/v1/tools/${toolId}`,
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body,
});
}
/**
* Resolve both the 200 (sync) and 202 (async poll) paths.
* Long-hint video tools may complete within the sync window or fall back to 202.
*/
async function resolveResult(res: Awaited<ReturnType<typeof postTool>>): Promise<Buffer> {
if (res.statusCode === 200) {
const envelope = JSON.parse(res.body);
expect(envelope.downloadUrl).toBeDefined();
const dl = await app.inject({
method: "GET",
url: envelope.downloadUrl,
headers: { authorization: `Bearer ${adminToken}` },
});
expect(dl.statusCode).toBe(200);
return dl.rawPayload;
}
// 202 async path: poll the job table until terminal
expect(res.statusCode).toBe(202);
const { jobId } = JSON.parse(res.body);
expect(jobId).toBeDefined();
const { db, schema } = await import("../../../apps/api/src/db/index.js");
const { eq } = await import("drizzle-orm");
let row: { status: string; outputRefs: unknown } | undefined;
for (let i = 0; i < 120; i++) {
[row] = await db.select().from(schema.jobs).where(eq(schema.jobs.id, jobId));
if (row && ["completed", "failed", "canceled"].includes(row.status)) break;
await new Promise((r) => setTimeout(r, 500));
}
expect(row?.status).toBe("completed");
const outName = (row?.outputRefs as string[])[0].split("/").pop() as string;
const dl = await app.inject({
method: "GET",
url: `/api/v1/download/${jobId}/${encodeURIComponent(outName)}`,
});
expect(dl.statusCode).toBe(200);
return dl.rawPayload;
}
/** Write a buffer to a temp file and probe it. */
async function probeBuffer(buf: Buffer, ext: string) {
const dir = mkdtempSync(join(tmpdir(), "vid-depth-"));
const filePath = join(dir, `output.${ext}`);
writeFileSync(filePath, buf);
return probeMedia(filePath);
}
// -----------------------------------------------------------------------
describe.skipIf(!ffmpegAvailable())("Video depth: convert-video hero", () => {
it("converts hero mp4 to webm, output ffprobes to vp8/vp9 with duration > 0", async () => {
const res = await postTool("convert-video", { format: "webm", quality: "small" });
const payload = await resolveResult(res);
expect(payload.length).toBeGreaterThan(100);
const info = await probeBuffer(payload, "webm");
const video = info.streams.find((s) => s.type === "video");
expect(video).toBeDefined();
expect(video!.codec).toMatch(/vp[89]/i);
expect(info.durationS).toBeGreaterThan(0);
}, 120_000);
it("converts hero mp4 to mp4 (re-encode), output has h264 video stream", async () => {
const res = await postTool("convert-video", { format: "mp4" });
const payload = await resolveResult(res);
expect(payload.length).toBeGreaterThan(100);
const info = await probeBuffer(payload, "mp4");
const video = info.streams.find((s) => s.type === "video");
expect(video).toBeDefined();
expect(info.durationS).toBeGreaterThan(0);
}, 120_000);
});
// -----------------------------------------------------------------------
describe.skipIf(!ffmpegAvailable())("Video depth: trim-video hero", () => {
it("trims hero to 0-5s, output duration is shorter than input", async () => {
const res = await postTool("trim-video", { startS: 0, endS: 5 });
const payload = await resolveResult(res);
expect(payload.length).toBeGreaterThan(100);
const info = await probeBuffer(payload, "mp4");
expect(info.durationS).toBeGreaterThan(0);
// Trimmed clip should be at most ~7s (keyframe alignment tolerance)
expect(info.durationS as number).toBeLessThanOrEqual(8);
// Original hero is ~30s, trimmed must be strictly shorter
const origDir = mkdtempSync(join(tmpdir(), "vid-orig-"));
const origPath = join(origDir, "orig.mp4");
writeFileSync(origPath, HERO_MP4);
const origInfo = await probeMedia(origPath);
expect(info.durationS as number).toBeLessThan(origInfo.durationS as number);
}, 120_000);
});
// -----------------------------------------------------------------------
describe.skipIf(!ffmpegAvailable())("Video depth: extract-audio hero", () => {
it("extracts audio from hero mp4 as mp3, output has an audio stream", async () => {
const res = await postTool("extract-audio", { format: "mp3" });
const payload = await resolveResult(res);
expect(payload.length).toBeGreaterThan(100);
const info = await probeBuffer(payload, "mp3");
const audio = info.streams.find((s) => s.type === "audio");
expect(audio).toBeDefined();
expect(info.durationS).toBeGreaterThan(0);
}, 120_000);
});