feat: 结构化 observation 替代 statusDetail,API 层动态构造 detail
- CheckResult: statusDetail -> observation (持久化) + detail (API 动态派生) - 存储: status_detail 列 -> observation TEXT (JSON) - CheckerDefinition: 新增 buildDetail(observation) 方法 - 各 checker 返回结构化 observation,API 层通过 registry 调用 buildDetail - HTTP: bodyPreview 在 status/header 失败时也提前采集 - UDP: observation 包含 durationMs,未响应归为 error failure - CMD: 超时/输出超限时保留已收集 observation - TCP: connectTimeMs 仅含连接建立耗时,不含 banner 等待 - 新增 buildDetail 单测和 mapCheckResult 覆盖测试 - 同步 openspec 主规范,归档 checker-observation 变更
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
|
||||
import { createApiError, createHeaders, formatDuration, jsonResponse } from "../../src/server/helpers";
|
||||
import type { StoredCheckResult } from "../../src/server/checker/types";
|
||||
|
||||
import { createApiError, createHeaders, formatDuration, jsonResponse, mapCheckResult } from "../../src/server/helpers";
|
||||
|
||||
describe("createApiError", () => {
|
||||
test("创建错误响应对象", () => {
|
||||
@@ -107,3 +109,42 @@ describe("formatDuration", () => {
|
||||
expect(formatDuration(61123)).toBe("61123ms");
|
||||
});
|
||||
});
|
||||
|
||||
function makeRow(overrides: Partial<StoredCheckResult> = {}): StoredCheckResult {
|
||||
return {
|
||||
duration_ms: 12,
|
||||
failure: null,
|
||||
id: 1,
|
||||
matched: 1,
|
||||
observation: null,
|
||||
target_id: "target-1",
|
||||
timestamp: "2025-01-01T00:00:00.000Z",
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("mapCheckResult", () => {
|
||||
test("反序列化 observation 并构造 detail", () => {
|
||||
const result = mapCheckResult(makeRow({ observation: JSON.stringify({ statusCode: 200 }) }), "http");
|
||||
expect(result.detail).toBe("HTTP 200");
|
||||
expect(result.observation).toEqual({ statusCode: 200 });
|
||||
});
|
||||
|
||||
test("null observation 返回 null detail", () => {
|
||||
const result = mapCheckResult(makeRow(), "http");
|
||||
expect(result.detail).toBeNull();
|
||||
expect(result.observation).toBeNull();
|
||||
});
|
||||
|
||||
test("未知 type 不影响响应序列化", () => {
|
||||
const result = mapCheckResult(makeRow({ observation: JSON.stringify({ statusCode: 200 }) }), "unknown");
|
||||
expect(result.detail).toBeNull();
|
||||
expect(result.observation).toEqual({ statusCode: 200 });
|
||||
});
|
||||
|
||||
test("损坏 observation JSON 返回 null observation", () => {
|
||||
const result = mapCheckResult(makeRow({ observation: "{invalid json" }), "http");
|
||||
expect(result.detail).toBeNull();
|
||||
expect(result.observation).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user