- ops.py + test_{upgrade,backup,restore}.py: seed ci_marker into the MySQL `ghost` DB (db service)
via the mysql CLI; rides the recipe's mysqldump --tab backup. recipe is MySQL not sqlite (stale
comment fixed). Expect restore RED -> recipe-PR (no backupbot.restore hook; immich/mattermost class).
- functional/_ghost.py: cookie-aware Ghost Admin API client (stdlib http.cookiejar; Origin CSRF hdr).
- functional/test_post_roundtrip.py: §4.3 create published post + read back (unique marker, non-vacuous);
closes the DEFERRED ghost create-post item.
- PARITY.md + recipe_meta.py updated. Authored node-free; full-lifecycle run next, NOT yet claimed.
53 lines
3.3 KiB
Markdown
53 lines
3.3 KiB
Markdown
# Parity — ghost
|
|
|
|
The recipe-maintainer corpus has **no** `recipe-info/ghost/tests/` directory — ghost was not in
|
|
their parity suite. This PARITY.md documents the Phase-2 health_check (parity-aligned baseline)
|
|
+ recipe-specific tests beyond.
|
|
|
|
## Recipe-specific tests (Phase-2 P3, ≥2 beyond parity)
|
|
|
|
Ghost is a **publishing platform** with a public themed site at `/`, an admin UI at `/ghost/`,
|
|
and a JSON Content/Admin API at `/ghost/api/*`. Defining behaviors exercised:
|
|
|
|
| cc-ci file | what's verified | rationale |
|
|
|---|---|---|
|
|
| `tests/ghost/functional/test_content_api.py` | GETs `/ghost/api/content/settings/`; asserts 200 with `{"settings": {...}}` envelope OR 401/403 with a Ghost error envelope. | Distinguishes "the ghost-server JS process is up + emitting its API" from "a static themed page is served at /." A wedged Ghost backend → 5xx; misrouted nginx → 404. |
|
|
| `tests/ghost/functional/test_admin_redirect.py` | GETs `/ghost/`; asserts 200 or 302 + Ghost branding/SPA references in the response (or a redirect to /ghost/#/setup on fresh deploy). | Proves the admin route is wired through the nginx proxy. Distinguishes "admin SPA bound" from "404 (route missing)" or "5xx (broken)." |
|
|
|
|
Two specific tests + parity health_check = ≥2 floor met.
|
|
|
|
## Plan §4.3 prescribed deeper test — AUTHORED (closes DEFERRED ghost create-post)
|
|
|
|
§4.3 named "create-a-post round-trip" for ghost. Implemented in
|
|
`tests/ghost/functional/test_post_roundtrip.py` (helper `functional/_ghost.py`):
|
|
1. Wait for the Admin API healthcheck (`GET /ghost/api/admin/site/` → 200).
|
|
2. Setup the Ghost owner (POST `/ghost/api/admin/authentication/setup/`, fresh deploy) + establish
|
|
an admin **session cookie** (POST `/ghost/api/admin/session/`) — cookie-aware stdlib opener,
|
|
version-negotiated (no `/v3/` in the path; recipe-versioned).
|
|
3. POST `/ghost/api/admin/posts/?source=html` to create a published post with a unique marker in
|
|
title + body.
|
|
4. GET `/ghost/api/admin/posts/<id>/?formats=html` to read it back; assert title + body marker
|
|
round-trip intact (unique-per-run → non-vacuous).
|
|
|
|
Admin creds are class-B run-scoped (destroyed at teardown with the app).
|
|
|
|
## Backup data-integrity (P4) — AUTHORED
|
|
|
|
`ops.py` + `test_install`-free lifecycle overlays (`test_upgrade.py` / `test_backup.py` /
|
|
`test_restore.py`) seed a deterministic `ci_marker` row into the **MySQL** `ghost` DB (the recipe's
|
|
real state store) via the `mysql` CLI in the `db` service. The recipe's backupbot pre-hook
|
|
(`mysqldump ghost --tab`) dumps that table into the backed-up path, so the marker rides
|
|
backup→restore the way a real post's row would. pre_restore drops the table (divergence); the
|
|
restore overlay asserts it returned.
|
|
|
|
**Expected RED until a recipe-PR lands:** the ghost recipe has a logical mysqldump backup but **no
|
|
`backupbot.restore.*` hook** (and the mysql data volume itself isn't backupbot-labelled), so a
|
|
file-level restore never reimports the dump — same defect class fixed in immich#1 / mattermost-lts#1.
|
|
If `test_restore_returns_state` goes RED, the durable fix is a recipe-PR adding a mysqldump-reimport
|
|
restore post-hook. (See `test_restore.py` docstring + DECISIONS.md.)
|
|
|
|
## Playwright (P6)
|
|
|
|
Not yet authored. Ghost's admin UI is an Ember SPA; a Playwright flow would exercise the
|
|
setup wizard + post creation. Q4 follow-up.
|