Files
open-design/apps/web/tests/sidecar-shutdown.test.ts
0688faaec3 reland(sidecar): converge product clients on package boundary (#7636)
* reland(sidecar): converge product clients after weekly release

Restore the sidecar convergence from #7244 after the release-scheduling revert in #7634. This tree matches the previously reviewed and cross-platform QA-accepted implementation at d83d4e032f.

* fix(sidecar): clarify identity and cache witnesses

Generated-By: looper 0.11.2 (runner=fixer, agent=codex)

* fix(packaged): probe cross-source desktop owners

Generated-By: looper 0.11.2 (runner=fixer, agent=codex)

* fix(sidecar): preserve cross-source launch ownership

Generated-By: looper 0.11.2 (runner=fixer, agent=codex)

* test(pack): assert cross-source Windows launch ownership

Generated-By: looper 0.11.2 (runner=fixer, agent=codex)

* fix(sidecar): align HyperFrames with client boundary

Generated-By: looper 0.11.2 (runner=fixer, agent=codex)

* fix(e2e): keep HyperFrames fixture in helper lane

Generated-By: looper 0.11.2 (runner=fixer, agent=codex)

* docs(sidecar): restore review and handoff contracts

Generated-By: looper 0.11.2 (runner=fixer, agent=codex)

* fix(sidecar): restore cache and daemon boundaries

Generated-By: looper 0.11.2 (runner=fixer, agent=codex)

* fix(pack): restore sourcemaps and Linux smoke discovery

Generated-By: looper 0.11.2 (runner=fixer, agent=codex)

* fix(sidecar): make payload handoff generation-atomic

* fix(sidecar): preserve packaged action and mode parity

Generated-By: looper 0.11.2 (runner=fixer, agent=codex)

* test(pack): align Linux cleanup resource count

Generated-By: looper 0.11.2 (runner=fixer, agent=codex)

* fix(packaged): preserve headless sidecar mode

* test: normalize source line endings

* fix(daemon): launch Codex cmd shims on Windows

---------

Co-authored-by: Looper <looper@noreply.github.com>
2026-09-01 13:22:04 +00:00

91 lines
3.2 KiB
TypeScript

import { createServer as createHttpServer } from 'node:http';
import { createConnection, type AddressInfo } from 'node:net';
import { describe, expect, it } from 'vitest';
import { createWebHttpServerShutdown } from '../sidecar/server';
describe('web sidecar HTTP shutdown', () => {
it('force-closes a long-lived connection after the bounded drain window', async () => {
const server = createHttpServer((_request, response) => {
response.writeHead(200, {
connection: 'keep-alive',
'content-type': 'text/event-stream',
});
response.write('event: ready\ndata: {}\n\n');
});
const closeServer = createWebHttpServerShutdown(server, 25);
await new Promise<void>((resolve) => server.listen(0, '127.0.0.1', resolve));
const port = (server.address() as AddressInfo).port;
const socket = createConnection(port, '127.0.0.1');
await new Promise<void>((resolve, reject) => {
socket.once('error', reject);
socket.once('connect', () => {
socket.write([
'GET /events HTTP/1.1',
`Host: 127.0.0.1:${port}`,
'Connection: keep-alive',
'',
'',
].join('\r\n'));
});
socket.once('data', () => resolve());
});
const socketClosed = new Promise<void>((resolve) => socket.once('close', () => resolve()));
await expect(Promise.race([
closeServer().then(() => 'closed'),
new Promise<string>((resolve) => setTimeout(() => resolve('timed-out'), 500)),
])).resolves.toBe('closed');
await expect(socketClosed).resolves.toBeUndefined();
expect(server.listening).toBe(false);
});
it('is idempotent after a fast idle shutdown', async () => {
const server = createHttpServer((_request, response) => response.end('ok'));
const closeServer = createWebHttpServerShutdown(server, 25);
await new Promise<void>((resolve) => server.listen(0, '127.0.0.1', resolve));
await closeServer();
await expect(closeServer()).resolves.toBeUndefined();
});
it('retires upgraded sockets even when the HTTP server closes immediately', async () => {
const server = createHttpServer();
server.on('upgrade', (_request, socket) => {
socket.write([
'HTTP/1.1 101 Switching Protocols',
'Connection: Upgrade',
'Upgrade: test',
'',
'',
].join('\r\n'));
});
const closeServer = createWebHttpServerShutdown(server, 250);
await new Promise<void>((resolve) => server.listen(0, '127.0.0.1', resolve));
const port = (server.address() as AddressInfo).port;
const socket = createConnection(port, '127.0.0.1');
await new Promise<void>((resolve, reject) => {
socket.once('error', reject);
socket.once('connect', () => {
socket.write([
'GET /upgrade HTTP/1.1',
`Host: 127.0.0.1:${port}`,
'Connection: Upgrade',
'Upgrade: test',
'',
'',
].join('\r\n'));
});
socket.once('data', () => resolve());
});
const socketClosed = new Promise<void>((resolve) => socket.once('close', () => resolve()));
await closeServer();
await expect(socketClosed).resolves.toBeUndefined();
expect(server.listening).toBe(false);
});
});