Files
SnapOtter/tests/qa/docker-compose.qa.yml
T
SnapOtterandGitHub bd1838e40b fix: repair docker validation QA tooling, dispatcher crash-accounting, and image-enhancement RAW hang (#391)
Found and fixed during a full local Docker build validation (amd64/arm64, all
four fleet targets, AI bundle installs, QA harness) and the follow-up bug
sweep requested afterward. None of the affected scripts run in CI, so these
had been silently broken indefinitely.

- docker/feature-manifest.json: pythonVersion was a flat "3.11", but the
  amd64 base (Ubuntu 24.04) ships Python 3.12 while arm64 (Debian bookworm)
  ships 3.11. Changed to a per-arch object matching the file's existing
  convention.
- tests/qa/api-sweep.mts and verify-ai.mts: bare "@snapotter/shared" import
  can't resolve since tests/ is not a pnpm workspace member, making both
  silently unrunnable via their own documented command on any fresh
  checkout. Switched to a relative import.
- tests/qa/generate-ledger.mts: wrote to docs/qa/ without creating the
  directory first; docs/ is gitignored except COMMUNITY_GUIDE.md, so a fresh
  checkout threw ENOENT.
- Seven QA Playwright spec files (input-preview, settings,
  settings-extended, multifile, output-preview, pipeline-ui, smoke) had
  ~115 fixture() calls using directory names that don't exist. Resolved
  every call programmatically against the real fixture tree.
- packages/ai/src/bridge.ts: AI dispatcher restart (happens on every bundle
  install) was falsely counted as a crash, risking permanent dispatcher
  disable after enough legitimate restarts within the crash window. Added a
  shuttingDown flag checked at all three recordCrash() call sites.
- packages/image-engine/src/operations/auto-enhance.ts: image-enhancement
  hung 40+ seconds on large RAW photos (confirmed on a real 20.2MP file) in
  Sharp's .clahe() step, whose cost scales with total pixel count regardless
  of tile size. Added a 16-megapixel cap above which CLAHE is skipped;
  verified against the real file (40+s -> 2.0s) with no regression to other
  RAW formats or normal-sized images. Fixing this surfaced a second,
  smaller bug where the saturation step's CLAHE compensation boost was
  keyed off the raw toggle instead of whether CLAHE actually ran.
- Two QA-harness robustness gaps closed per "fix everything, even the small
  bugs": the passport-photo/erase-object input-preview tests now skip
  cleanly with a clear reason on a container without their AI bundle
  installed, and docker-compose.qa.yml's hardcoded project/container name
  (the actual root cause of a mid-validation container swap between two
  concurrent sessions) is now parameterized via QA_PROJECT_NAME.

Full validation report is local-only per repo convention.
2026-07-02 14:21:13 +08:00

86 lines
2.6 KiB
YAML

# Isolated QA stack for the exhaustive tool QA sweep.
# Uses the pre-built snapotter/snapotter:latest image (built from docker/docker-compose.yml).
# Auth disabled (synthetic admin), high limits, fresh volumes, host port 13499 to avoid
# clashing with a native dev server on 1349.
# docker compose -f tests/qa/docker-compose.qa.yml up -d
# open http://localhost:13499
#
# Project/container names default to snapotter-qa. Two sessions on the same host
# running this file verbatim at the same time will silently steal each other's
# container (last `up` wins, no error) since container_name is fixed rather than
# derived from the project name. If you need a second concurrent stack, override:
# QA_PROJECT_NAME=snapotter-qa-2 docker compose -f tests/qa/docker-compose.qa.yml up -d
name: ${QA_PROJECT_NAME:-snapotter-qa}
services:
app:
image: snapotter/snapotter:latest
container_name: ${QA_PROJECT_NAME:-snapotter-qa}
ports:
- "13499:1349"
volumes:
- qa-data:/data
- qa-workspace:/tmp/workspace
environment:
- AUTH_ENABLED=false
- RATE_LIMIT_PER_MIN=100000
- PROCESSING_TIMEOUT_S=600
- MAX_UPLOAD_SIZE_MB=0
- MAX_BATCH_SIZE=0
- MAX_MEGAPIXELS=0
- CONCURRENT_JOBS=0
- MAX_WORKER_THREADS=0
- MAX_PIPELINE_STEPS=50
- ANALYTICS_ENABLED=false
- TRUST_PROXY=true
- DATABASE_URL=postgres://snapotter:snapotter@postgres:5432/snapotter
- REDIS_URL=redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
shm_size: "2gb"
healthcheck:
test: ["CMD", "curl", "-sf", "--max-time", "5", "http://localhost:1349/api/v1/health"]
interval: 15s
timeout: 5s
start_period: 90s
retries: 5
restart: unless-stopped
postgres:
image: postgres:17-alpine
container_name: ${QA_PROJECT_NAME:-snapotter-qa}-postgres
environment:
POSTGRES_USER: snapotter
POSTGRES_PASSWORD: snapotter
POSTGRES_DB: snapotter
volumes:
- qa-pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U snapotter"]
interval: 10s
timeout: 5s
retries: 12
start_period: 15s
redis:
image: redis:8-alpine
container_name: ${QA_PROJECT_NAME:-snapotter-qa}-redis
command: ["redis-server", "--maxmemory-policy", "noeviction", "--appendonly", "yes"]
volumes:
- qa-redisdata:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 12
start_period: 10s
volumes:
qa-data:
qa-workspace:
qa-pgdata:
qa-redisdata: