Files
thedotmack--claude-mem/tests/shared/worker-utils-version-recycle.test.ts
wehub-resource-sync f9447f8e5f
CI / typecheck · build · test · bundle-size (push) Failing after 1s
CI / clean-room dependency closure smoke (push) Failing after 1s
CI / server-runtime e2e (docker · pg + valkey) (push) Failing after 2s
Deploy Install Scripts / deploy (push) Failing after 2s
Windows / build (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:07:03 +08:00

96 lines
3.6 KiB
TypeScript

import { describe, it, expect, beforeEach, afterEach, afterAll, mock } from 'bun:test';
import * as realInfrastructure from '../../src/services/infrastructure/index.js';
import * as realSupervisor from '../../src/supervisor/index.js';
const realInfrastructureSnapshot = { ...realInfrastructure };
const realSupervisorSnapshot = { ...realSupervisor };
// Record every HTTP call the worker layer makes, so we can assert whether a
// restart was (or was not) issued. fetch is what workerHttpRequest ultimately
// calls via fetchWithTimeout.
const fetchLog: Array<{ url: string; method: string }> = [];
// Controls what checkVersionMatch reports for a given test.
let versionMatchResult: { matches: boolean; pluginVersion: string; workerVersion: string | null } = {
matches: true,
pluginVersion: '13.4.0',
workerVersion: '13.4.0',
};
// A worker is "alive" (healthy + pid ok) for these tests; we exercise the
// version-mismatch branch, not the lazy-spawn-from-dead path.
mock.module('../../src/services/infrastructure/index.js', () => ({
checkVersionMatch: () => Promise.resolve(versionMatchResult),
}));
mock.module('../../src/supervisor/index.js', () => ({
validateWorkerPidFile: () => 'alive',
}));
async function importWorkerUtilsFresh() {
return import(`../../src/shared/worker-utils.js?worker-utils-version-recycle=${Date.now()}-${Math.random()}`);
}
function installFetchMock(): void {
fetchLog.length = 0;
global.fetch = mock((url: string | URL | Request, init?: RequestInit) => {
const u = typeof url === 'string' ? url : url.toString();
const method = (init?.method ?? 'GET').toUpperCase();
fetchLog.push({ url: u, method });
// /api/health and /api/readiness must report OK so the worker is "alive"
// and "ready"; /api/admin/restart and anything else also returns OK.
// The health payload carries the PLUGIN version: the recycle path waits
// for a successor worker reporting the installed plugin's version on
// /api/health (the old worker spawns it after the port closes —
// src/services/worker-shutdown.ts), so this scripts "the successor came
// up immediately".
return Promise.resolve({
ok: true,
status: 200,
text: () => Promise.resolve(''),
json: () => Promise.resolve({ version: versionMatchResult.pluginVersion }),
} as unknown as Response);
}) as unknown as typeof fetch;
}
describe('ensureWorkerRunning — stale-worker recycle on version mismatch', () => {
const originalFetch = global.fetch;
beforeEach(() => {
installFetchMock();
});
afterEach(() => {
global.fetch = originalFetch;
mock.restore();
});
afterAll(() => {
mock.module('../../src/services/infrastructure/index.js', () => realInfrastructureSnapshot);
mock.module('../../src/supervisor/index.js', () => realSupervisorSnapshot);
});
it('POSTs /api/admin/restart when the running worker version differs', async () => {
versionMatchResult = { matches: false, pluginVersion: '13.4.0', workerVersion: '13.3.0' };
const { ensureWorkerRunning } = await importWorkerUtilsFresh();
await ensureWorkerRunning();
const restartCalls = fetchLog.filter(
c => c.url.includes('/api/admin/restart') && c.method === 'POST'
);
expect(restartCalls.length).toBeGreaterThanOrEqual(1);
});
it('does NOT restart when versions match', async () => {
versionMatchResult = { matches: true, pluginVersion: '13.4.0', workerVersion: '13.4.0' };
const { ensureWorkerRunning } = await importWorkerUtilsFresh();
await ensureWorkerRunning();
const restartCalls = fetchLog.filter(c => c.url.includes('/api/admin/restart'));
expect(restartCalls.length).toBe(0);
});
});