Files
obelisk/tests/codex-index.test.mjs
T

79 lines
3.5 KiB
JavaScript
Raw Normal View History

// Phase 5c: exercises the full codex buildIndex path (discover → codex.parse →
// persist) for both a fresh full build and an incremental rebuild after append.
// Codex is full-reparse with countMode 'total', so growth must REPLACE the count
// (not accumulate) and upsert messages (no duplicates).
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { createRequire } from 'node:module';
import { mkdtempSync, mkdirSync, writeFileSync, appendFileSync, utimesSync, statSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { spawnSync } from 'node:child_process';
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const require = createRequire(import.meta.url);
const { DatabaseSync } = require('node:sqlite');
function runRuntime(args, home) {
return spawnSync(process.execPath, ['scripts/runtime.mjs', ...args], {
cwd: repoRoot, env: { ...process.env, HOME: home }, encoding: 'utf8',
});
}
const ID = '019ed000-0000-7000-8000-000000000001';
function metaLine() {
return JSON.stringify({ type: 'session_meta', timestamp: '2026-06-15T10:00:00Z', payload: { id: ID, timestamp: '2026-06-15T10:00:00Z', cwd: '/tmp/cdx', cli_version: '1.0' } });
}
function evt(type, message, ts) {
return JSON.stringify({ type: 'event_msg', timestamp: ts, payload: { type, message } });
}
function clearDebounce(home) {
const db = new DatabaseSync(join(home, '.obelisk', 'obelisk.sqlite'));
db.prepare("DELETE FROM index_state WHERE jsonl_path='__last_build__'").run();
db.close();
}
function codexCounts(home) {
writeFileSync(join(home, 'q.mjs'), `return {
sessions: sql("SELECT COUNT(*) c FROM sessions WHERE source='codex'")[0].c,
mc: sql("SELECT message_count FROM sessions WHERE source='codex'")[0]?.message_count ?? null,
msgs: sql("SELECT COUNT(*) c FROM messages WHERE source='codex'")[0].c,
hits: search('followup', { source: 'codex', limit: 5 }).length,
};`);
const r = runRuntime(['--query', join(home, 'q.mjs')], home);
assert.equal(r.status, 0, r.stderr || r.stdout);
return JSON.parse(r.stdout);
}
test('codex full build then incremental rebuild replaces the total count without duplicates', () => {
const home = mkdtempSync(join(tmpdir(), 'obelisk-codex-idx-'));
const dir = join(home, '.codex', 'sessions', '2026', '06', '15');
mkdirSync(dir, { recursive: true });
const jsonl = join(dir, `rollout-2026-06-15T10-00-00-${ID}.jsonl`);
// Full build: one user + one agent message.
writeFileSync(jsonl, [metaLine(), evt('user_message', 'codex hello', '2026-06-15T10:00:01Z'), evt('agent_message', 'codex reply', '2026-06-15T10:00:02Z')].join('\n') + '\n');
assert.equal(runRuntime(['--build'], home).status, 0);
let c = codexCounts(home);
assert.equal(c.sessions, 1, 'one codex session indexed');
assert.equal(c.mc, 2, 'two messages counted');
assert.equal(c.msgs, 2);
// Append a third message; bump mtime; incremental rebuild (full-reparse).
appendFileSync(jsonl, evt('user_message', 'codex followup', '2026-06-15T10:01:00Z') + '\n');
const t = statSync(jsonl).mtimeMs / 1000 + 10;
utimesSync(jsonl, t, t);
clearDebounce(home);
c = codexCounts(home);
// 'total' replace: 3, not 5 (2+3) and not a stale 2.
assert.equal(c.mc, 3, 'message_count replaced with the new total');
assert.equal(c.msgs, 3, 'exactly three messages, upserted (no duplicates)');
assert.equal(c.hits, 1, 'the appended message is searchable');
});