const test = require('node:test'); const assert = require('node:assert/strict'); const { refreshApiSource, refreshRssFeed } = require('../src/web/lib/data-source-refresh'); function createConnection(options) { const state = Object.assign({ rssItemSnapshots: [], slideRows: [], screenRows: [] }, options || {}); const calls = []; return { calls, async beginTransaction() { calls.push(['beginTransaction']); }, async commit() { calls.push(['commit']); }, async rollback() { calls.push(['rollback']); }, release() { calls.push(['release']); }, async query(sql, params) { calls.push(['query', sql, params]); if (sql.includes('SELECT item_json')) { return [state.rssItemSnapshots.map(function (itemJson) { return { item_json: itemJson }; })]; } if (sql.includes('SELECT id, content_json FROM c_slides')) { return [state.slideRows]; } if (sql.includes('SELECT DISTINCT s.slug')) { return [state.screenRows]; } return [[]]; } }; } test('refreshApiSource notifies players only when the API snapshot changes', async () => { const connection = createConnection({ slideRows: [{ id: 10, content_json: JSON.stringify({ source_id: 7 }) }], screenRows: [{ slug: 'screen-a' }] }); const notifyCalls = []; const pool = { async getConnection() { return connection; } }; const common = { async fetchApiSourceResponse() { return { responseJson: JSON.stringify({ value: 'new' }, null, 2), responseStatus: 200, responseContentType: 'application/json' }; }, parseJsonSafe(value) { return JSON.parse(value); } }; await refreshApiSource(pool, common, { id: 7, last_response_json: JSON.stringify({ value: 'old' }, null, 2) }, 42, async function (slugs, payload) { notifyCalls.push({ slugs, payload }); }); assert.deepEqual(notifyCalls, [{ slugs: ['screen-a'], payload: 'refresh' }]); }); test('refreshApiSource skips notifications when the API snapshot is unchanged', async () => { const connection = createConnection({ slideRows: [{ id: 10, content_json: JSON.stringify({ source_id: 7 }) }], screenRows: [{ slug: 'screen-a' }] }); const notifyCalls = []; const pool = { async getConnection() { return connection; } }; const snapshot = JSON.stringify({ value: 'same' }, null, 2); const common = { async fetchApiSourceResponse() { return { responseJson: snapshot, responseStatus: 200, responseContentType: 'application/json' }; }, parseJsonSafe(value) { return JSON.parse(value); } }; await refreshApiSource(pool, common, { id: 7, last_response_json: snapshot }, 42, async function (slugs, payload) { notifyCalls.push({ slugs, payload }); }); assert.deepEqual(notifyCalls, []); }); test('refreshRssFeed notifies players only when the RSS items change', async () => { const connection = createConnection({ rssItemSnapshots: [JSON.stringify({ title: 'Old item' })], slideRows: [{ id: 11, content_json: JSON.stringify({ feed_id: 9 }) }], screenRows: [{ slug: 'screen-b' }] }); const notifyCalls = []; const pool = { async getConnection() { return connection; } }; const common = { async fetchRssFeedItems() { return [{ title: 'New item' }]; }, async replaceRssFeedItems() { return 1; }, parseJsonSafe(value) { return JSON.parse(value); } }; await refreshRssFeed(pool, common, 9, 'https://example.com/feed.xml', 10, 42, async function (slugs, payload) { notifyCalls.push({ slugs, payload }); }); assert.deepEqual(notifyCalls, [{ slugs: ['screen-b'], payload: 'refresh' }]); }); test('refreshRssFeed skips notifications when the RSS items are unchanged', async () => { const snapshot = JSON.stringify({ title: 'Same item' }); const connection = createConnection({ rssItemSnapshots: [snapshot], slideRows: [{ id: 11, content_json: JSON.stringify({ feed_id: 9 }) }], screenRows: [{ slug: 'screen-b' }] }); const notifyCalls = []; const pool = { async getConnection() { return connection; } }; const common = { async fetchRssFeedItems() { return [{ title: 'Same item' }]; }, async replaceRssFeedItems() { return 1; }, parseJsonSafe(value) { return JSON.parse(value); } }; await refreshRssFeed(pool, common, 9, 'https://example.com/feed.xml', 10, 42, async function (slugs, payload) { notifyCalls.push({ slugs, payload }); }); assert.deepEqual(notifyCalls, []); });