myavana-bot-test-core 1.0.7 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
Binary file
@@ -0,0 +1,454 @@
1
+ // packages/core/__tests__/logger.test.js
2
+ const { Logger, createLogger, getLogger } = require('../src/logger');
3
+ const winston = require('winston');
4
+
5
+ // Mock winston
6
+ jest.mock('winston', () => ({
7
+ format: {
8
+ combine: jest.fn(),
9
+ timestamp: jest.fn(),
10
+ errors: jest.fn(),
11
+ json: jest.fn(),
12
+ printf: jest.fn(),
13
+ colorize: jest.fn(),
14
+ simple: jest.fn()
15
+ },
16
+ transports: {
17
+ Console: jest.fn(),
18
+ File: jest.fn()
19
+ },
20
+ createLogger: jest.fn()
21
+ }));
22
+
23
+ describe('Logger', () => {
24
+ let mockWinstonLogger;
25
+
26
+ beforeEach(() => {
27
+ mockWinstonLogger = {
28
+ info: jest.fn(),
29
+ error: jest.fn(),
30
+ warn: jest.fn(),
31
+ debug: jest.fn()
32
+ };
33
+ winston.createLogger.mockReturnValue(mockWinstonLogger);
34
+ });
35
+
36
+ afterEach(() => {
37
+ jest.clearAllMocks();
38
+ });
39
+
40
+ describe('Constructor', () => {
41
+ test('should create logger with default options', () => {
42
+ const logger = new Logger();
43
+
44
+ expect(logger.serviceName).toBe('myavana-chatbot');
45
+ expect(logger.environment).toBe('test'); // From test environment
46
+ expect(winston.createLogger).toHaveBeenCalled();
47
+ });
48
+
49
+ test('should create logger with custom options', () => {
50
+ const options = {
51
+ serviceName: 'custom-service',
52
+ environment: 'production',
53
+ logLevel: 'debug'
54
+ };
55
+
56
+ const logger = new Logger(options);
57
+
58
+ expect(logger.serviceName).toBe('custom-service');
59
+ expect(logger.environment).toBe('production');
60
+ expect(logger.logLevel).toBe('debug');
61
+ });
62
+ });
63
+
64
+ describe('Basic Logging Methods', () => {
65
+ let logger;
66
+
67
+ beforeEach(() => {
68
+ logger = new Logger();
69
+ });
70
+
71
+ test('should log info messages', () => {
72
+ const message = 'Test info message';
73
+ const meta = { userId: 'test-123' };
74
+
75
+ logger.info(message, meta);
76
+
77
+ expect(mockWinstonLogger.info).toHaveBeenCalledWith(message, meta);
78
+ });
79
+
80
+ test('should log error messages', () => {
81
+ const message = 'Test error message';
82
+ const meta = { error: 'Test error' };
83
+
84
+ logger.error(message, meta);
85
+
86
+ expect(mockWinstonLogger.error).toHaveBeenCalledWith(message, meta);
87
+ });
88
+
89
+ test('should log warning messages', () => {
90
+ const message = 'Test warning message';
91
+ const meta = { warning: true };
92
+
93
+ logger.warn(message, meta);
94
+
95
+ expect(mockWinstonLogger.warn).toHaveBeenCalledWith(message, meta);
96
+ });
97
+
98
+ test('should log debug messages', () => {
99
+ const message = 'Test debug message';
100
+ const meta = { debug: true };
101
+
102
+ logger.debug(message, meta);
103
+
104
+ expect(mockWinstonLogger.debug).toHaveBeenCalledWith(message, meta);
105
+ });
106
+ });
107
+
108
+ describe('Specialized Logging Methods', () => {
109
+ let logger;
110
+
111
+ beforeEach(() => {
112
+ logger = new Logger();
113
+ });
114
+
115
+ test('should log request information', () => {
116
+ const requestId = 'req-123';
117
+ const method = 'POST';
118
+ const url = '/api/chat';
119
+ const body = { message: 'test' };
120
+
121
+ logger.logRequest(requestId, method, url, body);
122
+
123
+ expect(mockWinstonLogger.info).toHaveBeenCalledWith(
124
+ 'Incoming request',
125
+ expect.objectContaining({
126
+ requestId,
127
+ method,
128
+ url,
129
+ bodySize: JSON.stringify(body).length
130
+ })
131
+ );
132
+ });
133
+
134
+ test('should log response information', () => {
135
+ const requestId = 'req-123';
136
+ const statusCode = 200;
137
+ const responseTime = 150;
138
+
139
+ logger.logResponse(requestId, statusCode, responseTime);
140
+
141
+ expect(mockWinstonLogger.info).toHaveBeenCalledWith(
142
+ 'Response sent',
143
+ expect.objectContaining({
144
+ requestId,
145
+ statusCode,
146
+ responseTime: '150ms'
147
+ })
148
+ );
149
+ });
150
+
151
+ test('should log AI interactions', () => {
152
+ const requestId = 'req-123';
153
+ const model = 'gemini-pro';
154
+ const prompt = 'Test prompt';
155
+ const response = { message: 'AI response' };
156
+ const responseTime = 2000;
157
+
158
+ logger.logAIInteraction(requestId, model, prompt, response, responseTime);
159
+
160
+ expect(mockWinstonLogger.info).toHaveBeenCalledWith(
161
+ 'AI interaction',
162
+ expect.objectContaining({
163
+ requestId,
164
+ model,
165
+ promptLength: prompt.length,
166
+ responseLength: JSON.stringify(response).length,
167
+ responseTime: '2000ms'
168
+ })
169
+ );
170
+ });
171
+
172
+ test('should log database operations', () => {
173
+ const operation = 'SELECT';
174
+ const table = 'users';
175
+ const duration = 50;
176
+ const success = true;
177
+
178
+ logger.logDatabaseOperation(operation, table, duration, success);
179
+
180
+ expect(mockWinstonLogger.info).toHaveBeenCalledWith(
181
+ 'Database operation',
182
+ expect.objectContaining({
183
+ operation,
184
+ table,
185
+ duration: '50ms',
186
+ success
187
+ })
188
+ );
189
+ });
190
+
191
+ test('should log cache operations', () => {
192
+ const operation = 'GET';
193
+ const key = 'user:123:profile';
194
+ const hit = true;
195
+ const duration = 5;
196
+
197
+ logger.logCacheOperation(operation, key, hit, duration);
198
+
199
+ expect(mockWinstonLogger.info).toHaveBeenCalledWith(
200
+ 'Cache operation',
201
+ expect.objectContaining({
202
+ operation,
203
+ key: expect.stringContaining('user:***'),
204
+ hit,
205
+ duration: '5ms'
206
+ })
207
+ );
208
+ });
209
+
210
+ test('should log user actions', () => {
211
+ const userId = 'user-123';
212
+ const action = 'send_message';
213
+ const conversationId = 'conv-456';
214
+
215
+ logger.logUserAction(userId, action, conversationId);
216
+
217
+ expect(mockWinstonLogger.info).toHaveBeenCalledWith(
218
+ 'User action',
219
+ expect.objectContaining({
220
+ userId,
221
+ action,
222
+ conversationId
223
+ })
224
+ );
225
+ });
226
+
227
+ test('should log errors with context', () => {
228
+ const error = new Error('Test error');
229
+ error.stack = 'Error stack trace';
230
+ const context = { userId: 'user-123' };
231
+
232
+ logger.logError(error, context);
233
+
234
+ expect(mockWinstonLogger.error).toHaveBeenCalledWith(
235
+ 'Application error',
236
+ expect.objectContaining({
237
+ message: error.message,
238
+ stack: error.stack,
239
+ name: error.name,
240
+ userId: 'user-123'
241
+ })
242
+ );
243
+ });
244
+
245
+ test('should log performance metrics', () => {
246
+ const operation = 'ai_response';
247
+ const duration = 3000;
248
+
249
+ logger.logPerformance(operation, duration);
250
+
251
+ expect(mockWinstonLogger.info).toHaveBeenCalledWith(
252
+ 'Performance metric',
253
+ expect.objectContaining({
254
+ operation,
255
+ duration: '3000ms',
256
+ slow: false
257
+ })
258
+ );
259
+ });
260
+
261
+ test('should warn for slow operations', () => {
262
+ const operation = 'slow_operation';
263
+ const duration = 6000;
264
+
265
+ logger.logPerformance(operation, duration);
266
+
267
+ expect(mockWinstonLogger.warn).toHaveBeenCalledWith(
268
+ 'Performance metric',
269
+ expect.objectContaining({
270
+ operation,
271
+ duration: '6000ms',
272
+ slow: true
273
+ })
274
+ );
275
+ });
276
+
277
+ test('should log security events', () => {
278
+ const event = 'suspicious_login';
279
+ const userId = 'user-123';
280
+ const severity = 'high';
281
+
282
+ logger.logSecurityEvent(event, userId, severity);
283
+
284
+ expect(mockWinstonLogger.error).toHaveBeenCalledWith(
285
+ 'Security event',
286
+ expect.objectContaining({
287
+ event,
288
+ userId,
289
+ severity,
290
+ timestamp: expect.any(String)
291
+ })
292
+ );
293
+ });
294
+
295
+ test('should log business metrics', () => {
296
+ const metric = 'conversation_completed';
297
+ const value = 1;
298
+ const userId = 'user-123';
299
+ const conversationId = 'conv-456';
300
+
301
+ logger.logBusinessMetric(metric, value, userId, conversationId);
302
+
303
+ expect(mockWinstonLogger.info).toHaveBeenCalledWith(
304
+ 'Business metric',
305
+ expect.objectContaining({
306
+ metric,
307
+ value,
308
+ userId,
309
+ conversationId
310
+ })
311
+ );
312
+ });
313
+ });
314
+
315
+ describe('Utility Methods', () => {
316
+ let logger;
317
+
318
+ beforeEach(() => {
319
+ logger = new Logger();
320
+ });
321
+
322
+ test('should sanitize cache keys', () => {
323
+ const key = 'user:12345:session:data';
324
+ const sanitized = logger.sanitizeKey(key);
325
+
326
+ expect(sanitized).toBe('user:***:session:data');
327
+ });
328
+
329
+ test('should truncate long keys', () => {
330
+ const longKey = 'a'.repeat(200);
331
+ const sanitized = logger.sanitizeKey(longKey);
332
+
333
+ expect(sanitized).toHaveLength(100);
334
+ });
335
+
336
+ test('should create child logger', () => {
337
+ const childMeta = { requestId: 'req-123' };
338
+ const child = logger.child(childMeta);
339
+
340
+ child.info('Test message', { additional: 'data' });
341
+
342
+ expect(mockWinstonLogger.info).toHaveBeenCalledWith(
343
+ 'Test message',
344
+ expect.objectContaining({
345
+ requestId: 'req-123',
346
+ additional: 'data'
347
+ })
348
+ );
349
+ });
350
+ });
351
+
352
+ describe('Middleware', () => {
353
+ let logger;
354
+ let middleware;
355
+ let req, res, next;
356
+
357
+ beforeEach(() => {
358
+ logger = new Logger();
359
+ middleware = logger.middleware();
360
+
361
+ req = {
362
+ method: 'POST',
363
+ url: '/api/chat',
364
+ body: { message: 'test' },
365
+ get: jest.fn().mockReturnValue('test-agent'),
366
+ ip: '127.0.0.1'
367
+ };
368
+
369
+ res = {
370
+ end: jest.fn()
371
+ };
372
+
373
+ next = jest.fn();
374
+ });
375
+
376
+ test('should add logger and requestId to request', () => {
377
+ middleware(req, res, next);
378
+
379
+ expect(req.logger).toBeDefined();
380
+ expect(req.requestId).toMatch(/^req_\d+_[a-z0-9]+$/);
381
+ expect(next).toHaveBeenCalled();
382
+ });
383
+
384
+ test('should log request', () => {
385
+ middleware(req, res, next);
386
+
387
+ expect(mockWinstonLogger.info).toHaveBeenCalledWith(
388
+ 'Incoming request',
389
+ expect.objectContaining({
390
+ requestId: req.requestId,
391
+ method: 'POST',
392
+ url: '/api/chat'
393
+ })
394
+ );
395
+ });
396
+
397
+ test('should log response when res.end is called', () => {
398
+ res.statusCode = 200;
399
+ middleware(req, res, next);
400
+
401
+ // Simulate response ending
402
+ res.end();
403
+
404
+ expect(mockWinstonLogger.info).toHaveBeenCalledWith(
405
+ 'Response sent',
406
+ expect.objectContaining({
407
+ requestId: req.requestId,
408
+ statusCode: 200,
409
+ responseTime: expect.any(Number)
410
+ })
411
+ );
412
+ });
413
+ });
414
+
415
+ describe('Singleton Functions', () => {
416
+ test('should create singleton logger instance', () => {
417
+ const logger1 = createLogger({ serviceName: 'test-service' });
418
+ const logger2 = getLogger();
419
+
420
+ expect(logger1).toBe(logger2);
421
+ expect(logger1.serviceName).toBe('test-service');
422
+ });
423
+
424
+ test('should create default logger when getLogger called first', () => {
425
+ // Clear any existing instance
426
+ jest.resetModules();
427
+ const { getLogger } = require('../src/logger');
428
+
429
+ const logger = getLogger();
430
+
431
+ expect(logger).toBeInstanceOf(Logger);
432
+ expect(logger.serviceName).toBe('myavana-chatbot');
433
+ });
434
+ });
435
+
436
+ describe('Transport Configuration', () => {
437
+ test('should configure console transport for development', () => {
438
+ process.env.NODE_ENV = 'development';
439
+
440
+ new Logger({ environment: 'development' });
441
+
442
+ expect(winston.transports.Console).toHaveBeenCalled();
443
+ });
444
+
445
+ test('should configure file transports for production', () => {
446
+ process.env.NODE_ENV = 'production';
447
+
448
+ new Logger({ environment: 'production' });
449
+
450
+ expect(winston.transports.Console).toHaveBeenCalled();
451
+ expect(winston.transports.File).toHaveBeenCalledTimes(3); // error, combined, performance
452
+ });
453
+ });
454
+ });