7a0648088c
Merge PR #159 after Code Queue rebase and validation. Runner verified M3 cardinality, hardware-loop smoke, patch-panel runtime smoke, m3 readonly contract, and git diff check. This is source/config blocker removal only; it does not claim DEV-LIVE M3 pass.
166 lines
4.7 KiB
JavaScript
166 lines
4.7 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { readFile } from "node:fs/promises";
|
|
import test from "node:test";
|
|
import { createM3Topology, createMvpTopology } from "./fixture.mjs";
|
|
import { createPatchPanel, createWiringConfig } from "./model.mjs";
|
|
|
|
test("MVP topology includes two box simulators, two gateway simulators, and one patch panel", () => {
|
|
const topology = createMvpTopology();
|
|
|
|
assert.equal(topology.services.boxes.length, 2);
|
|
assert.equal(topology.services.gateways.length, 2);
|
|
assert.equal(topology.services.patchPanel.serviceId, "hwlab-patch-panel");
|
|
assert.equal(topology.constraints.patchPanelIsOnlyCrossDeviceChannel, true);
|
|
|
|
for (const box of topology.services.boxes) {
|
|
assert.equal(box.serviceId, "hwlab-box-simu");
|
|
assert.equal(box.constraints.localLoopbackEnabled, false);
|
|
assert.equal(box.constraints.crossDevicePropagation, "patch-panel-only");
|
|
}
|
|
});
|
|
|
|
test("M3 topology is the default trusted DO1 to DI1 patch-panel route", () => {
|
|
const topology = createM3Topology();
|
|
|
|
assert.equal(topology.projectId, "prj_m3_hardware_loop");
|
|
assert.equal(topology.services.boxes.length, 2);
|
|
assert.equal(topology.services.gateways.length, 2);
|
|
assert.deepEqual(
|
|
topology.services.boxes.map((box) => box.resource.resourceId),
|
|
["res_boxsimu_1", "res_boxsimu_2"]
|
|
);
|
|
assert.deepEqual(
|
|
topology.services.gateways.map((gateway) => gateway.gatewayId),
|
|
["gwsimu_1", "gwsimu_2"]
|
|
);
|
|
assert.deepEqual(topology.services.patchPanel.activeConnections, [
|
|
{
|
|
fromResourceId: "res_boxsimu_1",
|
|
fromPort: "DO1",
|
|
toResourceId: "res_boxsimu_2",
|
|
toPort: "DI1"
|
|
}
|
|
]);
|
|
assert.deepEqual(topology.wiringConfig.connections, [
|
|
{
|
|
from: {
|
|
resourceId: "res_boxsimu_1",
|
|
port: "DO1"
|
|
},
|
|
to: {
|
|
resourceId: "res_boxsimu_2",
|
|
port: "DI1"
|
|
},
|
|
mode: "exclusive"
|
|
}
|
|
]);
|
|
});
|
|
|
|
test("patch panel routes cross-device signals and records deliveries", () => {
|
|
const topology = createMvpTopology();
|
|
const patchPanel = createPatchPanel({
|
|
wiringConfig: topology.wiringConfig,
|
|
now: () => "2026-01-01T00:00:01.000Z"
|
|
});
|
|
|
|
const result = patchPanel.routeSignal({
|
|
fromResourceId: "res_boxsim_alpha",
|
|
fromPort: "uart0",
|
|
value: "boot>"
|
|
});
|
|
|
|
assert.equal(result.accepted, true);
|
|
assert.equal(result.propagatedBy, "hwlab-patch-panel");
|
|
assert.deepEqual(result.deliveries, [
|
|
{
|
|
resourceId: "res_boxsim_beta",
|
|
port: "uart0",
|
|
value: "boot>",
|
|
sourceResourceId: "res_boxsim_alpha",
|
|
sourcePort: "uart0",
|
|
observedAt: "2026-01-01T00:00:01.000Z"
|
|
}
|
|
]);
|
|
});
|
|
|
|
test("static dry-run fixture matches generated MVP topology", async () => {
|
|
const raw = await readFile(new URL("../../fixtures/mvp-topology/topology.json", import.meta.url), "utf8");
|
|
assert.deepEqual(JSON.parse(raw), createMvpTopology());
|
|
});
|
|
|
|
test("patch panel validates typed DO/AO/FREQ wiring and reports readable wrong-wire diagnostics", () => {
|
|
const wiringConfig = createWiringConfig({
|
|
wiringConfigId: "wir_typed_signals",
|
|
projectId: "prj_typed_signals",
|
|
gatewaySessionId: "gws_typed_signals",
|
|
connections: [
|
|
{
|
|
from: {
|
|
resourceId: "res_alpha",
|
|
port: "DO1"
|
|
},
|
|
to: {
|
|
resourceId: "res_beta",
|
|
port: "DI1"
|
|
},
|
|
mode: "exclusive"
|
|
},
|
|
{
|
|
from: {
|
|
resourceId: "res_alpha",
|
|
port: "AO1"
|
|
},
|
|
to: {
|
|
resourceId: "res_beta",
|
|
port: "AI1"
|
|
},
|
|
mode: "exclusive"
|
|
},
|
|
{
|
|
from: {
|
|
resourceId: "res_alpha",
|
|
port: "FREQ_OUT1"
|
|
},
|
|
to: {
|
|
resourceId: "res_beta",
|
|
port: "FREQ_IN1"
|
|
},
|
|
mode: "exclusive"
|
|
}
|
|
],
|
|
now: "2026-01-01T00:00:00.000Z"
|
|
});
|
|
const patchPanel = createPatchPanel({ wiringConfig });
|
|
|
|
assert.equal(patchPanel.diagnosticSummary.errorCount, 0);
|
|
assert.deepEqual(
|
|
patchPanel.normalizedConnections.map((connection) => connection.syncKind),
|
|
["DO->DI", "AO->AI", "FREQ_OUT->FREQ_IN"]
|
|
);
|
|
|
|
const invalid = createPatchPanel({
|
|
wiringConfig: createWiringConfig({
|
|
wiringConfigId: "wir_invalid_signal",
|
|
projectId: "prj_typed_signals",
|
|
gatewaySessionId: "gws_typed_signals",
|
|
connections: [
|
|
{
|
|
from: {
|
|
resourceId: "res_alpha",
|
|
port: "DI1"
|
|
},
|
|
to: {
|
|
resourceId: "res_beta",
|
|
port: "DO1"
|
|
},
|
|
mode: "exclusive"
|
|
}
|
|
],
|
|
now: "2026-01-01T00:00:00.000Z"
|
|
})
|
|
});
|
|
|
|
assert.equal(invalid.status().state, "faulted");
|
|
assert.ok(invalid.diagnostics.some((diagnostic) => diagnostic.code === "source_port_not_output"));
|
|
});
|