Merge pull request #260 from pikasTech/fix/d601-m3-control-live-e2e
test: tighten M3 IO durable-blocked evidence
This commit is contained in:
@@ -897,6 +897,14 @@ async function tryRuntimeWrite(label, write) {
|
||||
}
|
||||
try {
|
||||
const result = await write();
|
||||
if (result === undefined) {
|
||||
return {
|
||||
label,
|
||||
written: false,
|
||||
skipped: true,
|
||||
reason: "runtimeStore method unavailable"
|
||||
};
|
||||
}
|
||||
return {
|
||||
label,
|
||||
written: true,
|
||||
|
||||
@@ -97,6 +97,48 @@ test("M3 DO write dispatches through gateway-simu, ticks patch-panel, reads DI,
|
||||
assert.equal(evidence.records[0].operationId, result.operationId);
|
||||
});
|
||||
|
||||
test("M3 DO write can prove control reachability while durable trusted persistence stays blocked", async () => {
|
||||
const fixture = createM3ControlFixture();
|
||||
const result = await handleM3IoControl(
|
||||
{
|
||||
action: "do.write",
|
||||
gatewayId: "gwsimu_1",
|
||||
resourceId: "res_boxsimu_1",
|
||||
boxId: "boxsimu_1",
|
||||
port: "DO1",
|
||||
value: true,
|
||||
traceId: "trc_m3_control_durable_blocked",
|
||||
requestId: "req_m3_control_durable_blocked",
|
||||
actorId: "usr_m3_operator"
|
||||
},
|
||||
{
|
||||
runtimeStore: createBlockedDurableRuntimeStore(),
|
||||
now: () => fixedNow,
|
||||
env: {
|
||||
HWLAB_M3_GATEWAY_SIMU_1_URL: "http://gateway-1",
|
||||
HWLAB_M3_GATEWAY_SIMU_2_URL: "http://gateway-2",
|
||||
HWLAB_M3_PATCH_PANEL_URL: "http://patch-panel"
|
||||
},
|
||||
requestJson: fixture.requestJson
|
||||
}
|
||||
);
|
||||
|
||||
assert.equal(result.status, "succeeded");
|
||||
assert.equal(result.accepted, true);
|
||||
assert.equal(result.result.value, true);
|
||||
assert.equal(result.result.targetReadback.value, true);
|
||||
assert.equal(result.controlPath.status, "succeeded");
|
||||
assert.equal(result.controlPath.patchPanel, true);
|
||||
assert.equal(result.evidenceState.status, "blocked");
|
||||
assert.equal(result.evidenceState.sourceKind, "BLOCKED");
|
||||
assert.equal(result.evidenceState.durable, false);
|
||||
assert.equal(result.evidenceState.blocker, "runtime_durable_adapter_query_blocked");
|
||||
assert.equal(result.persistence.runtime.status, "blocked");
|
||||
assert.equal(result.persistence.runtime.liveRuntimeEvidence, false);
|
||||
assert.equal(result.persistence.writes.every((write) => write.written !== true), true);
|
||||
assert.match(result.evidenceState.reason, /操作链路可达性与可信持久化分离/u);
|
||||
});
|
||||
|
||||
test("M3 DI read returns structured value through gateway-simu without patch-panel mutation", async () => {
|
||||
const fixture = createM3ControlFixture();
|
||||
writeBoxPort({
|
||||
@@ -206,6 +248,38 @@ test("M3 IO control blocks invalid port direction instead of allowing generic wr
|
||||
assert.match(result.blocker.zh, /端口方向|资源越界/u);
|
||||
});
|
||||
|
||||
test("M3 IO control blocks invalid DI target before calling gateway-simu", async () => {
|
||||
const result = await handleM3IoControl(
|
||||
{
|
||||
action: "di.read",
|
||||
gatewayId: "gwsimu_2",
|
||||
resourceId: "res_boxsimu_1",
|
||||
port: "DO1",
|
||||
traceId: "trc_m3_invalid_di_target",
|
||||
requestId: "req_m3_invalid_di_target",
|
||||
actorId: "usr_m3_operator"
|
||||
},
|
||||
{
|
||||
runtimeStore: createCloudRuntimeStore({ now: () => fixedNow }),
|
||||
now: () => fixedNow,
|
||||
env: {
|
||||
HWLAB_M3_GATEWAY_SIMU_1_URL: "http://gateway-1",
|
||||
HWLAB_M3_GATEWAY_SIMU_2_URL: "http://gateway-2",
|
||||
HWLAB_M3_PATCH_PANEL_URL: "http://patch-panel"
|
||||
},
|
||||
requestJson: async () => {
|
||||
throw new Error("gateway must not be called for invalid DI target");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
assert.equal(result.status, "blocked");
|
||||
assert.equal(result.blocker.code, M3_IO_BLOCKER_CODES.portDirectionInvalid);
|
||||
assert.match(result.blocker.zh, /DI read|端口方向|资源越界/u);
|
||||
assert.equal(result.controlPath.gatewaySimu, false);
|
||||
assert.equal(result.controlPath.frontendBypass, false);
|
||||
});
|
||||
|
||||
test("M3 IO control fails closed when indexed gateway identity drifts", async () => {
|
||||
const fixture = createM3ControlFixture({
|
||||
gateway1Id: "gwsimu_2"
|
||||
@@ -283,6 +357,25 @@ test("M3 IO control blocks when patch-panel wiring does not deliver DO1 to DI1",
|
||||
assert.match(result.blocker.zh, /wiring 不存在|未路由/u);
|
||||
});
|
||||
|
||||
function createBlockedDurableRuntimeStore() {
|
||||
return {
|
||||
async readiness() {
|
||||
return {
|
||||
adapter: "postgres",
|
||||
durable: false,
|
||||
durableRequested: true,
|
||||
durableCapable: false,
|
||||
ready: false,
|
||||
status: "blocked",
|
||||
blocker: "runtime_durable_adapter_query_blocked",
|
||||
reason: "Postgres runtime adapter could not complete a live runtime schema query",
|
||||
liveRuntimeEvidence: false,
|
||||
fixtureEvidence: false
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createM3ControlFixture({ patchPanelBody, gateway1Id = "gwsimu_1", gateway2Id = "gwsimu_2" } = {}) {
|
||||
const calls = [];
|
||||
const box1 = createBoxState({
|
||||
|
||||
Reference in New Issue
Block a user