import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import { test } from "node:test"; import { type LlamaSwapConfig } from "../src/lib/util"; import { MetricsPoller } from "../src/lib/metrics-poller"; import { parseGpuMetrics } from "../src/lib/metrics-parser"; const FIXTURE = readFileSync(new URL("./fixtures/metrics.txt", import.meta.url), "utf8"); const FIXTURE2 = FIXTURE .replace(/llamaswap_gpu_util_percent\{id="0"[^}]*\} 100/, 'llamaswap_gpu_util_percent{id="0",name="RTX PRO 6000"} 30') .replace(/llamaswap_gpu_util_percent\{id="1"[^}]*\} 100/, 'llamaswap_gpu_util_percent{id="1",name="RTX PRO 6000"} 50') .replace(/llamaswap_gpu_util_percent\{id="2"[^}]*\} 100/, 'llamaswap_gpu_util_percent{id="2",name="RTX 5090"} 80'); function stubFetch(bodies: string[]) { let i = 0; return async () => bodies[Math.min(i++, bodies.length - 1)]; } const CFG: LlamaSwapConfig = { baseUrl: "http://test" }; test("poller stores per-GPU history and computes aggregates", async () => { const poller = new MetricsPoller(CFG, 5000, stubFetch([FIXTURE, FIXTURE2])); await poller.tick(); await poller.tick(); assert.equal(poller.getValue("0", "util_percent"), 30); assert.deepEqual(poller.getHistory("0", "util_percent"), [100, 30]); const allUtil = poller.getValue("all", "util_percent"); assert.ok(allUtil !== undefined); assert.ok(Math.abs(allUtil - (30 + 50 + 80) / 3) < 1e-9); const allTemp = poller.getValue("all", "temperature"); assert.equal(allTemp, 64); const allPower = poller.getValue("all", "power"); assert.ok(allPower !== undefined); assert.ok(Math.abs(allPower - (316.56 + 336.17 + 377.28)) < 1e-9); assert.deepEqual(poller.gpus().map((g) => g.id), ["0", "1", "2"]); assert.equal(poller.gpus()[2].name, "NVIDIA GeForce RTX 5090"); }); test("poller ring buffer caps at 60 samples", async () => { const poller = new MetricsPoller(CFG, 5000, stubFetch([FIXTURE])); for (let i = 0; i < 70; i++) await poller.tick(); assert.equal(poller.getHistory("0", "fan").length, 60); }); test("poller reports offline after a fetch failure and recovers", async () => { let fail = true; const fetchFn = async () => { if (fail) throw new Error("boom"); return FIXTURE; }; const poller = new MetricsPoller(CFG, 5000, fetchFn); await poller.tick(); assert.equal(poller.isOffline(), true); fail = false; await poller.tick(); assert.equal(poller.isOffline(), false); assert.ok(poller.getValue("0", "util_percent") !== undefined); });