1
0

refactor: 将 memory checker 重命名为 mem

- 类型标识符 memory → mem
- 类名 MemoryChecker → MemChecker
- 内部类型名统一 Memory* → Mem*
- 内部函数名统一 *Memory* → *Mem*
- 目录重命名 memory/ → mem/(源码、测试、文档)
- 配置键 memory: → mem:
- 重新生成 probe-config.schema.json
- 保留中文"内存"用户提示

破坏性变更:无向后兼容
This commit is contained in:
2026-05-27 18:16:33 +08:00
parent 3390eb5e8d
commit 2f8fd8bd9c
20 changed files with 194 additions and 196 deletions

View File

@@ -0,0 +1,244 @@
import type { Systeminformation } from "systeminformation";
import { describe, expect, test } from "bun:test";
import type { RawTargetConfig } from "../../../../../src/server/checker/types";
import { MemChecker } from "../../../../../src/server/checker/runner/mem/execute";
function makeMemData(overrides: Partial<Systeminformation.MemData> = {}): 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("MemChecker resolve", () => {
const checker = new MemChecker();
test("默认值mem 为空对象", () => {
const target: RawTargetConfig = { id: "mem-test", mem: {}, type: "mem" };
const resolved = checker.resolve(target, makeResolveContext());
expect(resolved.mem).toEqual({});
});
test("无 expect 时 expect 为 undefined", () => {
const target: RawTargetConfig = { id: "mem-test", mem: {}, type: "mem" };
const resolved = checker.resolve(target, makeResolveContext());
expect(resolved.expect).toBeUndefined();
});
test("保留 expect 字段", () => {
const target: RawTargetConfig = {
expect: { usagePercent: { lte: 85 } },
id: "mem-test",
mem: {},
type: "mem",
};
const resolved = checker.resolve(target, makeResolveContext());
expect(resolved.expect).toEqual({ usagePercent: { lte: 85 } });
});
test("type 为 mem", () => {
const target: RawTargetConfig = { id: "mem-test", mem: {}, type: "mem" };
const resolved = checker.resolve(target, makeResolveContext());
expect(resolved.type).toBe("mem");
});
});
describe("MemChecker execute", () => {
test("成功匹配", async () => {
const data = makeMemData({ active: 4294967296, total: 8589934592 });
const reader = () => Promise.resolve(data);
const checker = new MemChecker(reader);
const target: RawTargetConfig = { id: "mem-test", mem: {}, type: "mem" };
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 MemChecker(reader);
const target: RawTargetConfig = { id: "mem-test", mem: {}, type: "mem" };
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 MemChecker(reader);
const target: RawTargetConfig = { id: "mem-test", mem: {}, type: "mem" };
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 MemChecker(reader);
const target: RawTargetConfig = { id: "mem-test", mem: {}, type: "mem" };
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("mem");
expect(result.failure?.path).toBe("snapshot");
expect(result.observation).toBeNull();
});
test("signal 已 abort 时返回 timeout failure", async () => {
const reader = () => Promise.resolve(makeMemData());
const checker = new MemChecker(reader);
const target: RawTargetConfig = { id: "mem-test", mem: {}, type: "mem" };
const resolved = checker.resolve(target, makeResolveContext());
const controller = new AbortController();
controller.abort();
const result = await checker.execute(resolved, { signal: controller.signal });
expect(result.matched).toBe(false);
expect(result.failure?.phase).toBe("mem");
expect(result.failure?.path).toBe("timeout");
expect(result.observation).toBeNull();
});
test("pending reader 被 signal abort 后返回 timeout failure", async () => {
const reader = () =>
new Promise<Systeminformation.MemData>(() => {
// 故意永不 resolve模拟悬挂的 reader
});
const checker = new MemChecker(reader);
const target: RawTargetConfig = { id: "mem-test", mem: {}, type: "mem" };
const resolved = checker.resolve(target, makeResolveContext());
const controller = new AbortController();
const executePromise = checker.execute(resolved, { signal: controller.signal });
controller.abort();
const result = await executePromise;
expect(result.matched).toBe(false);
expect(result.failure?.phase).toBe("mem");
expect(result.failure?.path).toBe("timeout");
expect(result.observation).toBeNull();
});
test("reader 在 abort 前 resolve 时返回正常结果", async () => {
const data = makeMemData({ active: 4294967296, total: 8589934592 });
const reader = () => Promise.resolve(data);
const checker = new MemChecker(reader);
const target: RawTargetConfig = { id: "mem-test", mem: {}, type: "mem" };
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("detail 格式", async () => {
const data = makeMemData({ active: 4294967296, total: 8589934592 });
const reader = () => Promise.resolve(data);
const checker = new MemChecker(reader);
const target: RawTargetConfig = { id: "mem-test", mem: {}, type: "mem" };
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("MemChecker serialize", () => {
test("序列化输出", () => {
const checker = new MemChecker();
const target: RawTargetConfig = { id: "mem-test", mem: {}, type: "mem" };
const resolved = checker.resolve(target, makeResolveContext());
const result = checker.serialize(resolved);
expect(result.target).toBe("mem");
const config = JSON.parse(result.config) as Record<string, unknown>;
expect(config).toEqual({});
});
});