feat: target 软删除机制,配置移除时保留历史数据
This commit is contained in:
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user