import { beforeAll, afterAll, describe, expect, test } from "bun:test"; import { loadConfig, parseDuration } from "../../../src/server/checker/config-loader"; import { readRuntimeConfig } from "../../../src/server/config"; import { mkdir, rm, writeFile } from "node:fs/promises"; import { join } from "node:path"; import { tmpdir } from "node:os"; describe("parseDuration", () => { test("解析秒", () => { expect(parseDuration("30s")).toBe(30000); expect(parseDuration("1s")).toBe(1000); }); test("解析分钟", () => { expect(parseDuration("5m")).toBe(300000); expect(parseDuration("1m")).toBe(60000); }); test("解析毫秒", () => { expect(parseDuration("500ms")).toBe(500); expect(parseDuration("100ms")).toBe(100); }); test("解析小数", () => { expect(parseDuration("1.5s")).toBe(1500); }); test("无效格式抛出错误", () => { expect(() => parseDuration("30")).toThrow("无效的时长格式"); expect(() => parseDuration("abc")).toThrow("无效的时长格式"); expect(() => parseDuration("30x")).toThrow("无效的时长格式"); expect(() => parseDuration("")).toThrow("无效的时长格式"); }); }); describe("readRuntimeConfig", () => { test("返回配置文件路径", () => { expect(readRuntimeConfig(["./probes.yaml"])).toEqual({ configPath: "./probes.yaml" }); }); test("未提供参数抛出错误", () => { expect(() => readRuntimeConfig([])).toThrow("需要指定 YAML 配置文件路径"); }); }); describe("loadConfig", () => { let tempDir: string; beforeAll(async () => { tempDir = join(tmpdir(), `gc-test-${Date.now()}`); await mkdir(tempDir, { recursive: true }); }); afterAll(async () => { await rm(tempDir, { recursive: true, force: true }); }); test("解析完整配置", async () => { const configPath = join(tempDir, "full.yaml"); await writeFile( configPath, `server: host: "0.0.0.0" port: 8080 dataDir: "./my-data" defaults: interval: "15s" timeout: "5s" method: "POST" targets: - name: "test" url: "http://example.com" `, ); const config = await loadConfig(configPath); expect(config.host).toBe("0.0.0.0"); expect(config.port).toBe(8080); expect(config.dataDir).toBe("./my-data"); expect(config.targets).toHaveLength(1); expect(config.targets[0]).toEqual({ name: "test", url: "http://example.com", method: "POST", headers: {}, body: undefined, intervalMs: 15000, timeoutMs: 5000, expect: undefined, }); }); test("解析最简配置(只有 targets)", async () => { const configPath = join(tempDir, "minimal.yaml"); await writeFile( configPath, `targets: - name: "t1" url: "http://a.com" - name: "t2" url: "http://b.com" interval: "1m" `, ); const config = await loadConfig(configPath); expect(config.host).toBe("127.0.0.1"); expect(config.port).toBe(3000); expect(config.dataDir).toBe("./data"); expect(config.targets).toHaveLength(2); expect(config.targets[0]!.intervalMs).toBe(30000); expect(config.targets[1]!.intervalMs).toBe(60000); }); test("per-target 覆盖 defaults", async () => { const configPath = join(tempDir, "override.yaml"); await writeFile( configPath, `defaults: interval: "30s" timeout: "10s" method: "GET" headers: Authorization: "Bearer token" targets: - name: "override-all" url: "http://example.com" method: "POST" interval: "5m" timeout: "30s" headers: X-Custom: "value" `, ); const config = await loadConfig(configPath); const target = config.targets[0]!; expect(target.method).toBe("POST"); expect(target.intervalMs).toBe(300000); expect(target.timeoutMs).toBe(30000); expect(target.headers).toEqual({ Authorization: "Bearer token", "X-Custom": "value" }); }); test("配置文件不存在抛出错误", async () => { await expect(loadConfig("/nonexistent/file.yaml")).rejects.toThrow("配置文件不存在"); }); test("target 缺少 name 抛出错误", async () => { const configPath = join(tempDir, "no-name.yaml"); await writeFile( configPath, `targets: - url: "http://example.com" `, ); await expect(loadConfig(configPath)).rejects.toThrow("缺少 name 字段"); }); test("target 缺少 url 抛出错误", async () => { const configPath = join(tempDir, "no-url.yaml"); await writeFile( configPath, `targets: - name: "test" `, ); await expect(loadConfig(configPath)).rejects.toThrow("缺少 url 字段"); }); test("target name 重复抛出错误", async () => { const configPath = join(tempDir, "dup-name.yaml"); await writeFile( configPath, `targets: - name: "dup" url: "http://a.com" - name: "dup" url: "http://b.com" `, ); await expect(loadConfig(configPath)).rejects.toThrow("target name 重复"); }); test("targets 为空数组抛出错误", async () => { const configPath = join(tempDir, "empty-targets.yaml"); await writeFile(configPath, `targets: []`); await expect(loadConfig(configPath)).rejects.toThrow("至少一个 target"); }); test("无效端口号抛出错误", async () => { const configPath = join(tempDir, "bad-port.yaml"); await writeFile( configPath, `server: port: 99999 targets: - name: "t" url: "http://a.com" `, ); await expect(loadConfig(configPath)).rejects.toThrow("无效端口号"); }); test("解析 expect 配置", async () => { const configPath = join(tempDir, "expect.yaml"); await writeFile( configPath, `targets: - name: "with-expect" url: "http://example.com" expect: status: [200, 201] body: contains: "ok" maxLatencyMs: 3000 `, ); const config = await loadConfig(configPath); expect(config.targets[0]!.expect).toEqual({ status: [200, 201], body: { contains: "ok" }, maxLatencyMs: 3000, }); }); });