Files
tkmind_go/ui/desktop/src/backendStatus.test.ts
T

124 lines
3.9 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
import { checkBackendStatus } from './backendStatus';
type FetchInput = Parameters<typeof globalThis.fetch>[0];
type FetchInit = NonNullable<Parameters<typeof globalThis.fetch>[1]>;
const fetchInputUrl = (input: FetchInput): string => {
if (typeof input === 'string') {
return input;
}
if (input instanceof URL) {
return input.toString();
}
return input.url;
};
type FetchSignal = NonNullable<FetchInit['signal']>;
const expectAbortSignal = (init?: FetchInit): FetchSignal => {
expect(init?.signal).toBeInstanceOf(globalThis.AbortSignal);
return init!.signal!;
};
describe('checkBackendStatus', () => {
it('checks /status and validates the secret against /acp', async () => {
const fetch = vi.fn(async (input: FetchInput, init?: FetchInit) => {
const url = fetchInputUrl(input);
if (url === 'https://example.com/goose/status') {
expect(init?.headers).toEqual({ 'X-Secret-Key': 'test-secret' });
expectAbortSignal(init);
return new Response(null, { status: 200 });
}
if (url === 'https://example.com/goose/acp?token=test-secret') {
expectAbortSignal(init);
return new Response(null, { status: 406 });
}
throw new Error(`Unexpected URL: ${url}`);
});
await expect(
checkBackendStatus({
baseUrl: 'https://example.com/goose',
serverSecret: 'test-secret',
fetch,
})
).resolves.toBe(true);
expect(fetch).toHaveBeenCalledTimes(2);
expect(fetch.mock.calls.map(([input]) => fetchInputUrl(input))).toEqual([
'https://example.com/goose/status',
'https://example.com/goose/acp?token=test-secret',
]);
});
it('fails immediately when the ACP auth probe rejects the secret', async () => {
const onEvent = vi.fn();
const fetch = vi.fn(async (input: FetchInput) => {
const url = fetchInputUrl(input);
if (url === 'https://example.com/status') {
return new Response(null, { status: 200 });
}
if (url === 'https://example.com/acp?token=wrong-secret') {
return new Response(null, { status: 401 });
}
throw new Error(`Unexpected URL: ${url}`);
});
await expect(
checkBackendStatus({
baseUrl: 'https://example.com',
serverSecret: 'wrong-secret',
fetch,
options: { onEvent },
})
).resolves.toBe(false);
expect(fetch).toHaveBeenCalledTimes(2);
expect(onEvent).toHaveBeenCalledWith('healthcheck_auth_failed', { attempt: 1 });
});
it('aborts hanging ACP auth probes and reports the healthcheck timeout', async () => {
vi.useFakeTimers();
try {
const onEvent = vi.fn();
const acpSignals: FetchSignal[] = [];
const fetch = vi.fn((input: FetchInput, init?: FetchInit): Promise<Response> => {
const url = fetchInputUrl(input);
if (url === 'https://example.com/status') {
expectAbortSignal(init);
return Promise.resolve(new Response(null, { status: 200 }));
}
if (url === 'https://example.com/acp?token=test-secret') {
const signal = expectAbortSignal(init);
acpSignals.push(signal);
return new Promise<Response>((_, reject) => {
signal.addEventListener('abort', () => reject(new Error('aborted')), { once: true });
});
}
throw new Error(`Unexpected URL: ${url}`);
});
const result = checkBackendStatus({
baseUrl: 'https://example.com',
serverSecret: 'test-secret',
fetch,
options: { onEvent },
});
await vi.advanceTimersByTimeAsync(31000);
await expect(result).resolves.toBe(false);
expect(acpSignals.length).toBeGreaterThan(0);
expect(acpSignals.every((signal) => signal.aborted)).toBe(true);
expect(onEvent).toHaveBeenCalledWith('healthcheck_timeout', { timeoutMs: 30000 });
} finally {
vi.useRealTimers();
}
});
});