Files
gemini-nexus/src/tests/unit/logger.test.ts
T
Riccardo Venturini 5155e37fd1 fix: prevent sensitive data logging (#70)
* feat: Add sensitive data masking to packet logging by introducing a sanitization function and a list of sensitive keys.

* test: mock console methods in logger tests and restore after each test

* refactor: remove redundant sensitive data masking functions and integrate safeStringifyPacket utility for improved logging

* fix: run format:write

* test: enhance logger tests with error handling for directory setup and cleanup

* refactor: optimize sensitive data masking logic by checking for exact matches in sensitive keys

* refactor: enhance sensitive data masking to handle circular references and improve sanitization logic

* refactor: update sensitive data masking comments and test descriptions to improve clarity and consistency

* test: enhance logger tests with dynamic log directory creation and cleanup after tests

* test: add unit tests for handling malformed JSON strings in sensitive data masking
2026-02-08 14:31:04 +08:00

108 lines
3.1 KiB
TypeScript

import { describe, it, expect, vi, beforeEach, afterEach, afterAll } from 'vitest';
import { logger } from '../../utils/logger';
import fs from 'fs';
import path from 'path';
vi.mock('../../utils/paths', async () => {
const path = await import('path');
return {
getAgentDir: vi.fn(() => path.join(process.cwd(), 'temp_test_logs')),
};
});
describe('Logger Utilities', () => {
const testLogDir = path.join(process.cwd(), 'temp_test_logs');
const getLatestLogFile = () => {
const files = fs
.readdirSync(testLogDir)
.filter((file) => /^app-\d{4}-\d{2}-\d{2}(\.\d+)?\.log$/.test(file))
.sort();
if (files.length === 0) {
return null;
}
return path.join(testLogDir, files[files.length - 1]);
};
const waitForLogContains = async (text: string) => {
for (let i = 0; i < 20; i++) {
const filePath = getLatestLogFile();
if (filePath && fs.existsSync(filePath)) {
const content = fs.readFileSync(filePath, 'utf-8');
if (content.includes(text)) {
return filePath;
}
}
await new Promise((resolve) => setTimeout(resolve, 50));
}
return null;
};
beforeEach(() => {
vi.spyOn(console, 'log').mockImplementation(() => {});
vi.spyOn(console, 'error').mockImplementation(() => {});
try {
if (fs.existsSync(testLogDir)) {
fs.rmSync(testLogDir, { recursive: true, force: true });
}
fs.mkdirSync(testLogDir, { recursive: true });
} catch (err) {
console.error('beforeEach: setup testLogDir failed', err);
throw err;
}
});
afterEach(() => {
vi.restoreAllMocks();
try {
if (fs.existsSync(testLogDir)) {
fs.rmSync(testLogDir, { recursive: true, force: true });
}
} catch (err) {
console.error('afterEach: cleanup testLogDir failed', err);
throw err;
}
});
afterAll(() => {
try {
if (fs.existsSync(testLogDir)) {
fs.rmSync(testLogDir, { recursive: true, force: true });
}
} catch (err) {
console.error('afterAll: cleanup testLogDir failed', err);
}
});
it('should create rotated log file', async () => {
const message = 'Test message';
logger.info(message);
const filePath = await waitForLogContains(message);
expect(filePath).not.toBeNull();
expect(fs.existsSync(filePath as string)).toBe(true);
});
it('should write formatted message to file', async () => {
const message = 'Test info message';
logger.info(message);
const filePath = await waitForLogContains(message);
expect(filePath).not.toBeNull();
const content = fs.readFileSync(filePath as string, 'utf-8');
expect(content).toContain('[INFO]');
expect(content).toContain(message);
});
it('should log error messages', async () => {
const message = 'Test error message';
logger.error(message);
const filePath = await waitForLogContains(message);
expect(filePath).not.toBeNull();
const content = fs.readFileSync(filePath as string, 'utf-8');
expect(content).toContain('[ERROR]');
expect(content).toContain(message);
});
});