fix: harden resume after compaction failures (#195)
* fix: harden resume after compaction failures * test: cover resume compaction safeguards * fix: address resume safeguard review findings
This commit is contained in:
79
src/utils/conversationRecovery.test.ts
Normal file
79
src/utils/conversationRecovery.test.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { afterEach, expect, test } from 'bun:test'
|
||||
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
|
||||
import {
|
||||
loadConversationForResume,
|
||||
ResumeTranscriptTooLargeError,
|
||||
} from './conversationRecovery.ts'
|
||||
|
||||
const tempDirs: string[] = []
|
||||
const originalSimple = process.env.CLAUDE_CODE_SIMPLE
|
||||
const sessionId = '00000000-0000-4000-8000-000000001999'
|
||||
const ts = '2026-04-02T00:00:00.000Z'
|
||||
|
||||
function id(n: number): string {
|
||||
return `00000000-0000-4000-8000-${String(n).padStart(12, '0')}`
|
||||
}
|
||||
|
||||
function user(uuid: string, content: string) {
|
||||
return {
|
||||
type: 'user',
|
||||
uuid,
|
||||
parentUuid: null,
|
||||
timestamp: ts,
|
||||
cwd: '/tmp',
|
||||
userType: 'external',
|
||||
sessionId,
|
||||
version: 'test',
|
||||
isSidechain: false,
|
||||
isMeta: false,
|
||||
message: {
|
||||
role: 'user',
|
||||
content,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async function writeJsonl(entry: unknown): Promise<string> {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'openclaude-conversation-recovery-'))
|
||||
tempDirs.push(dir)
|
||||
const filePath = join(dir, 'resume.jsonl')
|
||||
await writeFile(filePath, `${JSON.stringify(entry)}\n`)
|
||||
return filePath
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
process.env.CLAUDE_CODE_SIMPLE = originalSimple
|
||||
await Promise.all(tempDirs.splice(0).map(dir => rm(dir, { recursive: true, force: true })))
|
||||
})
|
||||
|
||||
test('loadConversationForResume accepts a small transcript from jsonl path', async () => {
|
||||
process.env.CLAUDE_CODE_SIMPLE = '1'
|
||||
const path = await writeJsonl(user(id(1), 'hello'))
|
||||
|
||||
const result = await loadConversationForResume('fixture', path)
|
||||
expect(result).not.toBeNull()
|
||||
expect(result?.sessionId).toBe(sessionId)
|
||||
expect(result?.messages.length).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
test('loadConversationForResume rejects oversized reconstructed transcripts', async () => {
|
||||
process.env.CLAUDE_CODE_SIMPLE = '1'
|
||||
const hugeContent = 'x'.repeat(8 * 1024 * 1024 + 32 * 1024)
|
||||
const path = await writeJsonl(user(id(2), hugeContent))
|
||||
|
||||
let caught: unknown
|
||||
try {
|
||||
await loadConversationForResume('fixture', path)
|
||||
} catch (error) {
|
||||
caught = error
|
||||
}
|
||||
|
||||
expect(caught).toBeInstanceOf(ResumeTranscriptTooLargeError)
|
||||
expect((caught as Error).message).toContain(
|
||||
'Reconstructed transcript is too large to resume safely',
|
||||
)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user