Files
openhuman/docs/E2E-FULL-SUITE-SESSION-NOTES.md
T

16 KiB
Raw Blame History

E2E full-suite hardening — session handoff notes

Branch: ci/full-e2e-run-2026-05-23 on senamakel/openhuman (fork). Date: 2026-05-23 → 2026-05-24.

This is the snapshot of what's been done, what's known, and what to pick up next. Pair this with gitbooks/developing/e2e-testing.md (the existing E2E doc) — this file documents the multi-session push to get the full suite (all ~87 specs) reliably green on Linux + reproducible locally in Docker.


TL;DR — Current state

Surface Status
Full-suite CI on Linux 72 / 87 passing (15 failing), 6 parallel shards, ~25 min wall
Two shards 100% green commerce (11/0) + webhooks (9/0)
Local Docker runs same 6-shard layout bash app/scripts/e2e-run-shards.sh
Local + CI agree on shard pass/fail Yes (per-spec counts differ inside failing shards; see CEF instability below)
macOS / Windows full-suite Not yet validated this session — sharded jobs exist in workflow but only Linux was iterated on

Branch SHA at handoff: 2bad1f046 (revert: drop Escape press in openConnectorModal).


What changed (commits, top → bottom is most recent)

  1. revert: drop Escape press in openConnectorModal — regressed 7 connectors, reverted.
  2. fix(e2e): only press Escape in openConnectorModal when a modal backdrop is actually present — superseded by revert.
  3. perf(e2e): split integrations into providers + webhooks shards — 6-shard matrix.
  4. test(e2e): finish composio_sync URL-drop + close stale modal in openConnectorModal
  5. test(e2e): local shard runner + fix telegram-flow reference + connector log refs — adds app/scripts/e2e-run-shards.sh.
  6. test(e2e): drop URL-based assertions for composio_sync/_execute
  7. perf(e2e): isolate connector smoke specs into their own shard
  8. test(e2e): orchestrator coverage + state-bleed fixes — adds 23 missing specs to e2e-run-all-flows.sh.
  9. test(e2e): align Linux specs with PR #2550 settings restructure
  10. test(e2e): point auth-access-control logout test to /settings/account
  11. test(e2e): drop assertions for surfaces removed in PR #2550
  12. test(e2e): switch auth bypass from deep-link to loopback OAuth path — important fidelity change, see below.
  13. perf(e2e): hoist Linux full-suite build into a single job, fan-out tests — build-once → matrix shards.
  14. fix(e2e): gate build-skip on BOTH binary + CEF cache hits
  15. fix(e2e): align CEF cache paths with actual download location
  16. fix(e2e): set CEF_PATH when binary cache skips build
  17. perf(e2e): cache built binary across shard runs
  18. fix(e2e): install x86_64-apple-darwin target for Mac shard build
  19. perf(e2e): shard full suite across 4 parallel jobs per OS
  20. perf(e2e): run full suite in one shared session (no per-spec relaunch) — first big perf jump.
  21. fix(e2e): repair stale assertions in linux-cef-deb-runtime spec
  22. fix(e2e): put cargo-tauri install root on PATH for macOS/Windows CI — Mac/Win build fix.

Architecture as it stands today

CI workflow

.github/workflows/e2e-reusable.yml defines three Linux job tiers:

e2e-linux         (smoke + mega-flow only, runs when inputs.full == false)
rust-e2e-linux    (Rust-side `tests/*_e2e.rs` against mock backend)
build-linux-full  (one job: cargo tauri build + tar artifact, uploads)
e2e-linux-full    (matrix of 6 shards, each `needs: build-linux-full`)

The build job tars app/src-tauri/target/debug/OpenHuman, app/dist, and $HOME/Library/Caches/tauri-cef/ into a single tar -czf artifact (~600 MB). Each shard downloads + extracts to the canonical paths and skips the build step entirely. CEF/binary caches still live on the build job to keep cold builds fast.

Shard layout

