Files
MechaCat02 345db4a076 test: close the DB-suite hermeticity + vacuous-skip gaps
Three related fixes from the test audit.

**The CLI journey fixture gets its own database.** It spawned a real picloud —
whose dispatcher/orchestrator claim loops are global by design (one instance owns
one database) — against the shared dev DB, so it could claim the manager-core
suites' outbox/workflow rows (the same class of bug already fixed for the e2e
suites, one binary over). It now clones one dedicated database per journey run
from the migrated template. test-support gains `named_test_db_url` (explicit
stable name) + a blocking wrapper for the sync `LazyLock` fixture. The one journey
that talks to Postgres directly (dead-letter injection) now uses the fixture's DB
URL, not the base DATABASE_URL, so it hits the database the server reads.

**workflow_orchestrator moves to per-test databases.** Its `claim_ready_step` is
global, so the old harness serialized every test behind a process-wide CLAIM_LOCK
AND ran `DELETE FROM workflow_runs` (unscoped — it wiped every app's runs) before
each one. A private database per test makes the global claim see only that test's
rows, so both the lock and the unscoped DELETE are deleted.

**DB-backed suites fail loud instead of skipping green.** ~15 manager-core suites
`return None` when DATABASE_URL is unset and report PASS — so in any environment
that lost its database the entire integration surface reports green while running
nothing (why the CI gap went unnoticed for so long). New
`picloud_test_support::abort_if_db_required` panics when `PICLOUD_REQUIRE_DB` is
set (CI now sets it) but DATABASE_URL is not, injected into each suite's skip
path. Local runs without the var still skip cleanly.

Mutation-verified: with PICLOUD_REQUIRE_DB=1 and DATABASE_URL unset, a suite
panics; without the var it skips.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 19:39:13 +02:00

116 lines
4.5 KiB
YAML

name: CI
on:
push:
branches: [main]
pull_request:
env:
CARGO_TERM_COLOR: always
# Matches what docker-compose produces locally; the schema-snapshot
# guardrail and any other DB-backed tests run against this service.
DATABASE_URL: postgres://picloud:picloud@localhost:5432/picloud
# The DB-backed suites skip themselves when DATABASE_URL is unset. In CI it is
# always set, so this makes that skip a hard error: if the database ever goes
# missing (a broken service container, a lost env), the build goes RED instead
# of green-but-empty. Honored by picloud_test_support::abort_if_db_required.
PICLOUD_REQUIRE_DB: "1"
jobs:
rust:
name: Rust — fmt, clippy, test
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_USER: picloud
POSTGRES_PASSWORD: picloud
POSTGRES_DB: picloud
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U picloud"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
# rust-toolchain.toml pins the channel; this action honors it.
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- name: Cache cargo
uses: Swatinem/rust-cache@v2
- name: Format check
run: cargo fmt --all -- --check
- name: Clippy
run: cargo clippy --all-targets --all-features -- -D warnings
# `--include-ignored` is load-bearing. Every DB-backed integration test is
# `#[ignore = "needs DATABASE_URL..."]`, and CI omitted the flag — so CI ran
# 927 tests and silently skipped 237, among them ALL of authz.rs and api.rs
# and the entire CLI journey suite. The isolation and RBAC tests existed but
# never executed (AUDIT.md F-Q-014). CI does provide Postgres, so run them.
#
# `--all-targets` (not a bare `cargo test`) is deliberate: it runs the lib,
# bins, and integration tests but NOT doctests. `-- --include-ignored`
# un-ignores not just `#[ignore]` tests but also ` ```ignore ` DOCTESTS,
# which are illustrative pseudocode that does not compile — so a bare
# `cargo test ... -- --include-ignored` fails on them. Doctests run in their
# own step below, without the flag. (Clippy already uses `--all-targets` for
# the same doctest-excluding reason.)
#
# The CLI journeys are a SEPARATE step, and deliberately not part of the
# workspace run: they spawn a real picloud whose dispatcher/orchestrator
# claim loops are global by design (one instance owns one database). Run
# concurrently with the manager-core suites — which share this database —
# it would claim their outbox and workflow rows out from under them. Keeping
# the steps sequential keeps that live server off the shared DB while the
# other suites are using it.
- name: Test (workspace, including DB-backed tests)
run: cargo test --workspace --exclude picloud-cli --all-targets -- --include-ignored
# Doctests, run WITHOUT --include-ignored so ` ```ignore ` snippets stay
# ignored. `--all-targets` above skips these, so nothing else covers them.
- name: Doctests
run: cargo test --workspace --doc
# The journey harness execs the prebuilt target/debug/picloud and does NOT
# rebuild it, so a stale binary would silently test old server code.
- name: Build picloud (the journey harness execs this binary)
run: cargo build -p picloud
# The spawned server inherits this env; without a secret key it aborts at
# startup and every journey fails as "/healthz never returned 200".
# `--all-targets` for the same reason as above (the journeys are `#[ignore]`
# integration tests; picloud-cli's doctests, if any, run in the Doctests step).
- name: Test (CLI journeys)
env:
PICLOUD_DEV_MODE: "true"
PICLOUD_DEV_INSECURE_KEY: i-understand-this-is-insecure
run: cargo test -p picloud-cli --all-targets -- --include-ignored
dashboard:
name: Dashboard — check
runs-on: ubuntu-latest
defaults:
run:
working-directory: dashboard
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: dashboard/package-lock.json
- name: Install deps
run: npm ci
- name: Svelte check
run: npm run check