Genace

generation SSE disconnect tests: distinguish terminal records from broken transport

A progress observer needs different behavior for a received terminal event and a connection that disappears without one. Test those cases using a local stream fixture so parser and recovery logic do not depend on a real render. The fixture should control bytes, timing, and close position independently.

Build a deterministic event sequence

Genace streams named progress, done, and error events with JSON state, progress, outputs, and error fields. The initial progress record can already be terminal, followed by a terminal event. Construct a fixture that includes both so your publication logic proves tolerant of repeated success information.

const encoder = new TextEncoder();
export function localEventStream(parts) {
  return new ReadableStream({
    start(controller) {
      for (const part of parts) controller.enqueue(encoder.encode(part));
      controller.close();
    },
  });
}

const fixture = localEventStream([
  'event: progress\ndata: {"state":"run',
  'ning","progress":null,"outputs":null,"error":null}\n\n',
]);

This helper creates a normal local close after a pending event. Add separate variants that error the stream, leave an event incomplete, or emit a complete terminal record before close. Do not describe every close as a transport error; end-of-stream can be expected after completion.

Test byte and record boundaries separately

Split an encoded fixture inside a multibyte character and across CRLF boundaries. Deliver several events in one chunk and a single event one byte at a time. A complete blank-line-delimited event should parse once, while an incomplete final record should not be treated as a finished payload.

Then test malformed JSON in a complete event. The observer should preserve the saved job ID and report a protocol issue. It should not create another generation because a client-side parser could not interpret a record.

Verify recovery against current state

After an unexpected close, mock GET /api/v1/jobs/{id}?wait=0. Return succeeded, failed, canceled, and still-running snapshots in separate tests. Terminal snapshots finalize the local operation; active snapshots may reconnect with bounded backoff or continue polling.

The current stream formatter has no event IDs or replay cursor. Tests should not require Last-Event-ID replay. Recover the latest state and accept that intermediate progress samples may be absent. The route’s declared sixty second duration also means a connection lifetime is not the job lifetime.

Check cleanup and duplicate effects

Abort the observer and verify its upstream read and reconnect timer are released. Reload the interface and confirm it attaches to the durable operation instead of starting another submission. Emit terminal progress followed by done twice and assert only one local artifact operation exists.

Keep transport errors, payload errors, terminal job failures, and local delivery errors as distinct categories in captured telemetry. A status HTTP failure should retain the last known state rather than overwrite it with a fabricated remote failure.

Acceptance criteria

Run the suite without an API key and block external network requests. Verify every synthetic sequence has one documented expected outcome, no leaked timers, and no generation POST. These tests make reconnect behavior reviewable without consuming model credits.

See chunk parsing, reconnect recovery, and proxy buffering.

Continue with production operations

Where these facts come from