Release v2.19.0: indexed content references
This commit is contained in:
1 parent
24f1603b43
commit
b59fe08298
31 files changed
+663
-237
No files matched your search
@@ -0,0 +1,69 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const { collectContentReferences, replaceContentReferences } = require('../src/data/content-references');
|
||||
|
||||
test('content reference extraction indexes typed sources, media paths, and fonts by owner region', () => {
|
||||
const references = collectContentReferences('template', 42, {
|
||||
brand: {
|
||||
type: 'html',
|
||||
value: '<img src="https://signage.example/media/uploads/brand.png"><span style="font-family: \'Acme Display\', sans-serif">Brand</span>',
|
||||
font_family: 'Acme Display'
|
||||
},
|
||||
agenda: { type: 'timetable', timetable_group_id: 7 },
|
||||
feed: { type: 'rss', feed_id: '8' },
|
||||
forecast: { type: 'weather', weather_location_id: 9, weather_icon_set_id: 'night' },
|
||||
api: { type: 'api', source_id: 10 }
|
||||
});
|
||||
const values = references.map(reference => [reference.owner_key, reference.reference_type, reference.reference_value]);
|
||||
|
||||
assert.deepEqual(values, [
|
||||
['brand', 'media', '/media/uploads/brand.png'],
|
||||
['brand', 'font-family', 'acme display'],
|
||||
['agenda', 'timetable-group', '7'],
|
||||
['feed', 'rss-feed', '8'],
|
||||
['forecast', 'weather-location', '9'],
|
||||
['forecast', 'weather-icon-set', 'night'],
|
||||
['api', 'api-request', '10']
|
||||
]);
|
||||
assert.ok(references.every(reference => reference.owner_type === 'template' && reference.owner_id === 42 && Buffer.isBuffer(reference.reference_hash)));
|
||||
});
|
||||
|
||||
test('content reference extraction deduplicates repeated references within a region', () => {
|
||||
const references = collectContentReferences('slide', 12, {
|
||||
hero: { value: '/media/uploads/a.png', secondary: '/media/uploads/a.png', source_id: 4, copy: { source_id: 4 } }
|
||||
});
|
||||
assert.deepEqual(references.map(reference => [reference.reference_type, reference.reference_value]), [
|
||||
['media', '/media/uploads/a.png'],
|
||||
['api-request', '4']
|
||||
]);
|
||||
});
|
||||
|
||||
test('content reference replacement preserves unchanged rows and only deletes or inserts the delta', async () => {
|
||||
const calls = [];
|
||||
const connection = {
|
||||
async query(sql, params) {
|
||||
calls.push([sql, params]);
|
||||
if (sql.startsWith('SELECT id, owner_key')) {
|
||||
return [[
|
||||
{ id: 101, owner_key: 'hero', reference_type: 'media', reference_value: '/media/uploads/keep.png' },
|
||||
{ id: 102, owner_key: 'api', reference_type: 'api-request', reference_value: '4' },
|
||||
{ id: 103, owner_key: 'old', reference_type: 'font-family', reference_value: 'arial' }
|
||||
]];
|
||||
}
|
||||
return [{}];
|
||||
}
|
||||
};
|
||||
|
||||
await replaceContentReferences(connection, 'slide', 22, {
|
||||
hero: { value: '/media/uploads/keep.png' },
|
||||
api: { source_id: 5 },
|
||||
feed: { feed_id: 8 }
|
||||
});
|
||||
|
||||
const deletion = calls.find(([sql]) => sql.startsWith('DELETE FROM c_content_references'));
|
||||
assert.deepEqual(deletion[1], ['slide', 22, [102, 103]]);
|
||||
const insertion = calls.find(([sql]) => sql.startsWith('INSERT IGNORE INTO c_content_references'));
|
||||
assert.equal(insertion[1][0].length, 2);
|
||||
assert.deepEqual(insertion[1][0].map(row => [row[3], row[4]]), [['api-request', '5'], ['rss-feed', '8']]);
|
||||
assert.ok(!calls.some(([sql]) => sql === 'DELETE FROM c_content_references WHERE owner_type = ? AND owner_id = ?'));
|
||||
});
|
||||
Reference in new issue
Block a user