Adds tests/cryptpad/playwright/test_pad_content_roundtrip.py: open /pad/ → CryptPad auto-creates a fragment-keyed pad → type a unique marker into the CKEditor body → wait for encrypted sync → open a FRESH browser context (no shared localStorage/cookies) → navigate to the captured pad URL → assert the marker survives in the re-decrypted body. Proves genuine end-to-end-encrypted server-side persistence (the fresh session carries only the URL+fragment key), the §4.3 create-and-read-back floor F2-9 requires — not a health/SPA stand-in. Empirically mapped against CryptPad 2026.2.0 (the prior deferral cited version-fragility on 5.7.0): editor is the deep nested frame …/pad/ckeditor-inner.html; ~15s cold-cache LESS-compile init; the fragment-keyed pad URL DOES appear after init; transient net::ERR_NETWORK_CHANGED handled by the shared goto_with_retry + a mid-load reload retry in the frame wait. PASSED against a live probe instance. PARITY.md updated (roundtrip = the P3/§4.3 test; SPA-render test kept as fast liveness). F2-9 is Adversary-owned — left for the Adversary to close after cold-verify. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.2 KiB
Parity — cryptpad
Phase-2 P2 mapping table. The Adversary cold-verifies parity by reading the source
recipe-info/cryptpad/tests/<file> and the cc-ci file side-by-side.
| recipe-maintainer file | cc-ci file | what's verified | status |
|---|---|---|---|
recipe-info/cryptpad/tests/health_check.py |
tests/cryptpad/functional/test_health_check.py |
HTTP 200 from the served root. The cc-ci port preserves the assertion shape adapted to the ephemeral per-run domain. | ported |
recipe-info/cryptpad/tests/oidc_login.py |
(Q3.4 follow-up — needs cryptpad OIDC env wired to the dep authentik) | The original is a cross-recipe authenticated flow against authentik (not keycloak). The cc-ci port requires: (1) Q2.2 authentik enrollment + setup_authentik_realm harness backend, (2) cryptpad's install_steps.sh wiring the dep authentik's client_secret + OIDC env. Both are tracked Q5 catch-up items. |
deferred |
Recipe-specific tests (Phase-2 P3, ≥2 beyond parity)
CryptPad is client-side end-to-end encrypted: every pad's content lives in the browser, with the encryption key in the URL fragment that never reaches the server. So a meaningful "create-an- object + read-it-back" test (plan §4.3 floor) MUST use a real browser (per plan §4.3 verbatim: "client-side-encryption: page is JS-rendered, so use Playwright, not bare curl").
| cc-ci file | what's verified | rationale |
|---|---|---|
tests/cryptpad/playwright/test_pad_content_roundtrip.py |
§4.3 create-an-object + read-it-back (resolves F2-9). Opens /pad/ → CryptPad auto-creates a fragment-keyed pad (#/2/pad/edit/<key>/); types a unique marker into the CKEditor rich-text body (nested sandbox iframe …/pad/ckeditor-inner.html); waits for the encrypted update to sync ("Saved"); then opens a brand-new browser context (no shared localStorage/cookies) and navigates to the captured pad URL; asserts the marker is present in the re-decrypted body. |
Phase 2 P3/§4.3 floor — proves genuine end-to-end-encrypted persistence: the fresh session carries only the URL (incl. its fragment key), so a successful read-back means the content was persisted server-side as ciphertext and correctly decrypted by a new client. Not a health/SPA stand-in. Mapped empirically against CryptPad 2026.2.0 (editor in a deep nested frame; ~15s cold-cache LESS-compile init; transient net::ERR_NETWORK_CHANGED handled by the shared goto_with_retry + a mid-load reload retry). |
tests/cryptpad/playwright/test_pad_create.py |
Browses to /. Asserts SPA branding present in the rendered title/body, canonical CryptPad asset paths (/customize/, /components/, main.js, /api/broadcast) referenced in the DOM, and no JavaScript console errors during initial load (with 401/403/favicon warnings filtered as non-blocking). |
Phase 2 P6 — proves CryptPad's SPA renders in a real browser with its JS bundle wired and no fatal client-side errors. (Complements the roundtrip test above; was the "maximal subset" while create-and-read-back was deferred — now superseded by the full roundtrip, kept as a fast SPA-liveness check.) |
tests/cryptpad/functional/test_spa_assets.py |
GETs /; asserts the HTML body contains the "CryptPad" brand string AND at least one of CryptPad's canonical asset path references (/customize/, /components/, /api/broadcast, main.js). |
Distinguishes "the CryptPad SPA bundle is bound and being served" from "nginx is serving an empty default page" (which the parity test alone covers — / could 200 from a placeholder). Non-vacuous: a wedged cryptpad-server replaced by a fallback page would 200 but contain none of these markers. |
Two specific tests — the ≥2 floor is met. Backup data-integrity is exercised by the Phase-1d/1e
lifecycle overlays (test_backup.py/test_restore.py + ops.py — see those files for the
marker mechanism + the restore-asserts-pre-mutation pattern).
Playwright (P6)
tests/cryptpad/playwright/test_pad_create.py (above) is the canonical browser flow — covers P6
in full.
Non-ports
oidc_login.py is documented above as deferred. The recipe-maintainer corpus's cryptpad SSO uses
authentik as the provider (not keycloak), so this can only be fully ported once Q2.2
authentik enrollment lands. No silent omissions.