mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const { createHash } = require('node:crypto');
|
||||
const fs = require('node:fs/promises');
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
const { afterEach, test } = require('node:test');
|
||||
|
||||
let nodeSqlite;
|
||||
try {
|
||||
nodeSqlite = require('node:sqlite');
|
||||
} catch {
|
||||
nodeSqlite = undefined;
|
||||
}
|
||||
|
||||
const temporaryDirectories = [];
|
||||
const nodeMajor = Number(process.versions.node.split('.')[0]);
|
||||
|
||||
async function temporaryRoot() {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'ql3-backup-'));
|
||||
temporaryDirectories.push(root);
|
||||
return root;
|
||||
}
|
||||
|
||||
function sha256(value) {
|
||||
return createHash('sha256').update(value).digest('hex');
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all(
|
||||
temporaryDirectories
|
||||
.splice(0)
|
||||
.map((directory) => fs.rm(directory, { recursive: true, force: true })),
|
||||
);
|
||||
});
|
||||
|
||||
test(
|
||||
'Node 24 Online Backup produces an integrity-checked restorable snapshot',
|
||||
{ skip: nodeMajor < 24 },
|
||||
async () => {
|
||||
assert.equal(typeof nodeSqlite?.backup, 'function');
|
||||
const root = await temporaryRoot();
|
||||
const sourcePath = path.join(root, 'source.sqlite');
|
||||
const backupPath = path.join(root, 'backup.sqlite');
|
||||
const restoredPath = path.join(root, 'restored.sqlite');
|
||||
const source = new nodeSqlite.DatabaseSync(sourcePath);
|
||||
source.exec(
|
||||
'PRAGMA journal_mode=WAL; CREATE TABLE facts(id INTEGER PRIMARY KEY, value TEXT NOT NULL);',
|
||||
);
|
||||
const insert = source.prepare('INSERT INTO facts(value) VALUES (?)');
|
||||
for (let index = 0; index < 2_000; index += 1) {
|
||||
insert.run('fact-' + index + '-' + 'x'.repeat(128));
|
||||
}
|
||||
|
||||
let progressCalls = 0;
|
||||
let insertedDuringBackup = false;
|
||||
const pages = await nodeSqlite.backup(source, backupPath, {
|
||||
rate: 1,
|
||||
progress() {
|
||||
progressCalls += 1;
|
||||
if (!insertedDuringBackup) {
|
||||
insertedDuringBackup = true;
|
||||
insert.run('inserted-during-online-backup');
|
||||
}
|
||||
},
|
||||
});
|
||||
source.close();
|
||||
|
||||
assert.ok(pages > 0);
|
||||
assert.ok(progressCalls > 0);
|
||||
assert.equal(insertedDuringBackup, true);
|
||||
const backup = new nodeSqlite.DatabaseSync(backupPath, {
|
||||
readOnly: true,
|
||||
});
|
||||
assert.equal(
|
||||
backup.prepare('PRAGMA integrity_check').get().integrity_check,
|
||||
'ok',
|
||||
);
|
||||
assert.equal(
|
||||
backup
|
||||
.prepare(
|
||||
"SELECT COUNT(*) AS count FROM facts WHERE value = 'inserted-during-online-backup'",
|
||||
)
|
||||
.get().count,
|
||||
1,
|
||||
);
|
||||
const expectedCount = backup
|
||||
.prepare('SELECT COUNT(*) AS count FROM facts')
|
||||
.get().count;
|
||||
backup.close();
|
||||
|
||||
await fs.copyFile(backupPath, restoredPath);
|
||||
const restoredBytes = await fs.readFile(restoredPath);
|
||||
const backupBytes = await fs.readFile(backupPath);
|
||||
assert.equal(sha256(restoredBytes), sha256(backupBytes));
|
||||
const restored = new nodeSqlite.DatabaseSync(restoredPath, {
|
||||
readOnly: true,
|
||||
});
|
||||
assert.equal(
|
||||
restored.prepare('PRAGMA quick_check').get().quick_check,
|
||||
'ok',
|
||||
);
|
||||
assert.equal(
|
||||
restored.prepare('SELECT COUNT(*) AS count FROM facts').get().count,
|
||||
expectedCount,
|
||||
);
|
||||
restored.close();
|
||||
},
|
||||
);
|
||||
Reference in New Issue
Block a user