@rollercoaster-dev/rd-logger
Version:
A neurodivergent-friendly logger for Rollercoaster.dev projects
109 lines (108 loc) • 4.58 kB
JavaScript
/// <reference types="jest" />
// Mock chalk to disable color codes in tests for easier string matching
jest.mock('chalk', () => {
const chalkMock = {
gray: (msg) => msg,
whiteBright: (msg) => msg,
blue: (msg) => msg,
green: (msg) => msg,
yellow: (msg) => msg,
red: (msg) => msg,
magenta: (msg) => msg,
dim: (msg) => msg,
cyan: (msg) => msg,
};
return Object.assign({ __esModule: true, default: chalkMock }, chalkMock);
});
import { Logger } from '../logger.service';
import { SensitiveValue } from '../sensitive';
describe('Sensitive Logging', () => {
let logger;
let consoleSpy;
beforeEach(() => {
jest.clearAllMocks();
logger = new Logger();
consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
});
afterEach(() => {
consoleSpy.mockRestore();
});
it('should log sensitive data with approval', () => {
const apiKey = 'secret-api-key-12345';
const approval = {
reason: 'Testing sensitive data logging',
approvedBy: 'Test Team'
};
logger.logWithSensitiveData('info', 'API key debug', { key: apiKey }, approval);
expect(consoleSpy).toHaveBeenCalled();
const logOutput = consoleSpy.mock.calls[0][0];
// Should include warning prefix
expect(logOutput).toContain('SENSITIVE DATA');
// Should include the actual sensitive data
expect(logOutput).toContain(apiKey);
// Should include approval information
expect(logOutput).toContain('Testing sensitive data logging');
expect(logOutput).toContain('Test Team');
});
it('should not log sensitive data without proper approval', () => {
const apiKey = 'secret-api-key-12345';
const approval = {
reason: '', // Empty reason
approvedBy: 'Test Team'
};
logger.logWithSensitiveData('info', 'API key debug', { key: apiKey }, approval);
expect(consoleSpy).toHaveBeenCalled();
const logOutput = consoleSpy.mock.calls[0][0];
// Should not include the sensitive data
expect(logOutput).not.toContain(apiKey);
// Should include warning about missing approval
expect(logOutput).toContain('without proper approval');
});
it('should not log sensitive data with expired approval', () => {
const apiKey = 'secret-api-key-12345';
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
const approval = {
reason: 'Testing sensitive data logging',
approvedBy: 'Test Team',
expiresAt: yesterday // Expired
};
logger.logWithSensitiveData('info', 'API key debug', { key: apiKey }, approval);
expect(consoleSpy).toHaveBeenCalled();
const logOutput = consoleSpy.mock.calls[0][0];
// Should not include the sensitive data
expect(logOutput).not.toContain(apiKey);
// Should include warning about expired approval
expect(logOutput).toContain('expired approval');
});
it('should provide convenience methods for common log levels', () => {
const apiKey = 'secret-api-key-12345';
const approval = {
reason: 'Testing sensitive data logging',
approvedBy: 'Test Team'
};
// Test info level
logger.infoWithSensitiveData('Info with sensitive data', { key: apiKey }, approval);
expect(consoleSpy).toHaveBeenCalled();
let logOutput = consoleSpy.mock.calls[0][0];
expect(logOutput).toContain('INFO');
expect(logOutput).toContain(apiKey);
consoleSpy.mockClear();
// Test error level
logger.errorWithSensitiveData('Error with sensitive data', { key: apiKey }, approval);
expect(consoleSpy).toHaveBeenCalled();
logOutput = consoleSpy.mock.calls[0][0];
expect(logOutput).toContain('ERROR');
expect(logOutput).toContain(apiKey);
});
it('should redact SensitiveValue instances in normal logging', () => {
const apiKey = new SensitiveValue('secret-api-key-12345');
logger.info('API key created', { key: apiKey });
expect(consoleSpy).toHaveBeenCalled();
const logOutput = consoleSpy.mock.calls[0][0];
// Should not include the actual sensitive data
expect(logOutput).not.toContain('secret-api-key-12345');
// Should include the redacted value
expect(logOutput).toContain('[REDACTED]');
});
});