1
0
Files
DiAL/tests/web/constants/target-table-sorters.test.ts
lanyuanxiaoyao 1c5cfafda6 feat: 前端指标体系增强 — Dashboard/Metrics API、2×4 统计区、趋势图面积+异常标记、连续状态列
- 新增 GET /api/dashboard 合并原 summary+targets 首屏接口
- 新增 GET /api/targets/:id/metrics 合并原 stats+trend 概览接口
- 后端指标纯函数:可用率、百分位、故障段分析、连续状态、UTC 小时分桶
- ProbeStore 窗口取数方法替代全量历史查询
- SummaryCards 扩展为 4 卡片(新增异常事件数)+ 数据新鲜度展示
- 表格新增「连续」列(Tag 渲染 capped 状态)
- OverviewTab 重构为 2×4 Statistic 多维度布局
- TrendChart 改为延迟范围面积图 + 红色异常标记点
- 删除旧路由(summary/targets/trend)和 computeTrendStats
- 同步 delta specs 到主 specs 并归档变更
2026-05-14 12:32:41 +08:00

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");
});
});