Skip to content

Commit 217438a

Browse files
authored
🚀 release: v1.3.0 - Merge pull request #10 from wgtechlabs/dev
2 parents 9376bbc + 5726460 commit 217438a

28 files changed

+4123
-375
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,5 @@ dist
134134
.yarn/build-state.yml
135135
.yarn/install-state.gz
136136
.pnp.*
137+
138+
examples/

README.md

Lines changed: 278 additions & 13 deletions
Large diffs are not rendered by default.

jest.config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
/**
2+
* Jest configuration for Log Engine testing
3+
* Provides comprehensive test coverage with TypeScript support
4+
*/
15
module.exports = {
26
preset: 'ts-jest',
37
testEnvironment: 'node',

package.json

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
{
22
"name": "@wgtechlabs/log-engine",
3-
"version": "1.2.1",
4-
"description": "A lightweight and efficient logging utility designed specifically for bot applications running on Node.js.",
3+
"version": "1.3.0",
4+
"description": "A lightweight, security-first logging utility with automatic data redaction for Node.js applications - the first logging library with built-in PII protection.",
55
"keywords": [
66
"logging",
7+
"security",
8+
"redaction",
9+
"pii-protection",
10+
"data-redaction",
11+
"typescript",
12+
"nodejs",
713
"bot",
814
"discord",
915
"telegram",
10-
"nodejs",
11-
"typescript"
16+
"privacy",
17+
"gdpr",
18+
"compliance"
1219
],
1320
"license": "AGPL-3.0",
1421
"author": "WG Tech Labs <opensource@wgtechlabs.com> (https://wgtechlabs.com)",

src/__tests__/environment.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
*/
55

66
import { setupConsoleMocks, restoreConsoleMocks, ConsoleMocks } from './test-utils';
7+
import { EnvironmentDetector } from '../logger/environment';
8+
import { LogMode } from '../types';
79

810
describe('Environment-based configuration', () => {
911
const originalNodeEnv = process.env.NODE_ENV;
@@ -20,6 +22,58 @@ describe('Environment-based configuration', () => {
2022
restoreConsoleMocks(mocks);
2123
});
2224

25+
describe('EnvironmentDetector direct tests', () => {
26+
it('should detect test environment correctly', () => {
27+
process.env.NODE_ENV = 'test';
28+
expect(EnvironmentDetector.isTestEnvironment()).toBe(true);
29+
expect(EnvironmentDetector.isDevelopmentEnvironment()).toBe(false);
30+
expect(EnvironmentDetector.isProductionEnvironment()).toBe(false);
31+
});
32+
33+
it('should detect development environment correctly', () => {
34+
process.env.NODE_ENV = 'development';
35+
expect(EnvironmentDetector.isDevelopmentEnvironment()).toBe(true);
36+
expect(EnvironmentDetector.isTestEnvironment()).toBe(false);
37+
expect(EnvironmentDetector.isProductionEnvironment()).toBe(false);
38+
});
39+
40+
it('should detect production environment correctly', () => {
41+
process.env.NODE_ENV = 'production';
42+
expect(EnvironmentDetector.isProductionEnvironment()).toBe(true);
43+
expect(EnvironmentDetector.isTestEnvironment()).toBe(false);
44+
expect(EnvironmentDetector.isDevelopmentEnvironment()).toBe(false);
45+
});
46+
47+
it('should return correct log mode for each environment', () => {
48+
process.env.NODE_ENV = 'development';
49+
expect(EnvironmentDetector.getEnvironmentMode()).toBe(LogMode.DEBUG);
50+
51+
process.env.NODE_ENV = 'production';
52+
expect(EnvironmentDetector.getEnvironmentMode()).toBe(LogMode.INFO);
53+
54+
process.env.NODE_ENV = 'staging';
55+
expect(EnvironmentDetector.getEnvironmentMode()).toBe(LogMode.WARN);
56+
57+
process.env.NODE_ENV = 'test';
58+
expect(EnvironmentDetector.getEnvironmentMode()).toBe(LogMode.ERROR);
59+
60+
process.env.NODE_ENV = 'unknown';
61+
expect(EnvironmentDetector.getEnvironmentMode()).toBe(LogMode.INFO);
62+
});
63+
64+
it('should handle whitespace in NODE_ENV', () => {
65+
process.env.NODE_ENV = ' production ';
66+
expect(EnvironmentDetector.isProductionEnvironment()).toBe(true);
67+
expect(EnvironmentDetector.getEnvironmentMode()).toBe(LogMode.INFO);
68+
});
69+
70+
it('should handle case insensitive NODE_ENV', () => {
71+
process.env.NODE_ENV = 'PRODUCTION';
72+
expect(EnvironmentDetector.isProductionEnvironment()).toBe(true);
73+
expect(EnvironmentDetector.getEnvironmentMode()).toBe(LogMode.INFO);
74+
});
75+
});
76+
2377
it('should set INFO mode for production environment', () => {
2478
// Test production environment auto-configuration (show important info and above)
2579
process.env.NODE_ENV = 'production';

src/__tests__/formatter.test.ts

Lines changed: 124 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import { LogFormatter } from '../formatter';
77
import { LogLevel } from '../types';
8+
import { styleData } from '../formatter/data-formatter';
89

910
describe('LogFormatter', () => {
1011
it('should format messages with timestamp and level', () => {
@@ -94,8 +95,9 @@ describe('LogFormatter', () => {
9495
expect(formatted).toContain(message);
9596

9697
// Should contain timestamp components
97-
expect(formatted).toMatch(/\[\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z\]/); // ISO timestamp
98-
expect(formatted).toMatch(/\[\d{1,2}:\d{2}[AP]M\]/); // Local time
98+
const cleanFormatted = formatted.replace(/\x1b\[[0-9;]*m/g, '');
99+
expect(cleanFormatted).toMatch(/\[\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z\]/); // ISO timestamp
100+
expect(cleanFormatted).toMatch(/\[\d{1,2}:\d{2}[AP]M\]/); // Local time
99101
});
100102

101103
it('should format system messages with colors', () => {
@@ -122,4 +124,124 @@ describe('LogFormatter', () => {
122124
expect(cleanFormatted).toMatch(formatPattern);
123125
});
124126
});
127+
128+
describe('formatData edge cases', () => {
129+
it('should handle null values', () => {
130+
// Test null handling (covers line 98)
131+
const formatted = LogFormatter.format(LogLevel.INFO, 'Message', null);
132+
expect(formatted).toContain('null');
133+
});
134+
135+
it('should handle undefined values', () => {
136+
// Test undefined handling (covers line 102)
137+
const formatted = LogFormatter.format(LogLevel.INFO, 'Message', undefined);
138+
expect(formatted).toContain('Message');
139+
// The formatted message should not contain 'undefined'
140+
// Remove ANSI color codes before pattern matching
141+
const cleanFormatted = formatted.replace(/\x1b\[[0-9;]*m/g, '');
142+
expect(cleanFormatted).toMatch(/Message$/);
143+
});
144+
145+
it('should handle number values', () => {
146+
// Test number handling (covers line 106)
147+
const formatted = LogFormatter.format(LogLevel.INFO, 'Message', 42);
148+
expect(formatted).toContain('42');
149+
});
150+
151+
it('should handle boolean values', () => {
152+
// Test boolean handling (covers line 110)
153+
const formatted = LogFormatter.format(LogLevel.INFO, 'Message', true);
154+
expect(formatted).toContain('true');
155+
156+
const formatted2 = LogFormatter.format(LogLevel.INFO, 'Message', false);
157+
expect(formatted2).toContain('false');
158+
});
159+
160+
it('should handle objects that cannot be stringified', () => {
161+
// Test JSON.stringify error handling (covers line 117)
162+
const circularObj: any = {};
163+
circularObj.self = circularObj; // Create circular reference
164+
165+
const formatted = LogFormatter.format(LogLevel.INFO, 'Message', circularObj);
166+
expect(formatted).toContain('[Object]');
167+
});
168+
169+
it('should handle string data types', () => {
170+
// Test to cover line 106 - string handling
171+
const formatted = LogFormatter.format(LogLevel.INFO, 'Message', 'test string');
172+
expect(formatted).toContain('test string');
173+
});
174+
175+
it('should handle undefined values in formatData', () => {
176+
// Test to cover line 102 - undefined returns empty string
177+
// Import the formatData function from the new modular structure
178+
const { formatData } = require('../formatter/data-formatter');
179+
const result = formatData(undefined);
180+
expect(result).toBe('');
181+
});
182+
183+
it('should handle Symbol values in formatData', () => {
184+
// Test to verify Symbol handling - should return string representation
185+
const { formatData } = require('../formatter/data-formatter');
186+
const testSymbol = Symbol('test');
187+
const result = formatData(testSymbol);
188+
expect(typeof result).toBe('string');
189+
expect(result).toBe('Symbol(test)');
190+
});
191+
192+
it('should handle Symbol values without description in formatData', () => {
193+
// Test to verify Symbol handling for symbols without description
194+
const { formatData } = require('../formatter/data-formatter');
195+
const testSymbol = Symbol();
196+
const result = formatData(testSymbol);
197+
expect(typeof result).toBe('string');
198+
expect(result).toBe('Symbol()');
199+
});
200+
});
201+
202+
describe('styleData function', () => {
203+
const mockColors = { data: '\x1b[36m', reset: '\x1b[0m' };
204+
205+
it('should return empty string for empty dataString', () => {
206+
// Test to cover line 45 - empty string case
207+
expect(styleData('', mockColors)).toBe('');
208+
});
209+
210+
it('should return empty string for null/undefined dataString', () => {
211+
// Test to cover line 45 - falsy values
212+
expect(styleData(null as any, mockColors)).toBe('');
213+
expect(styleData(undefined as any, mockColors)).toBe('');
214+
});
215+
216+
it('should style non-empty data string correctly', () => {
217+
// Test normal case
218+
const result = styleData('test data', mockColors);
219+
expect(result).toBe(` ${mockColors.data}test data${mockColors.reset}`);
220+
});
221+
});
222+
223+
describe('Module exports', () => {
224+
it('should export all formatter functions and classes', () => {
225+
const formatter = require('../formatter');
226+
227+
// Test that all expected exports are available
228+
expect(formatter.MessageFormatter).toBeDefined();
229+
expect(formatter.LogFormatter).toBeDefined(); // Backward compatibility
230+
expect(formatter.colors).toBeDefined();
231+
expect(formatter.colorScheme).toBeDefined();
232+
expect(formatter.getTimestampComponents).toBeDefined();
233+
expect(formatter.formatTimestamp).toBeDefined();
234+
expect(formatter.formatData).toBeDefined();
235+
expect(formatter.styleData).toBeDefined();
236+
237+
// Test that LogFormatter is alias for MessageFormatter
238+
expect(formatter.LogFormatter).toBe(formatter.MessageFormatter);
239+
240+
// Test that functions are callable
241+
expect(typeof formatter.getTimestampComponents).toBe('function');
242+
expect(typeof formatter.formatTimestamp).toBe('function');
243+
expect(typeof formatter.formatData).toBe('function');
244+
expect(typeof formatter.styleData).toBe('function');
245+
});
246+
});
125247
});

src/__tests__/log-engine.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,37 @@ it('should log LOG level messages regardless of configuration', () => {
223223
expect(mocks.mockConsoleError).not.toHaveBeenCalled();
224224
});
225225
});
226+
227+
describe('Main module exports', () => {
228+
it('should export LogEngine and all types', () => {
229+
const main = require('../index');
230+
231+
// Test that main exports are available
232+
expect(main.LogEngine).toBeDefined();
233+
expect(main.LogLevel).toBeDefined();
234+
expect(main.LogMode).toBeDefined();
235+
236+
// Test that LogEngine has expected methods
237+
expect(typeof main.LogEngine.debug).toBe('function');
238+
expect(typeof main.LogEngine.info).toBe('function');
239+
expect(typeof main.LogEngine.warn).toBe('function');
240+
expect(typeof main.LogEngine.error).toBe('function');
241+
expect(typeof main.LogEngine.log).toBe('function');
242+
});
243+
244+
it('should test withoutRedaction method', () => {
245+
const main = require('../index');
246+
const withoutRedaction = main.LogEngine.withoutRedaction();
247+
248+
// Test that withoutRedaction returns an object with logging methods
249+
expect(withoutRedaction).toBeDefined();
250+
expect(typeof withoutRedaction.debug).toBe('function');
251+
expect(typeof withoutRedaction.info).toBe('function');
252+
expect(typeof withoutRedaction.warn).toBe('function');
253+
expect(typeof withoutRedaction.error).toBe('function');
254+
expect(typeof withoutRedaction.log).toBe('function');
255+
});
256+
});
226257
});
227258

228259

0 commit comments

Comments
 (0)