test: harden M3 identity wiring guards
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { mkdir, readFile, rm } from "node:fs/promises";
|
||||
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import test from "node:test";
|
||||
import { fileURLToPath } from "node:url";
|
||||
@@ -68,6 +68,44 @@ test("--dry-run aliases the same source/read-only no-write path", async () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("M3 report validator rejects DRY-RUN evidence promoted to live pass", async () => {
|
||||
await mkdir(path.join(repoRoot, testOutputDir), { recursive: true });
|
||||
const output = path.join(testOutputDir, "dry-run-promoted-live.json");
|
||||
const promoted = path.join(testOutputDir, "dry-run-promoted-live-tampered.json");
|
||||
try {
|
||||
runSmoke(["--dry-run", "--output", output]);
|
||||
const report = JSON.parse(await readFile(path.join(repoRoot, output), "utf8"));
|
||||
report.liveOperation = {
|
||||
status: "pass",
|
||||
operationId: "op_fake_dry_run",
|
||||
traceId: "trc_fake_dry_run",
|
||||
auditId: "aud_fake_dry_run",
|
||||
evidenceId: "evd_fake_dry_run",
|
||||
summary: "Incorrectly promoted DRY-RUN output to DEV-LIVE."
|
||||
};
|
||||
await writeFile(path.join(repoRoot, promoted), JSON.stringify(report, null, 2));
|
||||
|
||||
assert.throws(
|
||||
() =>
|
||||
execFileSync("node", ["scripts/validate-dev-gate-report.mjs", promoted], {
|
||||
cwd: repoRoot,
|
||||
encoding: "utf8",
|
||||
stdio: ["ignore", "pipe", "pipe"]
|
||||
}),
|
||||
(error) => {
|
||||
assert.match(
|
||||
`${error.message}\n${error.stderr ?? ""}`,
|
||||
/liveOperation\.status cannot be pass when only a DRY-RUN plan is present/u
|
||||
);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
} finally {
|
||||
await rm(path.join(repoRoot, output), { force: true });
|
||||
await rm(path.join(repoRoot, promoted), { force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("M3 safety gates keep incomplete live commands source/read-only", () => {
|
||||
assert.deepEqual(requireSafetyGates(parseArgs([])), {
|
||||
mode: "dry-run",
|
||||
@@ -103,12 +141,17 @@ test("M3 blocker scopes classify missing identities and patch-panel route gaps",
|
||||
assert.equal(plan.liveWriteWillRun, false);
|
||||
assert.ok(plan.failureClassifications.includes("identity_not_distinct"));
|
||||
assert.ok(plan.failureClassifications.includes("patch_panel_wiring_missing"));
|
||||
assert.ok(plan.failureClassifications.includes("patch_panel_route_owner_invalid"));
|
||||
assert.match(plan.refusalPolicy.join("\n"), /source\/read-only/u);
|
||||
});
|
||||
|
||||
test("patch-panel route preflight rejects missing res_boxsimu_1 DO1 to res_boxsimu_2 DI1 wiring", () => {
|
||||
test("patch-panel route preflight rejects missing res_boxsimu_1 DO1 to res_boxsimu_2 DI1 wiring or wrong owner", () => {
|
||||
const wrongRoute = hasRequiredM3Connection({
|
||||
status: {
|
||||
serviceId: "hwlab-patch-panel",
|
||||
metadata: {
|
||||
propagation: "patch-panel-only"
|
||||
},
|
||||
activeConnections: [
|
||||
{
|
||||
fromResourceId: "res_boxsimu_1",
|
||||
@@ -119,6 +162,10 @@ test("patch-panel route preflight rejects missing res_boxsimu_1 DO1 to res_boxsi
|
||||
]
|
||||
},
|
||||
wiring: {
|
||||
constraints: {
|
||||
propagation: "patch-panel-only",
|
||||
localLoopbackSubstituteAllowed: false
|
||||
},
|
||||
connections: [
|
||||
{
|
||||
from: { resourceId: "res_boxsimu_1", port: "DO1" },
|
||||
@@ -130,9 +177,14 @@ test("patch-panel route preflight rejects missing res_boxsimu_1 DO1 to res_boxsi
|
||||
});
|
||||
assert.equal(Boolean(wrongRoute.active), false);
|
||||
assert.equal(Boolean(wrongRoute.configured), false);
|
||||
assert.equal(Boolean(wrongRoute.owner), true);
|
||||
|
||||
const requiredRoute = hasRequiredM3Connection({
|
||||
status: {
|
||||
serviceId: "hwlab-patch-panel",
|
||||
metadata: {
|
||||
propagation: "patch-panel-only"
|
||||
},
|
||||
activeConnections: [
|
||||
{
|
||||
fromResourceId: "res_boxsimu_1",
|
||||
@@ -143,6 +195,10 @@ test("patch-panel route preflight rejects missing res_boxsimu_1 DO1 to res_boxsi
|
||||
]
|
||||
},
|
||||
wiring: {
|
||||
constraints: {
|
||||
propagation: "patch-panel-only",
|
||||
localLoopbackSubstituteAllowed: false
|
||||
},
|
||||
connections: [
|
||||
{
|
||||
from: { resourceId: "res_boxsimu_1", port: "DO1" },
|
||||
@@ -154,6 +210,33 @@ test("patch-panel route preflight rejects missing res_boxsimu_1 DO1 to res_boxsi
|
||||
});
|
||||
assert.equal(Boolean(requiredRoute.active), true);
|
||||
assert.equal(Boolean(requiredRoute.configured), true);
|
||||
assert.equal(Boolean(requiredRoute.owner), true);
|
||||
|
||||
const wrongOwner = hasRequiredM3Connection({
|
||||
status: {
|
||||
serviceId: "hwlab-box-simu",
|
||||
metadata: {
|
||||
propagation: "box-loopback"
|
||||
},
|
||||
activeConnections: requiredRoute.active ? [requiredRoute.active] : []
|
||||
},
|
||||
wiring: {
|
||||
constraints: {
|
||||
propagation: "patch-panel-only",
|
||||
localLoopbackSubstituteAllowed: false
|
||||
},
|
||||
connections: [
|
||||
{
|
||||
from: { resourceId: "res_boxsimu_1", port: "DO1" },
|
||||
to: { resourceId: "res_boxsimu_2", port: "DI1" },
|
||||
mode: "exclusive"
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
assert.equal(Boolean(wrongOwner.active), true);
|
||||
assert.equal(Boolean(wrongOwner.configured), true);
|
||||
assert.equal(Boolean(wrongOwner.owner), false);
|
||||
});
|
||||
|
||||
test("Kubernetes discovery preserves current indexed M3 service identities", () => {
|
||||
|
||||
Reference in New Issue
Block a user