2026-03-01 14:33:18 +01:00
|
|
|
/**
|
|
|
|
|
* Mock context for route handler testing.
|
|
|
|
|
*
|
|
|
|
|
* Satisfies ALL port interfaces (SessionPort, EventPort, RespawnPort,
|
|
|
|
|
* ConfigPort, InfraPort, AuthPort) so any route module can be tested.
|
|
|
|
|
* Override specific methods in individual tests as needed.
|
|
|
|
|
*
|
|
|
|
|
* Uses app.inject() — no real HTTP ports needed.
|
|
|
|
|
*/
|
|
|
|
|
import { vi } from 'vitest';
|
|
|
|
|
import { MockSession, createMockSession } from './mock-session.js';
|
2026-06-29 17:49:39 -04:00
|
|
|
import { resolveTerminalHistoryConfig } from '../../src/config/terminal-history.js';
|
2026-03-01 14:33:18 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates a mock context that satisfies all port interfaces.
|
|
|
|
|
* Pre-populated with one session for convenience.
|
|
|
|
|
*/
|
2026-08-09 11:49:30 +02:00
|
|
|
export function createMockRouteContext(options?: { sessionId?: string; agentSkillEnabled?: boolean }) {
|
2026-03-01 14:33:18 +01:00
|
|
|
const sessionId = options?.sessionId ?? 'test-session-1';
|
|
|
|
|
const session = createMockSession(sessionId);
|
|
|
|
|
const sessions = new Map<string, MockSession>();
|
|
|
|
|
sessions.set(sessionId, session);
|
|
|
|
|
|
2026-06-19 18:42:39 -04:00
|
|
|
// Stateful backing for the global tab order (COD-131) so route tests can
|
|
|
|
|
// assert that setSessionOrder() actually persists what the handler computed.
|
|
|
|
|
let sessionOrder: string[] = [];
|
|
|
|
|
|
2026-03-01 14:33:18 +01:00
|
|
|
return {
|
|
|
|
|
// -- SessionPort --
|
|
|
|
|
sessions,
|
|
|
|
|
addSession: vi.fn((s: MockSession) => {
|
|
|
|
|
sessions.set(s.id, s);
|
|
|
|
|
}),
|
|
|
|
|
cleanupSession: vi.fn(async () => {}),
|
|
|
|
|
setupSessionListeners: vi.fn(async () => {}),
|
|
|
|
|
persistSessionState: vi.fn(),
|
|
|
|
|
persistSessionStateNow: vi.fn(),
|
|
|
|
|
getSessionStateWithRespawn: vi.fn((s: MockSession) => s.toState()),
|
|
|
|
|
|
|
|
|
|
// -- EventPort --
|
|
|
|
|
broadcast: vi.fn(),
|
|
|
|
|
sendPushNotifications: vi.fn(),
|
|
|
|
|
batchTerminalData: vi.fn(),
|
|
|
|
|
broadcastSessionStateDebounced: vi.fn(),
|
|
|
|
|
batchTaskUpdate: vi.fn(),
|
2026-03-08 16:06:15 +01:00
|
|
|
getSseClientCount: vi.fn(() => 0),
|
2026-03-01 14:33:18 +01:00
|
|
|
|
|
|
|
|
// -- RespawnPort --
|
|
|
|
|
respawnControllers: new Map(),
|
|
|
|
|
respawnTimers: new Map(),
|
|
|
|
|
setupRespawnListeners: vi.fn(),
|
|
|
|
|
setupTimedRespawn: vi.fn(),
|
|
|
|
|
restoreRespawnController: vi.fn(),
|
|
|
|
|
saveRespawnConfig: vi.fn(),
|
|
|
|
|
|
|
|
|
|
// -- ConfigPort --
|
|
|
|
|
store: {
|
|
|
|
|
getConfig: vi.fn(() => ({ ralphEnabled: false, maxConcurrentSessions: 5 })),
|
|
|
|
|
getSessions: vi.fn(() => ({})),
|
|
|
|
|
getSession: vi.fn(),
|
|
|
|
|
setSession: vi.fn(),
|
|
|
|
|
removeSession: vi.fn(),
|
|
|
|
|
getSettings: vi.fn(() => ({})),
|
|
|
|
|
setSettings: vi.fn(),
|
|
|
|
|
getRalphLoopState: vi.fn(() => ({})),
|
|
|
|
|
setRalphLoopState: vi.fn(),
|
|
|
|
|
getTasks: vi.fn(() => ({})),
|
|
|
|
|
save: vi.fn(),
|
|
|
|
|
load: vi.fn(),
|
|
|
|
|
incrementSessionsCreated: vi.fn(),
|
|
|
|
|
setConfig: vi.fn(),
|
2026-06-19 18:42:39 -04:00
|
|
|
getSessionOrder: vi.fn(() => sessionOrder),
|
|
|
|
|
setSessionOrder: vi.fn((order: string[]) => {
|
|
|
|
|
sessionOrder = order;
|
|
|
|
|
}),
|
2026-03-01 14:33:18 +01:00
|
|
|
getAggregateStats: vi.fn(() => ({ totalInputTokens: 0, totalOutputTokens: 0, totalCost: 0 })),
|
|
|
|
|
getGlobalStats: vi.fn(() => ({ sessionsCreated: 0 })),
|
|
|
|
|
getDailyStats: vi.fn(() => []),
|
|
|
|
|
cleanupStaleSessions: vi.fn(() => ({ count: 0, cleaned: [] })),
|
|
|
|
|
},
|
|
|
|
|
port: 3000,
|
|
|
|
|
https: false,
|
|
|
|
|
testMode: true,
|
|
|
|
|
serverStartTime: Date.now(),
|
|
|
|
|
getGlobalNiceConfig: vi.fn(async () => undefined),
|
|
|
|
|
getModelConfig: vi.fn(async () => null),
|
|
|
|
|
getClaudeModeConfig: vi.fn(async () => ({})),
|
2026-06-29 17:49:39 -04:00
|
|
|
getTerminalHistoryConfig: vi.fn(async () => resolveTerminalHistoryConfig({})),
|
2026-08-09 11:49:30 +02:00
|
|
|
// Default OFF mirrors the shipped setting, so existing tests never touch a
|
|
|
|
|
// case's .claude/skills. Overridable per test because the create-time
|
|
|
|
|
// injection call sites are otherwise unreachable from a route test.
|
|
|
|
|
getAgentSkillEnabled: vi.fn(async () => options?.agentSkillEnabled ?? false),
|
2026-03-01 14:33:18 +01:00
|
|
|
getDefaultClaudeMdPath: vi.fn(async () => undefined),
|
|
|
|
|
getLightState: vi.fn(() => ({ sessions: [], status: 'ok' })),
|
|
|
|
|
getLightSessionsState: vi.fn(() => {
|
|
|
|
|
const result: Record<string, unknown>[] = [];
|
|
|
|
|
for (const s of sessions.values()) {
|
|
|
|
|
result.push(s.toState());
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}),
|
|
|
|
|
startTranscriptWatcher: vi.fn(),
|
|
|
|
|
stopTranscriptWatcher: vi.fn(),
|
|
|
|
|
|
|
|
|
|
// -- InfraPort --
|
|
|
|
|
mux: {
|
2026-05-26 05:28:58 +08:00
|
|
|
muxSocket: 'codeman',
|
2026-03-01 14:33:18 +01:00
|
|
|
createSession: vi.fn(),
|
|
|
|
|
killSession: vi.fn(),
|
|
|
|
|
listSessions: vi.fn(() => []),
|
|
|
|
|
getStats: vi.fn(() => ({})),
|
|
|
|
|
updateSessionName: vi.fn(() => true),
|
|
|
|
|
getSession: vi.fn(() => null),
|
|
|
|
|
clearRespawnConfig: vi.fn(),
|
|
|
|
|
updateRespawnConfig: vi.fn(),
|
2026-06-29 17:49:39 -04:00
|
|
|
setHistoryLimit: vi.fn(async () => {}),
|
2026-03-01 14:33:18 +01:00
|
|
|
},
|
|
|
|
|
runSummaryTrackers: new Map(),
|
|
|
|
|
activePlanOrchestrators: new Map(),
|
|
|
|
|
scheduledRuns: new Map(),
|
2026-03-01 16:27:40 +01:00
|
|
|
teamWatcher: { getTeams: vi.fn(() => []), getTeamTasks: vi.fn(() => []), hasActiveTeammates: vi.fn(() => false) },
|
2026-03-01 14:33:18 +01:00
|
|
|
tunnelManager: null,
|
|
|
|
|
pushStore: null,
|
|
|
|
|
startScheduledRun: vi.fn(),
|
|
|
|
|
stopScheduledRun: vi.fn(),
|
|
|
|
|
|
|
|
|
|
// -- AuthPort --
|
|
|
|
|
authSessions: null,
|
|
|
|
|
qrAuthFailures: null,
|
|
|
|
|
// https already declared above in ConfigPort (shared property)
|
|
|
|
|
|
2026-03-21 07:20:18 +01:00
|
|
|
// -- OrchestratorPort --
|
|
|
|
|
orchestratorLoop: null,
|
|
|
|
|
initOrchestratorLoop: vi.fn(),
|
|
|
|
|
|
2026-03-01 14:33:18 +01:00
|
|
|
// Convenience accessors (not part of any port interface)
|
|
|
|
|
_session: session,
|
|
|
|
|
_sessionId: sessionId,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export type MockRouteContext = ReturnType<typeof createMockRouteContext>;
|