Files
MikrotikManager/backend/src/db/controlled-migrations.test.ts
T

210 lines
14 KiB
TypeScript

import assert from "node:assert/strict"
import { createHash, createHmac, randomUUID } from "node:crypto"
import * as fs from "node:fs/promises"
import { test } from "node:test"
import { runInNewContext } from "node:vm"
import ts from "typescript"
import * as manifest from "./migration-manifest.js"
import * as model from "./database-schema-control-model.js"
import { PARTITION_WRITER_ADVISORY_KEY } from "../services/partition-write-transaction.js"
const pins = manifest.MIGRATION_MANIFEST
const sources = await Promise.all(pins.map(async pin => ({ pin, source: await fs.readFile(new URL("../../drizzle/" + pin.file, import.meta.url), "utf8") })))
const originalSource = await fs.readFile(new URL("./controlled-migrations.ts", import.meta.url), "utf8")
const compiled = ts.transpileModule(originalSource.replaceAll("import.meta.url", JSON.stringify(new URL("./controlled-migrations.ts", import.meta.url).href)), { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022, esModuleInterop: true } }).outputText
const canonical = (value: unknown): string => JSON.stringify(value, (_key, item: unknown) => item && typeof item === "object" && !Array.isArray(item) ? Object.fromEntries(Object.entries(item).sort(([a], [b]) => a.localeCompare(b))) : item)
/** Execute actual production dispatcher source with inert module I/O. These
* protocol tests do not claim PostgreSQL DDL execution or cryptographic key
* provenance; root's private PG suite independently covers actual SQL. */
function fixture() {
let state = model.initialDatabaseSchemaControlState(), revision = 0, publicPresent = false, markerIds = new Set<string>()
const history = new Map<string, { id: string; intent: model.DatabaseSchemaIntent; terminal: model.DatabaseSchemaTerminal | null }>()
const events: string[] = [], appReleases: boolean[] = [], reads = new Map<string, number>()
const faults = { beginFile: "", file: "", marker: "", terminal: false, lostCommit: false, rollbackTag: false, driftFile: "", extraFile: false, forgeMarker: false }
let activeLease = false
const control = { acquireDatabaseSchemaControl: async () => {
if (activeLease) throw Error("live control lease")
activeLease = true; state = model.claimDatabaseSchemaOwnership(state, randomUUID()); revision++
let closed = false
return {
read: async () => { if (closed) throw Error("closed"); return { revision: String(revision), state: structuredClone(state) } },
transition: async (command: model.DatabaseSchemaControlCommand) => {
if (closed) throw Error("closed")
if (command.type === "begin") {
const pin = pins.find(pin => pin.sha256 === command.artifactSha256)!
if (faults.beginFile === pin.file) { closed = true; activeLease = false; throw Error("intent unavailable BEFORE publication") }
events.push("intent:" + pin.file)
state = model.transitionDatabaseSchemaControl(state, state.owner!, structuredClone(command))
history.set(command.id, { id: command.id, intent: structuredClone(state.intent!), terminal: null })
} else {
const row = history.get(command.id)!
events.push("terminal:" + row.intent.artifactSha256 + ":" + command.outcome)
state = model.transitionDatabaseSchemaControl(state, state.owner!, structuredClone(command))
row.terminal = structuredClone(state.terminal)
if (faults.terminal) { closed = true; activeLease = false; throw Error("terminal ACK lost") }
}
revision++; return { revision: String(revision), state: structuredClone(state) }
},
close: async () => { closed = true; activeLease = false },
}
} }
// These exact strings are read from the actual module exports after load.
const pool = { connect: async () => {
let outerMarkers: Set<string> | undefined, outerPublic: boolean | undefined
return {
query: async (query: string, values: unknown[] = []) => {
if (query === sql.history) return { rows: [...history.values()].map(row => structuredClone(row)) }
if (query === sql.namespaces) return { rows: [{ name: "mmapp_control" }, { name: "public" }] }
if (query === sql.publicObjects) return { rows: [{ present: publicPresent }] }
if (query === sql.markerTable) return { rows: publicPresent ? [{ kind: "r", persistence: "p", rls: false, force_rls: false, user_triggers: false }] : [] }
if (query === sql.markers) return { rows: [...markerIds].sort().map(id => ({ id })) }
if (query === sql.marker) {
const id = String(values[0]); events.push("marker:" + id)
if (faults.marker === id) throw Error("marker ACK unavailable")
if (markerIds.has(id)) throw Error("duplicate marker")
markerIds.add(id); return { rows: [] }
}
if (query === "BEGIN") { assert(!outerMarkers); outerMarkers = new Set(markerIds); outerPublic = publicPresent; events.push("BEGIN"); return { rows: [] } }
if (query === "COMMIT") { assert(outerMarkers); outerMarkers = undefined; outerPublic = undefined; events.push("COMMIT"); if (faults.lostCommit) throw Error("application COMMIT ACK lost"); return { rows: [], command: faults.rollbackTag ? "ROLLBACK" : "COMMIT" } }
if (query.startsWith("SET ") || query.startsWith("RESET ")) { events.push(query); return { rows: [] } }
if (query.includes("pg_catalog.pg_advisory_lock(")) { assert.deepEqual(Array.from(values), [PARTITION_WRITER_ADVISORY_KEY]); events.push("exclusive lease"); return { rows: [] } }
if (query.includes("pg_catalog.pg_advisory_unlock(")) { events.push("unlock"); return { rows: [{ unlocked: true }] } }
const exact = sources.find(entry => entry.source === query)
assert(exact, "Only original pinned file bytes and fixed internal SQL may execute")
events.push("file:" + exact.pin.file)
if (faults.file === exact.pin.file) throw Error("file ACK unknown")
assert.equal(Boolean(outerMarkers), exact.pin.transaction === "owner-transaction")
publicPresent = true
if (exact.pin.selfMarker) markerIds.add(exact.pin.file.slice(0, -4))
if (faults.forgeMarker) markerIds.add("9999_unknown")
return exact.pin.transaction === "self-managed-single-transaction" ? [{ command: "BEGIN", rows: [] }, { command: "COMMIT", rows: [] }] : { rows: [] }
},
release: (destroy: boolean) => { appReleases.push(destroy); if (destroy && outerMarkers) { markerIds = outerMarkers; publicPresent = outerPublic! } },
}
} }
const modules: Record<string, unknown> = {
"node:crypto": { createHash, randomUUID },
"node:fs/promises": {
readdir: async (...args: Parameters<typeof fs.readdir>) => {
const rows = await Reflect.apply(fs.readdir, fs, args) as unknown as { name: string; isFile(): boolean }[]
return faults.extraFile ? [...rows, { name: "9999_unreviewed.sql", isFile: () => true }] : rows
},
readFile: async (file: URL) => { const name = file.pathname.split("/").at(-1)!; reads.set(name, (reads.get(name) ?? 0) + 1); const bytes = await fs.readFile(file); return faults.driftFile === name ? Buffer.concat([bytes, Buffer.from("\n")]) : bytes },
},
"./database-schema-control.js": control,
"./database-schema-control-model.js": { ...model, checkDatabaseSchemaControlState: (value: unknown) => model.checkDatabaseSchemaControlState(structuredClone(value)) },
"./migration-manifest.js": manifest,
"../services/partition-write-transaction.js": { PARTITION_WRITER_ADVISORY_KEY },
"../modules/mcp/security.js": {
fingerprint: (value: unknown) => createHash("sha256").update(canonical(value)).digest("hex"),
privateSecretFingerprint: async (value: string, domain: string) => createHmac("sha256", "inert-only-issuer:" + domain).update(value).digest("hex"),
},
"./index.js": { pool },
}
const exports = {} as { executeControlledSqlMigrations(): Promise<{ completedFiles: number; ready: false }>; CONTROLLED_MIGRATIONS_SQL: Record<string, string> }
runInNewContext(compiled, { exports, require: (name: string) => { assert(name in modules, "Unapproved import: " + name); return modules[name] }, Buffer, URL, TextDecoder, Promise, Error, Map, Set, Object, structuredClone }, { filename: "controlled-migrations.ts" })
const sql = exports.CONTROLLED_MIGRATIONS_SQL
return { events, faults, appReleases, reads, run: exports.executeControlledSqlMigrations, history, state: () => structuredClone(state), markerIds: () => new Set(markerIds), legacy: () => { publicPresent = true; markerIds.add("0000_postgresql") }, corruptMarker: () => markerIds.add("9999_unknown") }
}
test("actual dispatcher executes exact current files with durable intents and correct transaction contracts", async () => {
const f = fixture(), result = await f.run()
assert.equal(result.completedFiles, pins.length); assert.equal(result.ready, false)
assert.equal(f.events.filter(event => event.startsWith("file:")).length, pins.length)
assert.equal(f.events.filter(event => event === "BEGIN").length, 17)
assert.equal(f.events.filter(event => event === "COMMIT").length, 17)
assert.equal(f.events.filter(event => event.startsWith("marker:")).length, 34)
assert.equal(f.events.filter(event => event === "exclusive lease").length, 1)
for (const pin of pins) { assert(f.events.indexOf("intent:" + pin.file) < f.events.indexOf("file:" + pin.file)); assert.equal(f.reads.get(pin.file), 1) }
assert.deepEqual(f.appReleases, [false])
assert.equal(f.history.size, pins.length)
})
for (const previousLength of [39, 40, 41, 42, 43]) test("old controlled" + previousLength + " manifest history is not automatically reattested under44", async () => {
const f = fixture(); f.faults.beginFile = pins[previousLength].file
await assert.rejects(f.run()); assert.equal(f.history.size, previousLength)
const oldDigest = createHash("sha256").update(JSON.stringify(pins.slice(0, previousLength))).digest("hex")
for (const row of f.history.values()) {
row.intent.manifestSha256 = oldDigest
row.terminal!.intent.manifestSha256 = oldDigest
}
f.faults.beginFile = ""; f.events.length = 0
await assert.rejects(f.run())
assert.equal(f.events.some(event => event.startsWith("file:")), false)
assert.equal(f.history.size, previousLength)
})
test("known completed prefix resumes after fresh ownership without replaying earlier bytes", async () => {
const f = fixture(); f.faults.beginFile = pins[7].file
await assert.rejects(f.run()); assert.equal(f.history.size, 7)
f.faults.beginFile = ""; f.events.length = 0
const result = await f.run()
assert.equal(result.completedFiles, pins.length)
assert.deepEqual(f.events.filter(event => event.startsWith("file:")), pins.slice(7).map(pin => "file:" + pin.file))
})
test("fully completed restart validates ledger/markers and executes zero application files", async () => {
const f = fixture(); await f.run(); f.events.length = 0
assert.equal((await f.run()).completedFiles, pins.length)
assert.equal(f.events.filter(event => event.startsWith("file:")).length, 0)
})
test("lost control terminal ACK reconciles signed durable success and skips that file", async () => {
const f = fixture(); f.faults.terminal = true
await assert.rejects(f.run())
assert.equal(f.state().terminal?.outcome, "observed-success")
assert.equal(f.history.size, 1)
f.faults.terminal = false; f.events.length = 0
assert.equal((await f.run()).completedFiles, pins.length)
assert.deepEqual(f.events.filter(event => event.startsWith("file:")), pins.slice(1).map(pin => "file:" + pin.file))
})
test("legacy markers or source drift/new files never dispatch application SQL", async () => {
for (const kind of ["legacy", "drift", "new"] as const) {
const f = fixture()
if (kind === "legacy") f.legacy()
if (kind === "drift") f.faults.driftFile = pins[0].file
if (kind === "new") f.faults.extraFile = true
await assert.rejects(f.run())
assert.equal(f.events.filter(event => event.startsWith("file:")).length, 0)
assert.equal(f.history.size, 0)
}
})
test("application failure or lost COMMIT ACK records unknown and forbids restart replay", async () => {
for (const kind of ["file", "commit"] as const) {
const f = fixture()
if (kind === "file") f.faults.file = pins[0].file; else f.faults.lostCommit = true
await assert.rejects(f.run())
assert.equal(f.state().terminal?.outcome, "interrupted-unknown")
assert.equal(f.appReleases.at(-1), true)
f.faults.file = ""; f.faults.lostCommit = false; f.events.length = 0
await assert.rejects(f.run())
assert.equal(f.events.filter(event => event.startsWith("file:")).length, 0)
}
})
test("self-managed external-marker gap remains unknown and never retries file", async () => {
const f = fixture(); f.faults.marker = pins[7].file.slice(0, -4)
await assert.rejects(f.run())
assert.equal(f.state().terminal?.outcome, "interrupted-unknown")
assert.equal(f.events.filter(event => event === "file:" + pins[7].file).length, 1)
f.faults.marker = ""; f.events.length = 0
await assert.rejects(f.run()); assert(!f.events.some(event => event.startsWith("file:")))
})
test("resolved ROLLBACK command tag never becomes a successful COMMIT receipt", async () => {
const f = fixture(); f.faults.rollbackTag = true
await assert.rejects(f.run())
assert.equal(f.state().terminal?.outcome, "interrupted-unknown")
assert.equal(f.history.size, 1)
})
test("marker inconsistency rejects before issuing a successful file receipt", async () => {
const f = fixture(); f.faults.forgeMarker = true
await assert.rejects(f.run())
assert.equal(f.state().terminal?.outcome, "interrupted-unknown")
})
test("caller-forged receipt hash or marker mismatch cannot authorize prefix skipping", async () => {
for (const kind of ["hash", "marker"] as const) {
const f = fixture(); f.faults.beginFile = pins[1].file
await assert.rejects(f.run()); f.faults.beginFile = ""
if (kind === "hash") f.history.values().next().value!.terminal!.observationSha256 = "a".repeat(64)
else f.corruptMarker()
f.events.length = 0
await assert.rejects(f.run()); assert(!f.events.some(event => event.startsWith("file:")))
}
})