feat: ValueMatcher 支持 primitive 原始值简写,等价于 { equals: value }
This commit is contained in:
@@ -49,13 +49,13 @@ describe("validateDbConfig", () => {
|
||||
expect(unknownError!.code).toBe("unknown-field");
|
||||
});
|
||||
|
||||
test("expect.durationMs 非 matcher 返回错误", () => {
|
||||
test("expect.durationMs 数组简写返回错误", () => {
|
||||
const result = validateDbConfig({
|
||||
defaults: {},
|
||||
targets: [
|
||||
{
|
||||
db: { url: "sqlite://:memory:" },
|
||||
expect: { durationMs: "invalid" },
|
||||
expect: { durationMs: [1, 2] },
|
||||
id: "test",
|
||||
name: "test",
|
||||
type: "db",
|
||||
|
||||
@@ -48,19 +48,19 @@ describe("validatePingConfig", () => {
|
||||
expect(issues.some((item) => item.path.endsWith("expect.maxPacketLoss"))).toBe(true);
|
||||
});
|
||||
|
||||
test("durationMs 类型非法", () => {
|
||||
const issues = validate({ expect: { durationMs: -1 }, id: "ping", ping: { host: "127.0.0.1" }, type: "ping" });
|
||||
test("durationMs 数组简写非法", () => {
|
||||
const issues = validate({ expect: { durationMs: [1, 2] }, id: "ping", ping: { host: "127.0.0.1" }, type: "ping" });
|
||||
expect(issues.some((item) => item.path.endsWith("expect.durationMs"))).toBe(true);
|
||||
});
|
||||
|
||||
test("avgLatencyMs 类型非法", () => {
|
||||
test("avgLatencyMs 对象简写非法", () => {
|
||||
const issues = validate({
|
||||
expect: { avgLatencyMs: "slow" },
|
||||
expect: { avgLatencyMs: { foo: "bar" } },
|
||||
id: "ping",
|
||||
ping: { host: "127.0.0.1" },
|
||||
type: "ping",
|
||||
});
|
||||
expect(issues.some((item) => item.path.endsWith("expect.avgLatencyMs"))).toBe(true);
|
||||
expect(issues.some((item) => item.path.endsWith("expect.avgLatencyMs.foo"))).toBe(true);
|
||||
});
|
||||
|
||||
test("host 为空字符串", () => {
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
|
||||
import type { CheckerValidationInput } from "../../../../../src/server/checker/runner/types";
|
||||
|
||||
import { validateCommandConfig } from "../../../../../src/server/checker/runner/cmd/validate";
|
||||
import { validateDbConfig } from "../../../../../src/server/checker/runner/db/validate";
|
||||
import { validateHttpConfig } from "../../../../../src/server/checker/runner/http/validate";
|
||||
import { validatePingConfig } from "../../../../../src/server/checker/runner/icmp/validate";
|
||||
import { validateLlmConfig } from "../../../../../src/server/checker/runner/llm/validate";
|
||||
import { validateTcpConfig } from "../../../../../src/server/checker/runner/tcp/validate";
|
||||
import { validateUdpConfig } from "../../../../../src/server/checker/runner/udp/validate";
|
||||
|
||||
function input(target: Record<string, unknown>): CheckerValidationInput {
|
||||
return { defaults: {}, targets: [target as CheckerValidationInput["targets"][number]] };
|
||||
}
|
||||
|
||||
describe("ValueMatcher primitive shorthand in checker validators", () => {
|
||||
test("normalizes shorthand for all checker ValueMatcher fields", () => {
|
||||
const targets = [
|
||||
{
|
||||
expect: { durationMs: 100 },
|
||||
http: { url: "https://example.com" },
|
||||
id: "http",
|
||||
type: "http",
|
||||
validate: validateHttpConfig,
|
||||
},
|
||||
{
|
||||
expect: { durationMs: 100 },
|
||||
id: "tcp",
|
||||
tcp: { host: "127.0.0.1", port: 80 },
|
||||
type: "tcp",
|
||||
validate: validateTcpConfig,
|
||||
},
|
||||
{
|
||||
expect: { durationMs: 100, responseSize: 1, sourceHost: "127.0.0.1", sourcePort: 53 },
|
||||
id: "udp",
|
||||
type: "udp",
|
||||
udp: { host: "127.0.0.1", port: 53 },
|
||||
validate: validateUdpConfig,
|
||||
},
|
||||
{
|
||||
expect: { avgLatencyMs: 1, durationMs: 100, maxLatencyMs: 2, packetLossPercent: 0 },
|
||||
id: "ping",
|
||||
ping: { host: "127.0.0.1" },
|
||||
type: "ping",
|
||||
validate: validatePingConfig,
|
||||
},
|
||||
{
|
||||
cmd: { exec: "true" },
|
||||
expect: { durationMs: 100 },
|
||||
id: "cmd",
|
||||
type: "cmd",
|
||||
validate: validateCommandConfig,
|
||||
},
|
||||
{
|
||||
db: { url: "sqlite://:memory:" },
|
||||
expect: { durationMs: 100, rowCount: 1 },
|
||||
id: "db",
|
||||
type: "db",
|
||||
validate: validateDbConfig,
|
||||
},
|
||||
{
|
||||
expect: {
|
||||
durationMs: 100,
|
||||
finishReason: "stop",
|
||||
rawFinishReason: null,
|
||||
stream: { firstTokenMs: 10 },
|
||||
usage: { inputTokens: 1, outputTokens: 2, totalTokens: 3 },
|
||||
},
|
||||
id: "llm",
|
||||
llm: {
|
||||
mode: "stream",
|
||||
model: "test-model",
|
||||
prompt: "hello",
|
||||
provider: "openai",
|
||||
url: "https://example.com/v1/chat/completions",
|
||||
},
|
||||
type: "llm",
|
||||
validate: validateLlmConfig,
|
||||
},
|
||||
];
|
||||
|
||||
for (const target of targets) {
|
||||
const { validate, ...config } = target;
|
||||
|
||||
expect(validate(input(config))).toHaveLength(0);
|
||||
expect((config.expect as Record<string, unknown>)["durationMs"]).toEqual({ equals: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
test("rejects array and object shorthand while accepting explicit equals", () => {
|
||||
const arrayTarget = {
|
||||
expect: { durationMs: [1, 2] },
|
||||
http: { url: "https://example.com" },
|
||||
id: "array",
|
||||
type: "http",
|
||||
};
|
||||
const objectTarget = {
|
||||
expect: { durationMs: { foo: "bar" } },
|
||||
http: { url: "https://example.com" },
|
||||
id: "object",
|
||||
type: "http",
|
||||
};
|
||||
const equalsObjectTarget = {
|
||||
expect: { durationMs: { equals: { foo: "bar" } } },
|
||||
http: { url: "https://example.com" },
|
||||
id: "equals-object",
|
||||
type: "http",
|
||||
};
|
||||
|
||||
expect(validateHttpConfig(input(arrayTarget)).some((issue) => issue.path.includes("durationMs"))).toBe(true);
|
||||
expect(validateHttpConfig(input(objectTarget)).some((issue) => issue.code === "unknown-matcher")).toBe(true);
|
||||
expect(validateHttpConfig(input(equalsObjectTarget))).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
@@ -106,18 +106,18 @@ describe("validateTcpConfig", () => {
|
||||
expect(issues.some((i) => i.path.includes("connected"))).toBe(true);
|
||||
});
|
||||
|
||||
test("expect durationMs 非 matcher", () => {
|
||||
test("expect durationMs 数组简写非法", () => {
|
||||
const issues = validateTcpConfig(
|
||||
makeInput([
|
||||
{
|
||||
expect: { durationMs: "slow" },
|
||||
expect: { durationMs: [1, 2] },
|
||||
id: "t1",
|
||||
tcp: { host: "127.0.0.1", port: 80 },
|
||||
type: "tcp",
|
||||
},
|
||||
]),
|
||||
);
|
||||
expect(issues.some((i) => i.path.includes("durationMs"))).toBe(true);
|
||||
expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("durationMs"))).toBe(true);
|
||||
});
|
||||
|
||||
test("expect 未知字段", () => {
|
||||
|
||||
@@ -213,12 +213,12 @@ describe("validateUdpConfig", () => {
|
||||
expect(issues.some((i) => i.path.includes("responded") && i.message.includes("响应来源"))).toBe(true);
|
||||
});
|
||||
|
||||
it("reports invalid-type for non-matcher expect.durationMs", () => {
|
||||
it("reports invalid-type for array shorthand expect.durationMs", () => {
|
||||
const issues = validateUdpConfig(
|
||||
makeInput({
|
||||
targets: [
|
||||
{
|
||||
expect: { durationMs: -100 },
|
||||
expect: { durationMs: [1, 2] },
|
||||
id: "test",
|
||||
type: "udp",
|
||||
udp: { host: "127.0.0.1", port: 53 },
|
||||
|
||||
Reference in New Issue
Block a user