Files
pulse-signage/test/data-source-refresh.test.js
lzstealth 3f4f57020a
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile, web, pulse-signage-web) (push) Successful in 1m13s
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile.player, player, pulse-signage-player) (push) Successful in 31s
Release v2.10.3
2026-08-29 01:56:45 +01:00

264 lines
7.3 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const { refreshApiSource, refreshRssFeed, refreshWeatherLocation } = 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, []);
});
test('refreshWeatherLocation notifies players when the forecast changes', async () => {
const connection = createConnection({
slideRows: [{ id: 12, content_json: JSON.stringify({ weather_location_id: 4 }) }],
screenRows: [{ slug: 'screen-c' }]
});
const notifyCalls = [];
const pool = { async getConnection() { return connection; } };
const common = {
async fetchWeatherLocationForecast() {
return { responseJson: JSON.stringify({ temperature: 21 }) };
},
parseJsonSafe(value) {
return JSON.parse(value);
}
};
await refreshWeatherLocation(pool, common, {
id: 4,
last_pulled_at: new Date(),
last_response_json: JSON.stringify({ temperature: 20 })
}, 42, async function (slugs, payload) {
notifyCalls.push({ slugs, payload });
});
assert.deepEqual(notifyCalls, [{ slugs: ['screen-c'], payload: 'refresh' }]);
});
test('refreshWeatherLocation skips notifications when the forecast is unchanged within the hour', async () => {
const connection = createConnection({
slideRows: [{ id: 12, content_json: JSON.stringify({ weather_location_id: 4 }) }],
screenRows: [{ slug: 'screen-c' }]
});
const notifyCalls = [];
const pool = { async getConnection() { return connection; } };
const snapshot = JSON.stringify({ temperature: 20 });
const common = {
async fetchWeatherLocationForecast() {
return { responseJson: snapshot };
},
parseJsonSafe(value) {
return JSON.parse(value);
}
};
await refreshWeatherLocation(pool, common, {
id: 4,
last_pulled_at: new Date(),
last_response_json: snapshot
}, 42, async function (slugs, payload) {
notifyCalls.push({ slugs, payload });
});
assert.deepEqual(notifyCalls, []);
});
test('refreshWeatherLocation notifies when an unchanged forecast crosses into a new hour', async () => {
const connection = createConnection({
slideRows: [{ id: 12, content_json: JSON.stringify({ weather_location_id: 4 }) }],
screenRows: [{ slug: 'screen-c' }]
});
const notifyCalls = [];
const pool = { async getConnection() { return connection; } };
const snapshot = JSON.stringify({ temperature: 20 });
const common = {
async fetchWeatherLocationForecast() {
return { responseJson: snapshot };
},
parseJsonSafe(value) {
return JSON.parse(value);
}
};
await refreshWeatherLocation(pool, common, {
id: 4,
last_pulled_at: new Date('2000-01-01T00:00:00.000Z'),
last_response_json: snapshot
}, 42, async function (slugs, payload) {
notifyCalls.push({ slugs, payload });
});
assert.deepEqual(notifyCalls, [{ slugs: ['screen-c'], payload: 'refresh' }]);
});