@hz-9/a5-core 0.2.0-alpha.1
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/LICENSE +21 -0
- package/README.md +15 -0
- package/dist/all.d.ts +676 -0
- package/dist/const/index.d.ts +38 -0
- package/dist/const/index.js +46 -0
- package/dist/core/__import-reflect-metadata.d.ts +1 -0
- package/dist/core/__import-reflect-metadata.js +4 -0
- package/dist/core/a5-application.d.ts +154 -0
- package/dist/core/a5-application.js +315 -0
- package/dist/core/a5-console-logger.d.ts +163 -0
- package/dist/core/a5-console-logger.js +354 -0
- package/dist/core/a5-factory.d.ts +21 -0
- package/dist/core/a5-factory.js +49 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.js +20 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +24 -0
- package/dist/interface/base.d.ts +18 -0
- package/dist/interface/base.js +3 -0
- package/dist/interface/http.d.ts +16 -0
- package/dist/interface/http.js +3 -0
- package/dist/interface/index.d.ts +3 -0
- package/dist/interface/index.js +20 -0
- package/dist/interface/provide-token.d.ts +21 -0
- package/dist/interface/provide-token.js +3 -0
- package/dist/middleware/a5-console-logger.middleware.d.ts +9 -0
- package/dist/middleware/a5-console-logger.middleware.js +58 -0
- package/dist/middleware/index.d.ts +1 -0
- package/dist/middleware/index.js +18 -0
- package/dist/module/config/index.d.ts +18 -0
- package/dist/module/config/index.js +36 -0
- package/dist/module/config/interface.d.ts +9 -0
- package/dist/module/config/interface.js +15 -0
- package/dist/module/index.d.ts +2 -0
- package/dist/module/index.js +19 -0
- package/dist/module/log/index.d.ts +18 -0
- package/dist/module/log/index.js +44 -0
- package/dist/module/log/interface.d.ts +9 -0
- package/dist/module/log/interface.js +15 -0
- package/dist/plugins/index.d.ts +1 -0
- package/dist/plugins/index.js +18 -0
- package/dist/plugins/nanoid.d.ts +1 -0
- package/dist/plugins/nanoid.js +6 -0
- package/dist/test/integration/core/a5-factory.integration.spec.d.ts +1 -0
- package/dist/test/integration/core/a5-factory.integration.spec.js +99 -0
- package/dist/test/integration/core/with-logger-module.integration.spec.d.ts +1 -0
- package/dist/test/integration/core/with-logger-module.integration.spec.js +401 -0
- package/dist/test/unit/core/a5-application.unit.spec.d.ts +1 -0
- package/dist/test/unit/core/a5-application.unit.spec.js +450 -0
- package/dist/test/unit/core/a5-console-logger.unit.spec.d.ts +1 -0
- package/dist/test/unit/core/a5-console-logger.unit.spec.js +998 -0
- package/dist/test/unit/middleware/a5-console-logger.middleware.unit.spec.d.ts +1 -0
- package/dist/test/unit/middleware/a5-console-logger.middleware.unit.spec.js +379 -0
- package/dist/test/unit/util/a5.util.unit.spec.d.ts +1 -0
- package/dist/test/unit/util/a5.util.unit.spec.js +109 -0
- package/dist/test/unit/util/color.util.unit.spec.d.ts +1 -0
- package/dist/test/unit/util/color.util.unit.spec.js +277 -0
- package/dist/test/unit/util/logo.util.unit.spec.d.ts +1 -0
- package/dist/test/unit/util/logo.util.unit.spec.js +202 -0
- package/dist/test/unit/util/run-env.util.unit.spec.d.ts +1 -0
- package/dist/test/unit/util/run-env.util.unit.spec.js +183 -0
- package/dist/util/a5.util.d.ts +27 -0
- package/dist/util/a5.util.js +41 -0
- package/dist/util/color.util.d.ts +26 -0
- package/dist/util/color.util.js +62 -0
- package/dist/util/index.d.ts +5 -0
- package/dist/util/index.js +22 -0
- package/dist/util/load-package.util.d.ts +29 -0
- package/dist/util/load-package.util.js +71 -0
- package/dist/util/logo.util.d.ts +31 -0
- package/dist/util/logo.util.js +59 -0
- package/dist/util/run-env.util.d.ts +28 -0
- package/dist/util/run-env.util.js +48 -0
- package/logo +7 -0
- package/package.json +96 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const common_1 = require("@nestjs/common");
|
|
4
|
+
const a5_console_logger_1 = require("../../../core/a5-console-logger");
|
|
5
|
+
const a5_console_logger_middleware_1 = require("../../../middleware/a5-console-logger.middleware");
|
|
6
|
+
const color_util_1 = require("../../../util/color.util");
|
|
7
|
+
// Test helper class to access protected members
|
|
8
|
+
class TestableA5ConsoleLoggerMiddleware extends a5_console_logger_middleware_1.A5ConsoleLoggerMiddleware {
|
|
9
|
+
getLogger() {
|
|
10
|
+
return this.logger;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
describe('A5ConsoleLoggerMiddleware Unit Test', () => {
|
|
14
|
+
const toClearLines = (lines) => lines.map(([text]) => color_util_1.ColorUtil.clear(text.trim()));
|
|
15
|
+
// eslint-disable-next-line init-declarations
|
|
16
|
+
let middleware;
|
|
17
|
+
// eslint-disable-next-line init-declarations
|
|
18
|
+
let mockRequest;
|
|
19
|
+
// eslint-disable-next-line init-declarations
|
|
20
|
+
let mockResponse;
|
|
21
|
+
// eslint-disable-next-line init-declarations
|
|
22
|
+
let mockNext;
|
|
23
|
+
// eslint-disable-next-line init-declarations
|
|
24
|
+
let processStdoutWriteSpy;
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
middleware = new TestableA5ConsoleLoggerMiddleware();
|
|
27
|
+
common_1.Logger.overrideLogger(new a5_console_logger_1.A5ConsoleLogger());
|
|
28
|
+
mockNext = jest.fn();
|
|
29
|
+
// Mock request
|
|
30
|
+
mockRequest = {
|
|
31
|
+
method: 'GET',
|
|
32
|
+
url: '/test',
|
|
33
|
+
headers: {
|
|
34
|
+
referer: 'http://example.com',
|
|
35
|
+
'user-agent': 'Mozilla/5.0',
|
|
36
|
+
},
|
|
37
|
+
httpVersion: '1.1',
|
|
38
|
+
socket: {
|
|
39
|
+
remoteAddress: '127.0.0.1',
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
// Mock response
|
|
43
|
+
const mockListeners = {};
|
|
44
|
+
mockResponse = {
|
|
45
|
+
statusCode: 200,
|
|
46
|
+
write: jest.fn().mockReturnValue(true),
|
|
47
|
+
end: jest.fn().mockReturnValue(true),
|
|
48
|
+
on: jest.fn().mockImplementation((event, callback) => {
|
|
49
|
+
if (!mockListeners[event]) {
|
|
50
|
+
mockListeners[event] = [];
|
|
51
|
+
}
|
|
52
|
+
mockListeners[event].push(callback);
|
|
53
|
+
}),
|
|
54
|
+
emit: jest.fn().mockImplementation((event, ...args) => {
|
|
55
|
+
if (mockListeners[event]) {
|
|
56
|
+
mockListeners[event].forEach((callback) => callback(...args));
|
|
57
|
+
}
|
|
58
|
+
}),
|
|
59
|
+
};
|
|
60
|
+
// Spy on process.stdout.write to capture log output
|
|
61
|
+
processStdoutWriteSpy = jest.spyOn(process.stdout, 'write').mockImplementation();
|
|
62
|
+
jest.clearAllMocks();
|
|
63
|
+
});
|
|
64
|
+
afterEach(() => {
|
|
65
|
+
processStdoutWriteSpy.mockRestore();
|
|
66
|
+
jest.restoreAllMocks();
|
|
67
|
+
});
|
|
68
|
+
describe('[[Protected Properties]]', () => {
|
|
69
|
+
describe('logger', () => {
|
|
70
|
+
describe('instance', () => {
|
|
71
|
+
it('should have logger instance', () => {
|
|
72
|
+
const logger = middleware.getLogger();
|
|
73
|
+
expect(logger).toBeDefined();
|
|
74
|
+
expect(logger).toHaveProperty('log');
|
|
75
|
+
expect(typeof logger.log).toBe('function');
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe('[[Public Methods]]', () => {
|
|
81
|
+
describe('use', () => {
|
|
82
|
+
describe('req', () => {
|
|
83
|
+
it('should handle request with all required properties', () => {
|
|
84
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
85
|
+
expect(mockNext).toHaveBeenCalled();
|
|
86
|
+
});
|
|
87
|
+
it('should handle request without socket remoteAddress', () => {
|
|
88
|
+
mockRequest.socket = undefined;
|
|
89
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
90
|
+
mockResponse.emit('finish');
|
|
91
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
92
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
93
|
+
expect(lines.length).toBe(1);
|
|
94
|
+
expect(lines[0]).toMatch(/^\[A5\] \d+\s+- \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}\s+LOG \[HttpRequest\s+\] - - "GET \/test HTTP\/1\.1" 200 0 \d+ms "http:\/\/example\.com" "Mozilla\/5\.0"$/);
|
|
95
|
+
});
|
|
96
|
+
it('should handle request without referer header', () => {
|
|
97
|
+
delete mockRequest.headers.referer;
|
|
98
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
99
|
+
mockResponse.emit('finish');
|
|
100
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
101
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
102
|
+
expect(lines.length).toBe(1);
|
|
103
|
+
expect(lines[0]).toMatch(/^\[A5\] \d+\s+ - \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}\s+LOG \[HttpRequest\s+\] 127\.0\.0\.1 - "GET \/test HTTP\/1\.1" 200 0 \d+ms "-" "Mozilla\/5\.0"$/);
|
|
104
|
+
});
|
|
105
|
+
it('should handle request without user-agent header', () => {
|
|
106
|
+
delete mockRequest.headers['user-agent'];
|
|
107
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
108
|
+
mockResponse.emit('finish');
|
|
109
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
110
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
111
|
+
expect(lines.length).toBe(1);
|
|
112
|
+
expect(lines[0]).toMatch(/^\[A5\] \d+\s+ - \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}\s+LOG \[HttpRequest\s+\] 127\.0\.0\.1 - "GET \/test HTTP\/1\.1" 200 0 \d+ms "http:\/\/example\.com" "-"$/);
|
|
113
|
+
});
|
|
114
|
+
it('should handle POST request', () => {
|
|
115
|
+
mockRequest.method = 'POST';
|
|
116
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
117
|
+
mockResponse.emit('finish');
|
|
118
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
119
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
120
|
+
expect(lines.length).toBe(1);
|
|
121
|
+
expect(lines[0]).toMatch(/^\[A5\] \d+\s+ - \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}\s+LOG \[HttpRequest\s+\] 127\.0\.0\.1 - "POST \/test HTTP\/1\.1" 200 0 \d+ms "http:\/\/example\.com" "Mozilla\/5\.0"$/);
|
|
122
|
+
});
|
|
123
|
+
it('should handle PUT request', () => {
|
|
124
|
+
mockRequest.method = 'PUT';
|
|
125
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
126
|
+
mockResponse.emit('finish');
|
|
127
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
128
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
129
|
+
expect(lines.length).toBe(1);
|
|
130
|
+
expect(lines[0]).toMatch(/^\[A5\] \d+\s+ - \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}\s+LOG \[HttpRequest\s+\] 127\.0\.0\.1 - "PUT \/test HTTP\/1\.1" 200 0 \d+ms "http:\/\/example\.com" "Mozilla\/5\.0"$/);
|
|
131
|
+
});
|
|
132
|
+
it('should handle DELETE request', () => {
|
|
133
|
+
mockRequest.method = 'DELETE';
|
|
134
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
135
|
+
mockResponse.emit('finish');
|
|
136
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
137
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
138
|
+
expect(lines.length).toBe(1);
|
|
139
|
+
expect(lines[0]).toMatch(/^\[A5\] \d+\s+ - \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}\s+LOG \[HttpRequest\s+\] 127\.0\.0\.1 - "DELETE \/test HTTP\/1\.1" 200 0 \d+ms "http:\/\/example\.com" "Mozilla\/5\.0"$/);
|
|
140
|
+
});
|
|
141
|
+
it('should handle different HTTP versions', () => {
|
|
142
|
+
mockRequest.httpVersion = '2.0';
|
|
143
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
144
|
+
mockResponse.emit('finish');
|
|
145
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
146
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
147
|
+
expect(lines.length).toBe(1);
|
|
148
|
+
expect(lines[0]).toMatch(/^\[A5\] \d+\s+ - \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}\s+LOG \[HttpRequest\s+\] 127\.0\.0\.1 - "GET \/test HTTP\/2\.0" 200 0 \d+ms "http:\/\/example\.com" "Mozilla\/5\.0"$/);
|
|
149
|
+
});
|
|
150
|
+
it('should handle different URLs', () => {
|
|
151
|
+
mockRequest.url = '/api/users/123';
|
|
152
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
153
|
+
mockResponse.emit('finish');
|
|
154
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
155
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
156
|
+
expect(lines.length).toBe(1);
|
|
157
|
+
expect(lines[0]).toMatch(/^\[A5\] \d+\s+ - \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}\s+LOG \[HttpRequest\s+\] 127\.0\.0\.1 - "GET \/api\/users\/123 HTTP\/1\.1" 200 0 \d+ms "http:\/\/example\.com" "Mozilla\/5\.0"$/);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
describe('res', () => {
|
|
161
|
+
it('should handle response with 200 status code', () => {
|
|
162
|
+
mockResponse.statusCode = 200;
|
|
163
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
164
|
+
mockResponse.emit('finish');
|
|
165
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
166
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
167
|
+
expect(lines.length).toBe(1);
|
|
168
|
+
expect(lines[0]).toMatch(/^\[A5\] \d+\s+ - \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}\s+LOG \[HttpRequest\s+\] 127\.0\.0\.1 - "GET \/test HTTP\/1\.1" 200 0 \d+ms "http:\/\/example\.com" "Mozilla\/5\.0"$/);
|
|
169
|
+
});
|
|
170
|
+
it('should handle response with 404 status code', () => {
|
|
171
|
+
mockResponse.statusCode = 404;
|
|
172
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
173
|
+
mockResponse.emit('finish');
|
|
174
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
175
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
176
|
+
expect(lines.length).toBe(1);
|
|
177
|
+
expect(lines[0]).toMatch(/^\[A5\] \d+\s+ - \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}\s+LOG \[HttpRequest\s+\] 127\.0\.0\.1 - "GET \/test HTTP\/1\.1" 404 0 \d+ms "http:\/\/example\.com" "Mozilla\/5\.0"$/);
|
|
178
|
+
});
|
|
179
|
+
it('should handle response with 500 status code', () => {
|
|
180
|
+
mockResponse.statusCode = 500;
|
|
181
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
182
|
+
mockResponse.emit('finish');
|
|
183
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
184
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
185
|
+
expect(lines.length).toBe(1);
|
|
186
|
+
expect(lines[0]).toMatch(/^\[A5\] \d+\s+ - \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}\s+LOG \[HttpRequest\s+\] 127\.0\.0\.1 - "GET \/test HTTP\/1\.1" 500 0 \d+ms "http:\/\/example\.com" "Mozilla\/5\.0"$/);
|
|
187
|
+
});
|
|
188
|
+
it('should track bytes written through write method with string chunk', () => {
|
|
189
|
+
const testContent = 'test response content';
|
|
190
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
191
|
+
// Simulate writing content
|
|
192
|
+
mockResponse.write(testContent);
|
|
193
|
+
mockResponse.emit('finish');
|
|
194
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
195
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
196
|
+
expect(lines.length).toBe(1);
|
|
197
|
+
expect(lines[0]).toMatch(new RegExp(`^\\[A5\\] \\d+\\s+ - \\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}\\s+LOG \\[HttpRequest \\] 127\\.0\\.0\\.1 - "GET \\/test HTTP\\/1\\.1" 200 ${Buffer.byteLength(testContent)} \\d+ms "http:\\/\\/example\\.com" "Mozilla\\/5\\.0"$`));
|
|
198
|
+
});
|
|
199
|
+
it('should track bytes written through write method with Buffer chunk', () => {
|
|
200
|
+
const testBuffer = Buffer.from('test buffer content');
|
|
201
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
202
|
+
// Simulate writing buffer
|
|
203
|
+
mockResponse.write(testBuffer);
|
|
204
|
+
mockResponse.emit('finish');
|
|
205
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
206
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
207
|
+
expect(lines.length).toBe(1);
|
|
208
|
+
expect(lines[0]).toMatch(new RegExp(`^\\[A5\\] \\d+\\s+ - \\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}\\s+LOG \\[HttpRequest \\] 127\\.0\\.0\\.1 - "GET \\/test HTTP\\/1\\.1" 200 ${testBuffer.length} \\d+ms "http:\\/\\/example\\.com" "Mozilla\\/5\\.0"$`));
|
|
209
|
+
});
|
|
210
|
+
it('should track bytes written through end method with string chunk', () => {
|
|
211
|
+
const testContent = 'final response content';
|
|
212
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
213
|
+
// Simulate ending with content
|
|
214
|
+
mockResponse.end(testContent);
|
|
215
|
+
mockResponse.emit('finish');
|
|
216
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
217
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
218
|
+
expect(lines.length).toBe(1);
|
|
219
|
+
expect(lines[0]).toMatch(new RegExp(`^\\[A5\\] \\d+\\s+ - \\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}\\s+LOG \\[HttpRequest \\] 127\\.0\\.0\\.1 - "GET \\/test HTTP\\/1\\.1" 200 ${Buffer.byteLength(testContent)} \\d+ms "http:\\/\\/example\\.com" "Mozilla\\/5\\.0"$`));
|
|
220
|
+
});
|
|
221
|
+
it('should track bytes written through end method with Buffer chunk', () => {
|
|
222
|
+
const testBuffer = Buffer.from('final buffer content');
|
|
223
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
224
|
+
// Simulate ending with buffer
|
|
225
|
+
mockResponse.end(testBuffer);
|
|
226
|
+
mockResponse.emit('finish');
|
|
227
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
228
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
229
|
+
expect(lines.length).toBe(1);
|
|
230
|
+
expect(lines[0]).toMatch(new RegExp(`^\\[A5\\] \\d+\\s+ - \\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}\\s+LOG \\[HttpRequest \\] 127\\.0\\.0\\.1 - "GET \\/test HTTP\\/1\\.1" 200 ${testBuffer.length} \\d+ms "http:\\/\\/example\\.com" "Mozilla\\/5\\.0"$`));
|
|
231
|
+
});
|
|
232
|
+
it('should handle both write and end with content', () => {
|
|
233
|
+
const writeContent = 'first part';
|
|
234
|
+
const endContent = 'second part';
|
|
235
|
+
const totalBytes = Buffer.byteLength(writeContent) + Buffer.byteLength(endContent);
|
|
236
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
237
|
+
// Simulate writing and ending with content
|
|
238
|
+
mockResponse.write(writeContent);
|
|
239
|
+
mockResponse.end(endContent);
|
|
240
|
+
mockResponse.emit('finish');
|
|
241
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
242
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
243
|
+
expect(lines.length).toBe(1);
|
|
244
|
+
expect(lines[0]).toMatch(new RegExp(`^\\[A5\\] \\d+\\s+ - \\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}\\s+LOG \\[HttpRequest \\] 127\\.0\\.0\\.1 - "GET \\/test HTTP\\/1\\.1" 200 ${totalBytes} \\d+ms "http:\\/\\/example\\.com" "Mozilla\\/5\\.0"$`));
|
|
245
|
+
});
|
|
246
|
+
it('should handle write method with null chunk', () => {
|
|
247
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
248
|
+
// Simulate writing null
|
|
249
|
+
mockResponse.write(null);
|
|
250
|
+
mockResponse.emit('finish');
|
|
251
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
252
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
253
|
+
expect(lines.length).toBe(1);
|
|
254
|
+
expect(lines[0]).toMatch(/^\[A5\] \d+\s+ - \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}\s+LOG \[HttpRequest\s+\] 127\.0\.0\.1 - "GET \/test HTTP\/1\.1" 200 0 \d+ms "http:\/\/example\.com" "Mozilla\/5\.0"$/);
|
|
255
|
+
});
|
|
256
|
+
it('should handle end method with null chunk', () => {
|
|
257
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
258
|
+
// Simulate ending with null
|
|
259
|
+
mockResponse.end(null);
|
|
260
|
+
mockResponse.emit('finish');
|
|
261
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
262
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
263
|
+
expect(lines.length).toBe(1);
|
|
264
|
+
expect(lines[0]).toMatch(/^\[A5\] \d+\s+ - \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}\s+LOG \[HttpRequest\s+\] 127\.0\.0\.1 - "GET \/test HTTP\/1\.1" 200 0 \d+ms "http:\/\/example\.com" "Mozilla\/5\.0"$/);
|
|
265
|
+
});
|
|
266
|
+
it('should handle write method with undefined chunk', () => {
|
|
267
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
268
|
+
// Simulate writing undefined
|
|
269
|
+
mockResponse.write(undefined);
|
|
270
|
+
mockResponse.emit('finish');
|
|
271
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
272
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
273
|
+
expect(lines.length).toBe(1);
|
|
274
|
+
expect(lines[0]).toMatch(/^\[A5\] \d+\s+ - \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}\s+LOG \[HttpRequest\s+\] 127\.0\.0\.1 - "GET \/test HTTP\/1\.1" 200 0 \d+ms "http:\/\/example\.com" "Mozilla\/5\.0"$/);
|
|
275
|
+
});
|
|
276
|
+
it('should handle end method with undefined chunk', () => {
|
|
277
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
278
|
+
// Simulate ending with undefined
|
|
279
|
+
mockResponse.end(undefined);
|
|
280
|
+
mockResponse.emit('finish');
|
|
281
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
282
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
283
|
+
expect(lines.length).toBe(1);
|
|
284
|
+
expect(lines[0]).toMatch(/^\[A5\] \d+\s+ - \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}\s+LOG \[HttpRequest\s+\] 127\.0\.0\.1 - "GET \/test HTTP\/1\.1" 200 0 \d+ms "http:\/\/example\.com" "Mozilla\/5\.0"$/);
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
describe('next', () => {
|
|
288
|
+
it('should call next function immediately', () => {
|
|
289
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
290
|
+
expect(mockNext).toHaveBeenCalledWith();
|
|
291
|
+
expect(mockNext).toHaveBeenCalledTimes(1);
|
|
292
|
+
});
|
|
293
|
+
it('should call next function before response finish event', () => {
|
|
294
|
+
const callOrder = [];
|
|
295
|
+
mockNext.mockImplementation(() => {
|
|
296
|
+
callOrder.push('next');
|
|
297
|
+
});
|
|
298
|
+
// Mock the original stdout write to track log calls
|
|
299
|
+
processStdoutWriteSpy.mockImplementation((...args) => {
|
|
300
|
+
callOrder.push('log');
|
|
301
|
+
return true;
|
|
302
|
+
});
|
|
303
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
304
|
+
mockResponse.emit('finish');
|
|
305
|
+
expect(callOrder).toEqual(['next', 'log']);
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
describe('[[Default]]', () => {
|
|
309
|
+
it('should log request in Nginx access log format', () => {
|
|
310
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
311
|
+
mockResponse.emit('finish');
|
|
312
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
313
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
314
|
+
expect(lines.length).toBe(1);
|
|
315
|
+
expect(lines[0]).toMatch(/^\[A5\] \d+\s+ - \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}\s+LOG \[HttpRequest\s+\] 127\.0\.0\.1 - "GET \/test HTTP\/1\.1" 200 0 \d+ms "http:\/\/example\.com" "Mozilla\/5\.0"$/);
|
|
316
|
+
});
|
|
317
|
+
it('should calculate request time correctly', () => {
|
|
318
|
+
const startTime = Date.now();
|
|
319
|
+
jest
|
|
320
|
+
.spyOn(Date, 'now')
|
|
321
|
+
.mockReturnValueOnce(startTime) // First call in middleware
|
|
322
|
+
.mockReturnValueOnce(startTime + 100); // Second call in finish event
|
|
323
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
324
|
+
mockResponse.emit('finish');
|
|
325
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
326
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
327
|
+
expect(lines.length).toBe(1);
|
|
328
|
+
expect(lines[0]).toMatch(/^\[A5\] \d+\s+ - \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}\s+LOG \[HttpRequest\s+\] 127\.0\.0\.1 - "GET \/test HTTP\/1\.1" 200 0 100ms "http:\/\/example\.com" "Mozilla\/5\.0"$/);
|
|
329
|
+
});
|
|
330
|
+
it('should preserve original write and end methods functionality', () => {
|
|
331
|
+
const originalWrite = mockResponse.write;
|
|
332
|
+
const originalEnd = mockResponse.end;
|
|
333
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
334
|
+
// Verify that write and end methods are called
|
|
335
|
+
const testData = 'test data';
|
|
336
|
+
mockResponse.write(testData);
|
|
337
|
+
mockResponse.end(testData);
|
|
338
|
+
expect(originalWrite).toHaveBeenCalledWith(testData);
|
|
339
|
+
expect(originalEnd).toHaveBeenCalledWith(testData);
|
|
340
|
+
});
|
|
341
|
+
it('should handle multiple write calls before end', () => {
|
|
342
|
+
const data1 = 'first chunk';
|
|
343
|
+
const data2 = 'second chunk';
|
|
344
|
+
const data3 = 'third chunk';
|
|
345
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
346
|
+
mockResponse.write(data1);
|
|
347
|
+
mockResponse.write(data2);
|
|
348
|
+
mockResponse.end(data3);
|
|
349
|
+
mockResponse.emit('finish');
|
|
350
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
351
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
352
|
+
expect(lines.length).toBe(1);
|
|
353
|
+
expect(lines[0]).toMatch(/^\[A5\] \d+\s+- \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}\s+LOG \[HttpRequest\s+\] 127\.0\.0\.1 - "GET \/test HTTP\/1\.1" 200 \d+ \d+ms "http:\/\/example\.com" "Mozilla\/5\.0"$/);
|
|
354
|
+
});
|
|
355
|
+
it('should handle ArrayBuffer chunks in write method', () => {
|
|
356
|
+
const arrayBuffer = new ArrayBuffer(16);
|
|
357
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
358
|
+
mockResponse.write(arrayBuffer);
|
|
359
|
+
mockResponse.emit('finish');
|
|
360
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
361
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
362
|
+
expect(lines.length).toBe(1);
|
|
363
|
+
expect(lines[0]).toMatch(/^\[A5\] \d+\s+ - \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}\s+LOG \[HttpRequest\s+\] 127\.0\.0\.1 - "GET \/test HTTP\/1\.1" 200 16 \d+ms "http:\/\/example\.com" "Mozilla\/5\.0"$/);
|
|
364
|
+
});
|
|
365
|
+
it('should handle ArrayBuffer chunks in end method', () => {
|
|
366
|
+
const arrayBuffer = new ArrayBuffer(32);
|
|
367
|
+
middleware.use(mockRequest, mockResponse, mockNext);
|
|
368
|
+
mockResponse.end(arrayBuffer);
|
|
369
|
+
mockResponse.emit('finish');
|
|
370
|
+
expect(processStdoutWriteSpy).toHaveBeenCalled();
|
|
371
|
+
const lines = toClearLines(processStdoutWriteSpy.mock.calls);
|
|
372
|
+
expect(lines.length).toBe(1);
|
|
373
|
+
expect(lines[0]).toMatch(/^\[A5\] \d+\s+ - \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}\s+LOG \[HttpRequest\s+\] 127\.0\.0\.1 - "GET \/test HTTP\/1\.1" 200 32 \d+ms "http:\/\/example\.com" "Mozilla\/5\.0"$/);
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
//# sourceMappingURL=a5-console-logger.middleware.unit.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const node_path_1 = require("node:path");
|
|
4
|
+
const a5_util_1 = require("../../../util/a5.util");
|
|
5
|
+
describe('A5Util Unit Test', () => {
|
|
6
|
+
const originalEnv = process.env;
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
jest.resetModules();
|
|
9
|
+
process.env = { ...originalEnv };
|
|
10
|
+
});
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
process.env = originalEnv;
|
|
13
|
+
});
|
|
14
|
+
describe('[[Static Public Methods]]', () => {
|
|
15
|
+
describe('sleep', () => {
|
|
16
|
+
describe('t', () => {
|
|
17
|
+
it('should resolve after specified milliseconds for positive number', async () => {
|
|
18
|
+
const startTime = Date.now();
|
|
19
|
+
await a5_util_1.A5Util.sleep(100);
|
|
20
|
+
const endTime = Date.now();
|
|
21
|
+
expect(endTime - startTime).toBeGreaterThanOrEqual(90);
|
|
22
|
+
expect(endTime - startTime).toBeLessThan(150);
|
|
23
|
+
});
|
|
24
|
+
it('should resolve immediately for zero', async () => {
|
|
25
|
+
const startTime = Date.now();
|
|
26
|
+
await a5_util_1.A5Util.sleep(0);
|
|
27
|
+
const endTime = Date.now();
|
|
28
|
+
expect(endTime - startTime).toBeLessThan(50);
|
|
29
|
+
});
|
|
30
|
+
it('should resolve immediately for negative number', async () => {
|
|
31
|
+
const startTime = Date.now();
|
|
32
|
+
await a5_util_1.A5Util.sleep(-100);
|
|
33
|
+
const endTime = Date.now();
|
|
34
|
+
expect(endTime - startTime).toBeLessThan(50);
|
|
35
|
+
});
|
|
36
|
+
it('should resolve after specified milliseconds for large number', async () => {
|
|
37
|
+
const startTime = Date.now();
|
|
38
|
+
await a5_util_1.A5Util.sleep(200);
|
|
39
|
+
const endTime = Date.now();
|
|
40
|
+
expect(endTime - startTime).toBeGreaterThanOrEqual(190);
|
|
41
|
+
expect(endTime - startTime).toBeLessThan(250);
|
|
42
|
+
});
|
|
43
|
+
it('should resolve after specified milliseconds for decimal number', async () => {
|
|
44
|
+
const startTime = Date.now();
|
|
45
|
+
await a5_util_1.A5Util.sleep(50.5);
|
|
46
|
+
const endTime = Date.now();
|
|
47
|
+
expect(endTime - startTime).toBeGreaterThanOrEqual(40);
|
|
48
|
+
expect(endTime - startTime).toBeLessThan(100);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe('tryWithAbsolutePath', () => {
|
|
53
|
+
describe('sourcePath', () => {
|
|
54
|
+
it('should return normalized absolute path when sourcePath is absolute on Unix', () => {
|
|
55
|
+
const result = a5_util_1.A5Util.tryWithAbsolutePath('/home/user/file.txt', '/base/path');
|
|
56
|
+
expect(result).toBe('/home/user/file.txt');
|
|
57
|
+
});
|
|
58
|
+
// TODO: Add support for Windows
|
|
59
|
+
// it('should return normalized absolute path when sourcePath is absolute on Windows', () => {
|
|
60
|
+
// const result = A5Util.tryWithAbsolutePath('C:\\Users\\file.txt', '/base/path')
|
|
61
|
+
// expect(result).toBe('C:/Users/file.txt')
|
|
62
|
+
// })
|
|
63
|
+
it('should resolve relative path when sourcePath is relative', () => {
|
|
64
|
+
const result = a5_util_1.A5Util.tryWithAbsolutePath('relative/file.txt', '/base/path');
|
|
65
|
+
expect(result).toBe('/base/path/relative/file.txt');
|
|
66
|
+
});
|
|
67
|
+
it('should handle empty string sourcePath', () => {
|
|
68
|
+
const result = a5_util_1.A5Util.tryWithAbsolutePath('', '/base/path');
|
|
69
|
+
expect(result).toBe('/base/path');
|
|
70
|
+
});
|
|
71
|
+
it('should handle dot notation sourcePath', () => {
|
|
72
|
+
const result = a5_util_1.A5Util.tryWithAbsolutePath('./file.txt', '/base/path');
|
|
73
|
+
expect(result).toBe('/base/path/file.txt');
|
|
74
|
+
});
|
|
75
|
+
it('should handle double dot notation sourcePath', () => {
|
|
76
|
+
const result = a5_util_1.A5Util.tryWithAbsolutePath('../file.txt', '/base/path');
|
|
77
|
+
expect(result).toBe('/base/file.txt');
|
|
78
|
+
});
|
|
79
|
+
it('should handle sourcePath with special characters', () => {
|
|
80
|
+
const result = a5_util_1.A5Util.tryWithAbsolutePath('file with spaces.txt', '/base/path');
|
|
81
|
+
expect(result).toBe('/base/path/file with spaces.txt');
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
describe('basePath', () => {
|
|
85
|
+
it('should use basePath for resolving relative sourcePath', () => {
|
|
86
|
+
const result = a5_util_1.A5Util.tryWithAbsolutePath('file.txt', '/custom/base');
|
|
87
|
+
expect(result).toBe('/custom/base/file.txt');
|
|
88
|
+
});
|
|
89
|
+
it('should handle empty basePath', () => {
|
|
90
|
+
const result = a5_util_1.A5Util.tryWithAbsolutePath('file.txt', '');
|
|
91
|
+
expect((0, node_path_1.basename)(result)).toBe('file.txt');
|
|
92
|
+
});
|
|
93
|
+
it('should handle basePath with trailing slash', () => {
|
|
94
|
+
const result = a5_util_1.A5Util.tryWithAbsolutePath('file.txt', '/base/path/');
|
|
95
|
+
expect(result).toBe('/base/path/file.txt');
|
|
96
|
+
});
|
|
97
|
+
it('should handle basePath without leading slash', () => {
|
|
98
|
+
const result = a5_util_1.A5Util.tryWithAbsolutePath('file.txt', 'base/path');
|
|
99
|
+
expect(result).toContain('file.txt');
|
|
100
|
+
});
|
|
101
|
+
it('should handle basePath with special characters', () => {
|
|
102
|
+
const result = a5_util_1.A5Util.tryWithAbsolutePath('file.txt', '/base path/sub dir');
|
|
103
|
+
expect(result).toBe('/base path/sub dir/file.txt');
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
//# sourceMappingURL=a5.util.unit.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|