Skip to content

Commit 4455451

Browse files
committed
test(backup+cache): 15 test integrazione — cache metadata backup, invalidazione post-restore, stats via LRU
2 parents afdfb9d + e24e04e commit 4455451

1 file changed

Lines changed: 172 additions & 0 deletions

File tree

shared/backup/backup-cache.test.ts

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
/** Test integrazione — shared/backup + shared/cache */
2+
import { describe, it, beforeEach, afterEach } from "node:test";
3+
import assert from "node:assert/strict";
4+
import fs from "node:fs";
5+
import path from "node:path";
6+
import os from "node:os";
7+
import { createBackup, restoreBackup, listBackups, applyRetention } from "./runner.js";
8+
import { LRUCache } from "../cache/lru-cache.js";
9+
import type { BackupEntry } from "./types.js";
10+
11+
let tmpDir: string, backupDir: string, srcDir: string;
12+
const src = (files: Record<string, string>) => { for (const [n, c] of Object.entries(files)) fs.writeFileSync(path.join(srcDir, n), c); };
13+
14+
beforeEach(() => {
15+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "bkcache-"));
16+
backupDir = path.join(tmpDir, "backups");
17+
srcDir = path.join(tmpDir, "src");
18+
fs.mkdirSync(srcDir, { recursive: true });
19+
});
20+
afterEach(() => { fs.rmSync(tmpDir, { recursive: true, force: true }); });
21+
22+
describe("cache backup metadata", () => {
23+
it("cache lista backup dopo primo fetch", () => {
24+
const cache = new LRUCache<BackupEntry[]>();
25+
src({ "a.json": '{"x":1}' });
26+
createBackup([path.join(srcDir, "a.json")], { backupDir });
27+
cache.set("backup:list", listBackups({ backupDir }));
28+
assert.equal(cache.get("backup:list")!.length, 1);
29+
});
30+
31+
it("cache singolo backup entry per ID", () => {
32+
const cache = new LRUCache<BackupEntry>();
33+
src({ "b.json": '{"y":2}' });
34+
const r = createBackup([path.join(srcDir, "b.json")], { backupDir });
35+
cache.set(`backup:${r.entry!.id}`, r.entry!);
36+
const cached = cache.get(`backup:${r.entry!.id}`);
37+
assert.equal(cached!.id, r.entry!.id);
38+
assert.equal(cached!.compressed, true);
39+
});
40+
41+
it("cache hit evita re-fetch lista", () => {
42+
const cache = new LRUCache<BackupEntry[]>();
43+
src({ "c.json": "{}" });
44+
createBackup([path.join(srcDir, "c.json")], { backupDir });
45+
cache.set("backup:list", listBackups({ backupDir }));
46+
assert.ok(cache.get("backup:list"));
47+
assert.equal(cache.stats().hits, 1);
48+
});
49+
50+
it("cache size metadata per backup", () => {
51+
const cache = new LRUCache<number>();
52+
src({ "big.txt": "x".repeat(500) });
53+
const r = createBackup([path.join(srcDir, "big.txt")], { backupDir });
54+
cache.set(`backup:size:${r.entry!.id}`, r.entry!.sizeBytes);
55+
assert.ok(cache.get(`backup:size:${r.entry!.id}`)! > 0);
56+
});
57+
});
58+
59+
describe("invalidazione post-restore", () => {
60+
it("invalida cache lista dopo restore", () => {
61+
const cache = new LRUCache<BackupEntry[]>();
62+
src({ "d.json": '{"ok":true}' });
63+
const r = createBackup([path.join(srcDir, "d.json")], { backupDir });
64+
cache.set("backup:list", listBackups({ backupDir }));
65+
restoreBackup(r.entry!.id, path.join(tmpDir, "res1"), { backupDir });
66+
cache.delete("backup:list");
67+
assert.equal(cache.has("backup:list"), false);
68+
});
69+
70+
it("invalida per prefisso backup: dopo restore", () => {
71+
const cache = new LRUCache<unknown>();
72+
src({ "e.json": "{}" });
73+
const r = createBackup([path.join(srcDir, "e.json")], { backupDir });
74+
cache.set("backup:list", listBackups({ backupDir }));
75+
cache.set(`backup:${r.entry!.id}`, r.entry);
76+
cache.set(`backup:size:${r.entry!.id}`, r.entry!.sizeBytes);
77+
restoreBackup(r.entry!.id, path.join(tmpDir, "res2"), { backupDir });
78+
assert.equal(cache.invalidateByPrefix("backup:"), 3);
79+
assert.equal(cache.size, 0);
80+
});
81+
82+
it("invalida per pattern regex dopo restore", () => {
83+
const cache = new LRUCache<unknown>();
84+
src({ "f.json": "{}" });
85+
const r = createBackup([path.join(srcDir, "f.json")], { backupDir });
86+
cache.set(`backup:${r.entry!.id}`, r.entry);
87+
cache.set("other:key", "keep");
88+
cache.invalidateByPattern(/^backup:/);
89+
assert.equal(cache.has(`backup:${r.entry!.id}`), false);
90+
assert.equal(cache.get("other:key"), "keep");
91+
});
92+
93+
it("restore verifica file ripristinati dopo invalidazione cache", () => {
94+
const cache = new LRUCache<string[]>();
95+
src({ "g.json": '{"data":"test"}' });
96+
const r = createBackup([path.join(srcDir, "g.json")], { backupDir });
97+
const restoreDir = path.join(tmpDir, "res3");
98+
const result = restoreBackup(r.entry!.id, restoreDir, { backupDir });
99+
cache.set("restore:files", result.restoredFiles);
100+
assert.deepEqual(cache.get("restore:files"), ["g.json"]);
101+
assert.equal(JSON.parse(fs.readFileSync(path.join(restoreDir, "g.json"), "utf-8")).data, "test");
102+
});
103+
});
104+
105+
describe("backup stats via cache", () => {
106+
it("cache stats aggregazione backup", () => {
107+
const cache = new LRUCache<{ count: number; totalSize: number }>();
108+
src({ "h.json": '{"h":1}', "i.json": '{"i":2}' });
109+
createBackup([path.join(srcDir, "h.json")], { backupDir });
110+
createBackup([path.join(srcDir, "i.json")], { backupDir });
111+
const list = listBackups({ backupDir });
112+
cache.set("backup:stats", { count: list.length, totalSize: list.reduce((s, e) => s + e.sizeBytes, 0) });
113+
assert.equal(cache.get("backup:stats")!.count, 2);
114+
assert.ok(cache.get("backup:stats")!.totalSize > 0);
115+
});
116+
117+
it("stats cache invalida dopo retention", () => {
118+
const cache = new LRUCache<{ count: number }>();
119+
src({ "j.json": "{}" });
120+
createBackup([path.join(srcDir, "j.json")], { backupDir });
121+
createBackup([path.join(srcDir, "j.json")], { backupDir });
122+
cache.set("backup:stats", { count: 2 });
123+
applyRetention({ maxCount: 1 }, { backupDir });
124+
cache.delete("backup:stats");
125+
assert.equal(cache.has("backup:stats"), false);
126+
assert.equal(listBackups({ backupDir }).length, 1);
127+
});
128+
129+
it("cache hit rate traccia accessi backup metadata", () => {
130+
const cache = new LRUCache<BackupEntry[]>();
131+
src({ "k.json": "{}" });
132+
createBackup([path.join(srcDir, "k.json")], { backupDir });
133+
cache.set("backup:list", listBackups({ backupDir }));
134+
cache.get("backup:list"); cache.get("backup:list"); cache.get("backup:missing");
135+
const s = cache.stats();
136+
assert.equal(s.hits, 2); assert.equal(s.misses, 1); assert.ok(s.hitRate > 0.6);
137+
});
138+
139+
it("TTL scade metadata stale dopo intervallo", () => {
140+
const cache = new LRUCache<BackupEntry[]>({ defaultTTL: 1 });
141+
src({ "l.json": "{}" });
142+
createBackup([path.join(srcDir, "l.json")], { backupDir });
143+
cache.set("backup:list", listBackups({ backupDir }));
144+
const start = Date.now(); while (Date.now() - start < 5) { /* wait */ }
145+
assert.equal(cache.get("backup:list"), undefined);
146+
});
147+
148+
it("cache LRU evict vecchi backup metadata quando piena", () => {
149+
const cache = new LRUCache<string>({ maxEntries: 2 });
150+
cache.set("backup:a", "first"); cache.set("backup:b", "second"); cache.set("backup:c", "third");
151+
assert.equal(cache.has("backup:a"), false);
152+
assert.equal(cache.get("backup:b"), "second");
153+
assert.equal(cache.get("backup:c"), "third");
154+
});
155+
156+
it("clear cache reset completo stats backup", () => {
157+
const cache = new LRUCache<BackupEntry[]>();
158+
src({ "m.json": "{}" });
159+
createBackup([path.join(srcDir, "m.json")], { backupDir });
160+
cache.set("backup:list", listBackups({ backupDir }));
161+
cache.set("backup:stats", [] as any);
162+
cache.clear();
163+
assert.equal(cache.size, 0);
164+
});
165+
166+
it("onEvict callback traccia rimozione metadata backup", () => {
167+
const evicted: string[] = [];
168+
const cache = new LRUCache<string>({ maxEntries: 1, onEvict: (k) => evicted.push(k) });
169+
cache.set("backup:old", "v1"); cache.set("backup:new", "v2");
170+
assert.equal(evicted.length, 1); assert.equal(evicted[0], "backup:old");
171+
});
172+
});

0 commit comments

Comments
 (0)