Local two-API E2E evidence¶
The strict proof passed on PR #3765 at tested merge commit
0f3300c15982903df2eff9fa9d70fb9f3a9cb204, which includes main
d6f49aaa32efe28ce1b83db2677db67ad9ff9664 and the locally tested fence
release fix from PR #3767 at 36b671f9b. It used an isolated local
syrf_e2e MongoDB, real RabbitMQ, two distinct API processes on
localhost:12738 and localhost:12742, and only the fixed project
00000000-0000-0000-0000-00000000f024 for materialized writes and serving.
Both API hosts and project management used the test-only overlay with
statistics writes, screening and annotation families, membership families,
and SignalR enabled. Both API hosts additionally enabled Pages,
ReviewerProgress, and StageOverviewBundle.
The browser and direct API client used seeded local administrator,
reviewer, and standard E2ETest roles. No personal or staging account was
used; no credential, token, or browser storage state is committed.
Reproduce from the PR worktree with the supported isolated Chromium network namespace, which passed its local preflight and avoided unrelated Docker network changes interrupting Vite module loads:
cd e2e && node scripts/test-chromium-netns.cjs
cd ..
E2E_BROWSER_NETNS=1 bash e2e/run-local.sh --feat024-two-api --spec materialized-statistics-two-api
E2E_BROWSER_NETNS=1 bash e2e/run-local.sh --feat024-two-api --spec stage-overview-and-screening-eligibility --skip-build
pnpm --dir e2e exec tsc --noEmit
bash -n e2e/run-local.sh e2e/scripts/ports.sh e2e/scripts/teardown.sh
git diff --check
The first runner invocation completed a full .NET build. Each invocation
started its own isolated test data and cleaned up its owned services and
containers. The ignored Playwright report and trace directory is
e2e/test-results/; the local full-run logs were
/tmp/syrf-pr3765-e2e-pages-on-integrated.log and
/tmp/syrf-pr3765-eligibility-integrated.log. These machine-local logs can
contain local test-session diagnostics and are not committed.
| Focused result | Observed evidence |
|---|---|
| Materialized page consumer | Both API hosts reported effective Pages, ReviewerProgress, StageOverviewBundle, and SignalR flags. The Stage Overview browser's initial bundle reported readSource: Materialized. The administrator screening parity read reported readSource: Materialized and inParity: true. Stage reviewer progress has no response provenance field. Its materialized consumer was enabled and its supported baselines were built, but this test does not directly establish the source of each reviewer-progress response. |
| Real broker fanout and visible refresh | Immediately before screening, the test recorded the current project watermark after both browser views and the API 2 observer were mounted. A subsequent screening action produced the same newer ProjectStatisticsChanged revision 11 on the Stage Review API 1 browser socket at 1234 ms, Stage Overview API 1 browser socket at 1234 ms, and authenticated API 2 socket at 1231 ms. Stage Review rendered 1 done at 2160 ms and Stage Overview rendered the member's 1 screened count at 2094 ms after the action. Those renders were 18,843 ms and 12,763 ms after their respective navigation, before the first possible 30-second recovery poll. The test requires event timestamps after the action, a revision above the immediate pre-write watermark, and each live render within 20 seconds. |
| Disconnection and catch-up | The API 2 client disconnected before a second committed screening write. Its event count did not grow while disconnected; both mounted browser views rendered count 2. A fresh authenticated API 2 connection subscribed, then an API 2 materialized parity read observed a newer watermark in parity. A third write produced an API 2 event for this project with a revision newer than the recovered watermark. The assertions tolerate duplicate delivery of a revision without treating it as a new write. |
| Combined-stage eligibility | In a separate run against the same integrated code, seeded reviewer and standard roles each screened one study. The combined screening/annotation stage hid the screening action after sufficient decisions, annotation remained editable, another screening request returned 409, and the persisted screening count stayed at two. This spec creates a random project outside the fixed materialized allowlist, so it establishes source-path eligibility and UI behavior, not materialized annotation parity. |
| Run totals | Two-API proof: 4 passed (three account setup cases plus one proof), 0 failed, in 1.3 minutes after startup/build. Eligibility: 4 passed (three account setup cases plus one scenario), 0 failed, in 1.4 minutes using the compiled code. TypeScript, shell syntax, and diff whitespace checks passed. |
The earlier Pages-off proof passed on harness commit 5556d5e55, but it
established only broker delivery and live source-path refresh. The stricter
Pages-on run initially exposed a real search-import fence-release
WriteConflict: the source operation committed but left an active
ProjectScreening fence, and supported backfills correctly returned
409 FamilyFenced. PR #3767 adds bounded fresh-transaction retry to that
release path. The integrated run above used a fresh isolated database and
passed without deleting or bypassing a fence.
The reconnect assertion uses a fresh API 2 SignalR client plus an administrator parity read reporting materialized statistics. It does not prove automatic reconnection of an existing Angular client. This is a local two-host result, not deployed two-pod staging proof. Staging broker/routing, permission and rollback acceptance, and soak remain separate work with dedicated real test accounts; this local E2ETest authentication must not be enabled on shared staging.