mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-08-08 08:04:32 +00:00
117 lines
3.9 KiB
TypeScript
117 lines
3.9 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
import { rm, writeFile, mkdir } from 'fs/promises'
|
|
import { existsSync } from 'fs'
|
|
import { tmpdir } from 'os'
|
|
import { join } from 'path'
|
|
|
|
import {
|
|
CACHE_VERSION,
|
|
type CachedCall,
|
|
type CachedFile,
|
|
type SessionCache,
|
|
emptyCache,
|
|
loadCache,
|
|
saveCache,
|
|
sessionCachePath,
|
|
} from '../src/session-cache.js'
|
|
|
|
const TMP_DIR = join(tmpdir(), `codeburn-rich-cache-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`)
|
|
|
|
beforeEach(() => { process.env['CODEBURN_CACHE_DIR'] = TMP_DIR })
|
|
afterEach(async () => { if (existsSync(TMP_DIR)) await rm(TMP_DIR, { recursive: true }) })
|
|
|
|
function richCall(): CachedCall {
|
|
return {
|
|
provider: 'claude',
|
|
model: 'claude-sonnet-4-20250514',
|
|
usage: {
|
|
inputTokens: 100, outputTokens: 50, cacheCreationInputTokens: 0, cacheReadInputTokens: 0,
|
|
cachedInputTokens: 0, reasoningTokens: 0, webSearchRequests: 0, cacheCreationOneHourTokens: 0,
|
|
},
|
|
speed: 'standard',
|
|
timestamp: '2026-07-01T10:00:00Z',
|
|
tools: ['Edit'],
|
|
bashCommands: [],
|
|
skills: [],
|
|
subagentTypes: [],
|
|
deduplicationKey: 'm1',
|
|
locAdded: 12,
|
|
locRemoved: 4,
|
|
interrupted: true,
|
|
userModified: true,
|
|
toolErrors: 2,
|
|
editFailed: 1,
|
|
}
|
|
}
|
|
|
|
function richFile(): CachedFile {
|
|
return {
|
|
fingerprint: { dev: 1, ino: 2, mtimeMs: 3, sizeBytes: 4 },
|
|
mcpInventory: [],
|
|
title: 'A rich session',
|
|
prLinks: ['https://github.com/o/r/pull/1', 'https://github.com/o/r/pull/2'],
|
|
isSidechain: true,
|
|
turns: [
|
|
{ timestamp: '2026-07-01T10:00:00Z', sessionId: 's1', userMessage: 'hi', gitBranch: 'feature/x', calls: [richCall()] },
|
|
],
|
|
}
|
|
}
|
|
|
|
describe('session cache round-trip for rich-capture fields', () => {
|
|
it('preserves per-call, per-turn, and per-session fields through save+load', async () => {
|
|
const cache: SessionCache = {
|
|
...emptyCache(),
|
|
providers: { claude: { envFingerprint: 'fp', files: { '/x/s1.jsonl': richFile() } } },
|
|
}
|
|
await saveCache(cache)
|
|
|
|
const loaded = await loadCache()
|
|
const file = loaded.providers['claude']!.files['/x/s1.jsonl']!
|
|
expect(file.title).toBe('A rich session')
|
|
expect(file.prLinks).toEqual(['https://github.com/o/r/pull/1', 'https://github.com/o/r/pull/2'])
|
|
expect(file.isSidechain).toBe(true)
|
|
|
|
const turn = file.turns[0]!
|
|
expect(turn.gitBranch).toBe('feature/x')
|
|
|
|
const call = turn.calls[0]!
|
|
expect(call.locAdded).toBe(12)
|
|
expect(call.locRemoved).toBe(4)
|
|
expect(call.interrupted).toBe(true)
|
|
expect(call.userModified).toBe(true)
|
|
expect(call.toolErrors).toBe(2)
|
|
expect(call.editFailed).toBe(1)
|
|
})
|
|
|
|
it('still loads an old cache written without any rich-capture fields', async () => {
|
|
const oldCall = { ...richCall() }
|
|
for (const k of ['locAdded', 'locRemoved', 'interrupted', 'userModified', 'toolErrors', 'editFailed'] as const) {
|
|
delete (oldCall as Record<string, unknown>)[k]
|
|
}
|
|
const oldCache: SessionCache = {
|
|
version: CACHE_VERSION,
|
|
complete: true,
|
|
providers: {
|
|
claude: {
|
|
envFingerprint: 'fp',
|
|
files: {
|
|
'/x/old.jsonl': {
|
|
fingerprint: { dev: 1, ino: 2, mtimeMs: 3, sizeBytes: 4 },
|
|
mcpInventory: [],
|
|
turns: [{ timestamp: '2026-07-01T10:00:00Z', sessionId: 's1', userMessage: 'hi', calls: [oldCall] }],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
if (!existsSync(TMP_DIR)) await mkdir(TMP_DIR, { recursive: true })
|
|
await writeFile(sessionCachePath(), JSON.stringify(oldCache), 'utf-8')
|
|
|
|
const loaded = await loadCache()
|
|
const call = loaded.providers['claude']!.files['/x/old.jsonl']!.turns[0]!.calls[0]!
|
|
expect(call.deduplicationKey).toBe('m1')
|
|
expect(call.locAdded).toBeUndefined()
|
|
expect(call.editFailed).toBeUndefined()
|
|
expect(loaded.providers['claude']!.files['/x/old.jsonl']!.title).toBeUndefined()
|
|
})
|
|
})
|