1
0

feat: WS checker,支持可达性检测和单次请求-响应交互验证

This commit is contained in:
2026-05-25 14:13:43 +08:00
parent 714b635aef
commit c1db793073
20 changed files with 2339 additions and 4 deletions

View File

@@ -0,0 +1,155 @@
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { mkdir, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import type { ResolvedWsTarget } from "../../../../../src/server/checker/runner/ws/types";
import { loadConfig } from "../../../../../src/server/checker/config-loader";
describe("loadConfig with ws checker", () => {
let tempDir: string;
beforeAll(async () => {
tempDir = join(tmpdir(), `ws-cfg-test-${Date.now()}`);
await mkdir(tempDir, { recursive: true });
});
afterAll(async () => {
await rm(tempDir, { force: true, recursive: true });
});
test("解析最简 ws 配置", async () => {
const configPath = join(tempDir, "minimal-ws.yaml");
await writeFile(
configPath,
`targets:
- id: "ws-test"
type: ws
ws:
url: "ws://example.com/ws"
`,
);
const config = await loadConfig(configPath);
expect(config.targets).toHaveLength(1);
const t = config.targets[0]! as ResolvedWsTarget;
expect(t.type).toBe("ws");
expect(t.id).toBe("ws-test");
expect(t.ws.url).toBe("ws://example.com/ws");
expect(t.ws.headers).toEqual({});
expect(t.ws.ignoreSSL).toBe(false);
expect(t.ws.maxMessageBytes).toBe(4096);
expect(t.ws.receiveTimeout).toBe(5000);
expect(t.ws.send).toBeUndefined();
expect(t.ws.subprotocols).toEqual([]);
expect(t.expect).toEqual({ connected: true });
});
test("解析带 send 的 ws 配置", async () => {
const configPath = join(tempDir, "ws-send.yaml");
await writeFile(
configPath,
`targets:
- id: "ws-echo"
name: "WS Echo 检查"
type: ws
ws:
url: "wss://api.example.com/ws"
headers:
Authorization: "Bearer token"
subprotocols:
- "json"
ignoreSSL: true
send: "ping"
receiveTimeout: 3000
maxMessageBytes: "8KB"
expect:
message:
- contains: "pong"
durationMs:
lte: 5000
`,
);
const config = await loadConfig(configPath);
expect(config.targets).toHaveLength(1);
const t = config.targets[0]! as ResolvedWsTarget;
expect(t.type).toBe("ws");
expect(t.ws.url).toBe("wss://api.example.com/ws");
expect(t.ws.headers).toEqual({ Authorization: "Bearer token" });
expect(t.ws.ignoreSSL).toBe(true);
expect(t.ws.maxMessageBytes).toBe(8192);
expect(t.ws.receiveTimeout).toBe(3000);
expect(t.ws.send).toBe("ping");
expect(t.ws.subprotocols).toEqual(["json"]);
expect(t.expect?.connected).toBe(true);
expect(t.expect?.message).toBeDefined();
expect(t.expect?.durationMs).toEqual({ lte: 5000 });
});
test("ws 缺少 url 抛出错误", async () => {
const configPath = join(tempDir, "ws-no-url.yaml");
await writeFile(
configPath,
`targets:
- id: "t"
type: ws
ws: {}
`,
);
let error: unknown;
try {
await loadConfig(configPath);
} catch (caught) {
error = caught;
}
expect(error).toBeInstanceOf(Error);
expect((error as Error).message).toContain("ws.url");
});
test("ws url 非 ws/wss 协议抛出错误", async () => {
const configPath = join(tempDir, "ws-bad-url.yaml");
await writeFile(
configPath,
`targets:
- id: "t"
type: ws
ws:
url: "http://example.com"
`,
);
let error: unknown;
try {
await loadConfig(configPath);
} catch (caught) {
error = caught;
}
expect(error).toBeInstanceOf(Error);
expect((error as Error).message).toContain("ws:// 或 wss://");
});
test("ws expect.message 未配置 send 抛出错误", async () => {
const configPath = join(tempDir, "ws-no-send.yaml");
await writeFile(
configPath,
`targets:
- id: "t"
type: ws
ws:
url: "ws://example.com"
expect:
message:
- contains: "pong"
`,
);
let error: unknown;
try {
await loadConfig(configPath);
} catch (caught) {
error = caught;
}
expect(error).toBeInstanceOf(Error);
expect((error as Error).message).toContain("send");
});
});