Skip to content

test(messaging): #182 deterministic cross-member group encryption E2E#186

Merged
TortoiseWolfe merged 1 commit into
mainfrom
test/182-deterministic-group-e2e
Jul 5, 2026
Merged

test(messaging): #182 deterministic cross-member group encryption E2E#186
TortoiseWolfe merged 1 commit into
mainfrom
test/182-deterministic-group-e2e

Conversation

@TortoiseWolfe

Copy link
Copy Markdown
Owner

What

Adds a non-skipping E2E that proves an encrypted group message round-trips between two different members (A sends, B decrypts), by distributing the group key server-side in the fixture — off the slow browser critical path.

Backlog follow-up to #182 (the "deterministic group send/decrypt E2E" nice-to-have on #115).

Why

The existing group test (group-chat-multiuser.spec.ts, "sends and reads an encrypted message in a UI-created group") drives group creation through the UI, which runs createGroup() in-browser: generate a group AES key + ECDH-distribute it to every member (Argon2 + async crypto, routinely >90s on CI). So it test.skips on slow runners and gives no reliable send/decrypt proof — on the #182 merge run it SKIPPED, and the actual proof came from an earlier CI log rather than a green assertion. Weak guard for a feature that had 3 live RLS bugs.

How

  • tests/e2e/utils/test-user-factory.tsseedIsolatedGroup(n, { withKeys: true }) now also distributes the group key. distributeGroupKeysForFixture re-derives the creator's keypair in Node from their stored base64 salt (deterministic Argon2id), generates one AES-GCM group key, and wraps it per member via the real production GroupKeyService.encryptGroupKeyForMember — reused, not duplicated, so the seeded group_keys rows are byte-identical in shape to what the app writes (zero wire-format drift). All rows share created_by = creator, so each member unwraps with ECDH(memberPriv, creatorPub) at runtime — exactly the getGroupKeyForConversation path.
  • tests/e2e/messaging/group-chat-multiuser.spec.ts — new deterministic test in its own describe. All 6 existing tests are untouched (the UI create-group flow still guards /messages/new-group).
  • src/services/messaging/__tests__/group-key-roundtrip.node.test.ts — a real-crypto Node unit guard proving the exact wrap→unwrap→message-decrypt contract with real ECDH/AES-GCM. Catches a base64-salt decode drift in ~1s, before the browser.

Each browser still pays one Argon2 key-unlock (via the ReAuth modal, same as every 1:1 iso test); the eliminated cost is the compounding in-browser group creation. Runs in chromium-msg-iso (already in MSG_ISO_GLOBS — no config change).

Verification (local, served build + cloud Supabase)

  • E2E: 2 passed, 0 skipped (~29s). Logs show the fixture generating + ECDH-wrapping the group key per member, both members opening + unlocking, A sending, and B decrypting + rendering A's message.
  • Unit guard: 1 passed (real crypto).
  • type-check + full-project lint: clean.

Not a design decision — CI's chromium-msg-iso will run the same test.

🤖 Generated with Claude Code

The group-message E2E (group-chat-multiuser.spec.ts) drove group CREATION
through the UI, which runs createGroup() in-browser (Argon2 + in-browser ECDH
key distribution, routinely >90s). It test.skip'd on slow runners and gave no
reliable send/decrypt proof — on the #182 merge run it SKIPPED, so the actual
proof came from an earlier CI log rather than a green assertion.

Add a NON-skipping test that proves an encrypted group message round-trips
between two DIFFERENT members (A sends, B decrypts) by distributing the group
key SERVER-SIDE in the fixture, off the browser critical path:

- test-user-factory.ts: `seedIsolatedGroup(n, { withKeys: true })` now also
  distributes the group key. `distributeGroupKeysForFixture` re-derives the
  creator's keypair in Node from their stored base64 salt (deterministic
  Argon2id), generates one AES-GCM group key, and wraps it per member via the
  REAL production `GroupKeyService.encryptGroupKeyForMember` (reused, not
  duplicated — zero wire-format drift). All rows share created_by = creator so
  each member unwraps with ECDH(memberPriv, creatorPub) at runtime.
- group-chat-multiuser.spec.ts: new deterministic test in its own describe;
  the 6 existing tests (incl. the UI create-group flow) are untouched.
- group-key-roundtrip.node.test.ts: real-crypto Node unit guard proving the
  exact wrap→unwrap→message-decrypt contract (catches a base64-salt decode
  drift in ~1s, before the browser).

Each browser still pays one Argon2 key-unlock (same as every 1:1 iso test);
the eliminated cost is the compounding in-browser group creation. Runs in
chromium-msg-iso (already in MSG_ISO_GLOBS — no config change).

Verified locally against served build + cloud Supabase: 2 passed, 0 skipped
(~29s); unit guard 1 passed. Follows the #115 backlog "deterministic group
send/decrypt E2E" nice-to-have.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@TortoiseWolfe TortoiseWolfe merged commit 0a28463 into main Jul 5, 2026
18 checks passed
@TortoiseWolfe TortoiseWolfe deleted the test/182-deterministic-group-e2e branch July 5, 2026 22:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants