import type { Systeminformation } from "systeminformation"; import { describe, expect, test } from "bun:test"; import type { RawTargetConfig } from "../../../../../src/server/checker/types"; import { MemoryChecker } from "../../../../../src/server/checker/runner/memory/execute"; function makeMemData(overrides: Partial = {}): Systeminformation.MemData { return { active: 4294967296, available: 8589934592, buffcache: 1073741824, buffers: 536870912, cached: 536870912, dirty: null, free: 4294967296, reclaimable: 0, slab: 0, swapfree: 0, swaptotal: 0, swapused: 0, total: 17179869184, used: 8589934592, writeback: null, ...overrides, }; } function makeResolveContext( overrides: Partial<{ configDir: string; defaultIntervalMs: number; defaultTimeoutMs: number }> = {}, ) { return { configDir: "/test", defaultIntervalMs: 30000, defaultTimeoutMs: 10000, ...overrides, }; } describe("MemoryChecker resolve", () => { const checker = new MemoryChecker(); test("默认值:memory 为空对象", () => { const target: RawTargetConfig = { id: "mem-test", memory: {}, type: "memory" }; const resolved = checker.resolve(target, makeResolveContext()); expect(resolved.memory).toEqual({}); }); test("无 expect 时 expect 为 undefined", () => { const target: RawTargetConfig = { id: "mem-test", memory: {}, type: "memory" }; const resolved = checker.resolve(target, makeResolveContext()); expect(resolved.expect).toBeUndefined(); }); test("保留 expect 字段", () => { const target: RawTargetConfig = { expect: { usagePercent: { lte: 85 } }, id: "mem-test", memory: {}, type: "memory", }; const resolved = checker.resolve(target, makeResolveContext()); expect(resolved.expect).toEqual({ usagePercent: { lte: 85 } }); }); test("type 为 memory", () => { const target: RawTargetConfig = { id: "mem-test", memory: {}, type: "memory" }; const resolved = checker.resolve(target, makeResolveContext()); expect(resolved.type).toBe("memory"); }); }); describe("MemoryChecker execute", () => { test("成功匹配", async () => { const data = makeMemData({ active: 4294967296, total: 8589934592 }); const reader = () => Promise.resolve(data); const checker = new MemoryChecker(reader); const target: RawTargetConfig = { id: "mem-test", memory: {}, type: "memory" }; const resolved = checker.resolve(target, makeResolveContext()); resolved.expect = { usagePercent: { lte: 85 } }; const ctx = { signal: new AbortController().signal }; const result = await checker.execute(resolved, ctx); expect(result.matched).toBe(true); expect(result.failure).toBeNull(); expect(result.observation).toMatchObject({ totalBytes: 8589934592, usagePercent: 50, }); }); test("usagePercent mismatch", async () => { const data = makeMemData({ active: 7730941132, total: 8589934592 }); const reader = () => Promise.resolve(data); const checker = new MemoryChecker(reader); const target: RawTargetConfig = { id: "mem-test", memory: {}, type: "memory" }; const resolved = checker.resolve(target, makeResolveContext()); resolved.expect = { usagePercent: { lte: 50 } }; const ctx = { signal: new AbortController().signal }; const result = await checker.execute(resolved, ctx); expect(result.matched).toBe(false); expect(result.failure?.phase).toBe("usage"); }); test("observation 包含所有字段", async () => { const data = makeMemData(); const reader = () => Promise.resolve(data); const checker = new MemoryChecker(reader); const target: RawTargetConfig = { id: "mem-test", memory: {}, type: "memory" }; const resolved = checker.resolve(target, makeResolveContext()); const ctx = { signal: new AbortController().signal }; const result = await checker.execute(resolved, ctx); const obs = result.observation!; expect(obs).toHaveProperty("activeBytes"); expect(obs).toHaveProperty("activePercent"); expect(obs).toHaveProperty("availableBytes"); expect(obs).toHaveProperty("availablePercent"); expect(obs).toHaveProperty("buffcacheBytes"); expect(obs).toHaveProperty("freeBytes"); expect(obs).toHaveProperty("freePercent"); expect(obs).toHaveProperty("swapFreeBytes"); expect(obs).toHaveProperty("swapTotalBytes"); expect(obs).toHaveProperty("swapUsagePercent"); expect(obs).toHaveProperty("swapUsedBytes"); expect(obs).toHaveProperty("totalBytes"); expect(obs).toHaveProperty("usagePercent"); expect(obs).toHaveProperty("usedBytes"); expect(obs).toHaveProperty("usedPercent"); }); test("reader reject 返回失败结果", async () => { const reader = () => Promise.reject(new Error("read error")); const checker = new MemoryChecker(reader); const target: RawTargetConfig = { id: "mem-test", memory: {}, type: "memory" }; const resolved = checker.resolve(target, makeResolveContext()); const ctx = { signal: new AbortController().signal }; const result = await checker.execute(resolved, ctx); expect(result.matched).toBe(false); expect(result.failure?.phase).toBe("memory"); expect(result.failure?.path).toBe("snapshot"); expect(result.observation).toBeNull(); }); test("detail 格式", async () => { const data = makeMemData({ active: 4294967296, total: 8589934592 }); const reader = () => Promise.resolve(data); const checker = new MemoryChecker(reader); const target: RawTargetConfig = { id: "mem-test", memory: {}, type: "memory" }; const resolved = checker.resolve(target, makeResolveContext()); const ctx = { signal: new AbortController().signal }; const result = await checker.execute(resolved, ctx); const detail = checker.buildDetail(result.observation!); expect(detail).toContain("usage"); expect(detail).toContain("%"); expect(detail).toContain("total"); }); }); describe("MemoryChecker serialize", () => { test("序列化输出", () => { const checker = new MemoryChecker(); const target: RawTargetConfig = { id: "mem-test", memory: {}, type: "memory" }; const resolved = checker.resolve(target, makeResolveContext()); const result = checker.serialize(resolved); expect(result.target).toBe("memory"); const config = JSON.parse(result.config) as Record; expect(config).toEqual({}); }); });