Files
claude-code/packages/remote-control-server/src/__tests__/disconnect-monitor.test.ts
2026-05-01 21:39:30 +08:00

119 lines
3.2 KiB
TypeScript

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' },
})
})
})