diff --git a/src/server/__tests__/sessions.test.ts b/src/server/__tests__/sessions.test.ts index 040009ba..38a56d4c 100644 --- a/src/server/__tests__/sessions.test.ts +++ b/src/server/__tests__/sessions.test.ts @@ -8,6 +8,8 @@ import { execFileSync } from 'node:child_process' import * as path from 'node:path' import * as os from 'node:os' import { SessionService } from '../services/sessionService.js' +import { sessionService } from '../services/sessionService.js' +import { conversationService } from '../services/conversationService.js' import { clearCommandsCache } from '../../commands.js' import { sanitizePath } from '../../utils/sessionStoragePortable.js' @@ -1161,6 +1163,31 @@ describe('Sessions API', () => { expect(res2.status).toBe(404) }) + it('DELETE /api/sessions/:id should roll back the deleted marker when file deletion fails', async () => { + const sessionId = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' + await writeSessionFile('-tmp-api-test', sessionId, [makeSnapshotEntry()]) + + const originalDeleteSession = sessionService.deleteSession.bind(sessionService) + sessionService.deleteSession = (async (targetSessionId: string) => { + if (targetSessionId === sessionId) { + throw new Error('simulated unlink failure') + } + return originalDeleteSession(targetSessionId) + }) as typeof sessionService.deleteSession + + try { + const res = await fetch(`${baseUrl}/api/sessions/${sessionId}`, { method: 'DELETE' }) + expect(res.status).toBe(500) + expect((conversationService as any).deletedSessions.has(sessionId)).toBe(false) + + const detailRes = await fetch(`${baseUrl}/api/sessions/${sessionId}`) + expect(detailRes.status).toBe(200) + } finally { + sessionService.deleteSession = originalDeleteSession as typeof sessionService.deleteSession + conversationService.unmarkSessionDeleted(sessionId) + } + }) + it('PATCH /api/sessions/:id should rename the session', async () => { const sessionId = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' await writeSessionFile('-tmp-api-test', sessionId, [ diff --git a/src/server/api/sessions.ts b/src/server/api/sessions.ts index 0c940c04..3b2fed64 100644 --- a/src/server/api/sessions.ts +++ b/src/server/api/sessions.ts @@ -316,7 +316,12 @@ function isSessionNotFoundError(error: unknown): error is Error { async function deleteSession(sessionId: string): Promise { conversationService.markSessionDeleted(sessionId) - await sessionService.deleteSession(sessionId) + try { + await sessionService.deleteSession(sessionId) + } catch (error) { + conversationService.unmarkSessionDeleted(sessionId) + throw error + } return Response.json({ ok: true }) } diff --git a/src/server/services/conversationService.ts b/src/server/services/conversationService.ts index f7a20640..8457d508 100644 --- a/src/server/services/conversationService.ts +++ b/src/server/services/conversationService.ts @@ -545,6 +545,10 @@ export class ConversationService { this.stopSession(sessionId) } + unmarkSessionDeleted(sessionId: string): void { + this.deletedSessions.delete(sessionId) + } + getActiveSessions(): string[] { return Array.from(this.sessions.keys()) }