diff --git a/tests/soak/runner.ts b/tests/soak/runner.ts index e1b419f..0ed9d83 100644 --- a/tests/soak/runner.ts +++ b/tests/soak/runner.ts @@ -10,10 +10,12 @@ */ import * as path from 'path'; +import { spawn } from 'child_process'; import { parseArgs, mergeConfig, CliArgs } from './config'; import { createLogger } from './core/logger'; import { SessionPool } from './core/session-pool'; import { RoomCoordinator } from './core/room-coordinator'; +import { DashboardServer } from './dashboard/server'; import { getScenario, listScenarios } from './scenarios'; import type { DashboardReporter, ScenarioContext } from './core/types'; @@ -96,11 +98,6 @@ async function main(): Promise { return; } - if (watch !== 'none') { - logger.warn('watch_mode_not_yet_implemented', { watch }); - console.warn(`Watch mode "${watch}" not yet implemented — falling back to "none".`); - } - // Build dependencies const credFile = path.resolve(__dirname, '.env.stresstest'); const pool = new SessionPool({ @@ -110,7 +107,41 @@ async function main(): Promise { logger, }); const coordinator = new RoomCoordinator(); - const dashboard = noopDashboard(); + + // Optional dashboard server + let dashboardServer: DashboardServer | null = null; + let dashboard: DashboardReporter = noopDashboard(); + if (watch === 'dashboard') { + const port = Number(config.dashboardPort ?? 7777); + dashboardServer = new DashboardServer(port, logger, { + onStartStream: (key) => { + logger.info('stream_start_requested', { sessionKey: key }); + // Wired in Task 23 + }, + onStopStream: (key) => { + logger.info('stream_stop_requested', { sessionKey: key }); + }, + }); + await dashboardServer.start(); + dashboard = dashboardServer.reporter(); + const url = `http://localhost:${port}`; + console.log(`Dashboard: ${url}`); + try { + const opener = + process.platform === 'darwin' + ? 'open' + : process.platform === 'win32' + ? 'start' + : 'xdg-open'; + spawn(opener, [url], { stdio: 'ignore', detached: true }).unref(); + } catch { + // If auto-open fails, the URL is already printed. + } + } else if (watch === 'tiled') { + logger.warn('tiled_not_yet_implemented'); + console.warn('Watch mode "tiled" not yet implemented (Task 24). Falling back to none.'); + } + const abortController = new AbortController(); const onSignal = (sig: string) => { @@ -161,6 +192,9 @@ async function main(): Promise { exitCode = 1; } finally { await pool.release(); + if (dashboardServer) { + await dashboardServer.stop(); + } } if (abortController.signal.aborted && exitCode === 0) exitCode = 2;