1
0
Files
DiAL/tests/server/checker/runner/dns/validate.test.ts

474 lines
16 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from "bun:test";
import type { CheckerValidationInput } from "../../../../../src/server/checker/runner/types";
import { validateDnsConfig } from "../../../../../src/server/checker/runner/dns/validate";
function makeInput(overrides: { targets?: Array<Record<string, unknown>> }): CheckerValidationInput {
return { targets: (overrides.targets ?? []) as CheckerValidationInput["targets"] };
}
describe("validateDnsConfig", () => {
it("接受合法的 system 目标resolver/name/family", () => {
const issues = validateDnsConfig(
makeInput({
targets: [{ dns: { family: "ipv4", name: "example.com", resolver: "system" }, id: "t1", type: "dns" }],
}),
);
expect(issues).toHaveLength(0);
});
it("接受合法的 server 目标resolver/name/server/port/recordType", () => {
const issues = validateDnsConfig(
makeInput({
targets: [
{
dns: {
name: "example.com",
port: 53,
protocol: "udp",
recordType: "A",
resolver: "server",
server: "8.8.8.8",
},
id: "t2",
type: "dns",
},
],
}),
);
expect(issues).toHaveLength(0);
});
it("拒绝缺少 dns 配置分组", () => {
const issues = validateDnsConfig(makeInput({ targets: [{ id: "t3", type: "dns" }] }));
expect(issues).toHaveLength(1);
expect(issues[0]!.code).toBe("required");
expect(issues[0]!.path).toContain("dns");
expect(issues[0]!.message).toContain("dns");
});
it("拒绝缺少 dns.resolver", () => {
const issues = validateDnsConfig(makeInput({ targets: [{ dns: { name: "example.com" }, id: "t4", type: "dns" }] }));
expect(issues).toHaveLength(1);
expect(issues[0]!.code).toBe("required");
expect(issues[0]!.path).toContain("resolver");
});
it("拒绝无效的 dns.resolver 值", () => {
const issues = validateDnsConfig(
makeInput({ targets: [{ dns: { name: "example.com", resolver: "unknown" }, id: "t5", type: "dns" }] }),
);
expect(issues).toHaveLength(1);
expect(issues[0]!.code).toBe("invalid-value");
expect(issues[0]!.path).toContain("resolver");
expect(issues[0]!.message).toContain("system");
expect(issues[0]!.message).toContain("server");
});
it("拒绝缺少 dns.name", () => {
const issues = validateDnsConfig(makeInput({ targets: [{ dns: { resolver: "system" }, id: "t6", type: "dns" }] }));
expect(issues.length).toBeGreaterThanOrEqual(1);
expect(issues.some((i) => i.code === "required" && i.path.includes("name"))).toBe(true);
});
it("System 模式:拒绝空白 dns.name", () => {
const issues = validateDnsConfig(
makeInput({ targets: [{ dns: { name: " ", resolver: "system" }, id: "t7", type: "dns" }] }),
);
expect(issues.some((i) => i.code === "required" && i.path.includes("name"))).toBe(true);
});
it("System 模式:接受 family any/ipv4/ipv6拒绝无效 family", () => {
for (const family of ["any", "ipv4", "ipv6"]) {
const issues = validateDnsConfig(
makeInput({ targets: [{ dns: { family, name: "x.com", resolver: "system" }, id: "tf", type: "dns" }] }),
);
expect(issues).toHaveLength(0);
}
const issues = validateDnsConfig(
makeInput({ targets: [{ dns: { family: "ipx", name: "x.com", resolver: "system" }, id: "tf", type: "dns" }] }),
);
expect(issues.some((i) => i.code === "invalid-value" && i.path.includes("family"))).toBe(true);
});
it("System 模式:拒绝 dns 中的未知字段", () => {
const issues = validateDnsConfig(
makeInput({
targets: [{ dns: { bogus: true, name: "x.com", resolver: "system" }, id: "t8", type: "dns" }],
}),
);
expect(issues.some((i) => i.code === "unknown-field" && i.path.includes("bogus"))).toBe(true);
});
it("System 模式:拒绝 server 专用字段server/port/protocol/recordType", () => {
const issues = validateDnsConfig(
makeInput({
targets: [
{
dns: { name: "x.com", port: 53, protocol: "udp", recordType: "A", resolver: "system", server: "8.8.8.8" },
id: "t9",
type: "dns",
},
],
}),
);
expect(issues.some((i) => i.code === "unknown-field" && i.path.includes("server"))).toBe(true);
expect(issues.some((i) => i.code === "unknown-field" && i.path.includes("port"))).toBe(true);
expect(issues.some((i) => i.code === "unknown-field" && i.path.includes("protocol"))).toBe(true);
expect(issues.some((i) => i.code === "unknown-field" && i.path.includes("recordType"))).toBe(true);
});
it("Server 模式:拒绝缺少 dns.server", () => {
const issues = validateDnsConfig(
makeInput({ targets: [{ dns: { name: "x.com", resolver: "server" }, id: "t10", type: "dns" }] }),
);
expect(issues.some((i) => i.code === "required" && i.path.includes("server"))).toBe(true);
});
it("Server 模式:拒绝无效 port0、-1、65536、1.5", () => {
for (const port of [0, -1, 65536, 1.5]) {
const issues = validateDnsConfig(
makeInput({
targets: [{ dns: { name: "x.com", port, resolver: "server", server: "8.8.8.8" }, id: "tp", type: "dns" }],
}),
);
expect(issues.some((i) => i.code === "invalid-value" && i.path.includes("port"))).toBe(true);
}
});
it("Server 模式:接受 protocol udp/tcp拒绝无效值", () => {
for (const protocol of ["udp", "tcp"]) {
const issues = validateDnsConfig(
makeInput({
targets: [
{ dns: { name: "x.com", protocol, resolver: "server", server: "8.8.8.8" }, id: "tpr", type: "dns" },
],
}),
);
expect(issues).toHaveLength(0);
}
const issues = validateDnsConfig(
makeInput({
targets: [
{ dns: { name: "x.com", protocol: "http", resolver: "server", server: "8.8.8.8" }, id: "tpr", type: "dns" },
],
}),
);
expect(issues.some((i) => i.code === "invalid-value" && i.path.includes("protocol"))).toBe(true);
});
it("Server 模式:接受有效 recordType拒绝无效 recordType", () => {
const issues = validateDnsConfig(
makeInput({
targets: [
{ dns: { name: "x.com", recordType: "A", resolver: "server", server: "8.8.8.8" }, id: "trt", type: "dns" },
],
}),
);
expect(issues).toHaveLength(0);
const badIssues = validateDnsConfig(
makeInput({
targets: [
{ dns: { name: "x.com", recordType: "FAKE", resolver: "server", server: "8.8.8.8" }, id: "trt", type: "dns" },
],
}),
);
expect(badIssues.some((i) => i.code === "invalid-value" && i.path.includes("recordType"))).toBe(true);
});
it("Server 模式:接受有效 maxResponseBytes数字和字符串", () => {
const issues1 = validateDnsConfig(
makeInput({
targets: [
{
dns: { maxResponseBytes: 512, name: "x.com", resolver: "server", server: "8.8.8.8" },
id: "tmr",
type: "dns",
},
],
}),
);
expect(issues1).toHaveLength(0);
const issues2 = validateDnsConfig(
makeInput({
targets: [
{
dns: { maxResponseBytes: "1KB", name: "x.com", resolver: "server", server: "8.8.8.8" },
id: "tmr",
type: "dns",
},
],
}),
);
expect(issues2).toHaveLength(0);
});
it("Server 模式:拒绝无效 maxResponseBytes 字符串", () => {
const issues = validateDnsConfig(
makeInput({
targets: [
{
dns: { maxResponseBytes: "1kb", name: "x.com", resolver: "server", server: "8.8.8.8" },
id: "tmr-invalid",
type: "dns",
},
],
}),
);
expect(issues.some((i) => i.code === "invalid-value" && i.path.includes("maxResponseBytes"))).toBe(true);
});
it("Server 模式:拒绝 recursionDesired/tcpFallback 非布尔值", () => {
const issues = validateDnsConfig(
makeInput({
targets: [
{
dns: {
name: "x.com",
recursionDesired: "false",
resolver: "server",
server: "8.8.8.8",
tcpFallback: "true",
},
id: "tb-dns",
type: "dns",
},
],
}),
);
expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("recursionDesired"))).toBe(true);
expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("tcpFallback"))).toBe(true);
});
it("Server 模式:拒绝 dns 中的未知字段", () => {
const issues = validateDnsConfig(
makeInput({
targets: [
{ dns: { name: "x.com", resolver: "server", server: "8.8.8.8", unknown: true }, id: "t16", type: "dns" },
],
}),
);
expect(issues.some((i) => i.code === "unknown-field" && i.path.includes("unknown"))).toBe(true);
});
it("System 模式 expect拒绝 rcode/ttlMin/ttlMax/answerCount/authoritative/recursionAvailable/truncated/authenticatedData/result/responded → dns-unsupported-expect", () => {
const serverOnlyFields = [
"rcode",
"ttlMin",
"ttlMax",
"answerCount",
"authoritative",
"recursionAvailable",
"truncated",
"authenticatedData",
"result",
"responded",
];
for (const field of serverOnlyFields) {
const expectObj: Record<string, unknown> = {};
expectObj[field] = field === "rcode" ? ["NOERROR"] : field === "result" ? ["ok"] : true;
const issues = validateDnsConfig(
makeInput({
targets: [
{
dns: { name: "x.com", resolver: "system" },
expect: expectObj,
id: "tse",
name: "sys-target",
type: "dns",
},
],
}),
);
const matched = issues.find((i) => i.code === "dns-unsupported-expect" && i.path.includes(field));
expect(matched).toBeDefined();
expect(matched!.message).toContain("system");
}
});
it("System 模式 expect接受 values/valueCount/durationMs", () => {
const issues = validateDnsConfig(
makeInput({
targets: [
{
dns: { name: "x.com", resolver: "system" },
expect: { durationMs: { lte: 100 }, valueCount: { gte: 1 }, values: { exact: ["1.2.3.4"] } },
id: "t18",
type: "dns",
},
],
}),
);
expect(issues).toHaveLength(0);
});
it("Server 模式 expect接受所有 server expect 字段", () => {
const issues = validateDnsConfig(
makeInput({
targets: [
{
dns: { name: "x.com", resolver: "server", server: "8.8.8.8" },
expect: {
answerCount: { gte: 1 },
authenticatedData: false,
authoritative: false,
durationMs: { lte: 100 },
rcode: ["NOERROR"],
recursionAvailable: true,
responded: true,
result: [{ contains: "ok" }],
truncated: false,
ttlMax: { lte: 3600 },
ttlMin: { gte: 0 },
valueCount: { gte: 1 },
values: { exact: ["1.2.3.4"] },
},
id: "t19",
type: "dns",
},
],
}),
);
expect(issues).toHaveLength(0);
});
it("Server 模式 expectresponded=false 时拒绝协议级响应断言", () => {
const issues = validateDnsConfig(
makeInput({
targets: [
{
dns: { name: "x.com", resolver: "server", server: "8.8.8.8" },
expect: { rcode: ["NOERROR"], responded: false },
id: "trf",
type: "dns",
},
],
}),
);
expect(issues.some((i) => i.code === "invalid-value" && i.path.includes("responded"))).toBe(true);
});
it("Server 模式 expect拒绝未知 expect 字段", () => {
const issues = validateDnsConfig(
makeInput({
targets: [
{
dns: { name: "x.com", resolver: "server", server: "8.8.8.8" },
expect: { bogusField: 123 },
id: "t20",
type: "dns",
},
],
}),
);
expect(issues.some((i) => i.code === "unknown-field" && i.path.includes("bogusField"))).toBe(true);
});
it("验证 rcode 值必须为已知 RCODE", () => {
const issues = validateDnsConfig(
makeInput({
targets: [
{
dns: { name: "x.com", resolver: "server", server: "8.8.8.8" },
expect: { rcode: ["NOTAREALCODE"] },
id: "trc",
type: "dns",
},
],
}),
);
expect(issues.some((i) => i.code === "invalid-value" && i.path.includes("rcode"))).toBe(true);
});
it("验证布尔字段responded/authoritative 等)", () => {
const fields = ["responded", "authoritative", "recursionAvailable", "truncated", "authenticatedData"];
for (const field of fields) {
const expectObj: Record<string, unknown> = {};
expectObj[field] = "notbool";
const issues = validateDnsConfig(
makeInput({
targets: [
{
dns: { name: "x.com", resolver: "server", server: "8.8.8.8" },
expect: expectObj,
id: "tb",
type: "dns",
},
],
}),
);
expect(issues.some((i) => i.code === "invalid-type" && i.path.includes(field))).toBe(true);
}
});
it("验证 ValueExpectation 字段durationMs/valueCount/answerCount/ttlMin/ttlMax", () => {
const issues = validateDnsConfig(
makeInput({
targets: [
{
dns: { name: "x.com", resolver: "server", server: "8.8.8.8" },
expect: { answerCount: [4], durationMs: [1, 2], ttlMax: [6], ttlMin: [5], valueCount: [3] },
id: "tve",
type: "dns",
},
],
}),
);
expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("durationMs"))).toBe(true);
expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("valueCount"))).toBe(true);
expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("answerCount"))).toBe(true);
expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("ttlMin"))).toBe(true);
expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("ttlMax"))).toBe(true);
});
it("验证 ContentExpectations 字段result", () => {
const issues = validateDnsConfig(
makeInput({
targets: [
{
dns: { name: "x.com", resolver: "server", server: "8.8.8.8" },
expect: { result: "not-array" },
id: "tce",
type: "dns",
},
],
}),
);
expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("result"))).toBe(true);
});
it("验证 DnsValuesExpectationexact/include/exclude 数组)", () => {
const issues = validateDnsConfig(
makeInput({
targets: [
{
dns: { name: "x.com", resolver: "server", server: "8.8.8.8" },
expect: { values: { exact: "not-array", exclude: true, include: 123, unknownKey: "x" } },
id: "tdv",
type: "dns",
},
],
}),
);
expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("exact"))).toBe(true);
expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("include"))).toBe(true);
expect(issues.some((i) => i.code === "invalid-type" && i.path.includes("exclude"))).toBe(true);
expect(issues.some((i) => i.code === "unknown-field" && i.path.includes("unknownKey"))).toBe(true);
});
it("跳过非 dns 类型目标", () => {
const issues = validateDnsConfig(
makeInput({ targets: [{ http: { url: "http://example.com" }, id: "tother", type: "http" }] }),
);
expect(issues).toHaveLength(0);
});
it("跳过非对象目标", () => {
const issues = validateDnsConfig(makeInput({ targets: ["not-an-object" as unknown as Record<string, unknown>] }));
expect(issues).toHaveLength(0);
});
});