1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
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();
});
});
});
|