foundation   = auth,navigation,system          (~21 specs)
chat         = chat,skills,journeys            (~19 specs)
providers    = providers,notifications         (~14 specs)
webhooks     = webhooks                        (~9 specs)
connectors   = connectors                      (~16 specs)
commerce     = payments,settings               (~11 specs)

The 6th shard (webhooks carved out of the original integrations) was added because anything over ~18-20 specs in one shared CEF session goes unstable on Linux. The connectors suite is its own category in e2e-run-all-flows.sh for the same reason.

Local equivalent

app/scripts/e2e-run-shards.sh is the local mirror of the CI matrix. Runs each shard as a fresh e2e-run-all-flows.sh --suite=… invocation, so each shard gets a fresh CEF process.

docker compose -f e2e/docker-compose.yml run --rm e2e \
  bash -lc "bash app/scripts/e2e-run-shards.sh"
# or one shard:
docker compose -f e2e/docker-compose.yml run --rm e2e \
  bash -lc "bash app/scripts/e2e-run-shards.sh foundation"

Orchestrator (app/scripts/e2e-run-all-flows.sh)

Collects all spec paths into one list (_spec_paths[@]) and calls e2e-run-session.sh ONCE with the full list, instead of per-spec. That restored the design intent in wdio.conf.ts ("WDIO creates ONE session per worker ... all specs run sequentially in the same session"). Per-spec relaunch was costing ~15-30s of CEF cold-start × 65 specs = 15+ min of pure overhead before this change.

--suite= accepts a comma-separated list now (--suite=auth,navigation,system).

slack-flow is explicitly commented out in the orchestrator — it crashed the CEF session mid-spec consistently. Investigate before re-enabling.


Loopback auth bypass (production fidelity)

Per PR #2550 the real OAuth login flow uses an RFC 8252 loopback listener (http://127.0.0.1:53824/auth?state=…) instead of the openhuman:// deep-link. E2E auth bypass was still firing openhuman://auth?token=… directly through window.__simulateDeepLink, which is now the legacy fallback path.

Switched in app/test/e2e/helpers/loopback-auth-helpers.ts + reset-app.ts:

  1. WebView calls production startLoopbackOauthListener() (exposed on window.__startLoopbackOauthListener when the E2E build flag VITE_OPENHUMAN_E2E_RESTART_APP_AS_RELOAD === 'true' is set in app/src/utils/loopbackOauthListener.ts).
  2. WebView wires awaitCallback()__simulateDeepLink so the callback URL is rewritten http://127.0.0.1:…/auth?…openhuman://auth?… and dispatched through the existing deep-link handler — mirroring exactly what OAuthProviderButton.tsx does in production.
  3. Node-side fetch() hits the loopback URL with the bypass JWT + state nonce appended; the Rust listener accepts, validates state, emits loopback-oauth-callback.

This means every spec's resetApp() now exercises the same Rust HTTP server + state nonce check + Tauri event emit that ships to users.

