111 lines
3.8 KiB
JavaScript
111 lines
3.8 KiB
JavaScript
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);
|
|
});
|
|
});
|