diff options
| author | Hiroaki Mitsuyoshi <[email protected]> | 2025-07-28 07:18:12 +0900 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-07-27 22:18:12 +0000 |
| commit | bce6eb501462b32fc629b6febb357ab679b6bd05 (patch) | |
| tree | a8981f709434ad79d4142e7680af2bcc7bba64a6 /packages/core | |
| parent | 9ca48c00a6bfbf8fd25bebfb703ef299c0e38ae2 (diff) | |
feat(chat): Implement /chat delete command (#2401)
Diffstat (limited to 'packages/core')
| -rw-r--r-- | packages/core/src/core/logger.test.ts | 62 | ||||
| -rw-r--r-- | packages/core/src/core/logger.ts | 24 |
2 files changed, 86 insertions, 0 deletions
diff --git a/packages/core/src/core/logger.test.ts b/packages/core/src/core/logger.test.ts index 84cc1a0f..c74b92cf 100644 --- a/packages/core/src/core/logger.test.ts +++ b/packages/core/src/core/logger.test.ts @@ -490,6 +490,68 @@ describe('Logger', () => { }); }); + describe('deleteCheckpoint', () => { + const conversation: Content[] = [ + { role: 'user', parts: [{ text: 'Content to be deleted' }] }, + ]; + const tag = 'delete-me'; + let taggedFilePath: string; + + beforeEach(async () => { + taggedFilePath = path.join( + TEST_GEMINI_DIR, + `${CHECKPOINT_FILE_NAME.replace('.json', '')}-${tag}.json`, + ); + // Create a file to be deleted + await fs.writeFile(taggedFilePath, JSON.stringify(conversation)); + }); + + it('should delete the specified checkpoint file and return true', async () => { + const result = await logger.deleteCheckpoint(tag); + expect(result).toBe(true); + + // Verify the file is actually gone + await expect(fs.access(taggedFilePath)).rejects.toThrow(/ENOENT/); + }); + + it('should return false if the checkpoint file does not exist', async () => { + const result = await logger.deleteCheckpoint('non-existent-tag'); + expect(result).toBe(false); + }); + + it('should re-throw an error if file deletion fails for reasons other than not existing', async () => { + // Simulate a different error (e.g., permission denied) + vi.spyOn(fs, 'unlink').mockRejectedValueOnce( + new Error('EACCES: permission denied'), + ); + const consoleErrorSpy = vi + .spyOn(console, 'error') + .mockImplementation(() => {}); + + await expect(logger.deleteCheckpoint(tag)).rejects.toThrow( + 'EACCES: permission denied', + ); + expect(consoleErrorSpy).toHaveBeenCalledWith( + `Failed to delete checkpoint file ${taggedFilePath}:`, + expect.any(Error), + ); + }); + + it('should return false if logger is not initialized', async () => { + const uninitializedLogger = new Logger(testSessionId); + uninitializedLogger.close(); + const consoleErrorSpy = vi + .spyOn(console, 'error') + .mockImplementation(() => {}); + + const result = await uninitializedLogger.deleteCheckpoint(tag); + expect(result).toBe(false); + expect(consoleErrorSpy).toHaveBeenCalledWith( + 'Logger not initialized or checkpoint file path not set. Cannot delete checkpoint.', + ); + }); + }); + describe('close', () => { it('should reset logger state', async () => { await logger.logMessage(MessageSenderType.USER, 'A message'); diff --git a/packages/core/src/core/logger.ts b/packages/core/src/core/logger.ts index 450a0d2f..2be9f1d4 100644 --- a/packages/core/src/core/logger.ts +++ b/packages/core/src/core/logger.ts @@ -292,6 +292,30 @@ export class Logger { } } + async deleteCheckpoint(tag: string): Promise<boolean> { + if (!this.initialized || !this.geminiDir) { + console.error( + 'Logger not initialized or checkpoint file path not set. Cannot delete checkpoint.', + ); + return false; + } + + const path = this._checkpointPath(tag); + + try { + await fs.unlink(path); + return true; + } catch (error) { + const nodeError = error as NodeJS.ErrnoException; + if (nodeError.code === 'ENOENT') { + // File doesn't exist, which is fine. + return false; + } + console.error(`Failed to delete checkpoint file ${path}:`, error); + throw error; + } + } + close(): void { this.initialized = false; this.logFilePath = undefined; |
