Files
qinglong/test/back/nodeSqliteSchemaCompatibility.test.cjs
T

171 lines
4.8 KiB
JavaScript

require('ts-node/register/transpile-only');
const assert = require('node:assert/strict');
const { execFileSync } = require('node:child_process');
const fs = require('node:fs/promises');
const os = require('node:os');
const path = require('node:path');
const { afterEach, test } = require('node:test');
const { DataTypes, Sequelize } = require('sequelize');
const {
defineSchemaMigrationModel,
} = require('../../back/data/schemaMigration');
const {
RUN_ATTEMPT_TABLE,
RUN_EVENT_TABLE,
RUN_TABLE,
} = require('../../back/migrations/0002-run-schema');
const {
RUN_CANCELLATION_DISPATCH_TABLE,
} = require('../../back/migrations/0005-run-cancellation-dispatch');
const { migrations } = require('../../back/migrations');
const { runMigrations } = require('../../back/migrations/runner');
const {
parseArguments: parseLegacySchemaAuditArguments,
} = require('../../scripts/ql3-schema-audit.cjs');
let nodeSqlite;
try {
nodeSqlite = require('node:sqlite');
} catch {
nodeSqlite = undefined;
}
const nodeMajor = Number(process.versions.node.split('.')[0]);
const temporaryDirectories = [];
test('legacy schema audit never selects a live database implicitly', () => {
assert.throws(
() => parseLegacySchemaAuditArguments([]),
/requires an explicit --database path/,
);
assert.throws(
() =>
parseLegacySchemaAuditArguments([
'--database=/tmp/a.sqlite',
'--database=/tmp/b.sqlite',
]),
/must not be duplicated/,
);
});
async function createMigratedDatabase() {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'ql3-node-sqlite-'));
temporaryDirectories.push(root);
const storage = path.join(root, 'database.sqlite');
const database = new Sequelize({
dialect: 'sqlite',
storage,
logging: false,
});
const queryInterface = database.getQueryInterface();
for (const table of ['CrontabViews', 'Subscriptions', 'Crontabs', 'Envs']) {
await queryInterface.createTable(table, {
id: { type: DataTypes.INTEGER, primaryKey: true },
});
}
await queryInterface.createTable('RunningInstances', {
id: { type: DataTypes.INTEGER, primaryKey: true },
started_at: { type: DataTypes.INTEGER, allowNull: false },
});
await runMigrations({
database,
migrationModel: defineSchemaMigrationModel(database),
migrations,
logger: { info() {} },
});
await database.close();
return storage;
}
afterEach(async () => {
await Promise.all(
temporaryDirectories
.splice(0)
.map((directory) => fs.rm(directory, { recursive: true, force: true })),
);
});
test(
'Node 24 opens the Sequelize migration chain with defensive node:sqlite options',
{ skip: nodeMajor < 24 },
async () => {
assert.equal(typeof nodeSqlite?.DatabaseSync, 'function');
const storage = await createMigratedDatabase();
const database = new nodeSqlite.DatabaseSync(storage, {
allowExtension: false,
allowUnknownNamedParameters: false,
defensive: true,
enableDoubleQuotedStringLiterals: false,
enableForeignKeyConstraints: true,
readOnly: true,
timeout: 1_000,
});
try {
assert.equal(
database.prepare('PRAGMA integrity_check').get().integrity_check,
'ok',
);
const tables = database
.prepare(
"SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name",
)
.all()
.map((row) => row.name);
for (const table of [
RUN_TABLE,
RUN_ATTEMPT_TABLE,
RUN_EVENT_TABLE,
RUN_CANCELLATION_DISPATCH_TABLE,
'SchemaMigrations',
]) {
assert.ok(tables.includes(table), `missing ${table}`);
}
const runColumns = database
.prepare(`PRAGMA table_info(${RUN_TABLE})`)
.all()
.map((row) => row.name);
for (const column of [
'id',
'status',
'version',
'event_sequence',
'cancel_requested_at_ms',
]) {
assert.ok(runColumns.includes(column), `missing Runs.${column}`);
}
const attemptColumns = database
.prepare(`PRAGMA table_info(${RUN_ATTEMPT_TABLE})`)
.all()
.map((row) => row.name);
assert.ok(
attemptColumns.includes('deadline_at_ms'),
'missing RunAttempts.deadline_at_ms',
);
assert.throws(
() => database.exec('CREATE TABLE must_not_write(id INTEGER)'),
/read.?only/i,
);
} finally {
database.close();
}
const audit = JSON.parse(
execFileSync(
process.execPath,
[
path.resolve(__dirname, '../../scripts/ql3-schema-audit.cjs'),
'--json',
`--database=${storage}`,
],
{ encoding: 'utf8' },
),
);
assert.equal(audit.compatible, true);
assert.equal(audit.driftDetected, false);
},
);