summaryrefslogtreecommitdiff
path: root/packages/server/src/core/logger.test.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/server/src/core/logger.test.ts')
-rw-r--r--packages/server/src/core/logger.test.ts197
1 files changed, 197 insertions, 0 deletions
diff --git a/packages/server/src/core/logger.test.ts b/packages/server/src/core/logger.test.ts
new file mode 100644
index 00000000..9b4f4555
--- /dev/null
+++ b/packages/server/src/core/logger.test.ts
@@ -0,0 +1,197 @@
+/**
+ * @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();
+ });
+ });
+});