import test from 'node:test'; import assert from 'node:assert/strict'; import { createHash } from 'node:crypto'; import { loadConfig } from '../server/config.mjs'; import { createScenarioRunner } from '../server/scenarios.mjs'; const cookie = 'ridebus_board_device=test-device-secret'; const publicSettings = { cityId: 1, defaultStopId: 1, configured: true }; const managedSettings = { cityId: 2, defaultStopId: 28, configured: true }; function fixture({ device = { enabled: true, state: 'paired', config: managedSettings }, deviceStatus = 200, env = { STATUS_BOARD_TEST_COOKIE: cookie } } = {}) { const config = loadConfig(env); const service = config.services.find(item => item.id === 'board'); const requests = [], renders = []; const runner = createScenarioRunner(config, { fetchImpl: async (url, options) => { assert.equal(options.method || 'GET', 'GET'); const { pathname, search } = new URL(url); requests.push({ path: pathname + search, cookie: options.headers.Cookie }); if (pathname === '/api/config') return Response.json(publicSettings); if (pathname === '/api/device/status') return Response.json(device, { status: deviceStatus }); if (pathname === '/api/stops') return Response.json([{ id: 1, name: 'Default stop' }, { id: 28, name: 'Assigned stop' }, { id: 99, name: 'Override stop' }]); if (pathname === '/api/board') { const stopId = Number(new URL(url).searchParams.get('stopId')); if (device.enabled && env.STATUS_BOARD_TEST_COOKIE && stopId !== managedSettings.defaultStopId) return Response.json({ error: 'settings_changed' }, { status: 409 }); return Response.json({ mode: 'live', stop: { id: stopId }, departures: [], partial: false }); } throw new Error('Unexpected request'); }, browserChecks: { run: async (_spec, target) => { renders.push({ ...target }); return { ok: true, latencyMs: 20, httpCode: 200 }; }, close: async () => {} }, }); return { runner, service, requests, renders }; } test('paired board uses its assigned city and stop instead of public defaults or env override', async () => { const item = fixture({ env: { STATUS_BOARD_TEST_COOKIE: cookie, STATUS_TEST_CITY_ID: '1', STATUS_BOARD_TEST_STOP_ID: '99' } }); try { const result = await item.runner.run(item.service); assert.equal(result.ok, true); assert.deepEqual(item.renders, [{ cityId: 2, stopId: 28 }]); assert.deepEqual(item.requests.map(value => value.path), ['/api/config', '/api/device/status', '/api/stops', '/api/board?stopId=28']); assert.ok(item.requests.every(value => value.cookie === cookie)); assert.deepEqual(result.steps.map(value => value.id), ['config', 'device', 'stops', 'departures', 'render']); assert.doesNotMatch(JSON.stringify(result), /test-device-secret|Assigned stop/); } finally { await item.runner.close(); } }); test('standalone board keeps its explicit test stop and needs no device endpoint', async () => { const item = fixture({ env: { STATUS_BOARD_TEST_STOP_ID: '99' } }); try { assert.equal((await item.runner.run(item.service)).ok, true); assert.deepEqual(item.renders, [{ cityId: 1, stopId: 99 }]); assert.ok(item.requests.every(value => value.path !== '/api/device/status' && value.cookie === undefined)); } finally { await item.runner.close(); } }); test('cookie on a board with remote settings disabled retains standalone configuration', async () => { const item = fixture({ device: { enabled: false } }); try { assert.equal((await item.runner.run(item.service)).ok, true); assert.deepEqual(item.renders, [{ cityId: 1, stopId: 1 }]); } finally { await item.runner.close(); } }); test('unpaired, pending and expired test sessions are missing access rather than outages', async () => { for (const state of ['unpaired', 'pending', 'expired']) { const item = fixture({ device: { enabled: true, state } }); try { const result = await item.runner.run(item.service); assert.equal(result.ok, null); assert.equal(result.reason, 'auth_required'); assert.equal(result.steps.at(-1).id, 'device'); assert.equal(result.steps.at(-1).status, 'failed'); assert.equal(item.renders.length, 0); assert.equal(item.requests.length, 2); } finally { await item.runner.close(); } } }); test('revoked device access is unknown while malformed paired configuration remains a failure', async () => { for (const options of [{ device: { error: 'device_revoked' }, deviceStatus: 410 }, { device: { enabled: true, state: 'paired', config: { ...managedSettings, defaultStopId: null } } }]) { const item = fixture(options); try { const result = await item.runner.run(item.service); assert.equal(result.ok, options.deviceStatus === 410 ? null : false); assert.equal(result.reason, options.deviceStatus === 410 ? 'auth_required' : 'content'); assert.equal(item.renders.length, 0); } finally { await item.runner.close(); } } }); test('only board scenario history is separated from the old public-default probe', () => { for (const service of loadConfig({ STATUS_BOARD_TEST_COOKIE: cookie }).services.filter(item => item.scenario)) { const { signature: _signature, ...scenario } = service.scenario; const previousSignature = createHash('sha256').update(JSON.stringify({ ...scenario, apiKey: Boolean(scenario.apiKey), cookie: Boolean(scenario.cookie) })).digest('hex').slice(0, 24); if (service.id === 'board') assert.notEqual(service.scenario.signature, previousSignature); else assert.equal(service.scenario.signature, previousSignature); } });