import { describe, it, before, after } from 'node:test'; import assert from 'node:assert/strict'; import { mkdtempSync, rmSync } from 'fs'; import { tmpdir } from 'os'; import { join } from 'path'; let tmpDir; let origDataDir; describe('Session Favorites (metadata-db)', () => { let db; before(async () => { tmpDir = mkdtempSync(join(tmpdir(), 'meta-db-test-')); origDataDir = process.env.AGENT_DATA_DIR; process.env.AGENT_DATA_DIR = tmpDir; // Import and init db = await import('../src/metadata-db.js'); db.initMetadataDb(); }); after(() => { if (db) db.closeMetadataDb(); if (origDataDir !== undefined) { process.env.AGENT_DATA_DIR = origDataDir; } else { delete process.env.AGENT_DATA_DIR; } if (tmpDir) { rmSync(tmpDir, { recursive: true, force: true }); } }); it('should persist a favorite and return the favorited_at timestamp', () => { const ts = db.setFavorite('codex', 'session-1'); assert.notEqual(ts, null); assert.equal(typeof ts, 'number'); assert(ts > 0); // Verify via getFavorites const favs = db.getFavorites([{ engine: 'codex', id: 'session-1' }]); assert.deepEqual(favs, { 'codex:session-1': { favorited_at: ts } }); }); it('should return isMetadataReady() === true after init', () => { assert.equal(db.isMetadataReady(), true); }); it('should enforce engine + session_id isolation', () => { db.setFavorite('codex', 'session-a'); db.setFavorite('claude', 'session-b'); const favs = db.getFavorites([ { engine: 'codex', id: 'session-a' }, { engine: 'claude', id: 'session-b' }, { engine: 'codex', id: 'session-b' }, ]); // codex:session-a and claude:session-b should be favorited, codex:session-b should not assert.equal(favs['codex:session-a'] !== undefined, true); assert.equal(favs['claude:session-b'] !== undefined, true); assert.equal(favs['codex:session-b'], undefined); }); it('should be idempotent: repeat favorite returns original favorited_at across different timestamps', async () => { const first = db.setFavorite('codex', 'idempotent-ts'); // Wait past a Unix-second boundary to prove the original timestamp is preserved await new Promise(r => setTimeout(r, 1100)); const second = db.setFavorite('codex', 'idempotent-ts'); // Both calls must return the same timestamp (the original one) assert.equal(first, second); }); it('should unfavorite a session (idempotent)', () => { db.setFavorite('codex', 'to-delete'); assert.equal(db.getFavorites([{ engine: 'codex', id: 'to-delete' }])['codex:to-delete'] !== undefined, true); const result = db.deleteFavorite('codex', 'to-delete'); assert.equal(result, true); const afterDel = db.getFavorites([{ engine: 'codex', id: 'to-delete' }]); assert.equal(afterDel['codex:to-delete'], undefined); // Deleting again should be OK (idempotent) const result2 = db.deleteFavorite('codex', 'to-delete'); assert.equal(result2, true); }); it('should delete favorites by session (deleteFavoriteBySession)', () => { db.setFavorite('codex', 'delete-by-session'); db.deleteFavoriteBySession('codex', 'delete-by-session'); const after = db.getFavorites([{ engine: 'codex', id: 'delete-by-session' }]); assert.equal(after['codex:delete-by-session'], undefined); }); it('should return empty results for empty input', () => { assert.deepEqual(db.getFavorites([]), {}); }); it('should handle many sessions efficiently', () => { const sessions = []; for (let i = 0; i < 50; i++) { const engine = i % 2 === 0 ? 'codex' : 'claude'; const id = `mass-test-${i}`; db.setFavorite(engine, id); sessions.push({ engine, id }); } const favs = db.getFavorites(sessions); assert.equal(Object.keys(favs).length, 50); }); });