import assert from "node:assert/strict"; import { readFile } from "node:fs/promises"; import { test } from "node:test"; import ts from "typescript"; const sourceRoot = new URL("../src/", import.meta.url); async function read(relativePath) { return readFile(new URL(relativePath, sourceRoot), "utf8"); } test("Compute-contour telemetry remains a bounded system feature slice", async () => { const [ productModel, workspaceHub, core, computeWorkspace, pipelineStages, networkWorkspace, telemetryPolling, telemetryContext, panelActions, contourSettingsHook, contourSettings, contourContract, pollIntervalContract, pollingScheduler, telemetryStyles, styles, ] = await Promise.all([ read("productModel.ts"), read("workspaces/Workspaces.tsx"), read("core/system/workerTelemetry.ts"), read("workspaces/system/ComputeModulesWorkspace.tsx"), read("components/system/WorkerPipelineStages.tsx"), read("workspaces/system/NetworkWorkspace.tsx"), read("core/system/useWorkerTelemetry.ts"), read("core/system/ComputeContourContext.tsx"), read("components/useApplicationPanelActions.ts"), read("components/system/useComputeContourSettings.tsx"), read("components/system/ComputeContourSettingsWindow.tsx"), read("core/system/computeContours.ts"), read("core/system/telemetryPollInterval.ts"), read("core/system/sequentialPolling.ts"), read("styles/system-telemetry.css"), read("styles.css"), ]); assert.match(productModel, /kind: "compute-modules"/); assert.match(productModel, /kind: "network-monitor"/); assert.match(workspaceHub, //); assert.match(workspaceHub, //); assert.doesNotMatch(workspaceHub, /worker-telemetry|worker-profile|DESKTOP-OPJ8J04/); assert.match(core, /\/api\/v1\/system\/contours\/.*\/telemetry/); assert.match(core, /\/api\/v1\/system\/worker-profile/); assert.match(core, /share_percent/); assert.doesNotMatch(core, /@nodedc\/ui-react/); assert.match(computeWorkspace, /useWorkerTelemetry/); assert.match(computeWorkspace, /telemetry_poll_interval_seconds/); assert.doesNotMatch(computeWorkspace, />\s*Обновить\s*\s*Обновить\s*Worker 006<\/dt>/); assert.match(contourSettings, /contour\?\.display_name/); assert.match(telemetryPolling, /Вычислительный контур не ответил\./); assert.match(contourSettings, /FieldFrame label="Операционная система"/); assert.match(contourSettings, /label="Обновление интерфейса"/); assert.match(contourSettings, /label="Интервал MQTT агента"/); assert.match(contourSettings, /label="Публикация broker"/); assert.match(contourSettings, /MQTT · Mac ↔ Worker/); assert.match(contourSettings, /applyComputeContourNetwork/); assert.match(contourSettings, /Применить на Worker/); assert.match(contourSettings, /value=\{telemetryPollIntervalDraft\}/); assert.match(contourSettings, /onBlur=\{commitTelemetryPollInterval\}/); assert.match(contourSettings, /event\.key === "Escape"/); assert.doesNotMatch( contourSettings, /telemetry_poll_interval_seconds:\s*Number\(event\.currentTarget\.value\)/, ); assert.match(contourContract, /telemetry_poll_interval_seconds: number/); assert.match(contourContract, /mqtt_publish_interval_seconds: number/); assert.match(contourContract, /mqtt_bind_address: string/); assert.match( contourContract, /missioncore\.compute-contour-network-status\/v1/, ); assert.match(pollIntervalContract, /MIN_TELEMETRY_POLL_INTERVAL_SECONDS\s*=\s*1/); assert.match(telemetryPolling, /normalizeWorkerTelemetryPollMilliseconds/); assert.match(telemetryPolling, /startSequentialPolling/); assert.doesNotMatch(telemetryPolling, /if\s*\(\s*!enabled\s*\|\|\s*loading\s*\)/); assert.match(pollingScheduler, /A transient failure must not stop polling/); assert.match(styles, /system-telemetry\.css/); }); test("MQTT polling interval keeps an unrestricted draft until explicit commit", async () => { const source = await read("core/system/telemetryPollInterval.ts"); const javascript = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.ES2022, target: ts.ScriptTarget.ES2022, }, }).outputText; const contract = await import( `data:text/javascript;base64,${Buffer.from(javascript).toString("base64")}` ); assert.equal(contract.parseTelemetryPollIntervalDraft(""), null); assert.equal(contract.parseTelemetryPollIntervalDraft("0"), null); assert.equal(contract.parseTelemetryPollIntervalDraft("0,1"), null); assert.equal(contract.parseTelemetryPollIntervalDraft("0.1"), null); assert.equal(contract.parseTelemetryPollIntervalDraft("1"), 1); assert.equal(contract.parseTelemetryPollIntervalDraft("60"), 60); assert.equal(contract.parseTelemetryPollIntervalDraft("61"), null); assert.deepEqual(contract.resolveTelemetryPollIntervalDraft("", 3), { accepted: false, draft: "3", seconds: 3, }); assert.deepEqual(contract.resolveTelemetryPollIntervalDraft("01", 3), { accepted: true, draft: "1", seconds: 1, }); assert.equal(contract.normalizeWorkerTelemetryPollMilliseconds(100), 1_000); assert.equal(contract.normalizeWorkerTelemetryPollMilliseconds(3_000), 3_000); }); test("Worker telemetry polling repeats after fast responses and transient errors", async () => { const source = await read("core/system/sequentialPolling.ts"); const javascript = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.ES2022, target: ts.ScriptTarget.ES2022, }, }).outputText; const polling = await import( `data:text/javascript;base64,${Buffer.from(javascript).toString("base64")}` ); let nextHandle = 1; const timers = new Map(); const cleared = []; const scheduler = { setTimeout(callback, delayMilliseconds) { const handle = nextHandle++; timers.set(handle, { callback, delayMilliseconds }); return handle; }, clearTimeout(handle) { cleared.push(handle); timers.delete(handle); }, }; let attempts = 0; const stop = polling.startSequentialPolling(async () => { attempts += 1; if (attempts === 2) throw new Error("transient"); }, 3_000, scheduler); await new Promise((resolve) => setImmediate(resolve)); assert.equal(attempts, 1); assert.equal(timers.size, 1); assert.equal([...timers.values()][0].delayMilliseconds, 3_000); let [handle, scheduled] = [...timers.entries()][0]; timers.delete(handle); scheduled.callback(); await new Promise((resolve) => setImmediate(resolve)); assert.equal(attempts, 2); assert.equal(timers.size, 1, "a transient error must schedule the next poll"); [handle, scheduled] = [...timers.entries()][0]; timers.delete(handle); scheduled.callback(); await new Promise((resolve) => setImmediate(resolve)); assert.equal(attempts, 3); assert.equal(timers.size, 1); stop(); assert.equal(timers.size, 0); assert.deepEqual(cleared, [nextHandle - 1]); });