70 lines
3.8 KiB
TypeScript
70 lines
3.8 KiB
TypeScript
import assert from "node:assert/strict"
|
|
import { test } from "node:test"
|
|
import { createServerProbeRetentionForTests } from "./server-probe-retention.js"
|
|
import type { ProbeStorageConnection } from "./server-probe-journal-maintenance.js"
|
|
import { sharedDatabaseGate } from "./shared-database-gate.js"
|
|
|
|
function fixture(owner = false) {
|
|
const commands: string[] = [], releases: boolean[] = []
|
|
let settingsReads = 0, drift = false, failCommit = false, checkouts = 0
|
|
const connection: ProbeStorageConnection = {
|
|
async query(text) {
|
|
commands.push(text)
|
|
if (text.includes("probe-retention:settings")) return { rows: [{ requests_retention_days: drift && ++settingsReads > 5 ? 31 : 30 }] }
|
|
if (text.includes("probe-retention:owners")) return { rows: owner ? [{ owner_id: "owned-retention" }] : [] }
|
|
if (text.includes("probe-storage:counters")) return { rows: [{ scope: "global" }, { scope: "owner:owned-retention" }] }
|
|
if (text.includes("probe-storage:gc-candidates")) return { rows: [] }
|
|
if (text === "COMMIT" && failCommit) throw Error("PRIVATE_DRIVER_ERROR")
|
|
return { rows: [] }
|
|
}, release(unsafe) { releases.push(!!unsafe) },
|
|
}
|
|
const run = createServerProbeRetentionForTests(async options => {
|
|
checkouts++; assert(options.deadlineAt <= Date.now() + 20000); return connection
|
|
})
|
|
return { run, commands, releases, checkouts: () => checkouts, drift: () => { drift = true }, failCommit: () => { failCommit = true } }
|
|
}
|
|
test("empty retention uses one bounded backend and no mutation", async () => {
|
|
const f = fixture(); assert.deepEqual(await f.run(), { skipped: false, owners: 0, collected: 0 })
|
|
assert.equal(f.checkouts(), 1); assert.deepEqual(f.releases, [false])
|
|
assert.equal(f.commands.length, 2); assert(f.commands.every(text => text.includes("SELECT")))
|
|
})
|
|
test("selected owner GC reuses one physical backend for policy fences and transaction", async () => {
|
|
const f = fixture(true)
|
|
assert.deepEqual(await f.run(), { skipped: false, owners: 1, collected: 0 })
|
|
assert.equal(f.checkouts(), 1); assert.deepEqual(f.releases, [false])
|
|
assert.equal(f.commands.filter(text => text === "BEGIN").length, 1)
|
|
assert.equal(f.commands.filter(text => text === "COMMIT").length, 1)
|
|
assert(!f.commands.some(text => /DELETE FROM|\/tool\//.test(text)))
|
|
})
|
|
test("retention drift rolls back without committing cleanup", async () => {
|
|
const f = fixture(true); f.drift(); await assert.rejects(f.run())
|
|
assert(!f.commands.includes("COMMIT")); assert(f.commands.includes("ROLLBACK"))
|
|
assert.equal(f.releases.length, 1)
|
|
})
|
|
test("unknown commit destroys once and does not repeat the maintenance command", async () => {
|
|
const f = fixture(true); f.failCommit(); await assert.rejects(f.run())
|
|
assert.equal(f.commands.filter(text => text === "COMMIT").length, 1)
|
|
assert.deepEqual(f.releases, [true])
|
|
})
|
|
|
|
test("maintenance does not overlap and database replacement drains its real lifetime", async () => {
|
|
let entered!: () => void, resume!: () => void, checkouts = 0, replacementStarted = false
|
|
const started = new Promise<void>(resolve => { entered = resolve })
|
|
const pending = new Promise<void>(resolve => { resume = resolve })
|
|
const run = createServerProbeRetentionForTests(async () => {
|
|
checkouts++
|
|
return { async query(text) {
|
|
if (text.includes("probe-retention:settings")) {
|
|
entered(); await pending; return { rows: [{ requests_retention_days: 30 }] }
|
|
}
|
|
return { rows: [] }
|
|
}, release() {} }
|
|
})
|
|
const first = run(); await started
|
|
assert.deepEqual(await run(), { skipped: true, owners: 0, collected: 0 }); assert.equal(checkouts, 1)
|
|
const replacement = sharedDatabaseGate.exclusive(async () => { replacementStarted = true })
|
|
await Promise.resolve(); assert.equal(replacementStarted, false)
|
|
resume(); await first; await replacement
|
|
assert.equal(replacementStarted, true)
|
|
})
|