triggerAuthDeepLink / triggerAuthDeepLinkBypass are still kept for oauth-success deep links (e.g. mega-flow's connector callbacks) that the loopback path doesn't cover.


Known failures and root causes

Foundation (2 failing on CI, more on local)

Spec Cause Difficulty
onboarding-modes (Phase B) After Phase A reaches /home, resetOnboardingFlagAndReload resets onboarding_completed=false + reloads, but the Custom-card click in Phase B doesn't register (data-testid found but click is intercepted or stale). Needs DOM inspection of the wizard re-mount. medium
runtime-picker-login resetApp(skipAuth: true) should land on Welcome screen, but the renderer re-hydrates from a persisted snapshot and lands on /home instead. resetApp already polls for the Welcome heading + re-replaces #/ for up to 10s; insufficient. Likely needs to wait for snapshot.sessionToken to be cleared (via fetchCoreAppSnapshot) before considering the reset done. medium

Chat (3 failing)

Spec Cause
chat-harness-subagent Agent orchestrator doesn't produce expected canary string. Real product/agent behavior — not a test bug.
chat-harness-wallet-flow Crypto agent doesn't produce wallet quote. Real product behavior.
chat-multi-tool-round T2.1 (agent calls tool 1 (file_read); timeline shows it) — expect.toBe(true) fails. Could be timing or real product change.

chat-conversation-history H1.4 was fixed earlier — root cause was getSelectedThreadId() returning the prior-spec's stale thread id before the New-thread click had time to update Redux. Fix: capture prior id, wait for selectedThreadId !== priorThreadId.

Providers (3 failing)

conversations-web-channel-flow, telegram-channel-flow, whatsapp-flow — likely the same shared-CEF-session instability hitting late-shard specs. Worth re-checking after any further shard reduction.

Connectors (7 failing — all hit "expired auth" subtest)

The other 9 connector tests in each spec pass. The one consistent failure is "expired auth shows Reconnect button and does not log user out" — openConnectorModal()'s card click is intercepted because the previous test left a modal backdrop up. Attempted fix (Escape before click) regressed other tests; reverted. Real fix probably: guarantee modal close in afterEach rather than working around it in the open helper.


CEF shared-session instability — the recurring theme

Empirically, the shared-CEF debug build becomes unreliable past ~18-20 specs in a single session. Symptoms vary:

  • __simulateDeepLink ready? false (poll N) after the listener was previously fine
  • A sessionId is required for this command
  • ECONNREFUSED to Appium :4723 mid-suite
  • Mysterious esbuild platform-mismatch errors during WDIO's TS transform of a spec file (red herring — sub-symptom of WDIO failing to bring the spec into scope after a session loss)

Mitigations applied:

  • Shard so no shard runs more than ~16 specs (and the busiest two — foundation 21, chat 19 — are at the edge of what works).
  • Run each shard as a fresh e2e-run-session.sh invocation locally (mirrors CI matrix isolation).

What might fix it for real (not attempted this session):

  • Bump WDIO specFileRetries so a session loss restarts the failing spec.
  • Periodic openhuman.test_reset + reload at a fixed cadence (every 10 specs?) to clear in-process leaks.
  • Build the test binary in --release to reduce per-process memory pressure (debug CEF + tauri builds are heavy).

Stale assertions / PR #2550 drift — handled

PR #2550 ("fix(oauth): make loopback redirect actually work, plus settings cleanup") moved a bunch of settings surfaces. Fixed tests:

  • Logout/Clear App Data lives at /settings/account (was /settings). Updated logoutViaSettings helper + settings-data-management + auth-access-control.
  • /settings/connections route deleted (ConnectionsPanel removed). settings-account-preferences dropped the post-recovery-phrase wallet status assertion; navigation-settings-panels N2.2 .skip-ed with PR pointer.
  • "Notification Routing" no longer a top-level Developer Options entry — moved into a tab on /settings/notifications#routing. settings-advanced-config navigates to /settings/notifications and clicks the Routing tab.
  • screen-intelligence dropped the "Permissions" assertion on Linux (the section is gated behind status.platform_supported, true only on macOS).

Composio connector specs — the composio_sync URL gotcha

The 15 connector smoke specs each had:

clearRequestLog();
await callOpenhumanRpc('openhuman.composio_sync', { toolkit: TOOLKIT_SLUG });
const syncReq = getRequestLog().find(
  r => r.method === 'POST' && r.url.includes('/composio/sync')
);
expect(syncReq).toBeDefined();  // always failed

/composio/sync does not exist in the mock router and the composio_sync RPC short-circuits with "no native provider registered" for any connector without a Rust-side provider, so no HTTP request is ever logged. The probe-style assertion never had a chance.

The real intent (per the spec's PASS: log message: "sync does not nuke session") is covered by assertSessionNotNuked() on the next line. Dropped the URL check across all 15 specs.

Same fix for composio_execute / /composio/execute.


Local docker quirks

  • The docker-compose has named volumes per-platform for node_modules and .pnpm-store (the bind-mounted host node_modules would clobber Linux binaries with macOS ones).
  • e2e-bootstrap (in e2e/docker-entrypoint.sh) installs Appium 3 + chromium driver on first entry and caches into the npm volume.
  • Docker Desktop dies if the host has < ~1 GB free. Watch for ENOSPC while running long suites — output files grow fast.
  • tee /tmp/local-shards.log is the recommended way to capture the sharded run output; the bg-task output file gets cleaned up aggressively by the harness.

Suggested next-session priorities

  1. Foundation Phase B onboarding + runtime-picker Welcome. resetApp(skipAuth) is close — it polls for Welcome heading, force-replaces hash to #/, gives 10s. Needs to additionally poll fetchCoreAppSnapshot() until sessionToken is gone before returning. Probably 1-2 hours of careful work.

  2. Connector expired-auth openConnectorModal. Add an afterEach that explicitly closes any open modal (Escape + wait for backdrop to disappear) rather than the failed "Escape-before-open" approach. ~30 min.

  3. CEF session retry. Add WDIO specFileRetries: 1 so a session-loss in shard N+1 retries spec N+1 in a fresh slot instead of cascading the rest of the shard. This should recover maybe 5-8 of the late-shard failures.

  4. Validate macOS + Windows full-suite. Workflow already has the shard structure for both, but they haven't been exercised this session (Linux focus). Re-dispatch with -f run_macos=true -f run_windows=true -f full=true and triage.

  5. Re-enable slack-flow once the CEF stability fix lands. It's the only spec the orchestrator deliberately skips today.


Key file paths

  • Workflow: .github/workflows/e2e-reusable.yml
  • Orchestrator: app/scripts/e2e-run-all-flows.sh
  • Local sharder: app/scripts/e2e-run-shards.sh
  • Session runner: app/scripts/e2e-run-session.sh
  • Build script: app/scripts/e2e-build.sh
  • WDIO config: app/test/wdio.conf.ts
  • Loopback auth helper: app/test/e2e/helpers/loopback-auth-helpers.ts
  • Production loopback (exposes __startLoopbackOauthListener for E2E): app/src/utils/loopbackOauthListener.ts
  • Reset-app helper: app/test/e2e/helpers/reset-app.ts
  • Composio test helper: app/test/e2e/helpers/composio-helpers.ts
  • Docker setup: e2e/docker-compose.yml, e2e/docker-entrypoint.sh

Useful commands cheatsheet

# CI: dispatch a Linux-only full run on the fork (only run_macos / run_windows are inputs)
gh workflow run E2E --repo senamakel/openhuman \
  --ref ci/full-e2e-run-2026-05-23 \
  -f run_macos=false -f run_windows=false -f full=true

# CI: shard summary
gh run view <run-id> --repo senamakel/openhuman | grep -E '^(✓|X|\*|-) '

# CI: per-shard pass/fail + failing spec list
gh api repos/senamakel/openhuman/actions/jobs/<job-id>/logs > /tmp/job.log
grep -c 'PASSED in linux' /tmp/job.log
grep -c 'FAILED in linux' /tmp/job.log
grep 'FAILED in linux' /tmp/job.log \
  | sed -E 's|.*specs/||;s|\.spec\.ts.*||' | sort -u

# Local: full sharded run
docker compose -f e2e/docker-compose.yml run --rm e2e \
  bash -lc "bash app/scripts/e2e-run-shards.sh" 2>&1 | tee /tmp/local-shards.log

# Local: single shard
docker compose -f e2e/docker-compose.yml run --rm e2e \
  bash -lc "bash app/scripts/e2e-run-shards.sh foundation"

# Local: single spec
docker compose -f e2e/docker-compose.yml run --rm e2e \
  bash -lc "bash app/scripts/e2e-run-session.sh test/e2e/specs/<spec>.spec.ts"