- 新增顶层 variables 段支持 string/number/boolean 字面量
- target 字符串字段支持 、、{...} 转义语法
- 变量解析优先级: variables -> process.env -> 默认值 -> 报错
- 完整引用保留原始类型,部分引用拼接为字符串
- 变量替换在 YAML 解析后、AJV 校验前执行
- 替换仅作用于 targets,跳过 id/type 字段
- target 新增必填 id 字段作为唯一标识,name 改为可选展示名称
- 数据库存储/API/前端全面迁移到 id 标识
- 统一 checker 运行时类型检查为 es-toolkit predicates
- 同步 delta specs 到主 specs,归档 config-variables 变更
115 lines
3.9 KiB
TypeScript
115 lines
3.9 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
|
|
import type { TargetStatus } from "../../../src/shared/api";
|
|
|
|
import {
|
|
availabilitySorter,
|
|
latencySorter,
|
|
nameSorter,
|
|
statusSorter,
|
|
} from "../../../src/web/constants/target-table-sorters";
|
|
|
|
function makeTarget(overrides: Partial<TargetStatus> = {}): TargetStatus {
|
|
return {
|
|
currentStreak: null,
|
|
group: "default",
|
|
id: "1",
|
|
interval: "5s",
|
|
latestCheck: null,
|
|
name: "test",
|
|
recentSamples: [],
|
|
stats: { availability: 100, downChecks: 0, totalChecks: 0, upChecks: 0 },
|
|
target: "https://example.com",
|
|
type: "http",
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("statusSorter", () => {
|
|
test("DOWN 排在 UP 前面", () => {
|
|
const up = makeTarget({
|
|
latestCheck: { durationMs: 10, failure: null, matched: true, statusDetail: null, timestamp: "" },
|
|
});
|
|
const down = makeTarget({
|
|
latestCheck: { durationMs: 10, failure: null, matched: false, statusDetail: null, timestamp: "" },
|
|
});
|
|
expect(statusSorter(down, up)).toBeLessThan(0);
|
|
expect(statusSorter(up, down)).toBeGreaterThan(0);
|
|
});
|
|
|
|
test("相同状态返回 0", () => {
|
|
const a = makeTarget({
|
|
latestCheck: { durationMs: 10, failure: null, matched: true, statusDetail: null, timestamp: "" },
|
|
});
|
|
const b = makeTarget({
|
|
latestCheck: { durationMs: 20, failure: null, matched: true, statusDetail: null, timestamp: "" },
|
|
});
|
|
expect(statusSorter(a, b)).toBe(0);
|
|
});
|
|
|
|
test("无 latestCheck 的目标排在最后", () => {
|
|
const noCheck = makeTarget();
|
|
const up = makeTarget({
|
|
latestCheck: { durationMs: 10, failure: null, matched: true, statusDetail: null, timestamp: "" },
|
|
});
|
|
expect(statusSorter(noCheck, up)).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
describe("availabilitySorter", () => {
|
|
test("低可用率排前面", () => {
|
|
const low = makeTarget({ stats: { availability: 95, downChecks: 5, totalChecks: 100, upChecks: 95 } });
|
|
const high = makeTarget({ stats: { availability: 99.9, downChecks: 1, totalChecks: 100, upChecks: 99 } });
|
|
expect(availabilitySorter(low, high)).toBeLessThan(0);
|
|
});
|
|
|
|
test("相同可用率返回 0", () => {
|
|
const a = makeTarget({ stats: { availability: 99.9, downChecks: 1, totalChecks: 100, upChecks: 99 } });
|
|
const b = makeTarget({ stats: { availability: 99.9, downChecks: 1, totalChecks: 50, upChecks: 49 } });
|
|
expect(availabilitySorter(a, b)).toBe(0);
|
|
});
|
|
|
|
test("无 stats 按 0 处理", () => {
|
|
const noStats = makeTarget({ stats: undefined as unknown as TargetStatus["stats"] });
|
|
const high = makeTarget({ stats: { availability: 99.9, downChecks: 1, totalChecks: 100, upChecks: 99 } });
|
|
expect(availabilitySorter(noStats, high)).toBeLessThan(0);
|
|
});
|
|
});
|
|
|
|
describe("latencySorter", () => {
|
|
test("低延迟排前面", () => {
|
|
const fast = makeTarget({
|
|
latestCheck: { durationMs: 50, failure: null, matched: true, statusDetail: null, timestamp: "" },
|
|
});
|
|
const slow = makeTarget({
|
|
latestCheck: { durationMs: 200, failure: null, matched: true, statusDetail: null, timestamp: "" },
|
|
});
|
|
expect(latencySorter(fast, slow)).toBeLessThan(0);
|
|
});
|
|
|
|
test("无延迟排最后", () => {
|
|
const noLatency = makeTarget({
|
|
latestCheck: { durationMs: null, failure: null, matched: true, statusDetail: null, timestamp: "" },
|
|
});
|
|
const hasLatency = makeTarget({
|
|
latestCheck: { durationMs: 100, failure: null, matched: true, statusDetail: null, timestamp: "" },
|
|
});
|
|
expect(latencySorter(noLatency, hasLatency)).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
describe("nameSorter", () => {
|
|
test("按名称字母排序", () => {
|
|
const a = makeTarget({ name: "Alpha" });
|
|
const b = makeTarget({ name: "Beta" });
|
|
expect(nameSorter(a, b)).toBeLessThan(0);
|
|
});
|
|
|
|
test("中文名称排序", () => {
|
|
const a = makeTarget({ name: "百度" });
|
|
const b = makeTarget({ name: "谷歌" });
|
|
const result = nameSorter(a, b);
|
|
expect(typeof result).toBe("number");
|
|
});
|
|
});
|