Five fixes uncovered by actually running the suite in docker-compose.test.yml:
1. (real prod bug) PATCH /api/settings/digest mutated principal.user which
require_token had loaded in a now-closed session — the commit on the
handler's session persisted nothing. Re-fetch the user via the active
session before writing.
2. Portable PK type. SQLite only auto-fills `INTEGER PRIMARY KEY`; plain
BIGINT requires explicit values. Define a `_PK` alias of
`BigInteger().with_variant(Integer(), "sqlite")` and use it for all 10
autoincrement primary keys in app/models.py. No prod-schema change
(MariaDB still gets BIGINT).
3. job_lifecycle's MariaDB GET_LOCK / RELEASE_LOCK is now gated behind
`dialect.name == "mysql"`, so the test SQLite engine doesn't trip on
the missing function. Single-process test runs can't race themselves.
4. tests/test_news_window.py seeded Headline rows without `fingerprint`,
which is NOT NULL — added an `fp-{title}` value per row.
5. tests/test_email_digest_job.py now also patches `llm_configured` to
True so the job doesn't short-circuit on the missing API key.
6. (test container hygiene) Drop `COPY tests ./tests` from the test stage
in the Dockerfile — .dockerignore excludes `tests/` (correct: prod
image must not bake tests), and docker-compose.test.yml bind-mounts
./tests at run time anyway.
Suite now: 198 passed, 5 skipped, 1 pre-existing failure
(test_default_groups_present — Phase G dropped the "pie" group from
config/default.toml but the assertion wasn't updated; unrelated to this
branch).
62 lines
1.7 KiB
Docker
62 lines
1.7 KiB
Docker
# syntax=docker/dockerfile:1.7
|
|
FROM python:3.13-slim AS builder
|
|
|
|
ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \
|
|
PIP_NO_CACHE_DIR=1 \
|
|
PYTHONDONTWRITEBYTECODE=1
|
|
|
|
WORKDIR /build
|
|
COPY pyproject.toml ./
|
|
COPY app ./app
|
|
RUN python -m venv /opt/venv \
|
|
&& /opt/venv/bin/pip install --upgrade pip \
|
|
&& /opt/venv/bin/pip install .
|
|
|
|
FROM python:3.13-slim AS runtime
|
|
|
|
ENV PYTHONUNBUFFERED=1 \
|
|
PYTHONDONTWRITEBYTECODE=1 \
|
|
PATH="/opt/venv/bin:$PATH" \
|
|
TZ=UTC
|
|
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends curl \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
COPY --from=builder /opt/venv /opt/venv
|
|
WORKDIR /app
|
|
COPY app ./app
|
|
COPY alembic ./alembic
|
|
COPY alembic.ini ./
|
|
|
|
# Default command is the web app; scheduler container overrides via `command:`.
|
|
EXPOSE 8000
|
|
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1"]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Test stage — same Python, same prod deps, plus dev extras (pytest +
|
|
# aiosqlite). Built and run only via docker-compose.test.yml; never shipped.
|
|
# ---------------------------------------------------------------------------
|
|
FROM python:3.13-slim AS test
|
|
|
|
ENV PYTHONUNBUFFERED=1 \
|
|
PYTHONDONTWRITEBYTECODE=1 \
|
|
PATH="/opt/venv/bin:$PATH" \
|
|
TZ=UTC \
|
|
PIP_DISABLE_PIP_VERSION_CHECK=1 \
|
|
PIP_NO_CACHE_DIR=1
|
|
|
|
COPY --from=builder /opt/venv /opt/venv
|
|
WORKDIR /app
|
|
COPY pyproject.toml ./
|
|
COPY app ./app
|
|
COPY alembic ./alembic
|
|
COPY alembic.ini ./
|
|
# tests/ is excluded by .dockerignore (prod-correct: never bake tests into
|
|
# a shipped image). docker-compose.test.yml bind-mounts ./tests:/app/tests
|
|
# at run time, so the suite is always available without baking it in.
|
|
|
|
RUN /opt/venv/bin/pip install ".[dev]"
|
|
|
|
CMD ["pytest", "tests/", "-v"]
|