mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-10-21 05:23:19 +00:00
The session cache keys the Cursor parse on the DB fingerprint alone, but the parse stopped at the requested range start, so a narrow run (7 days) was served to every wider range afterwards (30 days showed 2 calls instead of 1,680). The parse now always reads the full 6 month lookback; the cursor parse version is bumped so cached narrow entries re-parse.
74 lines
2.7 KiB
TypeScript
74 lines
2.7 KiB
TypeScript
import { afterAll, beforeEach, describe, expect, it } from 'vitest'
|
|
import { mkdir, mkdtemp, rm } from 'fs/promises'
|
|
import { createRequire } from 'module'
|
|
import { join } from 'path'
|
|
import { tmpdir } from 'os'
|
|
|
|
import { getEditorDataDir } from '../src/editor-data-dir.js'
|
|
import { clearSessionCache, parseAllSessions } from '../src/parser.js'
|
|
import { isSqliteAvailable } from '../src/sqlite.js'
|
|
import type { DateRange, ProjectSummary } from '../src/types.js'
|
|
|
|
const DAY = 86_400_000
|
|
const daysAgo = (n: number) => new Date(Date.now() - n * DAY).toISOString()
|
|
const lastDays = (n: number): DateRange => ({ start: new Date(Date.now() - n * DAY), end: new Date() })
|
|
|
|
let root: string
|
|
|
|
async function createCursorDb(createdAt: string[]): Promise<void> {
|
|
const dir = join(getEditorDataDir('Cursor', root, process.platform), 'User', 'globalStorage')
|
|
await mkdir(dir, { recursive: true })
|
|
const { DatabaseSync } = createRequire(import.meta.url)('node:sqlite') as typeof import('node:sqlite')
|
|
const db = new DatabaseSync(join(dir, 'state.vscdb'))
|
|
db.exec('CREATE TABLE cursorDiskKV (key TEXT PRIMARY KEY, value TEXT)')
|
|
const stmt = db.prepare('INSERT INTO cursorDiskKV (key, value) VALUES (?, ?)')
|
|
createdAt.forEach((ts, i) => stmt.run(`bubbleId:conv-${i}:bubble-${i}`, JSON.stringify({
|
|
type: 2,
|
|
conversationId: `conv-${i}`,
|
|
text: 'def hello(): pass',
|
|
tokenCount: { inputTokens: 100, outputTokens: 100 },
|
|
createdAt: ts,
|
|
modelInfo: { modelName: 'gpt-5' },
|
|
})))
|
|
db.close()
|
|
}
|
|
|
|
function totals(projects: ProjectSummary[]): { calls: number; cost: number } {
|
|
let calls = 0
|
|
let cost = 0
|
|
for (const p of projects) for (const s of p.sessions) for (const t of s.turns) for (const c of t.assistantCalls) {
|
|
calls++
|
|
cost += c.costUSD
|
|
}
|
|
return { calls, cost }
|
|
}
|
|
|
|
async function parse(range: DateRange, cacheDir: string) {
|
|
process.env['CODEBURN_CACHE_DIR'] = cacheDir
|
|
clearSessionCache()
|
|
return totals(await parseAllSessions(range, 'cursor'))
|
|
}
|
|
|
|
beforeEach(async () => {
|
|
root = await mkdtemp(join(tmpdir(), 'cursor-range-cache-'))
|
|
process.env['HOME'] = root
|
|
process.env['USERPROFILE'] = root
|
|
process.env['APPDATA'] = join(root, 'AppData', 'Roaming')
|
|
delete process.env['XDG_CONFIG_HOME']
|
|
})
|
|
|
|
afterAll(async () => {
|
|
await rm(root, { recursive: true, force: true })
|
|
})
|
|
|
|
describe.skipIf(!isSqliteAvailable())('cursor parse cached across ranges', () => {
|
|
it('a wider range after a narrow one matches a cold wide parse', async () => {
|
|
await createCursorDb([daysAgo(1), daysAgo(10), daysAgo(20)])
|
|
const cold = await parse(lastDays(30), join(root, 'cold'))
|
|
expect(cold.calls).toBe(3)
|
|
|
|
const warm = join(root, 'warm')
|
|
expect((await parse(lastDays(7), warm)).calls).toBe(1)
|
|
expect(await parse(lastDays(30), warm)).toEqual(cold)
|
|
})
|
|
})
|