mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-08-29 18:33:01 +00:00
317 lines
12 KiB
TypeScript
317 lines
12 KiB
TypeScript
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'fs/promises'
|
|
import { existsSync } from 'fs'
|
|
import { tmpdir } from 'os'
|
|
import { join } from 'path'
|
|
|
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
|
|
import {
|
|
CODEX_CACHE_VERSION,
|
|
CODEX_LEGACY_CACHE_FILE,
|
|
clearCodexMemCaches,
|
|
codexCacheFileName,
|
|
fingerprintFile,
|
|
flushCodexCache,
|
|
readCachedCodexResults,
|
|
writeCachedCodexResults,
|
|
} from '../src/codex-cache.js'
|
|
import {
|
|
CURSOR_CACHE_VERSION,
|
|
CURSOR_LEGACY_CACHE_FILE,
|
|
cursorCacheFileName,
|
|
readCachedResults,
|
|
writeCachedResults,
|
|
} from '../src/cursor-cache.js'
|
|
import {
|
|
ANTIGRAVITY_CACHE_VERSION,
|
|
ANTIGRAVITY_LEGACY_CACHE_FILE,
|
|
antigravityCacheFileName,
|
|
clearAntigravityCacheStates,
|
|
createAntigravityProvider,
|
|
} from '../src/providers/antigravity.js'
|
|
import type { ParsedProviderCall } from '../src/providers/types.js'
|
|
|
|
const originalCacheDir = process.env['CODEBURN_CACHE_DIR']
|
|
let root: string
|
|
|
|
function call(provider: string, marker: string): ParsedProviderCall {
|
|
return {
|
|
provider,
|
|
model: marker,
|
|
inputTokens: 1,
|
|
outputTokens: 1,
|
|
cacheCreationInputTokens: 0,
|
|
cacheReadInputTokens: 0,
|
|
cachedInputTokens: 0,
|
|
reasoningTokens: 0,
|
|
webSearchRequests: 0,
|
|
costUSD: 0,
|
|
tools: [],
|
|
bashCommands: [],
|
|
timestamp: '2026-08-22T00:00:00.000Z',
|
|
speed: 'standard',
|
|
deduplicationKey: `${provider}:${marker}`,
|
|
userMessage: '',
|
|
sessionId: marker,
|
|
}
|
|
}
|
|
|
|
async function readAntigravityModel(sourcePath: string): Promise<string | undefined> {
|
|
const parser = createAntigravityProvider().createSessionParser({
|
|
path: sourcePath,
|
|
project: 'fixture',
|
|
provider: 'antigravity',
|
|
}, new Set())
|
|
for await (const parsed of parser.parse()) return parsed.model
|
|
return undefined
|
|
}
|
|
|
|
beforeEach(async () => {
|
|
root = await mkdtemp(join(tmpdir(), 'codeburn-result-suffix-'))
|
|
process.env['CODEBURN_CACHE_DIR'] = root
|
|
clearCodexMemCaches()
|
|
clearAntigravityCacheStates()
|
|
})
|
|
|
|
afterEach(async () => {
|
|
if (originalCacheDir === undefined) delete process.env['CODEBURN_CACHE_DIR']
|
|
else process.env['CODEBURN_CACHE_DIR'] = originalCacheDir
|
|
clearCodexMemCaches()
|
|
clearAntigravityCacheStates()
|
|
await rm(root, { recursive: true, force: true })
|
|
})
|
|
|
|
describe('unsuffixed result caches coexist with mixed-version binaries', () => {
|
|
it('publishes Codex to the versioned file and leaves an older unsuffixed file intact', async () => {
|
|
const sourcePath = join(root, 'rollout.jsonl')
|
|
await writeFile(sourcePath, '{}\n')
|
|
const fingerprint = await fingerprintFile(sourcePath)
|
|
expect(fingerprint).not.toBeNull()
|
|
|
|
const legacyPath = join(root, CODEX_LEGACY_CACHE_FILE)
|
|
await writeFile(legacyPath, JSON.stringify({
|
|
version: CODEX_CACHE_VERSION - 1,
|
|
files: { [sourcePath]: { mtimeMs: 1, sizeBytes: 1, project: 'old', calls: [call('codex', 'old-binary')] } },
|
|
}))
|
|
|
|
await writeCachedCodexResults(sourcePath, 'project', [call('codex', 'new-binary')], fingerprint!)
|
|
await flushCodexCache()
|
|
|
|
expect(existsSync(join(root, codexCacheFileName()))).toBe(true)
|
|
expect(existsSync(legacyPath)).toBe(true)
|
|
const versioned = JSON.parse(await readFile(join(root, codexCacheFileName()), 'utf8'))
|
|
const legacy = JSON.parse(await readFile(legacyPath, 'utf8'))
|
|
expect(versioned.version).toBe(CODEX_CACHE_VERSION)
|
|
expect(versioned.files[sourcePath].calls[0].model).toBe('new-binary')
|
|
expect(legacy.version).toBe(CODEX_CACHE_VERSION - 1)
|
|
expect(legacy.files[sourcePath].calls[0].model).toBe('old-binary')
|
|
})
|
|
|
|
it('adopts a matching-version unsuffixed Codex cache and ignores a mismatched one', async () => {
|
|
const sourcePath = join(root, 'rollout.jsonl')
|
|
await writeFile(sourcePath, '{}\n')
|
|
const fingerprint = await fingerprintFile(sourcePath)
|
|
expect(fingerprint).not.toBeNull()
|
|
|
|
await writeFile(join(root, CODEX_LEGACY_CACHE_FILE), JSON.stringify({
|
|
version: CODEX_CACHE_VERSION,
|
|
files: {
|
|
[sourcePath]: {
|
|
dev: fingerprint!.dev,
|
|
ino: fingerprint!.ino,
|
|
mtimeMs: fingerprint!.mtimeMs,
|
|
sizeBytes: fingerprint!.sizeBytes,
|
|
project: 'adopted',
|
|
calls: [call('codex', 'adopt-me')],
|
|
},
|
|
},
|
|
}))
|
|
|
|
expect((await readCachedCodexResults(sourcePath))?.calls.map(entry => entry.model)).toEqual(['adopt-me'])
|
|
expect(existsSync(join(root, CODEX_LEGACY_CACHE_FILE))).toBe(true)
|
|
expect(existsSync(join(root, codexCacheFileName()))).toBe(false)
|
|
|
|
clearCodexMemCaches()
|
|
await writeFile(join(root, CODEX_LEGACY_CACHE_FILE), JSON.stringify({
|
|
version: CODEX_CACHE_VERSION - 1,
|
|
files: {
|
|
[sourcePath]: {
|
|
dev: fingerprint!.dev,
|
|
ino: fingerprint!.ino,
|
|
mtimeMs: fingerprint!.mtimeMs,
|
|
sizeBytes: fingerprint!.sizeBytes,
|
|
project: 'stale',
|
|
calls: [call('codex', 'do-not-adopt')],
|
|
},
|
|
},
|
|
}))
|
|
expect(await readCachedCodexResults(sourcePath)).toBeNull()
|
|
})
|
|
|
|
it('publishes Cursor to the versioned file and leaves an older unsuffixed file intact', async () => {
|
|
const dbPath = join(root, 'state.vscdb')
|
|
await writeFile(dbPath, 'cursor-db')
|
|
const floor = '2026-01-01T00:00:00.000Z'
|
|
const legacyPath = join(root, CURSOR_LEGACY_CACHE_FILE)
|
|
await writeFile(legacyPath, JSON.stringify({
|
|
version: CURSOR_CACHE_VERSION - 1,
|
|
dbMtimeMs: 1,
|
|
dbSizeBytes: 1,
|
|
lookbackFloor: floor,
|
|
calls: [call('cursor', 'old-binary')],
|
|
}))
|
|
|
|
await writeCachedResults(dbPath, [call('cursor', 'new-binary')], floor)
|
|
|
|
expect(existsSync(join(root, cursorCacheFileName()))).toBe(true)
|
|
expect(existsSync(legacyPath)).toBe(true)
|
|
const versioned = JSON.parse(await readFile(join(root, cursorCacheFileName()), 'utf8'))
|
|
const legacy = JSON.parse(await readFile(legacyPath, 'utf8'))
|
|
expect(versioned.version).toBe(CURSOR_CACHE_VERSION)
|
|
expect(versioned.calls[0].model).toBe('new-binary')
|
|
expect(legacy.version).toBe(CURSOR_CACHE_VERSION - 1)
|
|
expect(legacy.calls[0].model).toBe('old-binary')
|
|
})
|
|
|
|
it('adopts a matching-version unsuffixed Cursor cache and ignores a mismatched one', async () => {
|
|
const dbPath = join(root, 'state.vscdb')
|
|
await writeFile(dbPath, 'cursor-db')
|
|
const { stat } = await import('fs/promises')
|
|
const fp = await stat(dbPath)
|
|
const floor = '2026-01-01T00:00:00.000Z'
|
|
|
|
await writeFile(join(root, CURSOR_LEGACY_CACHE_FILE), JSON.stringify({
|
|
version: CURSOR_CACHE_VERSION,
|
|
dbMtimeMs: fp.mtimeMs,
|
|
dbSizeBytes: fp.size,
|
|
lookbackFloor: floor,
|
|
calls: [call('cursor', 'adopt-me')],
|
|
}))
|
|
expect((await readCachedResults(dbPath, floor))?.map(entry => entry.model)).toEqual(['adopt-me'])
|
|
expect(existsSync(join(root, cursorCacheFileName()))).toBe(false)
|
|
|
|
await writeFile(join(root, CURSOR_LEGACY_CACHE_FILE), JSON.stringify({
|
|
version: CURSOR_CACHE_VERSION - 1,
|
|
dbMtimeMs: fp.mtimeMs,
|
|
dbSizeBytes: fp.size,
|
|
lookbackFloor: floor,
|
|
calls: [call('cursor', 'do-not-adopt')],
|
|
}))
|
|
expect(await readCachedResults(dbPath, floor)).toBeNull()
|
|
})
|
|
|
|
it('publishes Antigravity to the versioned file and leaves an older unsuffixed file intact', async () => {
|
|
const sourcePath = join(root, 'shared.pb')
|
|
await writeFile(sourcePath, 'fixture')
|
|
const { stat } = await import('fs/promises')
|
|
const sourceStat = await stat(sourcePath)
|
|
const legacyPath = join(root, ANTIGRAVITY_LEGACY_CACHE_FILE)
|
|
await mkdir(root, { recursive: true })
|
|
await writeFile(legacyPath, JSON.stringify({
|
|
version: ANTIGRAVITY_CACHE_VERSION - 1,
|
|
cascades: {
|
|
shared: { mtimeMs: sourceStat.mtimeMs, sizeBytes: sourceStat.size, calls: [call('antigravity', 'old-binary')] },
|
|
},
|
|
}))
|
|
|
|
await writeFile(join(root, antigravityCacheFileName()), JSON.stringify({
|
|
version: ANTIGRAVITY_CACHE_VERSION,
|
|
cascades: {
|
|
shared: { mtimeMs: sourceStat.mtimeMs, sizeBytes: sourceStat.size, calls: [call('antigravity', 'new-binary')] },
|
|
},
|
|
}))
|
|
|
|
expect(await readAntigravityModel(sourcePath)).toBe('new-binary')
|
|
const legacy = JSON.parse(await readFile(legacyPath, 'utf8'))
|
|
expect(legacy.cascades.shared.calls[0].model).toBe('old-binary')
|
|
})
|
|
|
|
it('adopts a matching-version unsuffixed Antigravity cache and ignores a mismatched one', async () => {
|
|
const sourcePath = join(root, 'shared.pb')
|
|
await writeFile(sourcePath, 'fixture')
|
|
const { stat } = await import('fs/promises')
|
|
const sourceStat = await stat(sourcePath)
|
|
|
|
await writeFile(join(root, ANTIGRAVITY_LEGACY_CACHE_FILE), JSON.stringify({
|
|
version: ANTIGRAVITY_CACHE_VERSION,
|
|
cascades: {
|
|
shared: { mtimeMs: sourceStat.mtimeMs, sizeBytes: sourceStat.size, calls: [call('antigravity', 'adopt-me')] },
|
|
},
|
|
}))
|
|
expect(await readAntigravityModel(sourcePath)).toBe('adopt-me')
|
|
expect(existsSync(join(root, antigravityCacheFileName()))).toBe(false)
|
|
|
|
clearAntigravityCacheStates()
|
|
await writeFile(join(root, ANTIGRAVITY_LEGACY_CACHE_FILE), JSON.stringify({
|
|
version: ANTIGRAVITY_CACHE_VERSION - 1,
|
|
cascades: {
|
|
shared: { mtimeMs: sourceStat.mtimeMs, sizeBytes: sourceStat.size, calls: [call('antigravity', 'do-not-adopt')] },
|
|
},
|
|
}))
|
|
expect(await readAntigravityModel(sourcePath)).toBeUndefined()
|
|
})
|
|
|
|
it('never serves matching legacy when a versioned file is present but invalid', async () => {
|
|
const sourcePath = join(root, 'rollout.jsonl')
|
|
const dbPath = join(root, 'state.vscdb')
|
|
const pbPath = join(root, 'shared.pb')
|
|
await writeFile(sourcePath, '{}\n')
|
|
await writeFile(dbPath, 'cursor-db')
|
|
await writeFile(pbPath, 'fixture')
|
|
const fingerprint = await fingerprintFile(sourcePath)
|
|
const { stat } = await import('fs/promises')
|
|
const dbStat = await stat(dbPath)
|
|
const pbStat = await stat(pbPath)
|
|
const floor = '2026-01-01T00:00:00.000Z'
|
|
expect(fingerprint).not.toBeNull()
|
|
|
|
await writeFile(join(root, CODEX_LEGACY_CACHE_FILE), JSON.stringify({
|
|
version: CODEX_CACHE_VERSION,
|
|
files: {
|
|
[sourcePath]: {
|
|
dev: fingerprint!.dev,
|
|
ino: fingerprint!.ino,
|
|
mtimeMs: fingerprint!.mtimeMs,
|
|
sizeBytes: fingerprint!.sizeBytes,
|
|
project: 'legacy',
|
|
calls: [call('codex', 'legacy-served')],
|
|
},
|
|
},
|
|
}))
|
|
await writeFile(join(root, CURSOR_LEGACY_CACHE_FILE), JSON.stringify({
|
|
version: CURSOR_CACHE_VERSION,
|
|
dbMtimeMs: dbStat.mtimeMs,
|
|
dbSizeBytes: dbStat.size,
|
|
lookbackFloor: floor,
|
|
calls: [call('cursor', 'legacy-served')],
|
|
}))
|
|
await writeFile(join(root, ANTIGRAVITY_LEGACY_CACHE_FILE), JSON.stringify({
|
|
version: ANTIGRAVITY_CACHE_VERSION,
|
|
cascades: {
|
|
shared: { mtimeMs: pbStat.mtimeMs, sizeBytes: pbStat.size, calls: [call('antigravity', 'legacy-served')] },
|
|
},
|
|
}))
|
|
|
|
await writeFile(join(root, codexCacheFileName()), '{not-json')
|
|
await writeFile(join(root, cursorCacheFileName()), '{not-json')
|
|
await writeFile(join(root, antigravityCacheFileName()), '{not-json')
|
|
expect(await readCachedCodexResults(sourcePath)).toBeNull()
|
|
expect(await readCachedResults(dbPath, floor)).toBeNull()
|
|
expect(await readAntigravityModel(pbPath)).toBeUndefined()
|
|
|
|
clearCodexMemCaches()
|
|
clearAntigravityCacheStates()
|
|
await writeFile(join(root, codexCacheFileName()), JSON.stringify({ version: CODEX_CACHE_VERSION - 1, files: {} }))
|
|
await writeFile(join(root, cursorCacheFileName()), JSON.stringify({
|
|
version: CURSOR_CACHE_VERSION - 1,
|
|
dbMtimeMs: dbStat.mtimeMs,
|
|
dbSizeBytes: dbStat.size,
|
|
lookbackFloor: floor,
|
|
calls: [call('cursor', 'wrong-version')],
|
|
}))
|
|
await writeFile(join(root, antigravityCacheFileName()), JSON.stringify({ version: ANTIGRAVITY_CACHE_VERSION - 1, cascades: {} }))
|
|
expect(await readCachedCodexResults(sourcePath)).toBeNull()
|
|
expect(await readCachedResults(dbPath, floor)).toBeNull()
|
|
expect(await readAntigravityModel(pbPath)).toBeUndefined()
|
|
})
|
|
})
|