Files
MikrotikManager/backend/src/db/server-poll-terminal-archives.sql.test.ts
T

110 lines
10 KiB
TypeScript

/** Schema upgrade/negative guards only. Positive archive acceptance must use the
* genuine public poll runtime and private historical codec, never these rows. */
import assert from "node:assert/strict"
import { randomUUID } from "node:crypto"
import { readFile } from "node:fs/promises"
import { basename, dirname, isAbsolute, resolve } from "node:path"
assert.equal(process.env.MMAPP_OWN_POLL_ARCHIVE_SCHEMA, "true")
assert.equal(process.env.NODE_ENV, "test")
const endpoint = new URL(process.env.DATABASE_URL ?? "")
assert.equal(endpoint.hostname, "127.0.0.1"); assert.equal(endpoint.username, "mcp_test"); assert(endpoint.password)
assert.equal(endpoint.pathname, "/postgres"); assert.equal(endpoint.search, ""); assert.equal(endpoint.hash, "")
assert(Number.isInteger(Number(endpoint.port)) && Number(endpoint.port) > 0 && Number(endpoint.port) <= 65535)
assert(process.env.MCP_STATE_DIR && isAbsolute(process.env.MCP_STATE_DIR))
assert.equal(basename(resolve(process.env.MCP_STATE_DIR)), "state")
assert(basename(dirname(resolve(process.env.MCP_STATE_DIR))).startsWith("mm-mcp-test-"))
const { Pool } = await import("pg")
const { pool, closePool } = await import("./index.js")
const { MIGRATION_MANIFEST, analyzeMigration } = await import("./migration-manifest.js")
const archivePin = MIGRATION_MANIFEST.find(pin => pin.file === "0041_server_poll_terminal_archives.sql")!
const migration = await readFile(new URL("../../drizzle/0041_server_poll_terminal_archives.sql", import.meta.url))
analyzeMigration(archivePin.file, migration, archivePin)
const databases: { name: string; pool: InstanceType<typeof Pool> }[] = [], accepted: string[] = []
const prefix40 = async () => {
const name = "mm_poll_archive_schema_" + randomUUID().replaceAll("-", "")
assert(/^mm_poll_archive_schema_[a-f0-9]{32}$/.test(name))
await pool.query(`CREATE DATABASE "${name}" TEMPLATE template0`)
const target = new URL(endpoint); target.pathname = "/" + name
const connection = new Pool({ connectionString: target.toString(), max: 4 })
databases.push({ name, pool: connection })
const pins = MIGRATION_MANIFEST.filter(pin => pin.file < "0041_")
assert.equal(pins.length, 40); assert.equal(pins.at(-1)?.file, "0040_mcp_routing_reservations.sql")
await connection.query("CREATE TABLE schema_migrations(id text PRIMARY KEY,applied_at timestamptz NOT NULL DEFAULT now())")
for (const pin of pins) {
const bytes = await readFile(new URL("../../drizzle/" + pin.file, import.meta.url)); analyzeMigration(pin.file, bytes, pin)
await connection.query(bytes.toString("utf8"))
await connection.query("INSERT INTO schema_migrations(id) VALUES($1) ON CONFLICT DO NOTHING", [pin.file.slice(0, -4)])
}
return connection
}
const seedFullRest = async (connection: InstanceType<typeof Pool>) => {
const receipt = randomUUID(), execution = randomUUID(), token = randomUUID()
await connection.query(`INSERT INTO server_poll_storage_admissions(receipt_id,identity_kind,execution_id,owner_id,binding_proof,context_proof,
authority_issued_at,authority_expires_at,max_stages,claim_token,retention_days,safety_ms,gc_not_before)
VALUES($1,'rest',$2,'owned-archive-schema',$3,$4,clock_timestamp()-interval '1 second',clock_timestamp()+interval '3 minutes',8,$5,1,60000,clock_timestamp()+interval '2 days')`,
[receipt, execution, "a".repeat(64), "b".repeat(64), token])
await connection.query("UPDATE server_poll_storage_admissions SET claim_token=NULL,claim_attempted_at=clock_timestamp() WHERE receipt_id=$1", [receipt])
await connection.query("INSERT INTO server_poll_executions(receipt_id,identity_kind,execution_id,binding_proof,context_ciphertext,context_proof) VALUES($1,'rest',$2,$3,'SYNTHETIC_SCHEMA_FIXTURE_ONLY',$4)", [receipt, execution, "a".repeat(64), "b".repeat(64)])
return { receipt, execution }
}
try {
const connection = await prefix40(), { receipt, execution } = await seedFullRest(connection)
const originalAdmission = (await connection.query("SELECT to_jsonb(a) AS value FROM server_poll_storage_admissions a WHERE receipt_id=$1", [receipt])).rows[0].value
const originalJournal = (await connection.query("SELECT to_jsonb(e) AS value FROM server_poll_executions e WHERE receipt_id=$1", [receipt])).rows[0].value
const originalUsage = (await connection.query("SELECT * FROM server_poll_storage_usage ORDER BY scope")).rows
console.log("Owned prefix40 poll constraint inventory:", JSON.stringify((await connection.query("SELECT conname,pg_get_constraintdef(oid) AS definition FROM pg_constraint WHERE conrelid='public.server_poll_storage_usage'::regclass AND contype='c' ORDER BY conname")).rows))
await connection.query(migration.toString("utf8"))
assert.equal(Number((await connection.query("SELECT count(*) AS n FROM schema_migrations")).rows[0].n), 41)
assert.deepEqual((await connection.query("SELECT to_jsonb(a)-ARRAY['storage_state','compact_at','archive_bytes','original_activity_at','compaction_claim','compaction_claimed_at','compaction_claim_xid'] AS value FROM server_poll_storage_admissions a WHERE receipt_id=$1", [receipt])).rows[0].value, originalAdmission)
assert.deepEqual((await connection.query("SELECT to_jsonb(e) AS value FROM server_poll_executions e WHERE receipt_id=$1", [receipt])).rows[0].value, originalJournal)
assert.deepEqual((await connection.query("SELECT * FROM server_poll_storage_usage ORDER BY scope")).rows, originalUsage.map(row => ({ ...row, compacted_admissions: 0, archive_bytes: 0 })))
assert.equal((await connection.query("SELECT server_poll_storage_accounting_valid() AS valid")).rows[0].valid, true)
assert.equal((await connection.query("SELECT server_poll_storage_source_payload($1) AS value", [receipt])).rows[0].value, null)
assert.deepEqual((await connection.query("SELECT compaction_claim,compaction_claimed_at,compaction_claim_xid FROM server_poll_storage_admissions WHERE receipt_id=$1", [receipt])).rows[0], { compaction_claim: null, compaction_claimed_at: null, compaction_claim_xid: null })
accepted.push("nonempty40 full REST journal, original admission/proofs/counters preserved; archive source remains unavailable for REST")
await assert.rejects(connection.query("UPDATE server_poll_storage_admissions SET storage_state='compacted',compact_at=clock_timestamp(),archive_bytes=1,original_activity_at=created_at WHERE receipt_id=$1", [receipt]))
await assert.rejects(connection.query("UPDATE server_poll_storage_admissions SET compaction_claim=$2,compaction_claimed_at=clock_timestamp() WHERE receipt_id=$1", [receipt, randomUUID()]))
await assert.rejects(connection.query("UPDATE server_poll_storage_admissions SET compaction_claimed_at=clock_timestamp() WHERE receipt_id=$1", [receipt]))
await assert.rejects(connection.query("UPDATE server_poll_storage_admissions SET compaction_claim_xid=pg_current_xact_id() WHERE receipt_id=$1", [receipt]))
await assert.rejects(connection.query("UPDATE server_poll_storage_admissions SET compaction_claim=NULL,compaction_claimed_at=NULL WHERE receipt_id=$1", [receipt]))
await assert.rejects(connection.query(`INSERT INTO server_poll_storage_admissions(receipt_id,identity_kind,execution_id,owner_id,binding_proof,context_proof,
authority_issued_at,authority_expires_at,max_stages,claim_token,retention_days,safety_ms,gc_not_before,compaction_claim,compaction_claimed_at)
VALUES($1,'rest',$2,'owned-archive-schema',$3,$3,clock_timestamp()-interval '1 second',clock_timestamp()+interval '3 minutes',8,$4,1,60000,clock_timestamp()+interval '2 days',$5,clock_timestamp())`,
[randomUUID(), randomUUID(), "a".repeat(64), randomUUID(), randomUUID()]))
await assert.rejects(connection.query(`INSERT INTO server_poll_archives(receipt_id,version,execution_id,connection_id,owner_id,binding_proof,context_proof,source_digest,source_sha256,terminal_digest,
original_activity_at,original_expires_at,decoded_bytes,compressed_bytes,ciphertext,ciphertext_bytes,archive_proof)
VALUES($1,1,$2,$3,'owned-archive-schema',$4,$4,$4,$4,$4,clock_timestamp(),clock_timestamp(),1,1,$5,$6,$4)`,
[receipt, execution, randomUUID(), "a".repeat(64), Buffer.alloc(29).toString("base64"), Buffer.alloc(29).toString("base64").length]))
const leased = await connection.connect()
try {
await leased.query("BEGIN"); await leased.query("SET LOCAL mm.poll_storage_compact='on'")
await assert.rejects(leased.query("DELETE FROM server_poll_executions WHERE receipt_id=$1", [receipt]))
} finally { await leased.query("ROLLBACK"); leased.release() }
assert.deepEqual((await connection.query("SELECT to_jsonb(e) AS value FROM server_poll_executions e WHERE receipt_id=$1", [receipt])).rows[0].value, originalJournal)
assert.equal(Number((await connection.query("SELECT count(*) AS n FROM server_poll_archives")).rows[0].n), 0)
accepted.push("no archive, prefilled/reset/altered claim, direct state edit or session flag can compact an ineligible source or delete immutable journal")
const inconsistent = await prefix40(); await seedFullRest(inconsistent)
// Deliberately valid OLD scalar quota shape but inconsistent with retained
// rows: additive enrollment must refuse rather than resetting the ledger.
await inconsistent.query("UPDATE server_poll_storage_usage SET admissions=0,reserved_rows=0,reserved_bytes=0")
const before = (await inconsistent.query("SELECT to_jsonb(a) AS value FROM server_poll_storage_admissions a")).rows
const target = await inconsistent.connect()
try { await assert.rejects(target.query(migration.toString("utf8"))) }
finally { await target.query("ROLLBACK"); target.release() }
assert.deepEqual((await inconsistent.query("SELECT to_jsonb(a) AS value FROM server_poll_storage_admissions a")).rows, before)
assert.equal((await inconsistent.query("SELECT to_regclass('public.server_poll_archives') AS name")).rows[0].name, null)
assert.equal(Number((await inconsistent.query("SELECT count(*) AS n FROM schema_migrations")).rows[0].n), 40)
accepted.push("inconsistent nonempty40 ledger fails before archive DDL and marker with original source untouched")
console.log(JSON.stringify({ accepted: accepted.length, cases: accepted, fullHistoryPreserved: true,
positiveArchiveAcceptance: false, nativeOrPublicActivation: false }))
} finally {
for (const item of databases) {
await item.pool.end()
assert(/^mm_poll_archive_schema_[a-f0-9]{32}$/.test(item.name))
await pool.query(`DROP DATABASE "${item.name}"`)
}
await closePool()
}