chore(2): bootstrap Phase 2 loop state + decisions

- STATUS-2.md / BACKLOG-2.md / JOURNAL-2.md seeded from plan §6 (Q0-Q5).
- DECISIONS.md appended Phase 2 section: functional/ + playwright/ subdirs,
  PARITY.md mapping convention, vendored helpers in runner/harness/
  (http, abra_tty, deps, sso, data_integrity), recipe-versioned tests.
- Bootstrap access re-verified: ssh cc-ci ok, Gitea API 200, wildcard DNS to
  gateway 143.244.213.108.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-28 04:34:27 +01:00
parent e7e3e24aed
commit 8f5df6d257
4 changed files with 275 additions and 0 deletions

View File

@ -379,3 +379,64 @@ Three operator-review corrections to the Phase-1d shared harness, settled here (
`coop-cloud.<stack>.chaos`/`.chaos-version`) — the chaos label IS the proof PR-head was deployed.
Non-PR `!testme` (no SRC/REF): "PR head" = the catalogue current checkout, so upgrade is prev→current
— still a genuine move via chaos. (Exact chaos label name verified on the live abra during E2.)
## Phase 2 — per-recipe test authoring (design, 2026-05-28)
Inherits the Phase 1d/1e shared-deployment + additive-overlay + op/assertion-split model. Phase 2
adds **content**, not infra, with a few small harness primitives ported from
`references/recipe-maintainer/utils/tests/helpers.py`.
- **Per-recipe layout (per plan §4.1).** The cc-ci `tests/<recipe>/` dir continues to use the
Phase-1d/1e overlays at the top level (`test_install.py`, `test_upgrade.py`, `test_backup.py`,
`test_restore.py`, `ops.py`, `recipe_meta.py`, optional `install_steps.sh`). NEW Phase-2
subdirectories:
- `tests/<recipe>/functional/` — parity-port tests (one per recipe-maintainer `tests/*.py`) +
≥2 NEW recipe-specific functional tests (P2/P3). Each file is `test_*.py` (pytest-discoverable);
each parity port carries a **`SOURCE = "recipe-info/<recipe>/tests/<file>"`** comment near the
top so the audit trail is in the file, not just in PARITY.md.
- `tests/<recipe>/playwright/` — browser flows (P6) where the app's UX is a UI flow. Same
`test_*.py` convention; each file imports `playwright.sync_api`.
- `tests/<recipe>/PARITY.md` — required mapping table (P2) with one row per
recipe-info parity test: `| recipe-maintainer file | cc-ci file | what's verified | status |`.
A deliberate non-port is a documented row in DECISIONS.md (linked from PARITY.md), not a silent
omission.
- **Discovery for the new subdirs.** `runner/harness/discovery.custom_tests` recurses into
`tests/<recipe>/functional/` and `tests/<recipe>/playwright/` (in addition to the top-level glob),
so Phase-2 functional tests run as part of the **custom** stage automatically. Repo-local (HC2)
gate still applies if the recipe is approved; otherwise only cc-ci's own functional/ + playwright/
run. The top-level `test_install.py`/etc. continue to drive the lifecycle overlays — the
`functional/` + `playwright/` files are **always custom-stage**, never lifecycle (so they don't
perform an op; they assert against the post-install live deployment).
- **Vendored helpers in `runner/harness/`.** Capabilities ported from `recipe-maintainer/utils/tests/
helpers.py` (cc-ci is self-contained at runtime — does NOT import recipe-maintainer's workspace,
per plan §8 default):
- `harness.http` — `http_get(url, headers=, timeout=) -> (status, json_or_None)`,
`http_post(...)`, `retry_http_get(url, timeout=, **)`, `wait_for_http(url, label, max_wait=)`,
`assert_converges(fn, description, max_wait=, interval=)`. (Several variants exist
`lifecycle.http_fetch/http_get/http_body` already; the harness.http module is the **canonical**
Phase-2 HTTP API for tests; lifecycle.* helpers stay for infra-level checks.)
- `harness.abra_tty` — `script -qefc "abra …" /dev/null` wrapper for the abra commands that
require a TTY (backup/restore/secret/run/logs/lint), used by parity tests that drive abra
directly. Lifecycle already exposes typed wrappers — this is for tests that need raw shell-abra.
- `harness.deps` — dependency resolver primitive. Reads `tests/<recipe>/recipe.toml`
(`requires` / `test_requires`), deploys each declared dep via the same `lifecycle.deploy_app`
+ `wait_healthy` path (so the dep is a real `<dep[:4]>-<6hex>.ci.commoninternet.net` on the
same swarm), persists per-run, tears down with the parent in the orchestrator's `finally`.
Heavy recipes sequence sequentially; `MAX_TESTS`/node budget is the cap.
- `harness.sso` — OIDC-flow primitive (Q2 deliverable). Given a deployed provider domain and a
recipe-defined realm/client/test-user, performs the full "deploy provider → setup realm/client
via admin API → obtain access token (password + client-credentials grants) → assert protected
API call accepts it" assertion. Reusable by every SSO-dependent recipe (cryptpad, lasuite-*,
immich, etc.). Setup scripts ported from `recipe-info/<dep>/setup_<provider>_integration.py`.
- `harness.data_integrity` — backup data-integrity primitive: a recipe-aware "seed a marker
→ backup → mutate → restore → assert seeded marker survived" helper around `lifecycle.exec_in_app`
/ `http_get` (the recipe chooses the marker mechanism, the helper guarantees the pattern).
- **Run-scoped credentials for SSO/recipe-specific tests** (plan §4.4 class-B). Generated secrets
(realm/client/test-user passwords, API tokens) persist for the run via the existing
`runs/<app-name>/` mechanism (Phase 1d). Destroyed at teardown alongside abra secrets/volumes.
- **Recipe-versioned tests (anti-anchoring).** Per plan §7.1, tests read versions/endpoints
dynamically (the app's own discovery endpoints, env from `live_app`) — never hardcode published
release values. Each functional test file declares the recipe-info SOURCE path it ports from so
the Adversary can audit parity cold.
- **Heavy-recipe parking.** Drone's `MAX_TESTS=1` + per-build timeout already serialize runs; for
Phase 2 we DO NOT lift it. Within a single run, the orchestrator deploys deps before the
recipe-under-test sequentially (never concurrently) per plan §4.2.