258 lines
10 KiB
JavaScript
258 lines
10 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
|
|
require('../src/common');
|
|
|
|
const { getPendingMigrations } = require('../src/db/migrations');
|
|
|
|
function createPool(responses) {
|
|
const queries = [];
|
|
return {
|
|
queries,
|
|
async query(sql, params) {
|
|
queries.push([sql, params]);
|
|
const text = String(sql);
|
|
for (const response of responses) {
|
|
if (response.match(text, params)) {
|
|
return response.result;
|
|
}
|
|
}
|
|
return [[]];
|
|
}
|
|
};
|
|
}
|
|
|
|
test('pending migrations are empty when the schema already matches the app version', async () => {
|
|
const pool = createPool([
|
|
{
|
|
match(sql) {
|
|
return sql.includes('FROM information_schema.COLUMNS') && sql.includes('TABLE_NAME = ?') && sql.includes('COLUMN_NAME = ?');
|
|
},
|
|
result: [[{ column_count: 1 }]]
|
|
},
|
|
{
|
|
match(_sql, params) {
|
|
return Array.isArray(params) && params[0] === 'i_schedule_groups';
|
|
},
|
|
result: [[{ table_count: 0 }]]
|
|
},
|
|
{
|
|
match(_sql, params) {
|
|
return Array.isArray(params) && params[0] === 'i_schedule_entries';
|
|
},
|
|
result: [[{ table_count: 0 }]]
|
|
}
|
|
]);
|
|
|
|
const pendingMigrations = await getPendingMigrations(pool, { currentVersion: require('../package.json').version });
|
|
|
|
assert.equal(pendingMigrations.length, 0);
|
|
});
|
|
|
|
test('query index migration upgrades a 2.17.4 installation', async () => {
|
|
const queries = [];
|
|
const pool = {
|
|
queries,
|
|
async query(sql, params) {
|
|
queries.push([sql, params]);
|
|
if (String(sql).includes('FROM information_schema.STATISTICS')) {
|
|
return [[{ index_count: params[1] === 'idx_background_tasks_status' ? 1 : 0 }]];
|
|
}
|
|
return [[]];
|
|
}
|
|
};
|
|
const pending = await getPendingMigrations(pool, { currentVersion: '2.17.4' });
|
|
const indexMigration = pending.find(migration => migration.version === '2.17.5');
|
|
|
|
assert.ok(indexMigration);
|
|
await indexMigration.run(pool);
|
|
|
|
const alterations = queries.map(([sql]) => String(sql)).filter(sql => sql.startsWith('ALTER TABLE'));
|
|
assert.equal(alterations.length, 9);
|
|
assert.ok(alterations.some(sql => sql.includes('ADD INDEX idx_playlist_slides_playlist_position')));
|
|
assert.ok(alterations.some(sql => sql.includes('ADD INDEX idx_template_regions_template_z')));
|
|
assert.ok(alterations.some(sql => sql.includes('ADD INDEX idx_slides_template')));
|
|
assert.ok(alterations.some(sql => sql.includes('ADD INDEX idx_templates_canvas_size')));
|
|
assert.ok(alterations.some(sql => sql.includes('ADD INDEX idx_sessions_expires_at')));
|
|
assert.ok(alterations.some(sql => sql.includes('ADD INDEX idx_background_tasks_status_created_at')));
|
|
assert.ok(alterations.some(sql => sql.includes('ADD INDEX idx_rss_feeds_modified_at')));
|
|
assert.ok(alterations.some(sql => sql.includes('ADD INDEX idx_api_source_groups_modified_at')));
|
|
assert.ok(alterations.some(sql => sql.includes('DROP INDEX idx_background_tasks_status')));
|
|
});
|
|
|
|
test('playlist shuffle migration upgrades an existing 2.15.0 installation', async () => {
|
|
const pool = createPool([]);
|
|
const pending = await getPendingMigrations(pool, { currentVersion: '2.15.0' });
|
|
const shuffleMigration = pending.find(migration => migration.version === '2.15.1');
|
|
assert.ok(shuffleMigration);
|
|
await shuffleMigration.run(pool);
|
|
assert.ok(pool.queries.some(([sql]) => sql.includes('ADD COLUMN group_shuffle')));
|
|
});
|
|
|
|
test('playlist repeat migration upgrades an existing 2.15.1 installation', async () => {
|
|
const pool = createPool([]);
|
|
const pending = await getPendingMigrations(pool, { currentVersion: '2.15.1' });
|
|
const repeatMigration = pending.find(migration => migration.version === '2.15.2');
|
|
assert.ok(repeatMigration);
|
|
await repeatMigration.run(pool);
|
|
assert.ok(pool.queries.some(([sql]) => sql.includes('ADD COLUMN group_mode')));
|
|
assert.ok(pool.queries.some(([sql]) => sql.includes('ADD COLUMN group_repeat_count')));
|
|
});
|
|
|
|
test('canvas size enabled migration upgrades an existing 2.15.2 installation', async () => {
|
|
const pool = createPool([]);
|
|
const pending = await getPendingMigrations(pool, { currentVersion: '2.15.2' });
|
|
const canvasMigration = pending.find(migration => migration.version === '2.15.3');
|
|
assert.ok(canvasMigration);
|
|
await canvasMigration.run(pool);
|
|
assert.ok(pool.queries.some(([sql]) => sql.includes('ALTER TABLE c_canvas_sizes ADD COLUMN enabled')));
|
|
});
|
|
|
|
test('playlist group schema migration is pending for version 2.14.3', async () => {
|
|
const pool = createPool([
|
|
{
|
|
match(sql) {
|
|
return sql.includes('FROM information_schema.COLUMNS') && sql.includes('TABLE_NAME = ?') && sql.includes('COLUMN_NAME = ?');
|
|
},
|
|
result: [[{ column_count: 1 }]]
|
|
},
|
|
{
|
|
match(_sql, params) {
|
|
return Array.isArray(params) && params[0] === 'i_schedule_groups';
|
|
},
|
|
result: [[{ table_count: 0 }]]
|
|
},
|
|
{
|
|
match(_sql, params) {
|
|
return Array.isArray(params) && params[0] === 'i_schedule_entries';
|
|
},
|
|
result: [[{ table_count: 0 }]]
|
|
}
|
|
]);
|
|
|
|
const pendingMigrations = await getPendingMigrations(pool, { currentVersion: '2.14.3' });
|
|
|
|
assert.ok(pendingMigrations.some(function (migration) { return migration.version === '2.15.0'; }));
|
|
});
|
|
|
|
test('pending migrations are reported when an older schema still needs scripts', async () => {
|
|
const pool = createPool([
|
|
{
|
|
match(sql) {
|
|
return sql.includes('FROM information_schema.COLUMNS') && sql.includes('TABLE_NAME = ?') && sql.includes('COLUMN_NAME = ?');
|
|
},
|
|
result: [[{ column_count: 0 }]]
|
|
},
|
|
{
|
|
match(_sql, params) {
|
|
return Array.isArray(params) && params[0] === 'i_schedule_groups';
|
|
},
|
|
result: [[{ table_count: 1 }]]
|
|
},
|
|
{
|
|
match(_sql, params) {
|
|
return Array.isArray(params) && params[0] === 'i_schedule_entries';
|
|
},
|
|
result: [[{ table_count: 1 }]]
|
|
}
|
|
]);
|
|
|
|
const pendingMigrations = await getPendingMigrations(pool, { currentVersion: '2.6.17' });
|
|
|
|
assert.ok(pendingMigrations.length > 0);
|
|
assert.equal(pendingMigrations.filter(function (migration) { return migration.version.indexOf('2.8.') === 0; }).length, 4);
|
|
assert.equal(pendingMigrations.find(function (migration) { return migration.version.indexOf('2.8.') === 0; }).version, '2.8.0');
|
|
});
|
|
|
|
test('a partial timetable schema does not fast-forward migration detection', async () => {
|
|
const pool = createPool([
|
|
{
|
|
match(sql) {
|
|
return sql.includes('FROM information_schema.COLUMNS') && sql.includes('TABLE_NAME = ?') && sql.includes('COLUMN_NAME = ?');
|
|
},
|
|
result: [[{ column_count: 0 }]]
|
|
},
|
|
{
|
|
match(_sql, params) {
|
|
return Array.isArray(params) && params[0] === 'i_timetable_groups';
|
|
},
|
|
result: [[{ table_count: 1 }]]
|
|
},
|
|
{
|
|
match(_sql, params) {
|
|
return Array.isArray(params) && params[0] === 'i_timetable_entries';
|
|
},
|
|
result: [[{ table_count: 0 }]]
|
|
},
|
|
{
|
|
match(_sql, params) {
|
|
return Array.isArray(params) && params[0] === 'i_schedule_groups';
|
|
},
|
|
result: [[{ table_count: 0 }]]
|
|
},
|
|
{
|
|
match(_sql, params) {
|
|
return Array.isArray(params) && params[0] === 'i_schedule_entries';
|
|
},
|
|
result: [[{ table_count: 0 }]]
|
|
}
|
|
]);
|
|
|
|
const pendingMigrations = await getPendingMigrations(pool, { currentVersion: '0.0.0' });
|
|
|
|
assert.ok(pendingMigrations.some(function (migration) { return migration.version === '2.6.16'; }));
|
|
assert.ok(pendingMigrations.some(function (migration) { return migration.version === '2.6.17'; }));
|
|
assert.ok(pendingMigrations.some(function (migration) { return migration.version === '2.6.18'; }));
|
|
});
|
|
|
|
test('template region defaults migration upgrades a 2.17.5 installation', async () => {
|
|
const pool = createPool([]);
|
|
const pending = await getPendingMigrations(pool, { currentVersion: '2.17.5' });
|
|
const regionContentMigration = pending.find(migration => migration.version === '2.18.0');
|
|
|
|
assert.ok(regionContentMigration);
|
|
await regionContentMigration.run(pool);
|
|
|
|
assert.ok(pool.queries.some(([sql]) => String(sql).includes('ALTER TABLE c_template_regions ADD COLUMN default_content_json JSON NULL')));
|
|
assert.ok(pool.queries.some(([sql]) => String(sql).includes('ALTER TABLE c_template_regions ADD COLUMN read_only TINYINT(1) NOT NULL DEFAULT 0')));
|
|
});
|
|
|
|
test('2.19.0 indexed-reference migration backfills slide and template defaults', async () => {
|
|
const queries = [];
|
|
const insertedReferences = [];
|
|
const pool = {
|
|
queries,
|
|
async query(sql, params) {
|
|
const text = String(sql);
|
|
queries.push([text, params]);
|
|
if (text.includes('FROM information_schema.COLUMNS')) return [[{ column_count: 1 }]];
|
|
if (text.includes('FROM information_schema.TABLES')) return [[{ table_count: 0 }]];
|
|
if (text.startsWith('SELECT id, content_json FROM c_slides')) {
|
|
return Number(params[0]) === 0
|
|
? [[{ id: 2, content_json: JSON.stringify({ hero: { type: 'image', value: '/media/uploads/hero.png' } }) }]]
|
|
: [[]];
|
|
}
|
|
if (text.startsWith('SELECT id, template_id, region_key, default_content_json FROM c_template_regions')) {
|
|
return Number(params[0]) === 0
|
|
? [[{ id: 7, template_id: 4, region_key: 'headline', default_content_json: { type: 'text', font_family: 'Acme Display' } }]]
|
|
: [[]];
|
|
}
|
|
if (text.startsWith('INSERT IGNORE INTO c_content_references')) {
|
|
insertedReferences.push.apply(insertedReferences, params[0]);
|
|
}
|
|
return [[]];
|
|
}
|
|
};
|
|
|
|
const pending = await getPendingMigrations(pool, { currentVersion: '2.18.0' });
|
|
const migration = pending.find(item => item.version === '2.19.0');
|
|
assert.ok(migration);
|
|
await migration.run(pool);
|
|
|
|
assert.ok(queries.some(([sql]) => sql.includes('CREATE TABLE IF NOT EXISTS c_content_references')));
|
|
assert.ok(queries.some(([sql]) => sql.includes('id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY')));
|
|
assert.ok(queries.some(([sql]) => sql.includes('UNIQUE KEY uq_content_reference_owner')));
|
|
assert.ok(queries.every(([sql]) => !sql.includes('idx_content_reference_owner')));
|
|
assert.ok(insertedReferences.some(row => row[0] === 'slide' && row[1] === 2 && row[3] === 'media' && row[4] === '/media/uploads/hero.png'));
|
|
assert.ok(insertedReferences.some(row => row[0] === 'template' && row[1] === 4 && row[2] === 'headline' && row[3] === 'font-family' && row[4] === 'acme display'));
|
|
}); |