test: add dev m3 blocked evidence detail
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { access, mkdir, writeFile } from "node:fs/promises";
|
||||
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
||||
import { request as httpRequest } from "node:http";
|
||||
import { request as httpsRequest } from "node:https";
|
||||
import path from "node:path";
|
||||
@@ -14,6 +14,7 @@ const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."
|
||||
const defaultReportPath = "reports/dev-gate/dev-m3-hardware-loop.json";
|
||||
const namespace = "hwlab-dev";
|
||||
const issue = "pikasTech/HWLAB#38";
|
||||
const deployWorkloadsPath = "deploy/k8s/base/workloads.yaml";
|
||||
|
||||
const serviceTargets = Object.freeze([
|
||||
{
|
||||
@@ -204,6 +205,70 @@ function collectLocalRuntimeObservations() {
|
||||
return observations;
|
||||
}
|
||||
|
||||
async function collectReadOnlySupplementalEvidence() {
|
||||
const raw = await readFile(path.join(repoRoot, deployWorkloadsPath), "utf8");
|
||||
const workloads = JSON.parse(raw);
|
||||
const targets = ["hwlab-box-simu", "hwlab-gateway-simu", "hwlab-patch-panel"];
|
||||
const observed = workloads.items
|
||||
.filter((item) => targets.includes(item.metadata?.name))
|
||||
.map((item) => {
|
||||
const container = item.spec?.template?.spec?.containers?.[0] ?? {};
|
||||
const env = Object.fromEntries((container.env ?? []).map((entry) => [entry.name, entry.value]));
|
||||
return {
|
||||
name: item.metadata.name,
|
||||
kind: item.kind,
|
||||
namespace: item.metadata.namespace,
|
||||
replicas: item.spec?.replicas ?? null,
|
||||
image: container.image,
|
||||
env
|
||||
};
|
||||
});
|
||||
|
||||
const box = observed.find((item) => item.name === "hwlab-box-simu");
|
||||
const gateway = observed.find((item) => item.name === "hwlab-gateway-simu");
|
||||
const patchPanel = observed.find((item) => item.name === "hwlab-patch-panel");
|
||||
const boxReadyForM3 = box?.replicas === 2 && String(box.env.HWLAB_BOX_ID ?? "").includes(",");
|
||||
const gatewayReadyForM3 =
|
||||
gateway?.replicas === 2 && String(gateway.env.HWLAB_BOX_RESOURCES ?? "").includes(",");
|
||||
|
||||
return [
|
||||
{
|
||||
id: "deploy-skeleton-m3-cardinality",
|
||||
status: boxReadyForM3 && gatewayReadyForM3 ? "pass" : "gap",
|
||||
blockerClass: boxReadyForM3 && gatewayReadyForM3 ? null : "runtime_blocker",
|
||||
source: deployWorkloadsPath,
|
||||
summary: boxReadyForM3 && gatewayReadyForM3
|
||||
? "Checked-in DEV deploy skeleton declares two simulator identities for M3."
|
||||
: "Read-only static comparison found the checked-in DEV deploy skeleton declares one box simulator replica and one gateway simulator replica; proving two of each in live DEV requires a separate deploy/runtime task or authorized runtime observation.",
|
||||
evidence: {
|
||||
required: {
|
||||
"hwlab-box-simu": 2,
|
||||
"hwlab-gateway-simu": 2,
|
||||
"hwlab-patch-panel": 1,
|
||||
wiring: "box-simu-1 DO1 -> box-simu-2 DI1"
|
||||
},
|
||||
observed: {
|
||||
"hwlab-box-simu": {
|
||||
replicas: box?.replicas ?? null,
|
||||
env: box?.env ?? null
|
||||
},
|
||||
"hwlab-gateway-simu": {
|
||||
replicas: gateway?.replicas ?? null,
|
||||
env: gateway?.env ?? null
|
||||
},
|
||||
"hwlab-patch-panel": {
|
||||
replicas: patchPanel?.replicas ?? null,
|
||||
env: patchPanel?.env ?? null
|
||||
}
|
||||
}
|
||||
},
|
||||
requiredFollowUp: boxReadyForM3 && gatewayReadyForM3
|
||||
? "None from static deploy cardinality."
|
||||
: "Create a separate authorized DEV deploy/runtime task to provision or expose two box-simu instances, two gateway-simu instances, M3 wiring, and audit/evidence endpoints; do not perform that mutation in this read-only continuation."
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
async function probeJson(url, options = {}) {
|
||||
const method = options.method ?? "GET";
|
||||
const startedAt = isoNow();
|
||||
@@ -533,6 +598,7 @@ function baseReport({ commitId, observedAt }) {
|
||||
evidence: [DEV_ENDPOINT]
|
||||
}
|
||||
],
|
||||
readOnlySupplementalEvidence: [],
|
||||
localRuntimeObservations: [],
|
||||
liveOperation: {
|
||||
status: "not_run",
|
||||
@@ -591,6 +657,7 @@ async function main() {
|
||||
commitId: currentCommit(),
|
||||
observedAt
|
||||
});
|
||||
report.readOnlySupplementalEvidence = await collectReadOnlySupplementalEvidence();
|
||||
|
||||
const ingress = await observeDevIngress();
|
||||
report.localRuntimeObservations = collectLocalRuntimeObservations();
|
||||
|
||||
@@ -339,6 +339,26 @@ async function validateDevM3Report(report, label) {
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.hasOwn(report, "readOnlySupplementalEvidence")) {
|
||||
assertArray(report.readOnlySupplementalEvidence, `${label}.readOnlySupplementalEvidence`);
|
||||
for (const [index, evidence] of report.readOnlySupplementalEvidence.entries()) {
|
||||
const evidenceLabel = `${label}.readOnlySupplementalEvidence[${index}]`;
|
||||
assertObject(evidence, evidenceLabel);
|
||||
for (const field of ["id", "status", "source", "summary", "evidence", "requiredFollowUp"]) {
|
||||
assert.ok(Object.hasOwn(evidence, field), `${evidenceLabel} missing ${field}`);
|
||||
}
|
||||
assertString(evidence.id, `${evidenceLabel}.id`);
|
||||
assert.ok(["pass", "gap"].includes(evidence.status), `${evidenceLabel}.status must be pass or gap`);
|
||||
assertRepoRelativePath(evidence.source, `${evidenceLabel}.source`);
|
||||
assertString(evidence.summary, `${evidenceLabel}.summary`);
|
||||
assertObject(evidence.evidence, `${evidenceLabel}.evidence`);
|
||||
assertString(evidence.requiredFollowUp, `${evidenceLabel}.requiredFollowUp`);
|
||||
if (Object.hasOwn(evidence, "blockerClass") && evidence.blockerClass !== null) {
|
||||
assert.ok(blockerTypes.has(evidence.blockerClass), `${evidenceLabel}.blockerClass must be known`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertObject(report.liveOperation, `${label}.liveOperation`);
|
||||
assertStatus(report.liveOperation.status, `${label}.liveOperation.status`);
|
||||
for (const field of ["operationId", "traceId", "auditId", "evidenceId", "summary"]) {
|
||||
|
||||
Reference in New Issue
Block a user