import { describe, test, expect, beforeEach, mock } from 'bun:test' // Mock config with very short timeout for testing const mockConfig = { port: 3000, host: '0.0.0.0', apiKeys: ['test-api-key'], baseUrl: 'http://localhost:3000', pollTimeout: 8, heartbeatInterval: 20, jwtExpiresIn: 3600, disconnectTimeout: 300, webCorsOrigins: [], wsIdleTimeout: 30, wsKeepaliveInterval: 20, } mock.module('../config', () => ({ config: mockConfig, getBaseUrl: () => 'http://localhost:3000', })) import { storeReset, storeCreateEnvironment, storeUpdateEnvironment, storeCreateSession, storeUpdateSession, storeGetEnvironment, storeGetSession, } from '../store' import { getEventBus, getAllEventBuses, removeEventBus, } from '../transport/event-bus' import { runDisconnectMonitorSweep } from '../services/disconnect-monitor' describe('Disconnect Monitor Logic', () => { beforeEach(() => { storeReset() for (const [key] of getAllEventBuses()) { removeEventBus(key) } }) test('environment times out when lastPollAt is too old', () => { const env = storeCreateEnvironment({ secret: 's' }) const timeoutMs = 300 * 1000 // 5 minutes // Simulate lastPollAt being 6 minutes ago const oldDate = new Date(Date.now() - timeoutMs - 60000) storeUpdateEnvironment(env.id, { lastPollAt: oldDate }) runDisconnectMonitorSweep() const updated = storeGetEnvironment(env.id) expect(updated?.status).toBe('disconnected') }) test('environment stays active when lastPollAt is recent', () => { const env = storeCreateEnvironment({ secret: 's' }) runDisconnectMonitorSweep() const updated = storeGetEnvironment(env.id) expect(updated?.status).toBe('active') }) test('session becomes inactive when updatedAt is too old', () => { const session = storeCreateSession({}) storeUpdateSession(session.id, { status: 'running' }) const rec = storeGetSession(session.id) expect(rec).toBeTruthy() if (!rec) return rec.updatedAt = new Date(Date.now() - 300 * 1000 * 2 - 60000) runDisconnectMonitorSweep() const updated = storeGetSession(session.id) expect(updated?.status).toBe('inactive') }) test('session stays running when recently updated', () => { const session = storeCreateSession({}) storeUpdateSession(session.id, { status: 'running' }) runDisconnectMonitorSweep() const updated = storeGetSession(session.id) expect(updated?.status).toBe('running') }) test('session timeout publishes an inactive session_status event', () => { const session = storeCreateSession({}) storeUpdateSession(session.id, { status: 'idle' }) const rec = storeGetSession(session.id) expect(rec).toBeTruthy() if (!rec) return rec.updatedAt = new Date(Date.now() - 300 * 1000 * 2 - 60000) const bus = getEventBus(session.id) const events: Array<{ type: string; payload: { status?: string } }> = [] bus.subscribe(event => { events.push({ type: event.type, payload: event.payload as { status?: string }, }) }) runDisconnectMonitorSweep() expect(events).toContainEqual({ type: 'session_status', payload: { status: 'inactive' }, }) }) })