const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('fs'); const path = require('path'); const { isAdmin } = require('../server/authz'); const { serializeState } = require('../server/routes/live'); const routeSource = fs.readFileSync(path.join(__dirname, '..', 'server', 'routes', 'live.js'), 'utf8'); test('projection status route requires authentication', () => { assert.match(routeSource, /router\.get\('\/projection\/status', requireAuth,/); }); test('projection rebuild route requires authentication and admin authorization', () => { assert.match(routeSource, /router\.post\('\/projection\/rebuild', requireAuth, requireAdmin,/); }); test('projection rebuild route requests a complete orchestration', () => { assert.match(routeSource, /liveProjection\.fullRebuildOrchestrator\(\)/); }); test('live channels route accepts pagination and forwards page bounds to the projection service', () => { assert.ok(/req\.query\.page/.test(routeSource), 'GET /channels should parse req.query.page'); assert.ok(/req\.query\.limit/.test(routeSource), 'GET /channels should parse req.query.limit'); assert.ok(/getChannelPage\(\{[^}]*page[^}]*limit/s.test(routeSource), 'GET /channels should forward page and limit to getChannelPage'); }); test('live variants route is paginated and authenticated', () => { assert.ok(/router\.get\('\/channels\/:logicalChannelId\/variants', requireAuth,/.test(routeSource), 'GET /channels/:logicalChannelId/variants should exist and require auth'); assert.ok(/getChannelVariants\(\{[^}]*logicalChannelId[^}]*page[^}]*limit/s.test(routeSource), 'variants route should call getChannelVariants with page and limit'); }); test('projection check-new route exists separately from complete rebuild', () => { assert.ok(/router\.post\('\/projection\/check-new', requireAuth, requireAdmin,/.test(routeSource), 'POST /projection/check-new should exist and require admin'); assert.ok(/liveProjection\.(?:checkNewStreams|startIncrementalAnalysis|analyzeNewStreams)/.test(routeSource), 'check-new route should call an incremental analysis helper'); assert.ok(!/\/projection\/check-new'[\s\S]*analyzeAll: true/.test(routeSource), 'check-new route must not request analyzeAll'); }); test('projection authorization distinguishes viewer and admin roles', () => { assert.equal(isAdmin({ role: 'viewer' }), false); assert.equal(isAdmin({ role: 'admin' }), true); assert.equal(isAdmin(null), false); }); test('projection success with live input but zero projected channels is exposed as stale', () => { const state = serializeState({ status: 'success', started_at: 100, completed_at: 200, input_hash: 'hash', projected_count: 0, processed_count: 3, total_count: 3, error: null }); assert.equal(state.stale, true); assert.equal(state.projectedCount, 0); assert.equal(state.total, 3); }); test('projection status exposes phase-specific Laya progress separately from catalog progress', () => { const state = serializeState({ status: 'building', phase: 'laya_identity', started_at: 100, completed_at: null, input_hash: null, projected_count: 0, processed_count: 0, total_count: 12, laya_processed_count: 1, laya_total_count: 2, error: null }); assert.equal(state.phase, 'laya_identity'); assert.equal(state.processed, 0); assert.equal(state.total, 12); assert.equal(state.layaProcessed, 1); assert.equal(state.layaTotal, 2); });