feat(mcp): подготовить сервер и доступ к приёмке

This commit is contained in:
Denozordec committed 2026-11-03 03:12:03 +07:00
commit bf3d50e1ff
3835 files changed
+537122

No files matched your search

@@ -0,0 +1,197 @@
import assert from "node:assert/strict"
import { randomUUID } from "node:crypto"
import { basename, dirname, resolve } from "node:path"
import { execFile } from "node:child_process"
import { writeFile } from "node:fs/promises"
assert.equal(process.env.MMAPP_OWN_DATABASE_SCHEMA_CONTROL, "true")
const databaseUrl = new URL(process.env.DATABASE_URL ?? "")
assert.equal(databaseUrl.hostname, "127.0.0.1"); assert.equal(databaseUrl.pathname, "/postgres")
assert(process.env.MCP_STATE_DIR && 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 { applySqlMigrations } = await import("./migrate.js")
const { acquireDatabaseSchemaControl } = await import("./database-schema-control.js")
const { initialDatabaseSchemaControlState, DatabaseSchemaControlError } = await import("./database-schema-control-model.js")
const { runPrivateDatabaseDump } = await import("../services/database-dump-connection.js")
const { restorePartitionedDatabase } = await import("../services/system-database-partition-restore.js")
type ControlPool = Parameters<typeof acquireDatabaseSchemaControl>[0]
const names: string[] = [], cases: string[] = [], handles: Awaited<ReturnType<typeof acquireDatabaseSchemaControl>>[] = []
const connections: InstanceType<typeof Pool>[] = []
async function database() {
const name = "mm_control_" + randomUUID().replaceAll("-", "")
assert(/^mm_control_[a-f0-9]{32}$/.test(name))
await pool.query('CREATE DATABASE "' + name + '" TEMPLATE template0')
const url = new URL(databaseUrl); url.pathname = "/" + name
const connection = new Pool({ connectionString: url.toString(), max: 6 })
names.push(name); connections.push(connection); return connection
}
async function seed(connection: InstanceType<typeof Pool>) {
await connection.query("CREATE SCHEMA mmapp_control; CREATE TABLE mmapp_control.database_schema_state(id smallint PRIMARY KEY CHECK(id=1),revision bigint NOT NULL CHECK(revision>=0),state jsonb NOT NULL); CREATE TABLE mmapp_control.database_schema_intents(id uuid PRIMARY KEY,intent jsonb NOT NULL,terminal jsonb)")
await connection.query("INSERT INTO mmapp_control.database_schema_state VALUES(1,0,$1::jsonb)", [JSON.stringify(initialDatabaseSchemaControlState())])
}
async function acquire(connection: ControlPool) { const owner = await acquireDatabaseSchemaControl(connection); handles.push(owner); return owner }
const command = () => ({ type: "begin" as const, id: randomUUID(), kind: "migration" as const, manifestSha256: "a".repeat(64), artifactSha256: "b".repeat(64) })
const terminal = (id: string, outcome: "observed-success" | "observed-failure" | "interrupted-unknown" = "observed-success") => ({ type: "terminal" as const, id, outcome, observationSha256: "c".repeat(64) })
const row = async (connection: InstanceType<typeof Pool>) => (await connection.query("SELECT revision::text,state FROM mmapp_control.database_schema_state WHERE id=1")).rows[0]
const history = async (connection: InstanceType<typeof Pool>, id: string) => (await connection.query("SELECT intent,terminal FROM mmapp_control.database_schema_intents WHERE id=$1", [id])).rows[0]
function lostCommit(connection: InstanceType<typeof Pool>, number: number): ControlPool {
return { async connect() {
const client = await connection.connect(); let commits = 0
return { release(destroy?: boolean) { client.release(destroy) }, async query(sql: string, values?: unknown[]) {
const result = await client.query(sql, values)
if (sql === "COMMIT" && ++commits === number) throw Error("owned fixture lost actual COMMIT acknowledgement")
return { rows: result.rows, command: result.command }
} }
} }
}
function abortedCommit(connection: InstanceType<typeof Pool>, number: number): ControlPool {
return { async connect() {
const client = await connection.connect(); let commits = 0
return { release(destroy?: boolean) { client.release(destroy) }, async query(sql: string, values?: unknown[]) {
if (sql === "COMMIT" && ++commits === number) {
// A real aborted PostgreSQL transaction acknowledges COMMIT as ROLLBACK.
await assert.rejects(client.query("SELECT 1/0"))
}
const result = await client.query(sql, values)
return { rows: result.rows, command: result.command }
} }
} }
}
async function commandIo(program: string, args: string[]): Promise<Buffer> {
return new Promise((resolve, reject) => {
execFile(program, args, { encoding: "buffer", timeout: 30000, maxBuffer: 32 * 1048576, windowsHide: true }, (error, stdout) => error ? reject(Error("Owned PostgreSQL archive command failed: " + program)) : resolve(stdout))
})
}
try {
// The runner's separate terminal restore fixture uses this owned database.
// Control protocol scenarios below each use their own independent database.
await applySqlMigrations(pool)
const missing = await database()
await assert.rejects(acquire(missing), DatabaseSchemaControlError)
assert.equal((await missing.query("SELECT count(*)::int AS n FROM pg_namespace WHERE nspname='mmapp_control'")).rows[0].n, 0)
cases.push("missing external control refuses without startup DDL or enrollment")
const regular = await database(); await seed(regular)
let owner = await acquire(regular)
const initial = await owner.read(); assert.equal(initial.state.epoch, 1); assert.equal(initial.revision, "1")
assert.equal((await owner.admission()).ready, false)
await assert.rejects(acquire(regular), DatabaseSchemaControlError)
assert.deepEqual(await owner.read(), initial)
cases.push("one checked-out session owns exclusive lease; another owner changes no epoch or revision")
const begin = command(), intended = await owner.transition(begin)
assert.equal(intended.revision, "2"); assert.equal(intended.state.phase, "INTENT")
assert.deepEqual((await history(regular, begin.id)).intent, intended.state.intent)
assert.equal((await owner.admission()).reason, "UNRESOLVED_INTENT")
cases.push("intent singleton and unique immutable history publish atomically before application dispatch")
await owner.close(); owner = await acquire(regular)
assert.equal((await owner.read()).state.intent?.id, begin.id)
await assert.rejects(owner.transition(terminal(begin.id)), DatabaseSchemaControlError)
assert.equal((await history(regular, begin.id)).terminal, null)
cases.push("restart preserves old intent and cannot claim an unobserved successful outcome")
owner = await acquire(regular)
const interrupted = await owner.transition(terminal(begin.id, "interrupted-unknown"))
assert.equal(interrupted.state.terminal?.outcome, "interrupted-unknown")
assert.equal((await owner.admission()).ready, false)
assert.deepEqual((await history(regular, begin.id)).terminal, interrupted.state.terminal)
cases.push("interrupted unknown can be recorded once but never enables consumers")
const second = command(); await owner.transition(second); await owner.transition(terminal(second.id))
const prior = await row(regular)
await assert.rejects(owner.transition(begin), DatabaseSchemaControlError)
assert.deepEqual(await row(regular), prior)
assert.equal((await regular.query("SELECT count(*)::int AS n FROM mmapp_control.database_schema_intents")).rows[0].n, 2)
cases.push("reuse of an older completed operation is refused by actual unique history with no singleton advance")
for (const stage of ["claim", "intent", "terminal"] as const) {
const connection = await database(); await seed(connection)
const wrapped = lostCommit(connection, stage === "claim" ? 1 : stage === "intent" ? 2 : 3)
const intent = command()
if (stage === "claim") await assert.rejects(acquire(wrapped), (error: unknown) => error instanceof DatabaseSchemaControlError && error.outcomeUnknown)
else {
const handle = await acquire(wrapped)
if (stage === "terminal") await handle.transition(intent)
await assert.rejects(handle.transition(stage === "intent" ? intent : terminal(intent.id)), (error: unknown) => error instanceof DatabaseSchemaControlError && error.outcomeUnknown)
await assert.rejects(handle.read(), DatabaseSchemaControlError)
}
const fresh = await acquire(connection), observed = await fresh.read()
assert.equal(observed.state.epoch, 2); assert.equal(observed.state.phase, "QUARANTINED")
assert.equal((await fresh.admission()).ready, false)
if (stage === "intent") { assert.equal(observed.state.intent?.id, intent.id); assert.equal((await history(connection, intent.id)).terminal, null) }
if (stage === "terminal") { assert.equal(observed.state.intent, null); assert.equal(observed.state.terminal?.intent.id, intent.id); assert.equal(observed.state.terminal?.outcome, "observed-success") }
await fresh.close()
cases.push("actual server COMMIT with lost " + stage + " ACK destroys owner; restart preserves quarantine and durable observation")
}
for (const stage of ["claim", "intent", "terminal"] as const) {
const connection = await database(); await seed(connection)
const wrapped = abortedCommit(connection, stage === "claim" ? 1 : stage === "intent" ? 2 : 3)
const intent = command()
if (stage === "claim") await assert.rejects(acquire(wrapped), (error: unknown) => error instanceof DatabaseSchemaControlError && error.outcomeUnknown)
else {
const handle = await acquire(wrapped)
if (stage === "terminal") await handle.transition(intent)
await assert.rejects(handle.transition(stage === "intent" ? intent : terminal(intent.id)), (error: unknown) => error instanceof DatabaseSchemaControlError && error.outcomeUnknown)
await assert.rejects(handle.read(), DatabaseSchemaControlError)
}
// Readback diagnoses the rolled-back publication without replaying it.
const saved = await row(connection)
assert.equal(saved.revision, stage === "claim" ? "0" : stage === "intent" ? "1" : "2")
assert.equal((await history(connection, intent.id))?.terminal ?? null, null)
assert.equal(saved.state.intent?.id ?? null, stage === "terminal" ? intent.id : null)
const fresh = await acquire(connection)
assert.equal((await fresh.admission()).ready, false)
if (stage === "terminal") await assert.rejects(fresh.transition(terminal(intent.id)), DatabaseSchemaControlError)
await fresh.close()
cases.push("actual aborted " + stage + " transaction returns ROLLBACK command; owner discarded without successful publication or replay")
}
const altered = await database(); await seed(altered)
const stale = await acquire(altered)
await altered.query("UPDATE mmapp_control.database_schema_state SET revision=revision+1")
await assert.rejects(stale.read(), DatabaseSchemaControlError)
cases.push("external revision drift invalidates a live owner before admission")
const corrupted = await database(); await seed(corrupted)
const corruptOwner = await acquire(corrupted), corruptIntent = command(); await corruptOwner.transition(corruptIntent)
await corrupted.query("UPDATE mmapp_control.database_schema_intents SET intent=jsonb_set(intent,'{artifactSha256}',to_jsonb($1::text)) WHERE id=$2", ["d".repeat(64), corruptIntent.id])
await assert.rejects(corruptOwner.read(), DatabaseSchemaControlError)
await assert.rejects(acquire(corrupted), DatabaseSchemaControlError)
cases.push("immutable history mismatch invalidates both current owner and new owner claim")
const reset = await database(); await seed(reset)
const resetOwner = await acquire(reset), resetIntent = command(); await resetOwner.transition(resetIntent)
// This database was created by this test in the owned disposable cluster.
await reset.query("CREATE TABLE public.owned_fixture(id integer); DROP SCHEMA public CASCADE; CREATE SCHEMA public")
assert.equal((await resetOwner.read()).state.intent?.id, resetIntent.id)
assert.equal((await history(reset, resetIntent.id)).terminal, null)
cases.push("control epoch and unresolved history survive replacement of the public application namespace")
const archiveDb = await database(); await seed(archiveDb)
await archiveDb.query("CREATE TABLE public.servers(id integer PRIMARY KEY); CREATE TABLE public.schema_migrations(id text PRIMARY KEY); INSERT INTO public.servers VALUES(1)")
const archiveOwner = await acquire(archiveDb), archiveIntent = command(); await archiveOwner.transition(archiveIntent)
const originalState = await row(archiveDb), archiveUrl = new URL(databaseUrl); archiveUrl.pathname = "/" + names.at(-1)!
const safeFile = resolve(process.env.MCP_STATE_DIR!, "control-excluded.dump"), unsafeFile = resolve(process.env.MCP_STATE_DIR!, "control-full.dump")
const actualPrivateDump = await runPrivateDatabaseDump(archiveUrl.toString(), { authorize: async () => {} })
await writeFile(safeFile, actualPrivateDump, { flag: "wx", mode: 0o600 })
const toc = (await commandIo("pg_restore", ["--list", safeFile])).toString("utf8")
assert(!toc.includes("mmapp_control"))
await archiveDb.query("UPDATE public.servers SET id=2")
let replacements = 0
await restorePartitionedDatabase(safeFile, archiveUrl.toString(), {
command: commandIo, prepareSql: file => writeFile(file, "", { flag: "wx", mode: 0o600 }), beforeTargetReplacement: () => { replacements++ },
})
assert.equal(replacements, 1)
assert.deepEqual((await archiveDb.query("SELECT id FROM public.servers")).rows, [{ id: 1 }])
assert.deepEqual(await row(archiveDb), originalState)
assert.equal((await archiveOwner.read()).state.intent?.id, archiveIntent.id)
cases.push("actual private pg_dump excludes control; actual application restore changes public data while preserving live owner epoch and intent")
await commandIo("pg_dump", ["-Fc", "--file", unsafeFile, archiveUrl.toString()])
let archiveCommands = 0, preparations = 0
await assert.rejects(restorePartitionedDatabase(unsafeFile, archiveUrl.toString(), {
command: async (program, args) => { archiveCommands++; return commandIo(program, args) },
prepareSql: async () => { preparations++ }, beforeTargetReplacement: () => { replacements++ },
}))
assert.equal(archiveCommands, 1); assert.equal(preparations, 0); assert.equal(replacements, 1)
assert.deepEqual(await row(archiveDb), originalState)
cases.push("actual full archive containing control refuses immediately after TOC before SQL decode or target replacement")
console.log("Owned database schema control SQL:", JSON.stringify({ cases: cases.length, names: cases, databases: names.length, actualSql: true, lostActualCommitAcks: 3, startupWired: false, readyAuthorized: false, productionDatabaseTouched: false }))
} finally {
for (const handle of handles) await handle.close().catch(() => {})
for (const connection of connections) await connection.end()
await closePool()
}