feat: 基础设施加固 — 修复构建、数据保留、错误边界、bundle 拆分
- 修复 build script 引用已删除的 registerCheckers,恢复生产构建 - 生产入口添加 SIGINT/SIGTERM 优雅关闭(与 dev.ts 一致) - 新增 runtime.retention 配置(默认 7d),ProbeStore.prune() 定时清理过期数据 - parseDuration 扩展支持 h/d 单位 - 新增前端 ErrorBoundary 组件,防止渲染错误白屏 - Vite codeSplitting.groups 拆分 vendor chunks(业务代码 1180KB → 47KB) - 同步 delta specs 到主规范
This commit is contained in:
@@ -43,6 +43,16 @@ describe("parseDuration", () => {
|
||||
expect(parseDuration("1.5s")).toBe(1500);
|
||||
});
|
||||
|
||||
test("解析小时", () => {
|
||||
expect(parseDuration("2h")).toBe(7200000);
|
||||
expect(parseDuration("1h")).toBe(3600000);
|
||||
});
|
||||
|
||||
test("解析天", () => {
|
||||
expect(parseDuration("7d")).toBe(604800000);
|
||||
expect(parseDuration("1d")).toBe(86400000);
|
||||
});
|
||||
|
||||
test("拒绝非正整数毫秒结果", () => {
|
||||
expect(() => parseDuration("0ms")).toThrow("正整数毫秒");
|
||||
expect(() => parseDuration("1.5ms")).toThrow("正整数毫秒");
|
||||
@@ -1214,4 +1224,51 @@ targets:
|
||||
"expect.status 是未知字段",
|
||||
);
|
||||
});
|
||||
|
||||
test("retention 默认值为 7d", async () => {
|
||||
const configPath = join(tempDir, "retention-default.yaml");
|
||||
await writeFile(
|
||||
configPath,
|
||||
`targets:
|
||||
- name: "test"
|
||||
type: http
|
||||
http:
|
||||
url: "http://example.com"
|
||||
`,
|
||||
);
|
||||
const config = await loadConfig(configPath);
|
||||
expect(config.retentionMs).toBe(604800000);
|
||||
});
|
||||
|
||||
test("retention 自定义值", async () => {
|
||||
const configPath = join(tempDir, "retention-custom.yaml");
|
||||
await writeFile(
|
||||
configPath,
|
||||
`runtime:
|
||||
retention: "24h"
|
||||
targets:
|
||||
- name: "test"
|
||||
type: http
|
||||
http:
|
||||
url: "http://example.com"
|
||||
`,
|
||||
);
|
||||
const config = await loadConfig(configPath);
|
||||
expect(config.retentionMs).toBe(86400000);
|
||||
});
|
||||
|
||||
test("retention 非法格式抛出错误", async () => {
|
||||
await expectConfigError(
|
||||
"bad-retention.yaml",
|
||||
`runtime:
|
||||
retention: "7x"
|
||||
targets:
|
||||
- name: "test"
|
||||
type: http
|
||||
http:
|
||||
url: "http://example.com"
|
||||
`,
|
||||
"无效的时长格式",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -220,4 +220,55 @@ describe("ProbeEngine", () => {
|
||||
void httpServer.stop();
|
||||
}
|
||||
});
|
||||
|
||||
test("retentionMs > 0 时 start 调用 prune", () => {
|
||||
let pruneCalled = false;
|
||||
const mockStore = {
|
||||
...createMockStore(["test"]),
|
||||
prune() {
|
||||
pruneCalled = true;
|
||||
return 0;
|
||||
},
|
||||
} as unknown as ProbeStore;
|
||||
|
||||
const targets: ResolvedTargetBase[] = [makeCommandTarget("test")];
|
||||
const engine = new ProbeEngine(mockStore, targets, 20, 86400000);
|
||||
engine.start();
|
||||
expect(pruneCalled).toBe(true);
|
||||
engine.stop();
|
||||
});
|
||||
|
||||
test("retentionMs = 0 时不调用 prune", () => {
|
||||
let pruneCalled = false;
|
||||
const mockStore = {
|
||||
...createMockStore(["test"]),
|
||||
prune() {
|
||||
pruneCalled = true;
|
||||
return 0;
|
||||
},
|
||||
} as unknown as ProbeStore;
|
||||
|
||||
const targets: ResolvedTargetBase[] = [makeCommandTarget("test")];
|
||||
const engine = new ProbeEngine(mockStore, targets, 20, 0);
|
||||
engine.start();
|
||||
expect(pruneCalled).toBe(false);
|
||||
engine.stop();
|
||||
});
|
||||
|
||||
test("retentionMs 未传时不调用 prune", () => {
|
||||
let pruneCalled = false;
|
||||
const mockStore = {
|
||||
...createMockStore(["test"]),
|
||||
prune() {
|
||||
pruneCalled = true;
|
||||
return 0;
|
||||
},
|
||||
} as unknown as ProbeStore;
|
||||
|
||||
const targets: ResolvedTargetBase[] = [makeCommandTarget("test")];
|
||||
const engine = new ProbeEngine(mockStore, targets);
|
||||
engine.start();
|
||||
expect(pruneCalled).toBe(false);
|
||||
engine.stop();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -419,4 +419,87 @@ describe("ProbeStore", () => {
|
||||
|
||||
freshStore.close();
|
||||
});
|
||||
|
||||
test("prune 删除过期数据", () => {
|
||||
const pruneStore = new ProbeStore(join(tempDir, "prune.db"));
|
||||
pruneStore.syncTargets([httpTarget]);
|
||||
const t = pruneStore.getTargets()[0]!;
|
||||
|
||||
pruneStore.insertCheckResult({
|
||||
durationMs: 100,
|
||||
failure: null,
|
||||
matched: true,
|
||||
statusDetail: "200 OK",
|
||||
targetId: t.id,
|
||||
timestamp: "2020-01-01T00:00:00.000Z",
|
||||
});
|
||||
pruneStore.insertCheckResult({
|
||||
durationMs: 100,
|
||||
failure: null,
|
||||
matched: true,
|
||||
statusDetail: "200 OK",
|
||||
targetId: t.id,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
const deleted = pruneStore.prune(86400000);
|
||||
expect(deleted).toBe(1);
|
||||
|
||||
const history = pruneStore.getHistory(t.id, "2000-01-01T00:00:00.000Z", "2099-12-31T23:59:59.999Z");
|
||||
expect(history.total).toBe(1);
|
||||
|
||||
pruneStore.close();
|
||||
});
|
||||
|
||||
test("prune 无过期数据返回 0", () => {
|
||||
const pruneStore = new ProbeStore(join(tempDir, "prune-none.db"));
|
||||
pruneStore.syncTargets([httpTarget]);
|
||||
const t = pruneStore.getTargets()[0]!;
|
||||
|
||||
pruneStore.insertCheckResult({
|
||||
durationMs: 100,
|
||||
failure: null,
|
||||
matched: true,
|
||||
statusDetail: "200 OK",
|
||||
targetId: t.id,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
const deleted = pruneStore.prune(86400000);
|
||||
expect(deleted).toBe(0);
|
||||
|
||||
pruneStore.close();
|
||||
});
|
||||
|
||||
test("prune 不影响保留期内数据", () => {
|
||||
const pruneStore = new ProbeStore(join(tempDir, "prune-keep.db"));
|
||||
pruneStore.syncTargets([httpTarget]);
|
||||
const t = pruneStore.getTargets()[0]!;
|
||||
|
||||
const now = Date.now();
|
||||
pruneStore.insertCheckResult({
|
||||
durationMs: 100,
|
||||
failure: null,
|
||||
matched: true,
|
||||
statusDetail: "200 OK",
|
||||
targetId: t.id,
|
||||
timestamp: new Date(now - 3600000).toISOString(),
|
||||
});
|
||||
pruneStore.insertCheckResult({
|
||||
durationMs: 200,
|
||||
failure: null,
|
||||
matched: true,
|
||||
statusDetail: "200 OK",
|
||||
targetId: t.id,
|
||||
timestamp: new Date(now).toISOString(),
|
||||
});
|
||||
|
||||
const deleted = pruneStore.prune(7200000);
|
||||
expect(deleted).toBe(0);
|
||||
|
||||
const history = pruneStore.getHistory(t.id, "2000-01-01T00:00:00.000Z", "2099-12-31T23:59:59.999Z");
|
||||
expect(history.total).toBe(2);
|
||||
|
||||
pruneStore.close();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user