202 lines
5.7 KiB
TypeScript
202 lines
5.7 KiB
TypeScript
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
|
|
import type { CheckerContext } from "../../../../../src/server/checker/runner/types";
|
|
import type { ResolvedWsTarget } from "../../../../../src/server/checker/runner/ws/types";
|
|
|
|
import { WsChecker } from "../../../../../src/server/checker/runner/ws/execute";
|
|
|
|
function createEchoServer() {
|
|
return Bun.serve({
|
|
fetch(req, server) {
|
|
const success = server.upgrade(req);
|
|
if (!success) return new Response("Upgrade failed", { status: 500 });
|
|
return undefined;
|
|
},
|
|
port: 0,
|
|
websocket: {
|
|
close() {
|
|
/* ws close */
|
|
},
|
|
message(ws, message) {
|
|
ws.send(message);
|
|
},
|
|
open() {
|
|
/* ws open */
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
function createNoReplyServer() {
|
|
return Bun.serve({
|
|
fetch(req, server) {
|
|
const success = server.upgrade(req);
|
|
if (!success) return new Response("Upgrade failed", { status: 500 });
|
|
return undefined;
|
|
},
|
|
port: 0,
|
|
websocket: {
|
|
close() {
|
|
/* ws close */
|
|
},
|
|
message() {
|
|
/* no reply */
|
|
},
|
|
open() {
|
|
/* ws open */
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
function createRejectServer() {
|
|
return Bun.serve({
|
|
fetch() {
|
|
return new Response("Forbidden", { status: 403 });
|
|
},
|
|
port: 0,
|
|
});
|
|
}
|
|
|
|
function makeContext(overrides?: Partial<CheckerContext>): CheckerContext {
|
|
return {
|
|
signal: AbortSignal.timeout(15000),
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function makeWsTarget(overrides?: Partial<ResolvedWsTarget>): ResolvedWsTarget {
|
|
return {
|
|
description: null,
|
|
expect: { connected: true },
|
|
group: "default",
|
|
id: "test-ws",
|
|
intervalMs: 30000,
|
|
name: null,
|
|
timeoutMs: 10000,
|
|
type: "ws",
|
|
ws: {
|
|
headers: {},
|
|
ignoreSSL: false,
|
|
maxMessageBytes: 4096,
|
|
receiveTimeout: 5000,
|
|
subprotocols: [],
|
|
url: "ws://127.0.0.1:19999/ws",
|
|
},
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
let echoServer: ReturnType<typeof createEchoServer>;
|
|
let noReplyServer: ReturnType<typeof createNoReplyServer>;
|
|
let rejectServer: ReturnType<typeof createRejectServer>;
|
|
|
|
beforeAll(() => {
|
|
echoServer = createEchoServer();
|
|
noReplyServer = createNoReplyServer();
|
|
rejectServer = createRejectServer();
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await echoServer.stop();
|
|
await noReplyServer.stop();
|
|
await rejectServer.stop();
|
|
});
|
|
|
|
describe("WsChecker execute", () => {
|
|
const checker = new WsChecker();
|
|
|
|
test("可达性检查 - 连接成功", async () => {
|
|
const target = makeWsTarget({ ws: { ...makeWsTarget().ws, url: `ws://127.0.0.1:${echoServer.port}` } });
|
|
const result = await checker.execute(target, makeContext());
|
|
expect(result.matched).toBe(true);
|
|
expect(result.failure).toBeNull();
|
|
expect(result.observation!["connected"]).toBe(true);
|
|
});
|
|
|
|
test("可达性检查 - 连接失败", async () => {
|
|
const target = makeWsTarget({ ws: { ...makeWsTarget().ws, url: "ws://127.0.0.1:1" } });
|
|
const result = await checker.execute(target, makeContext());
|
|
expect(result.matched).toBe(false);
|
|
expect(result.failure).not.toBeNull();
|
|
expect(result.observation!["connected"]).toBe(false);
|
|
});
|
|
|
|
test("可达性检查 - 连接失败但 expect.connected=false", async () => {
|
|
const target = makeWsTarget({
|
|
expect: { connected: false },
|
|
ws: { ...makeWsTarget().ws, url: "ws://127.0.0.1:1" },
|
|
});
|
|
const result = await checker.execute(target, makeContext());
|
|
expect(result.matched).toBe(true);
|
|
expect(result.failure).toBeNull();
|
|
});
|
|
|
|
test("交互模式 - 发送消息并收到响应", async () => {
|
|
const target = makeWsTarget({
|
|
expect: {
|
|
connected: true,
|
|
message: [{ kind: "value" as const, matcher: { equals: "ping" } }],
|
|
},
|
|
ws: {
|
|
...makeWsTarget().ws,
|
|
send: "ping",
|
|
url: `ws://127.0.0.1:${echoServer.port}`,
|
|
},
|
|
});
|
|
const result = await checker.execute(target, makeContext());
|
|
expect(result.matched).toBe(true);
|
|
expect(result.observation!["message"]).toBe("ping");
|
|
expect(result.observation!["messageSize"]).toBe(4);
|
|
});
|
|
|
|
test("交互模式 - 消息不匹配", async () => {
|
|
const target = makeWsTarget({
|
|
expect: {
|
|
connected: true,
|
|
message: [{ kind: "value" as const, matcher: { equals: "pong" } }],
|
|
},
|
|
ws: {
|
|
...makeWsTarget().ws,
|
|
send: "ping",
|
|
url: `ws://127.0.0.1:${echoServer.port}`,
|
|
},
|
|
});
|
|
const result = await checker.execute(target, makeContext());
|
|
expect(result.matched).toBe(false);
|
|
expect(result.failure).not.toBeNull();
|
|
});
|
|
|
|
test("交互模式 - receiveTimeout 超时", async () => {
|
|
const target = makeWsTarget({
|
|
ws: {
|
|
...makeWsTarget().ws,
|
|
receiveTimeout: 500,
|
|
send: "ping",
|
|
url: `ws://127.0.0.1:${noReplyServer.port}`,
|
|
},
|
|
});
|
|
const result = await checker.execute(target, makeContext());
|
|
expect(result.matched).toBe(false);
|
|
expect(result.failure?.phase).toBe("message");
|
|
});
|
|
|
|
test("HTTP 403 握手失败", async () => {
|
|
const target = makeWsTarget({ ws: { ...makeWsTarget().ws, url: `ws://127.0.0.1:${rejectServer.port}` } });
|
|
const result = await checker.execute(target, makeContext());
|
|
expect(result.matched).toBe(false);
|
|
expect(result.observation!["connected"]).toBe(false);
|
|
});
|
|
|
|
test("buildDetail 连接成功", () => {
|
|
const detail = checker.buildDetail({ connected: true, connectTimeMs: 50, message: "hello" });
|
|
expect(detail).toContain("connected");
|
|
expect(detail).toContain("hello");
|
|
});
|
|
|
|
test("buildDetail 连接失败", () => {
|
|
const detail = checker.buildDetail({ connected: false, error: "connection refused" });
|
|
expect(detail).toContain("connection failed");
|
|
});
|
|
});
|