import { describe, test, expect, beforeEach, afterEach } from 'vitest'; import { mkdtempSync, writeFileSync, rmSync, utimesSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { GraphCache } from '../src/cache.js'; describe('GraphCache (mtime revalidation)', () => { let dir: string; beforeEach(() => { dir = mkdtempSync(join(tmpdir(), 'wiki-graph-cache-')); writeFileSync(join(dir, 'a.md'), '# A\n[[b]]\n'); writeFileSync(join(dir, 'b.md'), '# B\n'); }); afterEach(() => { rmSync(dir, { recursive: true, force: true }); }); test('returns the same graph instance when nothing changed', () => { const cache = new GraphCache(); const g1 = cache.load(dir); const g2 = cache.load(dir); expect(g2).toBe(g1); expect(g1.stats().nodes).toBe(2); }); test('rebuilds when a new file appears', () => { const cache = new GraphCache(); const g1 = cache.load(dir); writeFileSync(join(dir, 'c.md'), '# C\n'); const g2 = cache.load(dir); expect(g2).not.toBe(g1); expect(g2.stats().nodes).toBe(3); }); test('rebuilds when a file is modified (mtime bump)', () => { const cache = new GraphCache(); const g1 = cache.load(dir); expect(g1.backlinks('c')).toEqual([]); writeFileSync(join(dir, 'a.md'), '# A\n[[b]] [[c]]\n'); writeFileSync(join(dir, 'c.md'), '# C\n'); const future = new Date(Date.now() + 5000); utimesSync(join(dir, 'a.md'), future, future); const g2 = cache.load(dir); expect(g2).not.toBe(g1); expect(g2.backlinks('c')).toEqual(['a']); }); test('only changed files are re-read', () => { const cache = new GraphCache(); cache.load(dir); writeFileSync(join(dir, 'c.md'), '# C\n'); const before = cache.reads; cache.load(dir); // a.md and b.md unchanged → only c.md read on the second build expect(cache.reads - before).toBe(1); }); });