mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-12 15:10:59 -07:00
198 lines
6.3 KiB
TypeScript
198 lines
6.3 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2025 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
import { Logger, MessageSenderType } from './logger.js';
|
|
|
|
// Mocks
|
|
const mockDb = {
|
|
exec: vi.fn((_sql, callback) => callback?.(null)),
|
|
all: vi.fn((_sql, _params, callback) => callback?.(null, [])),
|
|
run: vi.fn((_sql, _params, callback) => callback?.(null)),
|
|
close: vi.fn((callback) => callback?.(null)),
|
|
};
|
|
|
|
vi.mock('sqlite3', () => ({
|
|
Database: vi.fn((_dbPath, _options, callback) => {
|
|
process.nextTick(() => callback?.(null));
|
|
return mockDb;
|
|
}),
|
|
default: {
|
|
Database: vi.fn((_dbPath, _options, callback) => {
|
|
process.nextTick(() => callback?.(null));
|
|
return mockDb;
|
|
}),
|
|
},
|
|
}));
|
|
|
|
describe('Logger', () => {
|
|
let logger: Logger;
|
|
|
|
beforeEach(async () => {
|
|
vi.resetAllMocks();
|
|
|
|
// Get a new instance for each test to ensure isolation,
|
|
logger = new Logger();
|
|
// We need to wait for the async initialize to complete
|
|
await logger.initialize().catch((err) => {
|
|
console.error('Error initializing logger:', err);
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
logger.close(); // Close the database connection after each test
|
|
});
|
|
|
|
describe('initialize', () => {
|
|
it('should execute create tables if not exists', async () => {
|
|
expect(mockDb.exec).toHaveBeenCalledWith(
|
|
expect.stringMatching(/CREATE TABLE IF NOT EXISTS messages/),
|
|
expect.any(Function),
|
|
);
|
|
});
|
|
|
|
it('should be idempotent', async () => {
|
|
mockDb.exec.mockClear();
|
|
|
|
await logger.initialize(); // Second call
|
|
|
|
expect(mockDb.exec).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('logMessage', () => {
|
|
it('should insert a message into the database', async () => {
|
|
const type = MessageSenderType.USER;
|
|
const message = 'Hello, world!';
|
|
await logger.logMessage(type, message);
|
|
expect(mockDb.run).toHaveBeenCalledWith(
|
|
"INSERT INTO messages (session_id, message_id, type, message, timestamp) VALUES (?, ?, ?, ?, datetime('now'))",
|
|
[expect.any(Number), 0, type, message], // sessionId, messageId, type, message
|
|
expect.any(Function),
|
|
);
|
|
});
|
|
|
|
it('should increment messageId for subsequent messages', async () => {
|
|
await logger.logMessage(MessageSenderType.USER, 'First message');
|
|
expect(mockDb.run).toHaveBeenCalledWith(
|
|
expect.any(String),
|
|
[expect.any(Number), 0, MessageSenderType.USER, 'First message'],
|
|
expect.any(Function),
|
|
);
|
|
await logger.logMessage(MessageSenderType.USER, 'Second message');
|
|
expect(mockDb.run).toHaveBeenCalledWith(
|
|
expect.any(String),
|
|
[expect.any(Number), 1, MessageSenderType.USER, 'Second message'], // messageId is now 1
|
|
expect.any(Function),
|
|
);
|
|
});
|
|
|
|
it('should handle database not initialized', async () => {
|
|
const uninitializedLogger = new Logger();
|
|
// uninitializedLogger.initialize() is not called
|
|
const consoleErrorSpy = vi
|
|
.spyOn(console, 'error')
|
|
.mockImplementation(() => {});
|
|
|
|
await uninitializedLogger.logMessage(MessageSenderType.USER, 'test');
|
|
|
|
expect(consoleErrorSpy).toHaveBeenCalledWith('Database not initialized.');
|
|
expect(mockDb.run).not.toHaveBeenCalled();
|
|
consoleErrorSpy.mockRestore();
|
|
});
|
|
|
|
it('should handle error during db.run', async () => {
|
|
const error = new Error('db.run failed');
|
|
mockDb.run.mockImplementationOnce(
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
(_sql: any, _params: any, callback: any) => callback?.(error),
|
|
);
|
|
|
|
await expect(
|
|
logger.logMessage(MessageSenderType.USER, 'test'),
|
|
).rejects.toThrow('db.run failed');
|
|
});
|
|
});
|
|
|
|
describe('getPreviousUserMessages', () => {
|
|
it('should query the database for messages', async () => {
|
|
mockDb.all.mockImplementationOnce(
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
(_sql: any, params: any, callback: any) =>
|
|
callback?.(null, [{ message: 'msg1' }, { message: 'msg2' }]),
|
|
);
|
|
|
|
const messages = await logger.getPreviousUserMessages();
|
|
|
|
expect(mockDb.all).toHaveBeenCalledWith(
|
|
expect.stringMatching(/SELECT message FROM messages/),
|
|
[],
|
|
expect.any(Function),
|
|
);
|
|
expect(messages).toEqual(['msg1', 'msg2']);
|
|
});
|
|
|
|
it('should handle database not initialized', async () => {
|
|
const uninitializedLogger = new Logger();
|
|
// uninitializedLogger.initialize() is not called
|
|
const consoleErrorSpy = vi
|
|
.spyOn(console, 'error')
|
|
.mockImplementation(() => {});
|
|
|
|
const messages = await uninitializedLogger.getPreviousUserMessages();
|
|
|
|
expect(consoleErrorSpy).toHaveBeenCalledWith('Database not initialized.');
|
|
expect(messages).toEqual([]);
|
|
expect(mockDb.all).not.toHaveBeenCalled();
|
|
consoleErrorSpy.mockRestore();
|
|
});
|
|
|
|
it('should handle error during db.all', async () => {
|
|
const error = new Error('db.all failed');
|
|
mockDb.all.mockImplementationOnce(
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
(_sql: any, _params: any, callback: any) => callback?.(error, []),
|
|
);
|
|
|
|
await expect(logger.getPreviousUserMessages()).rejects.toThrow(
|
|
'db.all failed',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('close', () => {
|
|
it('should close the database connection', () => {
|
|
logger.close();
|
|
expect(mockDb.close).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should handle database not initialized', () => {
|
|
const uninitializedLogger = new Logger();
|
|
// uninitializedLogger.initialize() is not called
|
|
uninitializedLogger.close();
|
|
expect(() => uninitializedLogger.close()).not.toThrow();
|
|
});
|
|
|
|
it('should handle error during db.close', () => {
|
|
const error = new Error('db.close failed');
|
|
mockDb.close.mockImplementationOnce((callback: (error: Error) => void) =>
|
|
callback?.(error),
|
|
);
|
|
const consoleErrorSpy = vi
|
|
.spyOn(console, 'error')
|
|
.mockImplementation(() => {});
|
|
|
|
logger.close();
|
|
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
'Error closing database:',
|
|
error.message,
|
|
);
|
|
consoleErrorSpy.mockRestore();
|
|
});
|
|
});
|
|
});
|