Files
DeskClaw/tests/unit/gateway-ready-fallback.test.ts

128 lines
4.6 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest';
vi.mock('electron', () => ({
app: {
getPath: () => '/tmp',
isPackaged: false,
},
utilityProcess: {},
}));
vi.mock('@electron/utils/logger', () => ({
logger: {
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
debug: vi.fn(),
},
}));
vi.mock('@electron/utils/config', () => ({
PORTS: { OPENCLAW_GATEWAY: 18789 },
}));
describe('GatewayManager gatewayReady fallback', () => {
beforeEach(() => {
vi.useFakeTimers();
vi.clearAllMocks();
});
afterEach(() => {
vi.useRealTimers();
});
it('sets gatewayReady=false when entering starting state', async () => {
vi.resetModules();
const { GatewayManager } = await import('@electron/gateway/manager');
const manager = new GatewayManager();
const statusUpdates: Array<{ gatewayReady?: boolean }> = [];
manager.on('status', (status: { gatewayReady?: boolean }) => {
statusUpdates.push({ gatewayReady: status.gatewayReady });
});
// Simulate start attempt (will fail but we can check the initial status)
try {
await manager.start();
} catch {
// expected to fail — no actual gateway process
}
const startingUpdate = statusUpdates.find((u) => u.gatewayReady === false);
expect(startingUpdate).toBeDefined();
});
it('emits gatewayReady=true when gateway:ready event is received', async () => {
vi.resetModules();
const { GatewayManager } = await import('@electron/gateway/manager');
const manager = new GatewayManager();
// Force internal state to 'running' for the test
const stateController = (manager as unknown as { stateController: { setStatus: (u: Record<string, unknown>) => void } }).stateController;
stateController.setStatus({ state: 'running', connectedAt: Date.now() });
const statusUpdates: Array<{ gatewayReady?: boolean; state: string }> = [];
manager.on('status', (status: { gatewayReady?: boolean; state: string }) => {
statusUpdates.push({ gatewayReady: status.gatewayReady, state: status.state });
});
manager.emit('gateway:ready', {});
const readyUpdate = statusUpdates.find((u) => u.gatewayReady === true);
expect(readyUpdate).toBeDefined();
});
it('auto-sets gatewayReady=true after fallback timeout if no event received', async () => {
vi.resetModules();
const { GatewayManager } = await import('@electron/gateway/manager');
const manager = new GatewayManager();
// Force internal state to 'running' without gatewayReady
const stateController = (manager as unknown as { stateController: { setStatus: (u: Record<string, unknown>) => void } }).stateController;
stateController.setStatus({ state: 'running', connectedAt: Date.now() });
const statusUpdates: Array<{ gatewayReady?: boolean }> = [];
manager.on('status', (status: { gatewayReady?: boolean }) => {
statusUpdates.push({ gatewayReady: status.gatewayReady });
});
// Call the private scheduleGatewayReadyFallback method
(manager as unknown as { scheduleGatewayReadyFallback: () => void }).scheduleGatewayReadyFallback();
// Before timeout, no gatewayReady update
vi.advanceTimersByTime(29_000);
expect(statusUpdates.find((u) => u.gatewayReady === true)).toBeUndefined();
// After 30s fallback timeout
vi.advanceTimersByTime(2_000);
const readyUpdate = statusUpdates.find((u) => u.gatewayReady === true);
expect(readyUpdate).toBeDefined();
});
it('cancels fallback timer when gateway:ready event arrives first', async () => {
vi.resetModules();
const { GatewayManager } = await import('@electron/gateway/manager');
const manager = new GatewayManager();
const stateController = (manager as unknown as { stateController: { setStatus: (u: Record<string, unknown>) => void } }).stateController;
stateController.setStatus({ state: 'running', connectedAt: Date.now() });
const statusUpdates: Array<{ gatewayReady?: boolean }> = [];
manager.on('status', (status: { gatewayReady?: boolean }) => {
statusUpdates.push({ gatewayReady: status.gatewayReady });
});
// Schedule fallback
(manager as unknown as { scheduleGatewayReadyFallback: () => void }).scheduleGatewayReadyFallback();
// gateway:ready event arrives at 5s
vi.advanceTimersByTime(5_000);
manager.emit('gateway:ready', {});
expect(statusUpdates.filter((u) => u.gatewayReady === true)).toHaveLength(1);
// After 30s, no duplicate gatewayReady=true
vi.advanceTimersByTime(30_000);
expect(statusUpdates.filter((u) => u.gatewayReady === true)).toHaveLength(1);
});
});