The GitHub Actions job "Required Checks" on texera.git/gh-readonly-queue/main/pr-7911-54a9a597d87eb63304af77c67ab744da37086d03 has failed. Run started by GitHub user aglinxinyuan (triggered by aglinxinyuan).
Head commit for run: 9bb955ccd3082e1a8f43920113f8a82f5266451c / Meng Wang <[email protected]> test(frontend): cover the agent service's websocket lifecycle and message dispatch (#7911) ### What changes were proposed in this PR? Extends `agent.service.spec.ts` over the WebSocket lifecycle the issue lists — connection setup, the `handleWebSocketMessage` dispatch arms, and teardown — with 15 tests. They live in the existing `websocket lifecycle and events` block and reuse its `FakeWebSocket` double; no new harness. Measured locally with `--coverage`: | `agent.service.ts` | Before | After | | --- | --- | --- | | lines | 301/329 (91.49 %) | **306/329 (93.01 %)** | | branches | 147/186 (79.03 %) | **160/186 (86.02 %)** | | functions | 90/103 | **91/103** | **Connection setup (`startStatePolling`)** - The `wss:`/`ws:` ternary (411): `window.location` is swapped for one test with the `withLocation` helper `virtual-environment.service.spec.ts` already uses (jsdom's own is not writable), and the URL the double was constructed with is asserted in full. The `ws:` side was already covered. - The parse-failure path (424): a payload that is not JSON is logged and does not throw out of the handler; the connection is left intact. - The `onerror` handler (428-429), previously an uncalled function. - The close guard (435), both ways: a stale socket that reports an abnormal close after being swapped out must not clear the newer connection or flip the state. **Message dispatch (`handleWebSocketMessage`)** - `message.workflowContent` (466) — a snapshot with no workflow content leaves the workflow stream and `wsWorkflowActive` alone, so DB polling stays in charge. - `message.step` (478) — a step event with no step changes nothing. - `message.state` (517) — a status event with no state changes nothing. - The error arm (534) — an event with no `error` falls back to `"Agent error occurred"`; the explicit-message side was already covered. - The `default` arm (451/539) — an unrecognised type warns and changes nothing. **Teardown** - `stopStatePolling` (548) both ways, through `deleteAgent`: for a tracked agent the socket is closed and `stopPolling$` completes; for an id that was never tracked it is a no-op. - `activateAgent` (580) — an already-open socket is kept (no second socket) and a socket that is no longer `OPEN` is replaced. `deactivateAgent` always clears the socket, so the inactive-but-still-connected shape this guard defends against is set up directly on the tracking entry, with a comment saying so. - `deactivateAgent` (594-606) — both early returns (unknown agent; already inactive) and the no-socket side of the close branch, reached the ordinary way by letting a normal close event drop the socket first. **One arm is not reachable.** The issue asks for a step "that does not" carry an id (498). `convertApiReActStep` returns ``id: apiStep.id || `${apiStep.messageId}-${apiStep.stepId || 0}` `` — the fallback is a template literal, which always yields a non-empty string, so `convertedStep.id` is never falsy and `if (convertedStep.id)` cannot take its else branch. The test covers the fallback side instead (a step with no id still advances HEAD to `m9-0`) and records why the arm stays half-taken. The remaining uncovered lines are the state accessors and HTTP failure paths, which #7908 covers; the two PRs touch disjoint regions of the file. No production code was changed. ### Any related issues, documentation, discussions? Closes #7888. ### How was this PR tested? `ng test --watch=false --include src/app/workspace/service/agent/agent.service.spec.ts` — 62 passed (47 before, 15 new), repeated 3× for stability. `yarn format:ci` clean. Failure path verified by breaking one assertion in each of the 15 new tests: 16 failed (the 15 plus one pre-existing test that shares the mutated assertion), non-zero exit, then restored to green. Determinism: every socket event is delivered by invoking the double's `onmessage`/`onerror`/`onclose` directly — no real socket, no reconnect timer, and no new `vi.useFakeTimers()`. `withLocation` restores `window.location` in a `finally`, and `vi.restoreAllMocks()` was added to the block's `afterEach` so the `console` spies cannot leak into a later test even if an assertion throws. ### Was this PR authored or co-authored using generative AI tooling? Generated-by: Claude Code (Opus 4.8 [1M context]) Report URL: https://github.com/apache/texera/actions/runs/32796083992 With regards, GitHub Actions via GitBox
