import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { ProbeStore } from "../../../src/server/checker/store"; import type { ResolvedTarget } from "../../../src/server/checker/types"; import { mkdir, rm } from "node:fs/promises"; import { join } from "node:path"; import { tmpdir } from "node:os"; describe("ProbeStore", () => { let tempDir: string; let store: ProbeStore; const target1: ResolvedTarget = { name: "test-a", url: "http://a.com", method: "GET", headers: {}, intervalMs: 30000, timeoutMs: 10000, }; const target2: ResolvedTarget = { name: "test-b", url: "http://b.com", method: "POST", headers: { "Content-Type": "application/json" }, body: '{"ping": true}', intervalMs: 60000, timeoutMs: 5000, expect: { status: [200], maxLatencyMs: 3000 }, }; beforeAll(async () => { tempDir = join(tmpdir(), `gc-store-test-${Date.now()}`); await mkdir(tempDir, { recursive: true }); store = new ProbeStore(join(tempDir, "test.db")); }); afterAll(async () => { store.close(); await rm(tempDir, { recursive: true, force: true }); }); test("初始化后无 targets", () => { expect(store.getTargets()).toHaveLength(0); }); test("同步新增 targets", () => { store.syncTargets([target1, target2]); const targets = store.getTargets(); expect(targets).toHaveLength(2); expect(targets[0]!.name).toBe("test-a"); expect(targets[1]!.name).toBe("test-b"); }); test("同步后 target 字段正确", () => { const targets = store.getTargets(); const t2 = targets.find((t) => t.name === "test-b")!; expect(t2.url).toBe("http://b.com"); expect(t2.method).toBe("POST"); expect(JSON.parse(t2.headers)).toEqual({ "Content-Type": "application/json" }); expect(t2.body).toBe('{"ping": true}'); expect(t2.interval_ms).toBe(60000); expect(t2.expect).toBe(JSON.stringify({ status: [200], maxLatencyMs: 3000 })); }); test("同步更新已有 target", () => { store.syncTargets([{ ...target1, url: "http://a-v2.com" }, target2]); const targets = store.getTargets(); const t1 = targets.find((t) => t.name === "test-a")!; expect(t1.url).toBe("http://a-v2.com"); expect(targets).toHaveLength(2); }); test("同步删除 target", () => { store.syncTargets([target1]); const targets = store.getTargets(); expect(targets).toHaveLength(1); expect(targets[0]!.name).toBe("test-a"); }); test("重新同步回来", () => { store.syncTargets([target1, target2]); expect(store.getTargets()).toHaveLength(2); }); test("getTargetById", () => { const targets = store.getTargets(); const found = store.getTargetById(targets[0]!.id); expect(found).toBeDefined(); expect(found!.name).toBe("test-a"); }); test("getTargetById 不存在", () => { expect(store.getTargetById(99999)).toBeNull(); }); test("写入和查询 check result", () => { const targets = store.getTargets(); const t1Id = targets[0]!.id; store.insertCheckResult({ targetId: t1Id, timestamp: "2025-01-01T00:00:00.000Z", success: true, statusCode: 200, latencyMs: 150, error: null, matched: true, }); store.insertCheckResult({ targetId: t1Id, timestamp: "2025-01-01T00:00:30.000Z", success: true, statusCode: 200, latencyMs: 300, error: null, matched: true, }); store.insertCheckResult({ targetId: t1Id, timestamp: "2025-01-01T00:01:00.000Z", success: false, statusCode: null, latencyMs: null, error: "timeout", matched: false, }); const history = store.getHistory(t1Id, 10); expect(history).toHaveLength(3); expect(history[0]!.timestamp).toBe("2025-01-01T00:01:00.000Z"); const latest = store.getLatestCheck(t1Id)!; expect(latest.success).toBe(0); expect(latest.error).toBe("timeout"); }); test("getHistory 默认 limit=20", () => { const targets = store.getTargets(); const t1Id = targets[0]!.id; for (let i = 0; i < 25; i++) { store.insertCheckResult({ targetId: t1Id, timestamp: `2025-01-01T00:${String(i).padStart(2, "0")}:00.000Z`, success: true, statusCode: 200, latencyMs: 100 + i, error: null, matched: true, }); } const history = store.getHistory(t1Id); expect(history).toHaveLength(20); }); test("getTargetStats 计算可用率和延迟", () => { const targets = store.getTargets(); const t1Id = targets[0]!.id; const stats = store.getTargetStats(t1Id); expect(stats.totalChecks).toBeGreaterThan(0); expect(stats.availability).toBeGreaterThanOrEqual(0); expect(stats.availability).toBeLessThanOrEqual(100); expect(stats.avgLatencyMs).not.toBeNull(); }); test("无记录目标的 stats", () => { const targets = store.getTargets(); const t2Id = targets.find((t) => t.name === "test-b")!.id; const stats = store.getTargetStats(t2Id); expect(stats.totalChecks).toBe(0); expect(stats.availability).toBe(0); expect(stats.avgLatencyMs).toBeNull(); }); test("getSummary 返回总览统计", () => { const summary = store.getSummary(); expect(summary.total).toBe(2); expect(summary.up + summary.down).toBe(2); expect(summary.lastCheckTime).not.toBeNull(); }); test("getTrend 返回趋势数据", () => { const targets = store.getTargets(); const t1Id = targets[0]!.id; const trend = store.getTrend(t1Id, 24); expect(Array.isArray(trend)).toBe(true); }); });