Files
Nodecast/test/liveProjectionRoutes.test.js
blomios 4cca030afa feat: split live analysis actions and page catalog loading
Separate incremental live checks from full catalog rebuilds and page projected channel/variant loading so first live-list display stays bounded.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-07 23:45:42 +02:00

82 lines
3.6 KiB
JavaScript

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 analysis', () => {
assert.match(routeSource, /liveProjection\.startRebuild\(null, \{ analyzeAll: true \}\)/);
});
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);
});