US02-07: Automate Phase B End-to-End Acceptance (#60)

This commit was merged in pull request #60.
This commit is contained in:
2026-08-14 12:53:45 +02:00
parent b59bc93fcb
commit b9bcb47c68
11 changed files with 577 additions and 12 deletions

View File

@@ -17,6 +17,7 @@ ponytail: port the donor's retry+RPD throttling when analysis runs at real volum
from __future__ import annotations
import json
import os
import uuid
from datetime import datetime, timezone
from typing import Protocol
@@ -216,9 +217,45 @@ def _result_dict(row: AnalysisResult) -> dict:
def _default_provider() -> VisionProvider:
# Test seam (concept §18: deterministic fakes replace the vision edge, enabled
# only by test configuration). When this env var names a writable log file, the
# worker/API use a fake that records every analyzed path — so the SFW-only gate
# can be asserted end-to-end through the real integration layer — instead of the
# real OpenAI-compatible call. Never set in production.
log_path = os.environ.get("PHOTO_PIPELINE_FAKE_VISION_LOG")
if log_path:
return _RecordingFakeVision(log_path)
return OpenAIVisionProvider()
class _RecordingFakeVision:
"""Deterministic vision fake for end-to-end tests. Appends every analyzed file
path to its log so a test can prove NSFW assets never reach the provider, and
raises for a path whose stem contains ``boom`` to exercise per-asset error
handling. Constructed only when ``PHOTO_PIPELINE_FAKE_VISION_LOG`` is set."""
def __init__(self, log_path: str) -> None:
self._log_path = log_path
def analyze(self, path: str, *, album_hint: str) -> dict:
with open(self._log_path, "a", encoding="utf-8") as handle:
handle.write(path + "\n")
if "boom" in os.path.splitext(os.path.basename(path))[0]:
raise AnalysisError("fake vision failure")
return {
"description": f"a deterministic scene in {album_hint}",
"tags": ["fixture", "deterministic"],
"people_count": 1,
"setting": "outdoor",
"time_of_day": "day",
"season": "summer",
"mood": "calm",
"location_hint": None,
"approx_year": None,
"_tokens": 7,
}
class OpenAIVisionProvider:
"""The real provider: an OpenAI-compatible vision call (Gemini by default).