Skip to content

test(calling): add mobius socket WebSocket E2E test infrastructure#4995

Open
eigengravy wants to merge 7 commits into
webex:nextfrom
eigengravy:mobius-ws
Open

test(calling): add mobius socket WebSocket E2E test infrastructure#4995
eigengravy wants to merge 7 commits into
webex:nextfrom
eigengravy:mobius-ws

Conversation

@eigengravy

Copy link
Copy Markdown
Member

COMPLETES CAI-7883

This pull request addresses

The Calling SDK Playwright E2E suite needs to exercise flows over the new mobius WebSocket transport (introduced in #4906 / #4982) in addition to the existing HTTP transport. Without a way to intercept and mock mobius WS frames, the existing registration, keepalive, call lifecycle, and error-flow tests cannot be re-run against the WS transport, leaving the new socket path uncovered by E2E.

by making the following changes

  • Added packages/calling/playwright/utils/mobius-ws.ts — a reusable WebSocket route interceptor for mobius frames (auth/register/unregister/device_status/call_*) with hooks for request/response mocking, plus tracking-id correlation.
  • Extended playwright/utils/setup.ts and test-manager.ts to support running suites in mobius WS mode via env, including conditional WS route installation per worker.
  • Updated all calling test groups (registration-lifecycle, registration-failover, registration-keepalive, registration-errors, call-lifecycle, call-controls, call-keepalive, call-errors) to drive the same scenarios over the WS transport using the new interceptor.
  • Added the necessary selectors / test-data plumbing for the WS-mode runs and tweaked oauth.setup.ts to be transport-agnostic.

Change Type

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Tooling change
  • Internal code refactor

The following scenarios were tested

  • Ran the calling Playwright suites locally in mobius WS mode (MOBIUS_TRANSPORT=ws via env) covering:
    • SDK init project (CALLER + CALLEE + TRANSFER) in parallel.
    • SET_CALLER lifecycle (REG-001/002/008/010/014) and error flows (REG-011/012/013).
    • SET_CALLEE failover chain (REG-006 → REG-007).
    • SET_TRANSFER keepalive (REG-003/004/005).
    • Call lifecycle, controls, keepalive, and error flows.
  • Re-ran the same suites in HTTP mode to confirm no regression on the existing transport path.
  • Manually exercised the mobius WS switch in the calling sample app to validate the interceptor's frame shapes against real traffic.

The GAI Coding Policy And Copyright Annotation Best Practices

  • GAI was not used (or, no additional notation is required)
  • Code was generated entirely by GAI
  • GAI was used to create a draft that was subsequently customized or modified
  • Coder created a draft manually that was non-substantively modified by GAI (e.g., refactoring was performed by GAI on manually written code)
  • Tool used for AI assistance (GitHub Copilot / Other - specify)
    • Github Copilot
    • Other - Please Specify
      • Claude Code (Anthropic)
  • This PR is related to
    • Feature
    • Defect fix
    • Tech Debt
    • Automation

I certified that

  • I have read and followed contributing guidelines
  • I discussed changes with code owners prior to submitting this pull request
  • I have not skipped any automated checks
  • All existing and new tests passed
  • I have updated the documentation accordingly

Make sure to have followed the contributing guidelines before submitting.

@eigengravy eigengravy requested a review from a team as a code owner May 20, 2026 08:33
@aws-amplify-us-east-2

Copy link
Copy Markdown

This pull request is automatically being deployed by Amplify Hosting (learn more).

Access this pull request here: https://pr-4995.d3m3l2kee0btzx.amplifyapp.com

@Shreyas281299 Shreyas281299 added the validated If the pull request is validated for automation. label May 29, 2026
Comment thread packages/calling/playwright/test-groups/call-controls.ts Outdated
contentType: 'application/json',
body: JSON.stringify({error: 'Internal Server Error'}),
if (mobiusWsMode) {
failStatus = true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q. How are we using this value ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This flag gates the interceptor's onRequest callback — when failStatus = true, any CALL_STATUS frame hitting the interceptor returns a 500 (simulating a keepalive failure). The value is toggled from the test body after the call is established, so the interceptor only starts failing keepalives at the right moment.

Comment thread packages/calling/playwright/test-groups/call-lifecycle.ts Outdated
Comment thread packages/calling/playwright/test-groups/registration-errors.ts Outdated
Comment thread packages/calling/playwright/test-groups/registration-failover.ts Outdated
expectedPrimaryUrl = isInt ? PRIMARY_MOBIUS_URL.INT : PRIMARY_MOBIUS_URL.PROD;
tm = new TestManager(testInfo.project.name);
if (mobiusWsMode) {
mobiusWsInterceptor = new MobiusWsInterceptor({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think making this interceptor a singleton will make it easy and performant to use ?

We can create the interceptor once and then we can add the onResponse() through a setter based on the test. After each test we can reset the interceptor (clear the existing onResponse() )

Comment thread packages/calling/playwright/utils/mobius-ws.ts
Comment thread packages/calling/playwright/utils/mobius-ws.ts
Comment thread packages/calling/src/mobius-socket/errors.ts

@Shreyas281299 Shreyas281299 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Submitting existing pending review to allow fresh inline comments.

Comment thread packages/calling/playwright/test-groups/call-controls.ts Outdated
Comment thread packages/calling/playwright/test-groups/call-controls.ts Outdated
});
}

await navigateToCallingApp(page);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[High / Test correctness] REG-011 in WS mode passes vacuously. The interceptor is installed above (lines 19–39), but setMobiusWebSocket(page, true) is never called and this test deliberately bypasses initializeCallingSDK. The sample-app's localStorage flag stays unset, the SDK falls back to HTTP, the interceptor sees zero traffic, and registrationPosts stays at 0. The only WS-validating assertion (if (registrationPosts > 0) expect(registrationStatus).toBe(401)) is therefore skipped, and the remaining assertions ("not registered") pass regardless of transport with an invalid token — so MOBIUS=ws runs give zero coverage of the WS 401 path.

Suggestion: enable WS explicitly in the WS branch, then make the assertion unconditional:

await navigateToCallingApp(page);
await setServiceIndicator(page, 'calling');
if (mobiusWsMode) {
  await setMobiusWebSocket(page, true);
}
// ...
if (mobiusWsMode) {
  expect(registrationPosts).toBeGreaterThan(0);
  expect(registrationStatus).toBe(401);
} else if (registrationPosts > 0) {
  expect(registrationStatus).toBe(401);
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Investigated — in WS mode, the SDK fails at HTTP service discovery (region/server lookup returns 401) before it ever opens a WebSocket connection. The invalid token 'invalid-token-12345' gets rejected at the HTTP layer, so the WS interceptor was dead code that never fired.

Removed the WS interceptor, kept the test running in both modes (it still validates the "not registered" end state via UI assertions), and scoped the network-level registrationPosts/401 assertion to HTTP mode only. Added a comment documenting why.

Comment thread packages/calling/playwright/utils/mobius-ws.ts Outdated
Comment on lines +53 to +76
@@ -56,9 +71,11 @@ export function callErrorTests() {
await verifyLineRegistered(page);
await getMediaStreams(page);

await context.route(/\/devices\/[^/]+\/call$/, (route) => {
route.abort('failed');
});
if (!mobiusWsMode) {
await context.route(/\/devices\/[^/]+\/call$/, (route) => {
route.abort('failed');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Medium / Test correctness] CALL-013 exercises different failure modes in the two transports under one ID. The WS branch returns a structured {statusCode: 500, ...} response for CALL_SETUP — the SDK gets a server-side error. The HTTP branch uses route.abort('failed') — a transport-level network error (no response, fetch rejection). These hit different code paths in the SDK (response error vs transport error). The "no stuck call" assertion is true in both cases, but the test isn't validating equivalent behavior across transports.

Pick one failure mode and mirror it: either drop the WS connection / never reply on the WS side, or use route.fulfill({status: 500, ...}) on the HTTP side.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid — aligned the HTTP branch to use route.fulfill({status: 500, body: ...}) instead of route.abort('failed') so both transports now exercise the same failure mode (server-side 500 on call setup). Both still validate the same end state: no stuck call objects after cleanup.

Comment thread packages/calling/playwright/utils/mobius-ws.ts Outdated
Comment thread packages/calling/playwright/test-data.ts
…ments

- Replace nested ternary interceptor creation with if/else blocks
- Add try/catch around onRequest/onResponse callbacks in MobiusWsInterceptor
- Redact authorization tokens from captured frames to prevent trace leaks
…lback

- Rename ambiguous `tm` variable to `testManager` across all test groups
- Extract inline onRequest callback to named variable in registration-errors
…alignment

REG-011: Remove dead WS interceptor that never fires (SDK fails at HTTP
service discovery before opening a WebSocket with an invalid token). Keep
the test running in both modes — it still validates "not registered" end
state. Network-level 401 assertion now scoped to HTTP mode only.

CALL-013: Align HTTP branch to use route.fulfill({status: 500}) instead
of route.abort('failed') so both transports exercise server-side error
cleanup rather than testing different failure modes.

@rarajes2 rarajes2 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. We can discuss the singleton pattern for the interceptor in person, it's not a blocker for this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

validated If the pull request is validated for automation.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants