1
0
Files
DiAL/tests/server/checker/runner/db/expect.test.ts
lanyuanxiaoyao 60a54b483f refactor: expect 类型模型重构,Raw/Resolved 双层分离与断言基础设施内聚
- 重命名 ContentRules→ContentExpectations, KeyValueExpect→KeyedExpectations
- 新增 Raw/Resolved 双层模型:resolve 阶段物化为执行计划,store 持久化 Raw 快照
- HTTP body 按需读取:status/headers 失败或无 body expectation 时不读取 body
- 新增 displayValueExpectation() 解包 failure.expected 用户可读展示
- 修复 checkEarlyTimeout 独立 lte/lt 检查,修复 KeyedExpectations JSON Schema
- 新增 expect/value.ts(resolve/check/display)、keyed.ts、content.ts、headers.ts、status.ts
- 删除旧 normalize.ts/matcher.ts/validate-matcher.ts/key-value.ts
- 更新 DEVELOPMENT.md:expect 五层管线表、displayValueExpectation、1.7↔1.10 交叉引用
- 同步 13 个 main specs,归档 refactor-expect-type-model 变更(62/62 tasks)
2026-05-20 16:12:48 +08:00

141 lines
4.6 KiB
TypeScript
Raw 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, test } from "bun:test";
import type { KeyedExpectations, RawValueExpectation } from "../../../../../src/server/checker/expect/types";
import { resolveValueExpectation } from "../../../../../src/server/checker/expect/value";
import { checkRowCount, checkRows } from "../../../../../src/server/checker/runner/db/expect";
function row(record: Record<string, RawValueExpectation>): KeyedExpectations {
return Object.entries(record).map(([key, value]) => ({ key, matcher: resolveValueExpectation(value) }));
}
describe("checkRowCount", () => {
test("空数组通过 rowCount gte 0", () => {
const result = checkRowCount(0, { gte: 0 });
expect(result.matched).toBe(true);
expect(result.failure).toBeNull();
});
test("0 行通过 gte 0", () => {
const result = checkRowCount(0, { gte: 0 });
expect(result.matched).toBe(true);
});
test("rowCount gte 通过", () => {
const result = checkRowCount(3, { gte: 3 });
expect(result.matched).toBe(true);
});
test("rowCount gte 失败", () => {
const result = checkRowCount(2, { gte: 3 });
expect(result.matched).toBe(false);
expect(result.failure!.phase).toBe("rowCount");
expect(result.failure!.path).toBe("rowCount");
});
test("rowCount equals 通过", () => {
const result = checkRowCount(3, { equals: 3 });
expect(result.matched).toBe(true);
});
test("rowCount equals 失败", () => {
const result = checkRowCount(3, { equals: 5 });
expect(result.matched).toBe(false);
});
});
describe("checkRows", () => {
test("非数组返回失败", () => {
const result = checkRows(null, [row({ col: 1 })]);
expect(result.matched).toBe(false);
expect(result.failure!.phase).toBe("row");
expect(result.failure!.path).toBe("rows");
});
test("空规则通过", () => {
const result = checkRows([], []);
expect(result.matched).toBe(true);
});
test("单行单列匹配(字面量)", () => {
const result = checkRows([{ col: "value" }], [row({ col: "value" })]);
expect(result.matched).toBe(true);
});
test("单行单列匹配operator", () => {
const result = checkRows([{ col: 100 }], [row({ col: { gte: 50 } })]);
expect(result.matched).toBe(true);
});
test("单行单列不匹配", () => {
const result = checkRows([{ col: 10 }], [row({ col: { gte: 50 } })]);
expect(result.matched).toBe(false);
expect(result.failure!.phase).toBe("row");
expect(result.failure!.path).toBe("rows[0].col");
});
test("多行多列全部匹配", () => {
const result = checkRows(
[
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
],
[row({ id: { gte: 1 } }), row({ name: "Bob" })],
);
expect(result.matched).toBe(true);
});
test("多行中有一行不匹配", () => {
const result = checkRows([{ col: 1 }, { col: 2 }], [row({ col: { gte: 2 } }), row({ col: { gte: 3 } })]);
expect(result.matched).toBe(false);
expect(result.failure!.path).toBe("rows[0].col");
});
test("结果行数不足", () => {
const result = checkRows([{ col: 1 }], [row({ col: 1 }), row({ col: 2 })]);
expect(result.matched).toBe(false);
expect(result.failure!.message).toContain("行数不足");
});
test("只检查声明的列", () => {
const result = checkRows([{ col: 1, other: "ignored" }], [row({ col: { gte: 0 } })]);
expect(result.matched).toBe(true);
});
test("行不是对象返回失败", () => {
const result = checkRows(["not-an-object"] as unknown[], [row({ col: 1 })]);
expect(result.matched).toBe(false);
expect(result.failure!.path).toBe("rows[0]");
});
test("列不存在视为 undefined", () => {
const result = checkRows([{}], [row({ col: { exists: false } })]);
expect(result.matched).toBe(true);
});
test("列存在且值为 null", () => {
const result = checkRows([{ col: null }], [row({ col: { empty: true } })]);
expect(result.matched).toBe(true);
});
test("contains 匹配字符串", () => {
const result = checkRows([{ text: "hello world" }], [row({ text: { contains: "hello" } })]);
expect(result.matched).toBe(true);
});
test("regex 正则匹配", () => {
const result = checkRows([{ code: "ABC-123" }], [row({ code: { regex: "^ABC-" } })]);
expect(result.matched).toBe(true);
});
test("多个断言同时满足", () => {
const result = checkRows([{ val: 50 }], [row({ val: { gte: 10, lte: 100 } })]);
expect(result.matched).toBe(true);
});
test("多个断言中有一个不满足", () => {
const result = checkRows([{ val: 50 }], [row({ val: { gte: 10, lte: 30 } })]);
expect(result.matched).toBe(false);
});
});