diff --git a/scripts/src/hwlab-node-web-sentinel-p5-observe.test.ts b/scripts/src/hwlab-node-web-sentinel-p5-observe.test.ts index 79794782..15abae79 100644 --- a/scripts/src/hwlab-node-web-sentinel-p5-observe.test.ts +++ b/scripts/src/hwlab-node-web-sentinel-p5-observe.test.ts @@ -1,7 +1,50 @@ import assert from "node:assert/strict"; +import { mkdtempSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; import { test } from "bun:test"; -import { classifyQuickVerifyCleanupStep, quickVerifyCleanupFindings } from "./hwlab-node-web-sentinel-p5-observe"; +import { hwlabRuntimeLaneSpecForNode } from "./hwlab-node-lanes"; +import { classifyQuickVerifyCleanupStep, quickVerifyCleanupFindings, sentinelServiceProxyInvocation } from "./hwlab-node-web-sentinel-p5-observe"; +import { createWebProbeSentinelService } from "./hwlab-node-web-sentinel-service"; + +test("sentinel service proxy executes its script through trans standard input", () => { + assert.deepEqual(sentinelServiceProxyInvocation("NC01:k3s", "echo sentinel-service-proxy"), { + argv: ["trans", "NC01:k3s", "sh"], + input: "echo sentinel-service-proxy", + }); +}); + +test("recorded sentinel run is available through the latest report index", () => { + const stateRoot = mkdtempSync(join(tmpdir(), "unidesk-sentinel-index-")); + const service = createWebProbeSentinelService({ + spec: hwlabRuntimeLaneSpecForNode("v03", "NC01"), + sentinelId: "nc01-web-probe-sentinel", + stateRootOverride: stateRoot, + schedulerEnabled: false, + }); + try { + const scenarioId = String(service.config.scenarios[0]?.id); + const recorded = service.recordRun({ + runId: "sentinel-run-index-test", + scenarioId, + status: "succeeded", + reportJsonSha256: "sha256:index-test", + summary: { source: "test", valuesRedacted: true }, + views: { summary: { renderedText: "indexed summary" } }, + findingCount: 0, + artifactCount: 0, + valuesRedacted: true, + }); + const latest = service.report("summary", null); + assert.equal(recorded.ok, true); + assert.equal(latest.ok, true); + assert.equal((latest.run as Record).id, "sentinel-run-index-test"); + } finally { + service.close(); + rmSync(stateRoot, { recursive: true, force: true }); + } +}); function forceStopStep(observer: Record): Record { return { diff --git a/scripts/src/hwlab-node-web-sentinel-p5-observe.ts b/scripts/src/hwlab-node-web-sentinel-p5-observe.ts index 1bdc7b58..4c8648c2 100644 --- a/scripts/src/hwlab-node-web-sentinel-p5-observe.ts +++ b/scripts/src/hwlab-node-web-sentinel-p5-observe.ts @@ -915,7 +915,8 @@ function callSentinelService(state: SentinelCicdState, method: "GET" | "POST", p let result: CommandResult | null = null; let parsed: Record | null = null; for (let attempt = 1; attempt <= maxAttempts; attempt += 1) { - result = runCommand(["trans", stringAt(state.controlPlaneNode, "kubeRoute"), "sh", "--", script], repoRoot, { timeoutMs: attemptTimeoutSeconds * 1000 }); + const invocation = sentinelServiceProxyInvocation(stringAt(state.controlPlaneNode, "kubeRoute"), script); + result = runCommand(invocation.argv, repoRoot, { input: invocation.input, timeoutMs: attemptTimeoutSeconds * 1000 }); const parsedResolution = resolveCliChildJsonCommandResult({ result, requestedStdoutType: "web-probe sentinel service proxy response JSON", @@ -953,6 +954,10 @@ function isSentinelServiceResponseContract(value: Record): bool || Array.isArray(value.findings); } +export function sentinelServiceProxyInvocation(kubeRoute: string, script: string): { readonly argv: readonly string[]; readonly input: string } { + return { argv: ["trans", kubeRoute, "sh"], input: script }; +} + function compactSentinelServiceBodyJson(value: Record | null): unknown { if (value === null || typeof value.renderedText !== "string") return value; return {