Files
pikasTech-unidesk/scripts/src/monitor-terminal-ingest-migration.test.ts
T
2026-07-13 02:01:10 +00:00

309 lines
28 KiB
TypeScript

// SPEC: PJ2026-01060508 Web哨兵 draft-2026-07-12-p17-monitor-central-persistence.
import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import { Database } from "bun:sqlite";
import { createInMemoryMonitorCentralStore } from "./monitor-central-persistence";
import { exportMonitorSqliteSnapshot, importMonitorSqliteSnapshot, monitorSnapshotContentHash, planMonitorSqliteSources, verifyMonitorSqliteSnapshot, verifyMonitorSqliteSnapshotAgainstStore } from "./monitor-sqlite-migration";
import { buildMonitorTerminalIngest, MonitorIngestFailedError, resolveMonitorTerminalWriter, submitMonitorTerminalIngest } from "./monitor-terminal-ingest";
import { compactWebObserveAnalyzeAnalysisForRaw } from "./hwlab-node/web-probe-observe";
test("terminal ingest payload is immutable and retries bounded failures", async () => {
const report = { ok: false, findings: [{ id: "blocked" }], updatedAt: "2026-07-12T00:00:00.000Z" };
const hash = `sha256:${"a".repeat(64)}`;
const artifact = [{ relativePath: "analysis/report.json", kind: "report", sha256: hash, sizeBytes: 42 }];
const ingest = buildMonitorTerminalIngest({ sentinelId: "fixture", node: "NC01", lane: "v03", runId: "run-1" }, report, artifact, { ownerNode: "NC01", ownerLane: "v03", ownerPvc: "fixture-pvc", stateDir: "/state" });
report.findings[0].id = "changed";
assert.equal((ingest.payload.report as { findings: { id: string }[] }).findings[0].id, "blocked");
assert.match(ingest.payloadHash ?? "", /^sha256:/u);
assert.equal(ingest.payloadHash, buildMonitorTerminalIngest({ sentinelId: "fixture", node: "NC01", lane: "v03", runId: "run-1" }, { ok: false, findings: [{ id: "blocked" }], updatedAt: "2026-07-12T00:00:00.000Z" }, artifact, { ownerNode: "NC01", ownerLane: "v03", ownerPvc: "fixture-pvc", stateDir: "/state" }).payloadHash);
assert.throws(() => buildMonitorTerminalIngest({ sentinelId: "fixture", node: "NC01", lane: "v03", runId: "" }, report, artifact, { ownerNode: "NC01", ownerLane: "v03", ownerPvc: "fixture-pvc", stateDir: "/state" }), MonitorIngestFailedError);
await assert.rejects(
() => submitMonitorTerminalIngest({ endpoint: "https://monitor.example.test/api/ingest", timeoutMs: 20, retry: { maxAttempts: 2, delayMs: 0 } }, ingest, async () => new Response("{}", { status: 503 })),
(error: unknown) => error instanceof MonitorIngestFailedError && error.details.attempts === 2,
);
});
test("terminal writer requires explicit mutually exclusive authority", () => {
assert.throws(() => resolveMonitorTerminalWriter({}));
assert.deepEqual(resolveMonitorTerminalWriter({ sqlite: { path: "/state/legacy.sqlite" } }), { mode: "legacy-sqlite" });
const central = resolveMonitorTerminalWriter({ monitor: { terminalWriter: { mode: "central", ingest: { endpoint: "https://monitor.example.test/api/ingest", timeoutMs: 1_000, retry: { maxAttempts: 2, delayMs: 0 } } } } });
assert.equal(central.mode, "central");
assert.throws(() => resolveMonitorTerminalWriter({ monitor: { terminalWriter: { mode: "central" } } }));
assert.throws(() => resolveMonitorTerminalWriter({ sqlite: { path: "/state/legacy.sqlite" }, monitor: { terminalWriter: { mode: "central", ingest: { endpoint: "https://monitor.example.test/api/ingest", timeoutMs: 1_000, retry: { maxAttempts: 2, delayMs: 0 } } } } }));
});
test("real compact analyze projects stable report time and artifact facts", () => {
const compact = compactWebObserveAnalyzeAnalysisForRaw({ generatedAt: "2026-07-12T00:00:00.000Z", analyzer: { reportJsonPath: "/state/run-1/analysis/report.json", reportJsonSha256: `sha256:${"c".repeat(64)}`, reportJsonBytes: 12 } });
assert.deepEqual({ reportUpdatedAt: compact.reportUpdatedAt, reportJsonSha256: compact.reportJsonSha256, reportJsonBytes: compact.reportJsonBytes }, { reportUpdatedAt: "2026-07-12T00:00:00.000Z", reportJsonSha256: `sha256:${"c".repeat(64)}`, reportJsonBytes: 12 });
const ingest = buildMonitorTerminalIngest({ sentinelId: "fixture", node: "NC01", lane: "v03", runId: "generated-at" }, compact, [{ relativePath: "analysis/report.json", kind: "report-json", sha256: `sha256:${"c".repeat(64)}`, sizeBytes: 12 }], { ownerNode: "NC01", ownerLane: "v03", ownerPvc: "fixture-pvc", stateDir: "/state/run-1" });
assert.equal(ingest.updatedAt, "2026-07-12T00:00:00.000Z");
});
test("SQLite snapshot reconciles the 228/242/234 fixture shape without production assumptions", async () => {
const directory = mkdtempSync(join(tmpdir(), "monitor-migration-"));
try {
const sqlitePath = join(directory, "legacy.sqlite");
const snapshotPath = join(directory, "snapshot.json");
const database = new Database(sqlitePath);
const stateRoot = join(directory, "state");
const workspaceRoot = join(directory, "hwlab-v03");
const workspaceRun = join(workspaceRoot, "web-observe", "run-0");
const reportPath = join(workspaceRun, "analysis", "report.json");
mkdirSync(join(workspaceRun, "analysis"), { recursive: true });
writeFileSync(reportPath, JSON.stringify({ updatedAt: "2026-07-12T00:00:00.000Z", views: { summary: { title: "legacy summary" } }, findings: [{ id: "report-finding" }] }));
const reportHash = `sha256:${Bun.CryptoHasher.hash("sha256", readFileSync(reportPath), "hex")}`;
database.run("CREATE TABLE runs (id TEXT, state_dir TEXT, artifact_owner_pvc TEXT, artifact_count INTEGER, report_json_sha256 TEXT, updated_at TEXT)");
database.run("CREATE TABLE findings (run_id TEXT, sentinel_id TEXT, finding_id TEXT, severity TEXT, count INTEGER, summary TEXT, report_json_sha256 TEXT, created_at TEXT)");
database.run("CREATE TABLE metadata (id TEXT, key TEXT, value_json TEXT)");
for (let index = 0; index < 228; index += 1) database.run("INSERT INTO runs VALUES (?, ?, ?, ?, ?, ?)", [`run-${index}`, index === 0 ? workspaceRun : null, index === 0 ? "legacy-web-observe-workspace-nc01" : null, index === 0 ? 1 : 0, index === 0 ? reportHash : null, "2026-07-12T00:00:00.000Z"]);
for (let index = 0; index < 242; index += 1) database.run("INSERT INTO findings VALUES (?, ?, ?, ?, ?, ?, ?, ?)", [index === 0 ? "run-0" : `run-${index % 228}`, "fixture", index === 0 ? "report-finding" : `finding-${index}`, "warning", 1, "indexed", reportHash, "2026-07-12T00:00:00.000Z"]);
for (let index = 0; index < 234; index += 1) database.run("INSERT INTO metadata VALUES (?, ?, ?)", [`metadata-${index}`, index === 0 ? "run.report.run-0" : index === 1 ? "scheduler.heartbeat" : index === 2 ? "maintenance" : index === 3 ? "manual-trigger.latest" : `metadata-${index}`, index === 0 ? JSON.stringify({ views: { summary: { title: "legacy summary" } }, findings: [{ id: "report-finding", rootCause: "complete metadata detail" }] }) : index === 2 ? JSON.stringify(false) : JSON.stringify({ index })]);
database.close();
const source = { sentinelId: "fixture", node: "NC01", lane: "v03", path: sqlitePath, runtimeConfigRef: "fixture#runtime", stateRoot, ownerPvc: "fixture-pvc", artifactSources: [{ ownerPvc: "fixture-pvc", mountPath: stateRoot }, { ownerPvc: "legacy-web-observe-workspace-nc01", mountPath: workspaceRoot, hostPath: workspaceRoot }] };
const plan = planMonitorSqliteSources([source]);
assert.equal((plan.sources as { exists: boolean }[])[0].exists, true);
const snapshot = exportMonitorSqliteSnapshot(source, snapshotPath);
const verified = verifyMonitorSqliteSnapshot(snapshot);
assert.equal(verified.ok, true);
assert.deepEqual(verified.reconciliation, { runs: 228, findings: 242, metadata: 234, payloads: 0, locators: 0, latest: 0 });
const persisted = JSON.parse(readFileSync(snapshotPath, "utf8"));
assert.equal(persisted.schema, snapshot.schema);
const store = createInMemoryMonitorCentralStore();
assert.deepEqual((await importMonitorSqliteSnapshot(snapshot, store)).imported, { inserted: 228, idempotent: 0 });
assert.deepEqual((await importMonitorSqliteSnapshot(snapshot, store)).imported, { inserted: 0, idempotent: 228 });
const importedMetadata = await store.runtimeMetadata({ sentinelId: "fixture", node: "NC01", lane: "v03" });
assert.equal(importedMetadata.length, 234);
assert.equal(importedMetadata.find((item) => item.key === "scheduler.heartbeat")?.updatedAt, snapshot.createdAt);
const importedRun = await store.run({ sentinelId: "fixture", node: "NC01", lane: "v03" }, "run-0");
assert.equal(importedRun?.artifactLocators[0]?.sha256, reportHash);
assert.equal(importedRun?.artifactLocators[0]?.ownerPvc, "legacy-web-observe-workspace-nc01");
const reportFindings = importedRun?.findings.filter((finding) => finding.id === "report-finding") ?? [];
assert.deepEqual(reportFindings, [{ id: "report-finding", rootCause: "complete metadata detail" }]);
assert.equal((await store.view({ sentinelId: "fixture", node: "NC01", lane: "v03" }, "run-0", "summary"))?.value.title, "legacy summary");
const pgVerification = await verifyMonitorSqliteSnapshotAgainstStore(snapshot, store);
assert.equal(pgVerification.ok, true);
assert.deepEqual([...new Set((pgVerification.checks as { name: string }[]).map((item) => item.name))].sort(), ["artifactLocator", "finding", "globalOverview", "run", "runtimeMetadata", "scopedOverview", "timeline", "view"]);
assert.deepEqual((pgVerification.reconciliation as { target: Record<string, number> }).target, { runs: 228, findings: 242, payloads: 228, artifactLocators: 1, views: 1, runtimeMetadata: 234 });
await store.importSource({ manifestId: "sha256:orphan-metadata", sourceFingerprint: "sha256:orphan-metadata", rowCount: 0 }, [], [{ sentinelId: "fixture", node: "NC01", lane: "v03", key: "orphan", value: { unexpected: true }, updatedAt: snapshot.createdAt }]);
assert.equal((await verifyMonitorSqliteSnapshotAgainstStore(snapshot, store)).ok, false);
assert.equal(verifyMonitorSqliteSnapshot({ ...snapshot, manifestContentHash: "sha256:tampered" }).ok, false);
} finally {
rmSync(directory, { recursive: true, force: true });
}
});
test("single-source batch rolls back conflicts and preserves manifest idempotency", async () => {
const store = createInMemoryMonitorCentralStore();
const owner = { ownerNode: "NC01", ownerLane: "v03", ownerPvc: "fixture-pvc", stateDir: "run" };
const artifact = [{ relativePath: "analysis/report.json", kind: "report-json", sha256: `sha256:${"b".repeat(64)}`, sizeBytes: 1 }];
const first = buildMonitorTerminalIngest({ sentinelId: "fixture", node: "NC01", lane: "v03", runId: "first" }, { updatedAt: "2026-07-12T00:00:00.000Z" }, artifact, owner);
const existing = buildMonitorTerminalIngest({ sentinelId: "fixture", node: "NC01", lane: "v03", runId: "existing" }, { updatedAt: "2026-07-12T00:00:00.000Z", status: "ok" }, artifact, owner);
const conflicting = buildMonitorTerminalIngest({ sentinelId: "fixture", node: "NC01", lane: "v03", runId: "existing" }, { updatedAt: "2026-07-12T00:00:00.000Z", status: "blocked" }, artifact, owner);
await store.ingest(existing);
await assert.rejects(() => store.importSource({ manifestId: "sha256:batch-conflict", sourceFingerprint: "sha256:source", rowCount: 2 }, [first, conflicting]), (error: unknown) => (error as { code?: string }).code === "payload_hash_conflict");
assert.equal(await store.run({ sentinelId: "fixture", node: "NC01", lane: "v03" }, "first"), null);
const manifest = { manifestId: "sha256:batch-ok", sourceFingerprint: "sha256:source", rowCount: 1 };
assert.equal((await store.importSource(manifest, [first])).disposition, "inserted");
assert.equal((await store.importSource(manifest, [first])).disposition, "idempotent");
});
test("metadata-only snapshot verifies scoped runtime metadata and rejects extras", async () => {
const directory = mkdtempSync(join(tmpdir(), "monitor-metadata-only-"));
try {
const sqlitePath = join(directory, "index.sqlite");
const snapshotPath = join(directory, "snapshot.json");
const database = new Database(sqlitePath);
database.run("CREATE TABLE metadata (key TEXT, value_json TEXT)");
database.run("INSERT INTO metadata VALUES (?, ?)", ["scheduler.heartbeat", JSON.stringify({ healthy: true })]);
database.close();
const source = { sentinelId: "fixture", node: "NC01", lane: "v03", path: sqlitePath, runtimeConfigRef: "fixture#runtime", stateRoot: directory, ownerPvc: "fixture-pvc", artifactSources: [{ ownerPvc: "fixture-pvc", mountPath: directory }] };
const snapshot = exportMonitorSqliteSnapshot(source, snapshotPath);
const store = createInMemoryMonitorCentralStore();
await importMonitorSqliteSnapshot(snapshot, store);
assert.equal((await verifyMonitorSqliteSnapshotAgainstStore(snapshot, store)).ok, true);
await store.importSource({ manifestId: "sha256:metadata-only-extra", sourceFingerprint: "sha256:metadata-only-extra", rowCount: 0 }, [], [{ sentinelId: "fixture", node: "NC01", lane: "v03", key: "orphan", value: true, updatedAt: snapshot.createdAt }]);
assert.equal((await verifyMonitorSqliteSnapshotAgainstStore(snapshot, store)).ok, false);
} finally {
rmSync(directory, { recursive: true, force: true });
}
});
test("WAL export freezes a serialized SQLite snapshot independent of later live writes", () => {
const directory = mkdtempSync(join(tmpdir(), "monitor-wal-snapshot-"));
try {
const sqlitePath = join(directory, "index.sqlite");
const snapshotPath = join(directory, "snapshot.json");
const database = new Database(sqlitePath);
database.exec("PRAGMA journal_mode = WAL");
database.run("CREATE TABLE metadata (key TEXT, value_json TEXT)");
database.run("INSERT INTO metadata VALUES (?, ?)", ["scheduler.heartbeat", JSON.stringify({ sequence: 1 })]);
const source = { sentinelId: "fixture", node: "NC01", lane: "v03", path: sqlitePath, runtimeConfigRef: "fixture#runtime", stateRoot: directory, ownerPvc: "fixture-pvc", artifactSources: [{ ownerPvc: "fixture-pvc", mountPath: directory }] };
const snapshot = exportMonitorSqliteSnapshot(source, snapshotPath);
database.run("INSERT INTO metadata VALUES (?, ?)", ["scheduler.heartbeat.late", JSON.stringify({ sequence: 2 })]);
database.close();
assert.equal(snapshot.artifactLocator.path, join(directory, "snapshot.sqlite"));
assert.equal(snapshot.tables.find((table) => table.name === "metadata")?.rowCount, 1);
assert.equal(verifyMonitorSqliteSnapshot(snapshot).ok, true);
} finally {
rmSync(directory, { recursive: true, force: true });
}
});
test("workspace artifact source verifies locator identity hash and size", async () => {
const directory = mkdtempSync(join(tmpdir(), "monitor-workspace-artifact-"));
try {
const sqlitePath = join(directory, "index.sqlite");
const snapshotPath = join(directory, "snapshot.json");
const workspaceRoot = join(directory, "hwlab-v03");
const stateDir = join(workspaceRoot, "web-observe", "run-1");
const artifacts = [
["analysis/report.json", Buffer.from('{"ok":true}\n')],
["analysis/details.json", Buffer.from('{"details":true}\n')],
["artifacts/request.txt", Buffer.from("request\n")],
["artifacts/response.txt", Buffer.from("response\n")],
["screenshots/page.png", Buffer.from([0x89, 0x50, 0x4e, 0x47])],
] as const;
const reportBytes = artifacts[0][1];
mkdirSync(join(stateDir, "analysis"), { recursive: true });
for (const [relativePath, bytes] of artifacts) {
const path = join(stateDir, relativePath);
mkdirSync(join(path, ".."), { recursive: true });
writeFileSync(path, bytes);
}
const reportHash = `sha256:${createHash("sha256").update(reportBytes).digest("hex")}`;
const database = new Database(sqlitePath);
database.run("CREATE TABLE runs (id TEXT, state_dir TEXT, artifact_count INTEGER, report_json_sha256 TEXT, updated_at TEXT)");
database.run("INSERT INTO runs VALUES (?, ?, ?, ?, ?)", ["run-1", stateDir, 5, reportHash, "2026-07-12T00:00:00.000Z"]);
database.close();
const source = { sentinelId: "fixture", node: "NC01", lane: "v03", path: sqlitePath, runtimeConfigRef: "fixture#runtime", stateRoot: directory, ownerPvc: "fixture-pvc", artifactSources: [{ ownerPvc: "fixture-pvc", mountPath: directory }, { ownerPvc: "legacy-web-observe-workspace-nc01", mountPath: workspaceRoot, hostPath: workspaceRoot }] };
const snapshot = exportMonitorSqliteSnapshot(source, snapshotPath);
const store = createInMemoryMonitorCentralStore();
await importMonitorSqliteSnapshot(snapshot, store);
const run = await store.run({ sentinelId: "fixture", node: "NC01", lane: "v03" }, "run-1");
assert.equal(run?.artifactLocators.length, 5);
assert.deepEqual(run?.artifactLocators.map((locator) => ({ ownerPvc: locator.ownerPvc, stateDir: locator.stateDir, relativePath: locator.relativePath, sha256: locator.sha256, sizeBytes: locator.sizeBytes })), artifacts.map(([relativePath, bytes]) => ({ ownerPvc: "legacy-web-observe-workspace-nc01", stateDir, relativePath, sha256: `sha256:${createHash("sha256").update(bytes).digest("hex")}`, sizeBytes: bytes.byteLength })).sort((left, right) => left.relativePath.localeCompare(right.relativePath)));
assert.equal((await verifyMonitorSqliteSnapshotAgainstStore(snapshot, store)).ok, true);
writeFileSync(join(stateDir, "artifacts", "request.txt"), "changed\n");
await assert.rejects(() => importMonitorSqliteSnapshot(snapshot, createInMemoryMonitorCentralStore()), (error: unknown) => (error as { code?: string }).code === "monitor-migration-frozen-artifact-invalid");
writeFileSync(join(stateDir, "artifacts", "request.txt"), artifacts[2][1]);
writeFileSync(join(stateDir, "artifacts", "new-after-export.txt"), "new\n");
await assert.rejects(() => importMonitorSqliteSnapshot(snapshot, createInMemoryMonitorCentralStore()), (error: unknown) => (error as { code?: string }).code === "monitor-migration-frozen-artifact-invalid");
rmSync(join(stateDir, "artifacts", "new-after-export.txt"));
rmSync(join(stateDir, "artifacts", "response.txt"));
await assert.rejects(() => verifyMonitorSqliteSnapshotAgainstStore(snapshot, store), (error: unknown) => { const migrationError = error as { code?: string; diagnostic?: Record<string, unknown> }; return migrationError.code === "monitor-migration-frozen-artifact-invalid" && migrationError.diagnostic?.runId === "run-1" && migrationError.diagnostic?.stateDir === stateDir && migrationError.diagnostic?.expectedCount === 5; });
} finally {
rmSync(directory, { recursive: true, force: true });
}
});
test("declared pre-migration artifact gaps import empty locators and fail closed on identity drift", async () => {
const directory = mkdtempSync(join(tmpdir(), "monitor-artifact-gaps-"));
try {
const sqlitePath = join(directory, "index.sqlite");
const gaps = [
{ runId: "gap-run-a", artifactCount: 1, stateDir: ".state/missing/a", reportSha256: `sha256:${"a".repeat(64)}` },
{ runId: "gap-run-b", artifactCount: 4, stateDir: ".state/missing/b", reportSha256: `sha256:${"b".repeat(64)}` },
];
const database = new Database(sqlitePath);
database.run("CREATE TABLE runs (id TEXT, state_dir TEXT, artifact_count INTEGER, report_json_sha256 TEXT, updated_at TEXT)");
for (const gap of gaps) database.run("INSERT INTO runs VALUES (?, ?, ?, ?, ?)", [gap.runId, gap.stateDir, gap.artifactCount, gap.reportSha256, "2026-07-08T03:58:39.000Z"]);
database.close();
const source = { sentinelId: "fixture", node: "NC01", lane: "v03", path: sqlitePath, runtimeConfigRef: "fixture#runtime", stateRoot: directory, ownerPvc: "fixture-pvc", artifactSources: [{ ownerPvc: "fixture-pvc", mountPath: directory }], legacyArtifactGaps: gaps };
const snapshot = exportMonitorSqliteSnapshot(source, join(directory, "snapshot.json"));
const store = createInMemoryMonitorCentralStore();
await importMonitorSqliteSnapshot(snapshot, store);
for (const gap of gaps) {
const run = await store.run({ sentinelId: "fixture", node: "NC01", lane: "v03" }, gap.runId);
assert.equal(run?.artifactLocators.length, 0);
assert.equal((run?.timeline.at(-1) as { code?: string; locator?: unknown; bytesVerified?: boolean } | undefined)?.code, "legacy-artifact-bytes-missing-before-migration");
assert.equal((run?.timeline.at(-1) as { locator?: unknown }).locator, null);
assert.equal((run?.timeline.at(-1) as { bytesVerified?: boolean }).bytesVerified, false);
}
assert.equal((await verifyMonitorSqliteSnapshotAgainstStore(snapshot, store)).ok, true);
const timelineTamperedStore = { ...store, async run(scope: { sentinelId: string; node: string; lane: string }, runId: string) { const run = await store.run(scope, runId); return run === null ? null : { ...run, timeline: [] }; } };
assert.equal((await verifyMonitorSqliteSnapshotAgainstStore(snapshot, timelineTamperedStore)).ok, false);
const swappedManifests = snapshot.artifactManifests.map((manifest, index) => ({ ...manifest, missingBeforeMigration: { ...gaps[(index + 1) % gaps.length] } }));
const swappedUnsigned = { ...snapshot, artifactManifests: swappedManifests };
const swappedSnapshot = { ...swappedUnsigned, manifestContentHash: monitorSnapshotContentHash(swappedUnsigned) };
assert.equal(verifyMonitorSqliteSnapshot(swappedSnapshot).ok, false);
const recoveredPath = join(directory, gaps[0].stateDir, "recovered.bin");
mkdirSync(join(recoveredPath, ".."), { recursive: true });
writeFileSync(recoveredPath, "recovered\n");
await assert.rejects(() => importMonitorSqliteSnapshot(snapshot, createInMemoryMonitorCentralStore()), (error: unknown) => (error as { code?: string }).code === "monitor-migration-artifact-gap-recovered");
rmSync(recoveredPath);
assert.throws(() => exportMonitorSqliteSnapshot({ ...source, legacyArtifactGaps: [{ ...gaps[0], artifactCount: 2 }, gaps[1]] }, join(directory, "changed.json")), (error: unknown) => (error as { code?: string }).code === "monitor-migration-artifact-gap-identity-invalid");
assert.throws(() => exportMonitorSqliteSnapshot({ ...source, legacyArtifactGaps: [{ ...gaps[0], stateDir: "wrong-state-dir" }, gaps[1]] }, join(directory, "wrong-state-dir.json")), (error: unknown) => (error as { code?: string }).code === "monitor-migration-artifact-gap-identity-invalid");
assert.throws(() => exportMonitorSqliteSnapshot({ ...source, legacyArtifactGaps: [{ ...gaps[0], reportSha256: `sha256:${"c".repeat(64)}` }, gaps[1]] }, join(directory, "wrong-report.json")), (error: unknown) => (error as { code?: string }).code === "monitor-migration-artifact-gap-identity-invalid");
assert.throws(() => exportMonitorSqliteSnapshot({ ...source, legacyArtifactGaps: [...gaps, { runId: "third", artifactCount: 1, stateDir: "missing", reportSha256: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }] }, join(directory, "third.json")), (error: unknown) => (error as { code?: string }).code === "monitor-migration-artifact-gap-unmatched");
const unexpectedArtifact = Buffer.from("unexpected\n");
const unexpectedPath = join(directory, gaps[0].stateDir, "extra.bin");
mkdirSync(join(unexpectedPath, ".."), { recursive: true });
writeFileSync(unexpectedPath, unexpectedArtifact);
const locatorDatabase = new Database(sqlitePath);
locatorDatabase.run("CREATE TABLE artifact_locators (run_id TEXT, owner_pvc TEXT, state_dir TEXT, relative_path TEXT, sha256 TEXT, size_bytes INTEGER)");
locatorDatabase.run("INSERT INTO artifact_locators VALUES (?, ?, ?, ?, ?, ?)", [gaps[0].runId, "fixture-pvc", gaps[0].stateDir, "extra.bin", `sha256:${createHash("sha256").update(unexpectedArtifact).digest("hex")}`, unexpectedArtifact.byteLength]);
locatorDatabase.close();
assert.throws(() => exportMonitorSqliteSnapshot(source, join(directory, "unexpected-locator.json")), (error: unknown) => (error as { code?: string }).code === "monitor-migration-artifact-gap-locator-unexpected");
} finally {
rmSync(directory, { recursive: true, force: true });
}
});
test("empty normal artifact directories freeze owner state and normal manifests cannot be exchanged", async () => {
const directory = mkdtempSync(join(tmpdir(), "monitor-empty-artifact-"));
try {
const sqlitePath = join(directory, "index.sqlite");
const emptyStateDir = ".state/empty";
const fullStateDir = ".state/full";
mkdirSync(join(directory, emptyStateDir), { recursive: true });
const fullDirectory = join(directory, fullStateDir);
mkdirSync(join(fullDirectory, "analysis"), { recursive: true });
const report = Buffer.from("report\n");
writeFileSync(join(fullDirectory, "analysis", "report.json"), report);
const database = new Database(sqlitePath);
database.run("CREATE TABLE runs (id TEXT, state_dir TEXT, artifact_count INTEGER, report_json_sha256 TEXT, updated_at TEXT)");
database.run("INSERT INTO runs VALUES (?, ?, ?, ?, ?)", ["empty", emptyStateDir, 0, "", "2026-07-12T00:00:00.000Z"]);
database.run("INSERT INTO runs VALUES (?, ?, ?, ?, ?)", ["full", fullStateDir, 1, `sha256:${createHash("sha256").update(report).digest("hex")}`, "2026-07-12T00:00:00.000Z"]);
database.close();
const source = { sentinelId: "fixture", node: "NC01", lane: "v03", path: sqlitePath, runtimeConfigRef: "fixture#runtime", stateRoot: directory, ownerPvc: "fixture-pvc", artifactSources: [{ ownerPvc: "fixture-pvc", mountPath: directory }] };
const snapshot = exportMonitorSqliteSnapshot(source, join(directory, "snapshot.json"));
const parsedSnapshot = JSON.parse(readFileSync(join(directory, "snapshot.json"), "utf8"));
const roundtripStore = createInMemoryMonitorCentralStore();
await importMonitorSqliteSnapshot(parsedSnapshot, roundtripStore);
assert.equal((await verifyMonitorSqliteSnapshotAgainstStore(parsedSnapshot, roundtripStore)).ok, true);
const emptyManifest = snapshot.artifactManifests.find((manifest) => manifest.runId === "empty");
assert.deepEqual({ artifactCount: emptyManifest?.artifactCount, stateDir: emptyManifest?.stateDir, ownerPvc: emptyManifest?.ownerPvc, locatorCount: emptyManifest?.locators.length }, { artifactCount: 0, stateDir: emptyStateDir, ownerPvc: "fixture-pvc", locatorCount: 0 });
const swappedManifests = snapshot.artifactManifests.map((manifest, index, manifests) => ({ ...manifests[(index + 1) % manifests.length], runId: manifest.runId }));
const swappedUnsigned = { ...snapshot, artifactManifests: swappedManifests };
assert.equal(verifyMonitorSqliteSnapshot({ ...swappedUnsigned, manifestContentHash: monitorSnapshotContentHash(swappedUnsigned) }).ok, false);
const emptyDirectory = join(directory, emptyStateDir);
mkdirSync(emptyDirectory, { recursive: true });
writeFileSync(join(emptyDirectory, "added-after-export.bin"), "added\n");
await assert.rejects(() => importMonitorSqliteSnapshot(snapshot, createInMemoryMonitorCentralStore()), (error: unknown) => (error as { code?: string }).code === "monitor-migration-frozen-artifact-invalid");
} finally {
rmSync(directory, { recursive: true, force: true });
}
});
test("artifact count without a registry-backed locator fails closed", async () => {
const directory = mkdtempSync(join(tmpdir(), "monitor-artifact-count-"));
try {
const sqlitePath = join(directory, "index.sqlite");
const snapshotPath = join(directory, "snapshot.json");
const database = new Database(sqlitePath);
database.run("CREATE TABLE runs (id TEXT, artifact_count INTEGER, updated_at TEXT)");
database.run("INSERT INTO runs VALUES (?, ?, ?)", ["run-1", 1, "2026-07-12T00:00:00.000Z"]);
database.close();
const source = { sentinelId: "fixture", node: "NC01", lane: "v03", path: sqlitePath, runtimeConfigRef: "fixture#runtime", stateRoot: directory, ownerPvc: "fixture-pvc", artifactSources: [{ ownerPvc: "fixture-pvc", mountPath: directory }] };
assert.throws(() => exportMonitorSqliteSnapshot(source, snapshotPath), (error: unknown) => (error as { code?: string }).code === "monitor-migration-artifact-count-mismatch");
} finally {
rmSync(directory, { recursive: true, force: true });
}
});