Files
2026-07-13 13:39:12 +08:00

155 lines
4.7 KiB
TypeScript

import { describe, it, beforeEach, afterEach } from "node:test";
import assert from "node:assert/strict";
const originalFetch = globalThis.fetch;
afterEach(() => {
globalThis.fetch = originalFetch;
});
describe("healthMonitor", () => {
let mod;
beforeEach(async () => {
try {
const prev = await import("../../src/lib/versionManager/healthMonitor.ts");
prev.stopMonitoring("test-tool");
prev.stopMonitoring("tool-a");
prev.stopMonitoring("tool-b");
} catch {}
mod = await import("../../src/lib/versionManager/healthMonitor.ts");
});
afterEach(() => {
try {
mod.stopMonitoring("test-tool");
} catch {}
try {
mod.stopMonitoring("tool-a");
} catch {}
try {
mod.stopMonitoring("tool-b");
} catch {}
});
describe("checkHealth", () => {
it("should return healthy for 200 with models", async () => {
globalThis.fetch = async () => ({
ok: true,
status: 200,
json: async () => ({ data: [{ id: "m1" }, { id: "m2" }] }),
});
const r = await mod.checkHealth("http://127.0.0.1:8317");
assert.equal(r.healthy, true);
assert.equal(r.modelCount, 2);
assert.equal(r.error, null);
assert.ok(r.latency >= 0);
});
it("should return unhealthy for non-200", async () => {
globalThis.fetch = async () => ({
ok: false,
status: 503,
json: async () => ({}),
});
const r = await mod.checkHealth("http://127.0.0.1:8317");
assert.equal(r.healthy, false);
assert.equal(r.modelCount, 0);
assert.equal(r.error, "HTTP 503");
});
it("should return unhealthy on network error", async () => {
globalThis.fetch = async () => {
throw new Error("ECONNREFUSED");
};
const r = await mod.checkHealth("http://127.0.0.1:8317");
assert.equal(r.healthy, false);
assert.ok(r.error.includes("ECONNREFUSED"));
});
it("should handle non-array data.data", async () => {
globalThis.fetch = async () => ({
ok: true,
status: 200,
json: async () => ({ data: "not-array" }),
});
const r = await mod.checkHealth("http://127.0.0.1:8317");
assert.equal(r.healthy, true);
assert.equal(r.modelCount, 0);
});
it("should use custom health path", async () => {
let capturedUrl;
globalThis.fetch = async (url) => {
capturedUrl = url;
return { ok: true, status: 200, json: async () => ({ data: [] }) };
};
await mod.checkHealth("http://127.0.0.1:8317", "/health");
assert.ok(capturedUrl.includes("/health"));
});
it("should default to /v1/models", async () => {
let capturedUrl;
globalThis.fetch = async (url) => {
capturedUrl = url;
return { ok: true, status: 200, json: async () => ({ data: [] }) };
};
await mod.checkHealth("http://127.0.0.1:8317");
assert.ok(capturedUrl.includes("/v1/models"));
});
it("should handle non-Error throws", async () => {
globalThis.fetch = async () => {
throw "string error";
};
const r = await mod.checkHealth("http://127.0.0.1:8317");
assert.equal(r.healthy, false);
assert.equal(r.error, "string error");
});
it("should return unhealthy for 500", async () => {
globalThis.fetch = async () => ({
ok: false,
status: 500,
json: async () => ({}),
});
const r = await mod.checkHealth("http://127.0.0.1:8317");
assert.equal(r.healthy, false);
assert.equal(r.error, "HTTP 500");
});
});
describe("startMonitoring / stopMonitoring / isMonitoring", () => {
it("should start and stop monitoring", () => {
globalThis.fetch = async () => ({
ok: true,
status: 200,
json: async () => ({ data: [] }),
});
mod.startMonitoring("tool-a", "http://127.0.0.1:8317", 60_000);
assert.equal(mod.isMonitoring("tool-a"), true);
mod.stopMonitoring("tool-a");
assert.equal(mod.isMonitoring("tool-a"), false);
});
it("should replace previous monitoring on re-start", () => {
globalThis.fetch = async () => ({
ok: true,
status: 200,
json: async () => ({ data: [] }),
});
mod.startMonitoring("tool-b", "http://127.0.0.1:8317", 60_000);
mod.startMonitoring("tool-b", "http://127.0.0.1:8317", 30_000);
assert.equal(mod.isMonitoring("tool-b"), true);
mod.stopMonitoring("tool-b");
});
it("should return false for non-monitored tool", () => {
assert.equal(mod.isMonitoring("nonexistent"), false);
});
it("should handle stopMonitoring for non-existent tool", () => {
assert.doesNotThrow(() => mod.stopMonitoring("ghost"));
});
});
});