@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.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +15 -0
  3. package/dist/all.d.ts +676 -0
  4. package/dist/const/index.d.ts +38 -0
  5. package/dist/const/index.js +46 -0
  6. package/dist/core/__import-reflect-metadata.d.ts +1 -0
  7. package/dist/core/__import-reflect-metadata.js +4 -0
  8. package/dist/core/a5-application.d.ts +154 -0
  9. package/dist/core/a5-application.js +315 -0
  10. package/dist/core/a5-console-logger.d.ts +163 -0
  11. package/dist/core/a5-console-logger.js +354 -0
  12. package/dist/core/a5-factory.d.ts +21 -0
  13. package/dist/core/a5-factory.js +49 -0
  14. package/dist/core/index.d.ts +3 -0
  15. package/dist/core/index.js +20 -0
  16. package/dist/index.d.ts +7 -0
  17. package/dist/index.js +24 -0
  18. package/dist/interface/base.d.ts +18 -0
  19. package/dist/interface/base.js +3 -0
  20. package/dist/interface/http.d.ts +16 -0
  21. package/dist/interface/http.js +3 -0
  22. package/dist/interface/index.d.ts +3 -0
  23. package/dist/interface/index.js +20 -0
  24. package/dist/interface/provide-token.d.ts +21 -0
  25. package/dist/interface/provide-token.js +3 -0
  26. package/dist/middleware/a5-console-logger.middleware.d.ts +9 -0
  27. package/dist/middleware/a5-console-logger.middleware.js +58 -0
  28. package/dist/middleware/index.d.ts +1 -0
  29. package/dist/middleware/index.js +18 -0
  30. package/dist/module/config/index.d.ts +18 -0
  31. package/dist/module/config/index.js +36 -0
  32. package/dist/module/config/interface.d.ts +9 -0
  33. package/dist/module/config/interface.js +15 -0
  34. package/dist/module/index.d.ts +2 -0
  35. package/dist/module/index.js +19 -0
  36. package/dist/module/log/index.d.ts +18 -0
  37. package/dist/module/log/index.js +44 -0
  38. package/dist/module/log/interface.d.ts +9 -0
  39. package/dist/module/log/interface.js +15 -0
  40. package/dist/plugins/index.d.ts +1 -0
  41. package/dist/plugins/index.js +18 -0
  42. package/dist/plugins/nanoid.d.ts +1 -0
  43. package/dist/plugins/nanoid.js +6 -0
  44. package/dist/test/integration/core/a5-factory.integration.spec.d.ts +1 -0
  45. package/dist/test/integration/core/a5-factory.integration.spec.js +99 -0
  46. package/dist/test/integration/core/with-logger-module.integration.spec.d.ts +1 -0
  47. package/dist/test/integration/core/with-logger-module.integration.spec.js +401 -0
  48. package/dist/test/unit/core/a5-application.unit.spec.d.ts +1 -0
  49. package/dist/test/unit/core/a5-application.unit.spec.js +450 -0
  50. package/dist/test/unit/core/a5-console-logger.unit.spec.d.ts +1 -0
  51. package/dist/test/unit/core/a5-console-logger.unit.spec.js +998 -0
  52. package/dist/test/unit/middleware/a5-console-logger.middleware.unit.spec.d.ts +1 -0
  53. package/dist/test/unit/middleware/a5-console-logger.middleware.unit.spec.js +379 -0
  54. package/dist/test/unit/util/a5.util.unit.spec.d.ts +1 -0
  55. package/dist/test/unit/util/a5.util.unit.spec.js +109 -0
  56. package/dist/test/unit/util/color.util.unit.spec.d.ts +1 -0
  57. package/dist/test/unit/util/color.util.unit.spec.js +277 -0
  58. package/dist/test/unit/util/logo.util.unit.spec.d.ts +1 -0
  59. package/dist/test/unit/util/logo.util.unit.spec.js +202 -0
  60. package/dist/test/unit/util/run-env.util.unit.spec.d.ts +1 -0
  61. package/dist/test/unit/util/run-env.util.unit.spec.js +183 -0
  62. package/dist/util/a5.util.d.ts +27 -0
  63. package/dist/util/a5.util.js +41 -0
  64. package/dist/util/color.util.d.ts +26 -0
  65. package/dist/util/color.util.js +62 -0
  66. package/dist/util/index.d.ts +5 -0
  67. package/dist/util/index.js +22 -0
  68. package/dist/util/load-package.util.d.ts +29 -0
  69. package/dist/util/load-package.util.js +71 -0
  70. package/dist/util/logo.util.d.ts +31 -0
  71. package/dist/util/logo.util.js +59 -0
  72. package/dist/util/run-env.util.d.ts +28 -0
  73. package/dist/util/run-env.util.js +48 -0
  74. package/logo +7 -0
  75. package/package.json +96 -0
@@ -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 {};