Keep live stream variant expansion scoped to the selected language while preserving the legacy all-language variants response when no language is provided. Display projected current EPG programme data in live rows and distinguish selected streams from actively playing streams in the collapsed variant UI. Refs #43 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
105 lines
4.4 KiB
JavaScript
105 lines
4.4 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 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');
|
|
assert.ok(/language[^\n]*valid language code/.test(routeSource), 'variants route should reject invalid language codes');
|
|
assert.ok(/getChannelVariants\(\{[^}]*logicalChannelId[^}]*language[^}]*page[^}]*limit/s.test(routeSource), 'variants route should forward language to getChannelVariants');
|
|
});
|
|
|
|
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);
|
|
assert.equal(state.phaseProcessed, 1);
|
|
assert.equal(state.phaseTotal, 2);
|
|
});
|
|
|
|
test('projection status exposes generic phase progress outside Laya analysis', () => {
|
|
const state = serializeState({
|
|
status: 'building',
|
|
phase: 'projecting',
|
|
started_at: 100,
|
|
completed_at: null,
|
|
input_hash: null,
|
|
projected_count: 0,
|
|
processed_count: 7,
|
|
total_count: 12,
|
|
laya_processed_count: 0,
|
|
laya_total_count: 0,
|
|
error: null
|
|
});
|
|
|
|
assert.equal(state.phaseProcessed, 7);
|
|
assert.equal(state.phaseTotal, 12);
|
|
});
|