Files
Zakaria a46764fb1b
ci / Validate workspace (push) Has been cancelled
landing-page-ci / Validate landing page (push) Has been cancelled
landing-page-deploy / Deploy landing page (push) Has been cancelled
github-metrics / Generate repository metrics SVG (push) Has been cancelled
refresh-contributors-wall / Refresh contributors wall cache bust (push) Waiting to run
first-commit
2026-05-04 14:58:14 -04:00

126 lines
3.3 KiB
TypeScript

// @ts-nocheck
import { EventEmitter } from 'node:events';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { createCompatApiErrorResponse, createSseResponse } from '../src/server.js';
afterEach(() => {
vi.useRealTimers();
});
describe('createSseResponse', () => {
it('sets SSE headers and sends JSON app events', () => {
const res = new FakeResponse();
const sse = createSseResponse(res, { keepAliveIntervalMs: 0 });
expect(res.headers).toEqual({
'Cache-Control': 'no-cache, no-transform',
Connection: 'keep-alive',
'Content-Type': 'text/event-stream',
'X-Accel-Buffering': 'no',
});
expect(res.flushed).toBe(true);
expect(sse.send('start', { ok: true })).toBe(true);
expect(res.writes.join('')).toBe('event: start\ndata: {"ok":true}\n\n');
});
it('can attach SSE event ids for resumable streams', () => {
const res = new FakeResponse();
const sse = createSseResponse(res, { keepAliveIntervalMs: 0 });
expect(sse.send('stdout', { chunk: 'hello' }, 12)).toBe(true);
expect(res.writes.join('')).toBe('id: 12\nevent: stdout\ndata: {"chunk":"hello"}\n\n');
});
it('emits heartbeat comments before real events', () => {
const res = new FakeResponse();
const sse = createSseResponse(res, { keepAliveIntervalMs: 0 });
expect(sse.writeKeepAlive()).toBe(true);
expect(sse.send('end', {})).toBe(true);
expect(res.writes.join('')).toBe(': keepalive\n\nevent: end\ndata: {}\n\n');
});
it('clears interval heartbeat on close', () => {
vi.useFakeTimers();
const res = new FakeResponse();
createSseResponse(res, { keepAliveIntervalMs: 10 });
vi.advanceTimersByTime(10);
expect(res.writes).toEqual([': keepalive\n\n']);
res.emit('close');
vi.advanceTimersByTime(30);
expect(res.writes).toEqual([': keepalive\n\n']);
});
it('skips writes after the response ends', () => {
const res = new FakeResponse();
const sse = createSseResponse(res, { keepAliveIntervalMs: 0 });
sse.end();
expect(res.ended).toBe(true);
expect(sse.writeKeepAlive()).toBe(false);
expect(sse.send('end', {})).toBe(false);
expect(res.writes).toEqual([]);
});
});
describe('createCompatApiErrorResponse', () => {
it('wraps legacy string errors in the shared ApiError response shape', () => {
expect(createCompatApiErrorResponse('BAD_REQUEST', 'message required')).toEqual({
error: {
code: 'BAD_REQUEST',
message: 'message required',
},
});
});
it('preserves shared ApiError metadata fields', () => {
expect(
createCompatApiErrorResponse('AGENT_UNAVAILABLE', 'missing agent', {
retryable: true,
details: { legacyCode: 'ENOENT' },
}),
).toEqual({
error: {
code: 'AGENT_UNAVAILABLE',
message: 'missing agent',
retryable: true,
details: { legacyCode: 'ENOENT' },
},
});
});
});
class FakeResponse extends EventEmitter {
headers = {};
writes = [];
destroyed = false;
writableEnded = false;
flushed = false;
ended = false;
setHeader(name, value) {
this.headers[name] = value;
}
flushHeaders() {
this.flushed = true;
}
write(chunk) {
this.writes.push(chunk);
return true;
}
end() {
this.ended = true;
this.writableEnded = true;
this.emit('finish');
}
}