quality / changes (push) Successful in 6s
quality / commitlint (push) Skipped
quality / openapi (push) Successful in 28s
quality / web (push) Successful in 57s
quality / api (push) Successful in 1m35s
quality / linux-agent-smoke (push) Successful in 24s
quality / docker-check (push) Failing after 3m20s
CD / quality (push) Failing after 6m56s
CD / publish (push) Skipped
67 lines
3.7 KiB
JavaScript
67 lines
3.7 KiB
JavaScript
import test from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { createRequire } from 'node:module'
|
|
import { mkdtempSync, rmSync, writeFileSync, existsSync, mkdirSync, symlinkSync } from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import { resolve, join } from 'node:path'
|
|
import { spawnSync } from 'node:child_process'
|
|
import { fixtureDatabaseConstructor } from '../bench/fixture.mjs'
|
|
const root = resolve(import.meta.dirname, '../..')
|
|
const Database = createRequire(join(root, 'packages/db/package.json'))('better-sqlite3')
|
|
const script = join(root, 'deploy/docker/evofw/sqlite-backup.mjs')
|
|
|
|
test('online backup includes committed WAL and restore verifies before offline replacement', () => {
|
|
const directory = mkdtempSync(join(tmpdir(), 'evofw-backup-test-'))
|
|
let db
|
|
try {
|
|
// Isolated pnpm-shaped tree: the dependency is a sibling of the real package,
|
|
// never a top-level dependency reachable from its logical symlink location.
|
|
const store = join(directory, 'virtual-store/node_modules')
|
|
const realPackage = join(store, '@evofw/db')
|
|
mkdirSync(realPackage, { recursive: true })
|
|
writeFileSync(join(realPackage, 'package.json'), '{"name":"@evofw/db"}')
|
|
mkdirSync(join(directory, 'node_modules/@evofw'), { recursive: true })
|
|
const actualDependency = createRequire(join(root, 'packages/db/package.json')).resolve('better-sqlite3/package.json')
|
|
symlinkSync(resolve(actualDependency, '..'), join(store, 'better-sqlite3'), 'junction')
|
|
symlinkSync(realPackage, join(directory, 'node_modules/@evofw/db'), 'junction')
|
|
const env = { ...process.env }
|
|
delete env.NODE_PATH
|
|
const run = (...args) => spawnSync(process.execPath, [script, ...args], { cwd: directory, encoding: 'utf8', env })
|
|
assert.throws(() => createRequire(join(directory, 'node_modules/@evofw/db/package.json'))('better-sqlite3'), { code: 'MODULE_NOT_FOUND' })
|
|
const previousDirectory = process.cwd()
|
|
try { process.chdir(directory); assert.equal(fixtureDatabaseConstructor(), Database) } finally { process.chdir(previousDirectory) }
|
|
const source = join(directory, 'source.db')
|
|
const backup = join(directory, 'backup.db')
|
|
const restored = join(directory, 'restored.db')
|
|
db = new Database(source)
|
|
db.pragma('journal_mode = WAL')
|
|
db.exec('CREATE TABLE records(id INTEGER PRIMARY KEY, value TEXT); INSERT INTO records VALUES(1,\'committed WAL\')')
|
|
assert.ok(existsSync(`${source}-wal`))
|
|
const result = run('backup', source, backup)
|
|
assert.equal(result.status, 0, result.stderr)
|
|
const snapshot = new Database(backup, { readonly: true })
|
|
assert.equal(snapshot.prepare('SELECT value FROM records').get().value, 'committed WAL')
|
|
snapshot.close()
|
|
assert.notEqual(run('backup', source, backup).status, 0, 'never overwrite an existing backup')
|
|
assert.notEqual(run('restore', backup, restored).status, 0, 'offline declaration required')
|
|
writeFileSync(restored, 'old offline data')
|
|
writeFileSync(`${restored}-wal`, 'stale')
|
|
writeFileSync(`${restored}-shm`, 'stale')
|
|
const recovery = run('restore', backup, restored, '--offline')
|
|
assert.equal(recovery.status, 0, recovery.stderr)
|
|
assert.ok(!existsSync(`${restored}-wal`))
|
|
const recovered = new Database(restored, { readonly: true })
|
|
assert.equal(recovered.prepare('SELECT value FROM records').get().value, 'committed WAL')
|
|
recovered.close()
|
|
const invalid = join(directory, 'invalid.db')
|
|
writeFileSync(invalid, 'not sqlite')
|
|
assert.notEqual(run('restore', invalid, restored, '--offline').status, 0)
|
|
const preserved = new Database(restored, { readonly: true })
|
|
assert.equal(preserved.prepare('SELECT count(*) AS n FROM records').get().n, 1)
|
|
preserved.close()
|
|
} finally {
|
|
db?.close()
|
|
rmSync(directory, { recursive: true, force: true })
|
|
}
|
|
})
|