1
0

feat: target 软删除机制,配置移除时保留历史数据

This commit is contained in:
2026-05-20 00:43:39 +08:00
parent 9b53c746f6
commit b591dcca97
7 changed files with 294 additions and 38 deletions

View File

@@ -134,7 +134,7 @@ describe("ProbeStore", () => {
expect(store.getTargets()).toHaveLength(2);
});
test("同步删除 target", () => {
test("同步删除 target", () => {
store.syncTargets([httpTarget]);
const targets = store.getTargets();
expect(targets).toHaveLength(1);
@@ -346,7 +346,7 @@ describe("ProbeStore", () => {
expect(closedStore.getTargetById("closed-target")).toBeNull();
});
test("除 target 级联删除 check_results", () => {
test("除 target 软删除保留 check_results", () => {
const cascadeStore = new ProbeStore(join(tempDir, "cascade.db"));
const cascadeTarget: ResolvedHttpTarget = {
description: null,
@@ -391,7 +391,8 @@ describe("ProbeStore", () => {
cascadeStore.syncTargets([]);
expect(cascadeStore.getTargets()).toHaveLength(0);
expect(cascadeStore.getLatestCheck(t.id)).toBeNull();
expect(cascadeStore.getLatestCheck(t.id)).not.toBeNull();
expect(cascadeStore.getHistory(t.id, "2000-01-01T00:00:00.000Z", "2099-12-31T23:59:59.999Z").total).toBe(2);
cascadeStore.close();
});
@@ -686,4 +687,185 @@ describe("ProbeStore", () => {
expect(t.name).toBeNull();
nullNameStore.close();
});
test("targets 表 active 列默认值为 1", () => {
const activeStore = new ProbeStore(join(tempDir, "active-default.db"));
activeStore.syncTargets([{ ...httpTarget, id: "active-test", name: "active-test" }]);
const t = activeStore.getTargets()[0]!;
expect(t.active).toBe(1);
activeStore.close();
});
test("check_results 外键约束为 RESTRICT", () => {
const restrictStore = new ProbeStore(join(tempDir, "restrict.db"));
restrictStore.syncTargets([{ ...httpTarget, id: "restrict-test", name: "restrict-test" }]);
const t = restrictStore.getTargets()[0]!;
restrictStore.insertCheckResult({
durationMs: 100,
failure: null,
matched: true,
observation: null,
targetId: t.id,
timestamp: "2025-01-01T00:00:00.000Z",
});
expect(() => {
restrictStore.deleteTargetRaw(t.id);
}).toThrow();
restrictStore.close();
});
test("syncTargets 新增目标 active=1", () => {
const softStore = new ProbeStore(join(tempDir, "soft-insert.db"));
softStore.syncTargets([{ ...httpTarget, id: "soft-a", name: "soft-a" }]);
const t = softStore.getTargets()[0]!;
expect(t.active).toBe(1);
softStore.close();
});
test("syncTargets 移除目标设置 active=0", () => {
const softStore = new ProbeStore(join(tempDir, "soft-remove.db"));
softStore.syncTargets([
{ ...httpTarget, id: "soft-remove-a", name: "soft-remove-a" },
{ ...httpTarget, id: "soft-remove-b", name: "soft-remove-b" },
]);
expect(softStore.getTargets()).toHaveLength(2);
softStore.syncTargets([{ ...httpTarget, id: "soft-remove-a", name: "soft-remove-a" }]);
expect(softStore.getTargets()).toHaveLength(1);
expect(softStore.getTargets()[0]!.id).toBe("soft-remove-a");
expect(softStore.getTargetActive("soft-remove-b")).toBe(0);
softStore.close();
});
test("syncTargets 恢复已移除目标 active=1", () => {
const restoreStore = new ProbeStore(join(tempDir, "soft-restore.db"));
restoreStore.syncTargets([{ ...httpTarget, id: "restore-a", name: "restore-a" }]);
restoreStore.insertCheckResult({
durationMs: 100,
failure: null,
matched: true,
observation: null,
targetId: "restore-a",
timestamp: "2025-01-01T00:00:00.000Z",
});
restoreStore.syncTargets([]);
expect(restoreStore.getTargets()).toHaveLength(0);
restoreStore.syncTargets([{ ...httpTarget, id: "restore-a", name: "restore-a-updated" }]);
expect(restoreStore.getTargets()).toHaveLength(1);
expect(restoreStore.getTargets()[0]!.active).toBe(1);
expect(restoreStore.getTargets()[0]!.name).toBe("restore-a-updated");
const history = restoreStore.getHistory("restore-a", "2000-01-01T00:00:00.000Z", "2099-12-31T23:59:59.999Z");
expect(history.total).toBe(1);
restoreStore.close();
});
test("syncTargets 更新属性同时保持 active=1", () => {
const updateStore = new ProbeStore(join(tempDir, "soft-update.db"));
updateStore.syncTargets([{ ...httpTarget, id: "update-active", name: "old-name" }]);
updateStore.syncTargets([{ ...httpTarget, id: "update-active", name: "new-name" }]);
const t = updateStore.getTargets()[0]!;
expect(t.name).toBe("new-name");
expect(t.active).toBe(1);
updateStore.close();
});
test("getTargets 不返回 inactive target", () => {
const filterStore = new ProbeStore(join(tempDir, "filter-targets.db"));
filterStore.syncTargets([
{ ...httpTarget, id: "filter-active", name: "filter-active" },
{ ...httpTarget, id: "filter-inactive", name: "filter-inactive" },
]);
filterStore.syncTargets([{ ...httpTarget, id: "filter-active", name: "filter-active" }]);
const targets = filterStore.getTargets();
expect(targets).toHaveLength(1);
expect(targets[0]!.id).toBe("filter-active");
filterStore.close();
});
test("getTargetById 对 inactive target 返回 null", () => {
const filterStore = new ProbeStore(join(tempDir, "filter-byid.db"));
filterStore.syncTargets([{ ...httpTarget, id: "filter-id-test", name: "filter-id-test" }]);
expect(filterStore.getTargetById("filter-id-test")).not.toBeNull();
filterStore.syncTargets([]);
expect(filterStore.getTargetById("filter-id-test")).toBeNull();
filterStore.close();
});
test("prune 清理空壳 inactive target", () => {
const pruneStore = new ProbeStore(join(tempDir, "prune-shell.db"));
pruneStore.syncTargets([{ ...httpTarget, id: "prune-shell-target", name: "prune-shell-target" }]);
pruneStore.insertCheckResult({
durationMs: 100,
failure: null,
matched: true,
observation: null,
targetId: "prune-shell-target",
timestamp: "2020-01-01T00:00:00.000Z",
});
pruneStore.syncTargets([]);
expect(pruneStore.getTargetActive("prune-shell-target")).toBe(0);
pruneStore.prune(86400000);
expect(pruneStore.hasTargetRow("prune-shell-target")).toBeFalse();
pruneStore.close();
});
test("prune 保留有历史数据的 inactive target", () => {
const pruneStore = new ProbeStore(join(tempDir, "prune-keep-inactive.db"));
pruneStore.syncTargets([{ ...httpTarget, id: "prune-keep-target", name: "prune-keep-target" }]);
pruneStore.insertCheckResult({
durationMs: 100,
failure: null,
matched: true,
observation: null,
targetId: "prune-keep-target",
timestamp: "2020-01-01T00:00:00.000Z",
});
pruneStore.insertCheckResult({
durationMs: 100,
failure: null,
matched: true,
observation: null,
targetId: "prune-keep-target",
timestamp: new Date().toISOString(),
});
pruneStore.syncTargets([]);
pruneStore.prune(86400000);
expect(pruneStore.getTargetActive("prune-keep-target")).toBe(0);
pruneStore.close();
});
test("prune 不清理无数据的 active target", () => {
const pruneStore = new ProbeStore(join(tempDir, "prune-no-active.db"));
pruneStore.syncTargets([{ ...httpTarget, id: "prune-active-target", name: "prune-active-target" }]);
pruneStore.prune(86400000);
expect(pruneStore.getTargetActive("prune-active-target")).toBe(1);
pruneStore.close();
});
});