test: downstream wire-compat (vectors + smoke)#78
Conversation
Phase 2 of ether/etherpad#7923. Phase 1 added a canonical wire-format fixture that all Etherpad clients must decode identically. The desktop/mobile apps are thin shells: they embed core's Ace editor in a webview and load a server URL, so there is no local changeset decoder. The vectors test is therefore a fixture-integrity guard (shape/contract of the vendored wire-vectors.json), not a decode test. The smoke test is a headless-light HTTP roundtrip against the server contract the shell depends on; the full Electron e2e stays in this repo's own CI. - packages/shell/tests/fixtures/wire-vectors.json: vendored canonical fixture (overridable via ETHERPAD_WIRE_VECTORS). - packages/shell/tests/wire/vectors.spec.ts: asserts every record has the 5 fields with correct types; pool.numToAttrib is a plain object, nextNum a non-negative integer; initial/resultText non-empty and \n-terminated. - packages/shell/tests/wire/smoke.spec.ts: reads ETHERPAD_SMOKE_URL (default http://localhost:9003) + ETHERPAD_SMOKE_APIKEY; skips cleanly unless both a reachable server and a key are present. When live: create pad via HTTP API, fetch /p/<pad> (the URL the shell loads) -> 200, getText to confirm content. - root package.json: add test:vectors / test:smoke scripts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
Code Review by Qodo
1.
|
- Skip the reachability probe entirely when ETHERPAD_SMOKE_APIKEY is unset (no key => the test always skips, so the probe + timeout was wasted work). - Wrap the create -> fetch -> getText roundtrip in try/finally so the pad is always deleted even when an assertion throws; swallow delete errors so cleanup never masks the real failure. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks @qodo — both findings addressed in 1e77b74:
|
Etherpad guarantees a pad ends with exactly one trailing newline, so
setText("X\n") reads back as "X\n\n". Compare normalized text instead of
exact equality. Verified live against a real core on :9013.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implements the per-kind orchestration (clone @ pinned ref, inject core's freshly-generated wire-vectors fixture via ETHERPAD_WIRE_VECTORS, run each client's vectorTest + smokeCmd against the booted server) in a testable run-clients.sh, and flips the three manifest entries to enabled, pinned to the commits that carry their Phase 2 tests: ether/pad#1, ether/etherpad-cli-client#136, ether/etherpad-desktop#78. Validated end-to-end locally against a real core: all three clients' vectors + live smoke pass. Refs should be bumped to the squash-merge commits once those client PRs land. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The three client Phase-2 PRs merged; bump each manifest ref from its pre-merge PR-branch tip (now deleted / unfetchable) to its squash-merge commit on main: etherpad-pad -> 91620c6 (ether/pad#1) etherpad-cli-client -> ebc516e (ether/etherpad-cli-client#136) etherpad-desktop -> ab83da6 (ether/etherpad-desktop#78) Verified each merged main carries the test entry points (vectors.rs/smoke.rs; test:vectors/test:smoke scripts). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…7927) * ci(downstream): robust per-client error handling in run-clients.sh (Qodo) - Move clone/fetch/checkout inside the per-client guarded block with explicit '|| exit 1' on every step. set -e is suspended inside a subshell used as an '||' operand, so relying on it silently swallowed clone/checkout failures (and continued from the wrong cwd); explicit guards make one client's failure a per-client fail=1 while the loop continues, and the run exits non-zero. - Stop suppressing fetch errors; fetch only when the pinned commit isn't already reachable, and surface the real error. - Run manifest commands via 'bash -c' instead of 'eval' (trusted in-repo allowlist; avoids double-parsing / leaking into this script's shell). Verified: two bogus clients -> both reported, loop continues, exit 1; happy path (cli, no server) -> vectors pass, smoke skips, exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci(downstream): pin clients to their merged-commit SHAs The three client Phase-2 PRs merged; bump each manifest ref from its pre-merge PR-branch tip (now deleted / unfetchable) to its squash-merge commit on main: etherpad-pad -> 91620c6 (ether/pad#1) etherpad-cli-client -> ebc516e (ether/etherpad-cli-client#136) etherpad-desktop -> ab83da6 (ether/etherpad-desktop#78) Verified each merged main carries the test entry points (vectors.rs/smoke.rs; test:vectors/test:smoke scripts). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci(downstream): run manifest commands under bash strict mode (Qodo) bash -c spawns a fresh shell without -e/-u/-o pipefail, so a pipeline-stage failure inside a client's vectorTest/smokeCmd could be masked. Use 'bash -euo pipefail -c' so those failures surface as a non-zero exit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PR Summary by Qodo
Add downstream wire-compat vectors guard and optional live smoke test
🧪 Tests⚙️ Configuration changes🕐 20-40 MinutesWalkthroughs
User Description
Phase 2 of ether/etherpad#7923
Phase 1 (core PR ether/etherpad#7923) added a canonical wire-format fixture (
wire-vectors.json) that every Etherpad client must decode identically. This PR adds the downstream guard for the desktop/mobile shells.Why the vectors test is a fixture-integrity guard here
The Rust/CLI clients re-implement changeset decoding, so they run these vectors through their own decoders. The desktop and mobile apps are thin shells — they load an Etherpad server URL and embed core's Ace editor in a webview. There is no local changeset decoder to exercise. So instead of a decode test,
vectors.spec.tsasserts the shape/contract of the vendored fixture:name,initialText,changeset,pool,resultText) with the right types,pool.numToAttribis a plain object andpool.nextNuma non-negative integer,initialText/resultTextare non-empty and newline-terminated,Z:header, names are unique.This guards against a malformed/empty fixture being injected into the repo and documents the contract the embedded editor relies on.
Smoke test (headless-light, by design)
The full Electron e2e stays in this repo's own CI.
smoke.spec.tsis a deliberately headless-light HTTP roundtrip proving the shell could talk to a real server without booting Electron:/p/<pad>(the exact URL the shell loads in its webview) → assert HTTP 200,getTextvia the API to confirm content.It skips cleanly (never fails CI) unless both a reachable server and an API key are present.
Env contract
ETHERPAD_WIRE_VECTORSpackages/shell/tests/fixtures/wire-vectors.jsonETHERPAD_SMOKE_URLhttp://localhost:9003ETHERPAD_SMOKE_APIKEYFiles
packages/shell/tests/fixtures/wire-vectors.json— vendored canonical fixturepackages/shell/tests/wire/vectors.spec.ts— fixture-integrity guard (22 cases)packages/shell/tests/wire/smoke.spec.ts— live headless-light roundtrip / clean skippackage.json—test:vectorsandtest:smokeroot scriptsVerification
pnpm run test:vectors→ 22 passedpnpm run test:smoke(no server / no key) → skips cleanly, suite greenpnpm --filter @etherpad/shell test→ 232 passed; lint + typecheck clean🤖 Generated with Claude Code
AI Description
Diagram
graph TD CI["CI / Developer"] --> PKG["package.json scripts"] --> V["Vitest runner"] --> VEC["vectors.spec.ts"] --> FIX[/"wire-vectors.json"/] V --> SMK["smoke.spec.ts"] --> SRV{{"Etherpad server"}} --> EP[/"HTTP API + /p/<pad>"/] subgraph Legend direction LR _proc["Test/process"] ~~~ _io[/"Fixture/IO"/] ~~~ _ext{{"External system"}} endHigh-Level Assessment
The following are alternative approaches to this PR:
1. Provision an ephemeral Etherpad (Docker) for smoke in CI
2. Mock the server contract (fake /api + /p/)
3. Run a minimal Electron/webview e2e in this repo
Recommendation: Keep the current approach: a strict fixture-integrity guard plus an opt-in, clean-skipping live HTTP smoke test. It delivers downstream protection without introducing CI infrastructure requirements or duplicating Electron e2e coverage that belongs in downstream pipelines.
File Changes
Tests (3)
Other (1)