import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import { test } from "node:test"; import { type LlamaSwapConfig } from "../src/lib/util"; import { combineSeries, MetricsPoller } from "../src/lib/metrics-poller"; 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); }); test("combineSeries averages util and sums power", () => { const util = combineSeries( [ [10, 20, 30], [20, 40, 60], ], "util_percent", ); assert.deepEqual(util, { value: 45, history: [15, 30, 45] }); const power = combineSeries( [ [100, 200], [150, 250], ], "power", ); assert.deepEqual(power, { value: 450, history: [250, 450] }); }); test("combineSeries takes the max temperature and skips gaps", () => { const temp = combineSeries([[50, 70], [60]], "temperature"); assert.deepEqual(temp, { value: 70, history: [60, 70] }); const empty = combineSeries([[], []], "temperature"); assert.deepEqual(empty, { value: undefined, history: [] }); });