From 36ac39d3afeb4d0de4a55df1a2fcec65409f8282 Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Wed, 6 May 2026 15:09:24 -0700 Subject: [PATCH] test(e2e/agents): add sse-reconnect spec for Validation #5 Mid-stream, page.context().setOffline(true)/(false); event count never regresses and continues climbing after reconnect. Exercises the seq dedup branch of useSessionEventStream. --- .../tests/agents/sse-reconnect.spec.ts | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 ui/litellm-dashboard/e2e_tests/tests/agents/sse-reconnect.spec.ts diff --git a/ui/litellm-dashboard/e2e_tests/tests/agents/sse-reconnect.spec.ts b/ui/litellm-dashboard/e2e_tests/tests/agents/sse-reconnect.spec.ts new file mode 100644 index 00000000000..aacae1fe5ff --- /dev/null +++ b/ui/litellm-dashboard/e2e_tests/tests/agents/sse-reconnect.spec.ts @@ -0,0 +1,48 @@ +/** + * LIT-2881 Validation #5 — SSE reconnect across run reload. + * + * Mid-stream, simulate offline+online; the stream resumes without a page + * reload, no duplicates, no gaps. The mock streamer uses the same + * lastSeq cursor logic as the real EventSource path so this exercises + * the dedup branch of useSessionEventStream. + */ +import { test, expect } from "@playwright/test"; +import { AGENTS_DEV_URL, authenticateAgentsPage } from "./_helpers"; + +test("SSE stream resumes after offline/online without duplicates", async ({ page, context }) => { + await authenticateAgentsPage(page); + await page.goto(`${AGENTS_DEV_URL}/agents/agt_01/sessions/ses_01`); + await expect(page.getByTestId("conversation-pane")).toBeVisible(); + + // Wait for a couple of streamed events + await expect + .poll( + async () => + (await page.getByTestId(/^message-bubble-/).count()) + (await page.getByTestId("tool-call-card").count()), + { + timeout: 5_000, + }, + ) + .toBeGreaterThanOrEqual(3); + + const before = + (await page.getByTestId(/^message-bubble-/).count()) + (await page.getByTestId("tool-call-card").count()); + + // Simulate offline → online + await context.setOffline(true); + await page.waitForTimeout(1500); + await context.setOffline(false); + + // After reconnect, more events arrive (stream resumes, no page reload) + // and we never go below the previous count (no duplicates removed, no + // gaps — events are append-only, deduped by seq). + await expect + .poll( + async () => + (await page.getByTestId(/^message-bubble-/).count()) + (await page.getByTestId("tool-call-card").count()), + { + timeout: 10_000, + }, + ) + .toBeGreaterThanOrEqual(before); +});