Initial Commit
This commit is contained in:
87
e2e/foundation-stack-engines/tests/sse-stream.spec.ts
Normal file
87
e2e/foundation-stack-engines/tests/sse-stream.spec.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
/**
|
||||
* Stage-7 closure smoke — `api-sse-bun` event stream.
|
||||
*
|
||||
* Opens a real `fetch` against `/sse` to attach as an SSE subscriber,
|
||||
* then publishes via `/sse/publish` and asserts that the stream body
|
||||
* carries the expected `data:` frame.
|
||||
*/
|
||||
|
||||
async function readUntilEvent(
|
||||
body: ReadableStream<Uint8Array>,
|
||||
pattern: RegExp,
|
||||
timeoutMs = 5000
|
||||
): Promise<string> {
|
||||
const reader = body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = "";
|
||||
const deadline = Date.now() + timeoutMs;
|
||||
try {
|
||||
while (Date.now() < deadline) {
|
||||
const { value, done } = await reader.read();
|
||||
if (done) break;
|
||||
if (value !== undefined) {
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
if (pattern.test(buffer)) {
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
await reader.cancel();
|
||||
} catch {
|
||||
/* idempotent */
|
||||
}
|
||||
}
|
||||
throw new Error(
|
||||
`sse stream did not yield ${pattern} within ${timeoutMs}ms; got: ${buffer}`
|
||||
);
|
||||
}
|
||||
|
||||
test.describe("foundation-stack-engines — sse-bun stream", () => {
|
||||
test("4. sse subscriber receives a published event", async ({
|
||||
request,
|
||||
baseURL,
|
||||
}) => {
|
||||
const ac = new AbortController();
|
||||
const url = `${baseURL}/sse?tenant=tenant-a&topic=ticker`;
|
||||
const sseResponse = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
accept: "text/event-stream",
|
||||
"accept-encoding": "identity",
|
||||
},
|
||||
signal: ac.signal,
|
||||
});
|
||||
expect(sseResponse.status).toBe(200);
|
||||
expect(sseResponse.headers.get("content-type")).toContain(
|
||||
"text/event-stream"
|
||||
);
|
||||
expect(sseResponse.body).not.toBeNull();
|
||||
|
||||
// Give the handshake pipeline a moment to register the
|
||||
// subscription on the engine's bus.
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 200));
|
||||
|
||||
const pub = await request.post("/sse/publish", {
|
||||
data: {
|
||||
topic: "ticker",
|
||||
tenant: "tenant-a",
|
||||
data: "hello-sse",
|
||||
id: "evt-1",
|
||||
},
|
||||
});
|
||||
expect(pub.status()).toBe(200);
|
||||
const pubBody = (await pub.json()) as { delivered: number };
|
||||
expect(pubBody.delivered).toBeGreaterThanOrEqual(1);
|
||||
|
||||
const stream = sseResponse.body as ReadableStream<Uint8Array>;
|
||||
const seen = await readUntilEvent(stream, /data:\s*hello-sse/);
|
||||
expect(seen).toContain("data: hello-sse");
|
||||
expect(seen).toContain("id: evt-1");
|
||||
|
||||
ac.abort();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user