import { describe, test, expect, beforeAll, afterAll } from 'bun:test'; import { mkdtempSync, rmSync, writeFileSync, mkdirSync } from 'fs'; import { join } from 'path'; import { tmpdir } from 'os'; import { resolveFile, parseRedirect, parseMarker } from '../src/core/file-resolver.ts'; import { LocalStorage } from '../src/core/storage/local.ts'; describe('file-resolver', () => { let brainRoot: string; let storageDir: string; let storage: LocalStorage; beforeAll(() => { brainRoot = mkdtempSync(join(tmpdir(), 'gbrain-resolver-')); storageDir = mkdtempSync(join(tmpdir(), 'gbrain-resolver-storage-')); storage = new LocalStorage(storageDir); // Create a local file mkdirSync(join(brainRoot, 'people'), { recursive: true }); writeFileSync(join(brainRoot, 'people/sarah.json'), '{"name":"Sarah"}'); }); afterAll(() => { rmSync(brainRoot, { recursive: true }); rmSync(storageDir, { recursive: true }); }); test('resolves local file', async () => { const result = await resolveFile('people/sarah.json', brainRoot); expect(result.source).toBe('local'); expect(result.data.toString()).toBe('{"name":"Sarah"}'); }); test('throws for missing file with no redirect or marker', async () => { expect(resolveFile('nonexistent.json', brainRoot)).rejects.toThrow('not found'); }); test('resolves via .redirect breadcrumb', async () => { // Upload to storage await storage.upload('redirected/file.json', Buffer.from('{"from":"storage"}')); // Create redirect breadcrumb writeFileSync(join(brainRoot, 'people/redirected.json.redirect'), 'moved_to: supabase\nbucket: brain-files\npath: redirected/file.json\nmoved_at: 2026-04-09\noriginal_hash: sha256:abc\n' ); const result = await resolveFile('people/redirected.json', brainRoot, storage); expect(result.source).toBe('redirect'); expect(result.data.toString()).toBe('{"from":"storage"}'); }); test('throws when redirect exists but no storage backend', async () => { writeFileSync(join(brainRoot, 'people/no-storage.json.redirect'), 'moved_to: supabase\nbucket: test\npath: test.json\nmoved_at: 2026-04-09\noriginal_hash: sha256:abc\n' ); expect(resolveFile('people/no-storage.json', brainRoot)).rejects.toThrow('no storage backend'); }); }); describe('parseRedirect', () => { let tmpDir: string; beforeAll(() => { tmpDir = mkdtempSync(join(tmpdir(), 'gbrain-redirect-')); }); afterAll(() => { rmSync(tmpDir, { recursive: true }); }); test('parses redirect YAML', () => { const path = join(tmpDir, 'test.redirect'); writeFileSync(path, 'moved_to: supabase\nbucket: brain-files\npath: people/sarah.json\nmoved_at: 2026-04-09\noriginal_hash: sha256:abc123\n'); const info = parseRedirect(path); expect(info.moved_to).toBe('supabase'); expect(info.bucket).toBe('brain-files'); expect(info.path).toBe('people/sarah.json'); expect(info.original_hash).toBe('sha256:abc123'); }); }); describe('parseMarker', () => { let tmpDir: string; beforeAll(() => { tmpDir = mkdtempSync(join(tmpdir(), 'gbrain-marker-')); }); afterAll(() => { rmSync(tmpDir, { recursive: true }); }); test('parses .supabase marker YAML', () => { const path = join(tmpDir, '.supabase'); writeFileSync(path, 'synced_at: 2026-04-09T14:58:00Z\nbucket: brain-files\nprefix: people/.raw/\nfile_count: 484\n'); const info = parseMarker(path); expect(info.synced_at).toBe('2026-04-09T14:58:00Z'); expect(info.bucket).toBe('brain-files'); expect(info.prefix).toBe('people/.raw/'); expect(info.file_count as any).toBe('484'); }); });