Files
pikasTech-HWLAB/scripts/src/rpt004-mvp-e2e-harness.test.mjs
T
2026-05-23 15:26:15 +00:00

844 lines
25 KiB
JavaScript

import assert from "node:assert/strict";
import test from "node:test";
import {
buildRpt004Report,
classifyCodeAgentSessionTurns,
classifyCodeAgentSkillPath,
classifySkillCliM3Route,
classifyM3DurableEvidence,
classifyRpt004Conclusion,
parseArgs,
validateLiveBoundary
} from "./rpt004-mvp-e2e-harness.mjs";
const fixtureCommit = "667349554b1f";
test("RPT-004 CLI accepts the required live URL/API URL shape", () => {
const args = parseArgs([
"--live",
"--url",
"http://74.48.78.17:16666/",
"--api-url",
"http://74.48.78.17:16667/"
]);
assert.equal(args.live, true);
assert.equal(args.url, "http://74.48.78.17:16666/");
assert.equal(args.apiUrl, "http://74.48.78.17:16667/");
assert.equal(args.writeReport, true);
assert.equal(args.allowM3Write, false);
assert.doesNotThrow(() => validateLiveBoundary(args));
});
test("RPT-004 CLI requires explicit flag before live M3 writes", () => {
const defaultLive = parseArgs([
"--live",
"--url",
"http://74.48.78.17:16666/",
"--api-url",
"http://74.48.78.17:16667/"
]);
const writeLive = parseArgs([
"--live",
"--allow-m3-write",
"--url",
"http://74.48.78.17:16666/",
"--api-url",
"http://74.48.78.17:16667/"
]);
assert.equal(defaultLive.allowM3Write, false);
assert.equal(writeLive.allowM3Write, true);
});
test("RPT-004 live boundary rejects deprecated or swapped active DEV ports", () => {
assert.throws(
() => validateLiveBoundary({ url: "http://74.48.78.17:6666/", apiUrl: "http://74.48.78.17:16667/" }),
/16666/u
);
assert.throws(
() => validateLiveBoundary({ url: "http://74.48.78.17:16667/", apiUrl: "http://74.48.78.17:16666/" }),
/16666/u
);
});
test("RPT-004 classifies deployed revision drift as NOT_CURRENT, never MVP PASS", () => {
const conclusion = classifyRpt004Conclusion([
check("expected-artifact-current", "pass"),
check("live-revision-current", "not_current"),
check("live-health-ready", "pass"),
check("16666-default-chinese-workbench", "pass")
]);
assert.equal(conclusion.status, "not_current");
assert.equal(conclusion.classification, "NOT_CURRENT");
assert.equal(conclusion.mvpPass, false);
});
test("RPT-004 classifies degraded live health as BLOCKED, never MVP PASS", () => {
const conclusion = classifyRpt004Conclusion([
check("expected-artifact-current", "pass"),
check("live-revision-current", "pass"),
check("live-health-ready", "blocked"),
check("16666-default-chinese-workbench", "pass"),
check("m3-v1-io-trusted-loop", "not_run")
]);
assert.equal(conclusion.status, "blocked");
assert.equal(conclusion.classification, "BLOCKED");
assert.equal(conclusion.mvpPass, false);
});
test("RPT-004 reports MVP_PASS only when every required live dimension passes", () => {
const conclusion = classifyRpt004Conclusion([
check("expected-artifact-current", "pass"),
check("live-revision-current", "pass"),
check("live-health-ready", "pass"),
check("16666-default-chinese-workbench", "pass"),
check("layout-smoke-hit-target", "pass"),
check("code-agent-skill-path", "pass"),
check("code-agent-session-thread", "pass"),
check("skill-cli-hwlab-api-m3-route", "pass"),
check("m3-v1-io-trusted-loop", "pass"),
check("operation-audit-evidence-durable-green", "pass")
]);
assert.equal(conclusion.status, "pass");
assert.equal(conclusion.classification, "MVP_PASS");
assert.equal(conclusion.mvpPass, true);
});
test("Code Agent OpenAI fallback cannot satisfy skill path/toolCalls readiness", () => {
const result = classifyCodeAgentSkillPath({
status: "completed",
provider: "openai-responses",
model: "gpt-5.5",
backend: "hwlab-cloud-api/openai-responses",
implementationType: "openai-responses-fallback",
runner: {
kind: "openai-responses-fallback"
},
reply: {
content: "text answer"
},
toolCalls: [],
skills: {
status: "not_requested",
items: []
}
}, { httpStatus: 200, httpOk: true });
assert.equal(result.status, "blocked");
assert.match(result.summary, /openai-fallback/u);
});
test("Code Agent Codex stdio skill discovery passes with completed toolCalls and repo skill", () => {
const result = classifyCodeAgentSkillPath({
status: "completed",
conversationId: "cnv_test",
sessionId: "ses_test",
messageId: "msg_test",
traceId: "trc_test",
provider: "codex-stdio",
model: "gpt-5.5",
backend: "hwlab-cloud-api/codex-mcp-stdio-runner",
workspace: "/workspace/hwlab",
sandbox: "workspace-write",
capabilityLevel: "long-lived-codex-stdio-session",
sessionMode: "codex-mcp-stdio-long-lived",
implementationType: "repo-owned-codex-mcp-stdio-session",
session: {
sessionId: "ses_test",
status: "idle",
idleTimeoutMs: 1800000,
lastTraceId: "trc_test",
longLivedSession: true,
codexStdio: true
},
sessionReuse: {
reused: false,
turn: 1
},
runner: {
kind: "codex-mcp-stdio-runner",
workspace: "/workspace/hwlab",
sandbox: "workspace-write",
sessionMode: "codex-mcp-stdio-long-lived",
implementationType: "repo-owned-codex-mcp-stdio-session",
codexStdio: true,
writeCapable: true,
durableSession: true
},
runnerTrace: {
runnerKind: "codex-mcp-stdio-runner"
},
longLivedSessionGate: {
status: "pass",
pass: true
},
toolCalls: [{
name: "skills.discover",
status: "completed",
type: "file-read"
}],
skills: {
status: "ready",
items: [{
name: "hwlab-agent-runtime",
source: "/workspace/hwlab/skills/hwlab-agent-runtime/SKILL.md"
}],
count: 1
}
}, { httpStatus: 200, httpOk: true });
assert.equal(result.status, "pass");
});
test("Code Agent controlled read-only session skill discovery passes the harness skill path gate", () => {
const result = classifyCodeAgentSkillPath({
...codexSessionPayload({
toolName: "skills.discover",
skills: {
status: "ready",
items: [{
name: "hwlab-agent-runtime",
source: "/workspace/hwlab/skills/hwlab-agent-runtime/SKILL.md"
}],
count: 1
}
}),
provider: "codex-readonly-runner",
backend: "hwlab-cloud-api/codex-readonly-runner",
sandbox: "read-only",
capabilityLevel: "read-only-session-tools",
sessionMode: "controlled-readonly-session-registry",
implementationType: "controlled-readonly-session-registry",
runner: {
kind: "hwlab-readonly-runner",
workspace: "/workspace/hwlab",
sandbox: "read-only",
sessionMode: "controlled-readonly-session-registry",
implementationType: "controlled-readonly-session-registry",
codexStdio: false,
writeCapable: false,
durableSession: false
},
runnerTrace: {
runnerKind: "hwlab-readonly-runner",
sessionMode: "controlled-readonly-session-registry"
}
}, { httpStatus: 200, httpOk: true });
assert.equal(result.status, "pass");
assert.equal(result.observations.controlledSessionRunner, true);
});
test("Code Agent three-turn same session classifier passes with pwd, skills, and context reference", () => {
const turns = [
sessionTurn("pwd", codexSessionPayload({
toolName: "pwd",
turn: 1,
reused: false,
reply: "当前工作目录是 /workspace/hwlab。"
})),
sessionTurn("skills", codexSessionPayload({
toolName: "skills.discover",
turn: 2,
reused: true,
skills: {
status: "ready",
items: [{
name: "hwlab-agent-runtime",
source: "/workspace/hwlab/skills/hwlab-agent-runtime/SKILL.md"
}],
count: 1
},
reply: "可用 skill 包括 hwlab-agent-runtime。"
})),
sessionTurn("context-pwd", codexSessionPayload({
toolName: "pwd",
turn: 3,
reused: true,
reply: "前文第一轮你问的是 pwd;当前工作目录仍是 /workspace/hwlab。"
}))
];
const result = classifyCodeAgentSessionTurns(turns, { expectedConversationId: "cnv_rpt004_fixture" });
assert.equal(result.status, "pass");
assert.deepEqual(result.observations.conversationIds, ["cnv_rpt004_fixture"]);
assert.deepEqual(result.observations.sessionIds, ["ses_rpt004_fixture"]);
assert.equal(result.observations.turns[2].referencesPrevious, true);
});
test("Code Agent fallback-only turns cannot satisfy session checks", () => {
const turns = [
sessionTurn("pwd", openAiFallbackPayload()),
sessionTurn("skills", openAiFallbackPayload()),
sessionTurn("context-pwd", openAiFallbackPayload({ reply: "第一轮是 pwd。" }))
];
const result = classifyCodeAgentSessionTurns(turns, { expectedConversationId: "cnv_rpt004_fixture" });
assert.equal(result.status, "blocked");
assert.equal(result.observations.fallbackOnly, true);
assert.ok(result.observations.blockers.includes("fallback-only"));
});
test("Skill CLI M3 route passes with operation/audit/evidence ids and no direct path", () => {
const result = classifySkillCliM3Route(skillCliPayload(), { httpStatus: 200, httpOk: true });
assert.equal(result.status, "pass");
assert.equal(result.observations.route, "/v1/m3/io");
assert.equal(result.observations.method, "POST");
assert.equal(result.observations.idsPresent, true);
assert.equal(result.observations.accepted, true);
assert.equal(result.observations.controlReady, true);
assert.equal(result.observations.readback.value, true);
assert.equal(result.observations.safety.directGatewayCalls, false);
assert.equal(result.observations.safety.fallbackUsed, false);
});
test("Skill CLI M3 route passes with structured blocker and no direct path", () => {
const payload = skillCliPayload({
accepted: false,
status: "blocked",
operationId: null,
auditId: null,
evidenceId: null,
blocker: {
code: "hwlab_api_unavailable",
message: "HWLAB API unavailable"
}
});
const result = classifySkillCliM3Route(payload, { httpStatus: 200, httpOk: true });
assert.equal(result.status, "pass");
assert.equal(result.observations.structuredBlocker, true);
assert.equal(result.observations.idsPresent, false);
});
test("Skill CLI M3 route blocks missing route and direct gateway evidence", () => {
const payload = skillCliPayload({
route: "http://hwlab-gateway-simu-1.hwlab-dev.svc.cluster.local:7101/invoke",
directGatewayCalls: true
});
const result = classifySkillCliM3Route(payload, { httpStatus: 200, httpOk: true });
assert.equal(result.status, "blocked");
assert.match(result.summary, /route=\/v1\/m3\/io/u);
assert.equal(result.observations.safety.directGatewayCalls, true);
});
test("Skill CLI M3 route requires explicit POST method evidence", () => {
const result = classifySkillCliM3Route(skillCliPayload({ method: null }), { httpStatus: 200, httpOk: true });
assert.equal(result.status, "blocked");
assert.match(result.summary, /method=POST/u);
assert.ok(result.observations.missing.includes("method=POST"));
});
test("RPT-004 live report defaults to read-only M3 route checks and records no live mutation", async () => {
const report = await buildRpt004Report({
...parseArgs([
"--live",
"--url",
"http://74.48.78.17:16666/",
"--api-url",
"http://74.48.78.17:16667/",
"--expected-commit",
fixtureCommit
]),
writeReport: false
}, {
repoRoot: "/fixture-rpt004",
fsJson: fixtureArtifactJson(),
now: () => "2026-05-23T00:00:00.000Z",
httpGetJson: async (url) => ({ ok: true, status: 200, json: healthPayload(url) }),
httpGetText: async () => ({
ok: true,
status: 200,
contentType: "text/html; charset=utf-8",
body: workbenchHtml()
}),
layoutRunner: async () => ({ status: "pass", summary: "layout ok", artifacts: { screenshots: [] } }),
codeAgentRunner: async ({ turn, conversationId, sessionId }) => ({
ok: true,
status: 200,
json: codexSessionPayload({
conversationId,
sessionId,
toolName: turn.kind === "skills" ? "skills.discover" : "pwd",
turn: turn.kind === "pwd" ? 1 : turn.kind === "skills" ? 2 : 3,
reused: turn.kind !== "pwd",
skills: turn.kind === "skills" ? {
status: "ready",
items: [{
name: "hwlab-agent-runtime",
source: "/workspace/hwlab/skills/hwlab-agent-runtime/SKILL.md"
}],
count: 1
} : undefined,
reply: turn.kind === "context-pwd"
? "前文第一轮你问的是 pwd;当前工作目录仍是 /workspace/hwlab。"
: "当前工作目录是 /workspace/hwlab。"
})
}),
skillCliRunner: async () => ({
ok: true,
status: 200,
json: skillCliPayload()
})
});
assert.equal(report.status, "pass");
assert.equal(report.liveMutationUsed, false);
assert.equal(report.safety.liveMutationUsed, false);
assert.equal(report.commanderSummary.allowedLiveMutationUsed, false);
assert.equal(report.commanderSummary.sessionResult.status, "pass");
assert.equal(report.commanderSummary.skillCliResult.status, "pass");
assert.equal(report.commanderSummary.m3RouteResult.route, "/v1/m3/io");
assert.equal(report.commanderSummary.m3RouteResult.method, "POST");
assert.equal(report.commanderSummary.m3RouteResult.accepted, true);
assert.equal(report.commanderSummary.m3RouteResult.controlReady, true);
assert.equal(report.commanderSummary.m3RouteResult.readback.value, true);
assert.equal(report.commanderSummary.exactRouteEvidenceContract.visible, true);
assert.equal(report.commanderSummary.exactRouteEvidenceContract.route, "/v1/m3/io");
assert.equal(report.commanderSummary.exactRouteEvidenceContract.method, "POST");
assert.equal(report.commanderSummary.exactRouteEvidenceContract.operationId, "op_rpt004_skill_m3");
assert.equal(report.commanderSummary.exactRouteEvidenceContract.traceId, "trc_rpt004_skill");
assert.equal(report.commanderSummary.exactRouteEvidenceContract.auditId, "aud_rpt004_skill_m3");
assert.equal(report.commanderSummary.exactRouteEvidenceContract.evidenceId, "evd_rpt004_skill_m3");
assert.equal(report.commanderSummary.exactRouteEvidenceContract.accepted, true);
assert.equal(report.commanderSummary.exactRouteEvidenceContract.status, "completed");
assert.equal(report.commanderSummary.exactRouteEvidenceContract.readback.value, true);
assert.equal(report.commanderSummary.evidenceIds.evidenceId, "evd_rpt004_skill_m3");
assert.equal(report.dimensions.skillCliM3Route.status, "pass");
assert.equal(report.dimensions.m3TrustedLoop.observations.skippedWriteLoop, true);
assert.equal(report.dimensions.durableEvidence.observations.notApplicableInDefaultReadOnlyMode, true);
});
test("RPT-004 live report emits NOT_CURRENT and skips downstream checks", async () => {
const report = await buildRpt004Report({
...parseArgs([
"--live",
"--url",
"http://74.48.78.17:16666/",
"--api-url",
"http://74.48.78.17:16667/",
"--expected-commit",
fixtureCommit
]),
writeReport: false
}, {
repoRoot: "/fixture-rpt004",
fsJson: fixtureArtifactJson(),
now: () => "2026-05-23T00:00:00.000Z",
httpGetJson: async (url) => ({ ok: true, status: 200, json: healthPayload(url, { revision: "1111111" }) }),
httpGetText: async () => ({
ok: true,
status: 200,
contentType: "text/html; charset=utf-8",
body: workbenchHtml()
}),
layoutRunner: async () => {
throw new Error("layout must not run when NOT_CURRENT");
}
});
assert.equal(report.status, "not_current");
assert.equal(report.conclusion.classification, "NOT_CURRENT");
assert.equal(report.dimensions.codeAgentSession.status, "not_run");
assert.equal(report.dimensions.skillCliM3Route.status, "not_run");
assert.equal(report.liveMutationUsed, false);
});
test("M3 durable evidence requires operation, audit, evidence, DEV-LIVE green, and persisted writes", () => {
const green = classifyM3DurableEvidence(greenOperations());
assert.equal(green.status, "pass");
const blockedOps = greenOperations();
blockedOps[2].evidenceState.writeStatus = "written_non_durable";
const blocked = classifyM3DurableEvidence(blockedOps);
assert.equal(blocked.status, "blocked");
assert.equal(blocked.observations.sequence[2].persisted, false);
});
function check(id, status) {
return {
id,
status,
summary: `${id} ${status}`
};
}
function greenOperations() {
return [
operation("write-do1-true", "do.write", true),
operation("read-di1-true", "di.read", true),
operation("write-do1-false", "do.write", false),
operation("read-di1-false", "di.read", false)
];
}
function operation(id, action, value) {
return {
id,
action,
status: "succeeded",
operationId: `op_${id}`,
traceId: `trc_${id}`,
auditId: `aud_${id}`,
evidenceId: `evd_${id}`,
resultValue: value,
evidenceState: {
status: "green",
sourceKind: "DEV-LIVE",
durable: true,
writeStatus: "persisted"
}
};
}
function sessionTurn(kind, payload, response = { ok: true, status: 200 }) {
return {
kind,
response,
payload
};
}
function codexSessionPayload({
conversationId = "cnv_rpt004_fixture",
sessionId = "ses_rpt004_fixture",
toolName = "pwd",
turn = 1,
reused = false,
reply = "当前工作目录是 /workspace/hwlab。",
skills = {
status: "not_requested",
items: [],
count: 0
}
} = {}) {
return {
status: "completed",
conversationId,
sessionId,
messageId: `msg_rpt004_${turn}`,
traceId: `trc_rpt004_${turn}`,
provider: "codex-stdio",
model: "gpt-5.5",
backend: "hwlab-cloud-api/codex-mcp-stdio-runner",
workspace: "/workspace/hwlab",
sandbox: "workspace-write",
capabilityLevel: "long-lived-codex-stdio-session",
sessionMode: "codex-mcp-stdio-long-lived",
implementationType: "repo-owned-codex-mcp-stdio-session",
session: {
sessionId,
status: "idle",
idleTimeoutMs: 1800000,
lastTraceId: `trc_rpt004_${turn}`,
turn,
longLivedSession: true,
codexStdio: true,
durable: true,
writeCapable: true
},
sessionReuse: {
conversationId,
sessionId,
reused,
turn,
status: "idle"
},
runner: {
kind: "codex-mcp-stdio-runner",
workspace: "/workspace/hwlab",
sandbox: "workspace-write",
session: "codex-mcp-stdio-long-lived",
sessionMode: "codex-mcp-stdio-long-lived",
implementationType: "repo-owned-codex-mcp-stdio-session",
codexStdio: true,
writeCapable: true,
durableSession: true
},
runnerTrace: {
traceId: `trc_rpt004_${turn}`,
runnerKind: "codex-mcp-stdio-runner",
sessionMode: "codex-mcp-stdio-long-lived",
sessionId,
turn,
sessionReused: reused,
events: [`turn:${turn}`, `tool:${toolName}:completed`]
},
longLivedSessionGate: {
status: "pass",
pass: true
},
toolCalls: [{
name: toolName,
status: "completed",
type: toolName === "skills.discover" ? "file-read" : "shell",
traceId: `trc_rpt004_${turn}`
}],
skills,
reply: {
content: reply
}
};
}
function openAiFallbackPayload({ reply = "text fallback" } = {}) {
return {
status: "completed",
conversationId: "cnv_rpt004_fixture",
sessionId: "ses_rpt004_fixture",
messageId: "msg_fallback",
traceId: "trc_fallback",
provider: "openai-responses",
model: "gpt-5.5",
backend: "hwlab-cloud-api/openai-responses",
implementationType: "openai-responses-fallback",
capabilityLevel: "text-chat-only",
sessionMode: "provider-text-request",
runner: {
kind: "openai-responses-fallback"
},
runnerTrace: {
runnerKind: "openai-responses-fallback"
},
toolCalls: [],
skills: {
status: "not_requested",
items: []
},
reply: {
content: reply
}
};
}
function skillCliPayload({
route = "/v1/m3/io",
method = "POST",
accepted = true,
status = "succeeded",
operationId = "op_rpt004_skill_m3",
auditId = "aud_rpt004_skill_m3",
evidenceId = "evd_rpt004_skill_m3",
blocker = null,
directGatewayCalls = false,
directBoxCalls = false,
directPatchPanelCalls = false
} = {}) {
return {
status: "completed",
conversationId: "cnv_rpt004_skill",
sessionId: "ses_rpt004_skill",
messageId: "msg_rpt004_skill",
traceId: "trc_rpt004_skill",
provider: "hwlab-skill-cli",
model: "controlled-m3-io",
backend: "hwlab-cloud-api/hwlab-agent-runtime-skill-cli",
workspace: "/workspace/hwlab",
sandbox: "hwlab-api-route-only",
capabilityLevel: accepted ? "hwlab-api-control-ready" : "hwlab-api-control-blocked",
sessionMode: "controlled-m3-io-skill-cli",
implementationType: "skill-cli-hwlab-api-adapter",
runner: {
kind: "hwlab-m3-io-skill-cli",
safety: {
directGatewayCallsAllowed: false,
directBoxSimuCallsAllowed: false,
directPatchPanelCallsAllowed: false
}
},
runnerTrace: {
traceId: "trc_rpt004_skill",
runnerKind: "hwlab-m3-io-skill-cli",
method,
route,
fallbackUsed: false,
directGatewayCalls,
directBoxCalls,
directPatchPanelCalls
},
providerTrace: {
runnerKind: "hwlab-m3-io-skill-cli",
route,
method,
traceId: "trc_rpt004_skill",
operationId,
auditId,
evidenceId,
accepted,
controlReady: accepted,
fallbackUsed: false,
readback: {
status: "succeeded",
value: true,
resourceId: "res_boxsimu_2",
port: "DI1"
}
},
toolCalls: [{
name: "hwlab-agent-runtime.m3-io",
status: accepted ? "completed" : "blocked",
type: "skill-cli",
route,
method,
accepted,
controlReady: accepted,
capabilityLevel: accepted ? "hwlab-api-control-ready" : "hwlab-api-control-blocked",
operationId,
traceId: "trc_rpt004_skill",
auditId,
evidenceId,
audit: {
auditId,
status
},
evidence: {
evidenceId,
status: accepted ? "blocked" : "blocked",
sourceKind: accepted ? "BLOCKED" : "BLOCKED",
blocker: blocker?.code ?? "runtime_durable_not_green",
writeStatus: "written_non_durable"
},
durable: {
status: "degraded",
blocker: blocker?.code ?? "runtime_durable_not_green"
},
readback: {
status: "succeeded",
value: true,
resourceId: "res_boxsimu_2",
port: "DI1"
},
blocker,
blockers: blocker ? [blocker] : [{
code: "runtime_durable_not_green",
message: "durable trust is not green"
}],
directGatewayCalls,
directBoxCalls,
directPatchPanelCalls,
fallbackUsed: false
}],
skills: {
status: "used",
items: [{
name: "hwlab-agent-runtime.m3-io",
route
}],
blockers: blocker ? [blocker] : []
},
reply: {
content: "M3 IO Skill CLI result"
}
};
}
function healthPayload(url, { revision = fixtureCommit } = {}) {
const isWeb = String(url).includes(":16666");
return {
serviceId: isWeb ? "hwlab-cloud-web" : "hwlab-cloud-api",
environment: "dev",
status: "ready",
ready: true,
commit: {
id: revision
},
image: {
tag: revision.slice(0, 7),
digest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
},
runtime: {
durable: true,
ready: true,
status: "ready",
blocker: null,
liveRuntimeEvidence: true
},
readiness: {
ready: true,
status: "ready",
durability: {
ready: true,
status: "ready",
blocker: null
}
}
};
}
function workbenchHtml() {
return `
<!doctype html>
<html lang="zh-CN">
<body data-app-shell>
<main class="workbench-shell">
<section data-view="workspace">
<h1>HWLAB 云工作台</h1>
<aside class="resource-tree">硬件资源</aside>
<section class="center-workspace">Agent 对话 工作清单 使用说明</section>
<aside class="right-sidebar">可信记录 内部复核</aside>
<form id="command-form" class="command-form"></form>
</section>
<section data-view="gate" hidden>gate</section>
</main>
</body>
</html>`;
}
function fixtureArtifactJson() {
const serviceIds = [
"hwlab-cloud-api",
"hwlab-cloud-web",
"hwlab-agent-mgr",
"hwlab-agent-worker",
"hwlab-gateway",
"hwlab-gateway-simu",
"hwlab-box-simu",
"hwlab-patch-panel",
"hwlab-router",
"hwlab-tunnel-client",
"hwlab-edge-proxy",
"hwlab-cli",
"hwlab-agent-skills"
];
const deploy = {
commitId: fixtureCommit
};
const catalog = {
commitId: fixtureCommit,
artifactState: "published",
publish: {
ciPublished: true,
registryVerified: true
},
services: serviceIds.map((serviceId) => ({
serviceId,
commitId: fixtureCommit,
imageTag: fixtureCommit.slice(0, 7),
digest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
publishState: "published"
}))
};
const artifactReport = {
commitId: fixtureCommit,
artifactPublish: {
sourceCommitId: fixtureCommit
}
};
return async (relativePath) => {
if (relativePath === "deploy/deploy.json") return deploy;
if (relativePath === "deploy/artifact-catalog.dev.json") return catalog;
if (relativePath === "reports/dev-gate/dev-artifacts.json") return artifactReport;
return null;
};
}