Release 2.6.15
This commit is contained in:
@@ -23,6 +23,18 @@ test('background task pages opt into 24-hour timestamps, confirm clearing tasks,
|
||||
playerLabel: 'Player Alpha'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Media sync task',
|
||||
key: 'media-sync:slide:update:2:lzstealthcom',
|
||||
category: 'Refresh',
|
||||
status: 'completed',
|
||||
createdAt: '2026-08-04T11:15:00.000Z',
|
||||
startedAt: '2026-08-04T11:16:00.000Z',
|
||||
finishedAt: '2026-08-04T11:17:00.000Z',
|
||||
metadata: {
|
||||
playerLabel: 'lzstealthcom'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Older task',
|
||||
key: 'older-key',
|
||||
@@ -73,6 +85,8 @@ test('background task pages opt into 24-hour timestamps, confirm clearing tasks,
|
||||
assert.match(queueHtml, /data-confirm-message="Clear finished background tasks\?"/);
|
||||
assert.match(queueHtml, /data-local-datetime-format="24h"/);
|
||||
assert.match(queueHtml, /Player Alpha:secret-key/);
|
||||
assert.match(queueHtml, /lzstealthcom:media-sync:slide:update:2/);
|
||||
assert.doesNotMatch(queueHtml, /lzstealthcom:media-sync:slide:update:2:lzstealthcom/);
|
||||
assert.doesNotMatch(queueKeySearchHtml, /Example task/);
|
||||
assert.match(queueDateSearchHtml, /Example task/);
|
||||
assert.match(scheduledHtml, /data-local-datetime-format="24h"/);
|
||||
|
||||
@@ -276,6 +276,103 @@ test('stale player registrations stop media sync retries and warnings', async ()
|
||||
}
|
||||
});
|
||||
|
||||
test('slide update sync removes uploads that were removed from the slide on the player', async () => {
|
||||
const uploadDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pulse-signage-upload-sync-slide-remove-'));
|
||||
fs.mkdirSync(path.join(uploadDir, 'uploads'), { recursive: true });
|
||||
fs.writeFileSync(path.join(uploadDir, 'uploads', 'keep.bin'), Buffer.from('keep'));
|
||||
fs.writeFileSync(path.join(uploadDir, 'uploads', 'remove.bin'), Buffer.from('remove'));
|
||||
const liveLastSeenAt = new Date(Date.now() - 10_000).toISOString();
|
||||
|
||||
const fetchCalls = [];
|
||||
const originalFetch = global.fetch;
|
||||
global.fetch = async function (url, init) {
|
||||
fetchCalls.push({ url, method: init && init.method ? init.method : 'GET' });
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
statusText: 'OK',
|
||||
headers: {
|
||||
get() {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
async text() {
|
||||
return JSON.stringify({ ok: true });
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const uploadSyncService = createUploadSyncService({
|
||||
common: {
|
||||
async fetchAdminData() {
|
||||
return {
|
||||
slides: [
|
||||
{
|
||||
content_json: JSON.stringify({
|
||||
imageRegion: {
|
||||
type: 'image',
|
||||
value: '/media/uploads/keep.bin'
|
||||
}
|
||||
})
|
||||
}
|
||||
],
|
||||
templates: []
|
||||
};
|
||||
}
|
||||
},
|
||||
pool: {
|
||||
async query(sql, params) {
|
||||
if (String(sql).includes('COUNT(*) AS ref_count')) {
|
||||
return [[{
|
||||
ref_count: params && params[0] === '/media/uploads/keep.bin' ? 1 : 0
|
||||
}]];
|
||||
}
|
||||
|
||||
return [[
|
||||
{
|
||||
identifier: 'player-one',
|
||||
internal_base_url: 'http://player-one:8081',
|
||||
last_seen_at: liveLastSeenAt
|
||||
}
|
||||
]];
|
||||
}
|
||||
},
|
||||
playerSnapshotCache: new Map(),
|
||||
notifyPlayerScreens: async () => {},
|
||||
backgroundTaskQueue: {
|
||||
async enqueueTaskAndWait(definition) {
|
||||
await uploadSyncService.runMediaSyncTask(definition.payload);
|
||||
return definition;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await uploadSyncService.syncPlaylistUploadsOnChange({
|
||||
key: 'slide:update:123',
|
||||
pool: {},
|
||||
localUploadDir: uploadDir,
|
||||
previousUploadRefs: ['/media/uploads/keep.bin', '/media/uploads/remove.bin'],
|
||||
nextUploadRefs: ['/media/uploads/keep.bin']
|
||||
});
|
||||
|
||||
assert.equal(fs.existsSync(path.join(uploadDir, 'uploads', 'keep.bin')), true);
|
||||
assert.equal(fs.existsSync(path.join(uploadDir, 'uploads', 'remove.bin')), false);
|
||||
assert.equal(fetchCalls.filter(function (call) {
|
||||
return call.method === 'PUT';
|
||||
}).length, 1);
|
||||
assert.equal(fetchCalls.filter(function (call) {
|
||||
return call.method === 'DELETE';
|
||||
}).length, 1);
|
||||
assert.ok(fetchCalls.some(function (call) {
|
||||
return call.method === 'DELETE' && call.url.includes('remove.bin');
|
||||
}));
|
||||
} finally {
|
||||
global.fetch = originalFetch;
|
||||
fs.rmSync(uploadDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('slide update sync queues one media task per live player and targets each player base url', async () => {
|
||||
const uploadDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pulse-signage-upload-sync-multi-'));
|
||||
fs.mkdirSync(path.join(uploadDir, 'uploads'), { recursive: true });
|
||||
|
||||
Reference in New Issue
Block a user