@hazeljs/core 0.2.0-beta.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 (194) hide show
  1. package/README.md +522 -0
  2. package/dist/__tests__/container.test.d.ts +2 -0
  3. package/dist/__tests__/container.test.d.ts.map +1 -0
  4. package/dist/__tests__/container.test.js +454 -0
  5. package/dist/__tests__/decorators.test.d.ts +2 -0
  6. package/dist/__tests__/decorators.test.d.ts.map +1 -0
  7. package/dist/__tests__/decorators.test.js +693 -0
  8. package/dist/__tests__/errors/http.error.test.d.ts +2 -0
  9. package/dist/__tests__/errors/http.error.test.d.ts.map +1 -0
  10. package/dist/__tests__/errors/http.error.test.js +117 -0
  11. package/dist/__tests__/filters/exception-filter.test.d.ts +2 -0
  12. package/dist/__tests__/filters/exception-filter.test.d.ts.map +1 -0
  13. package/dist/__tests__/filters/exception-filter.test.js +135 -0
  14. package/dist/__tests__/filters/http-exception.filter.test.d.ts +2 -0
  15. package/dist/__tests__/filters/http-exception.filter.test.d.ts.map +1 -0
  16. package/dist/__tests__/filters/http-exception.filter.test.js +119 -0
  17. package/dist/__tests__/hazel-app.test.d.ts +2 -0
  18. package/dist/__tests__/hazel-app.test.d.ts.map +1 -0
  19. package/dist/__tests__/hazel-app.test.js +682 -0
  20. package/dist/__tests__/hazel-module.test.d.ts +2 -0
  21. package/dist/__tests__/hazel-module.test.d.ts.map +1 -0
  22. package/dist/__tests__/hazel-module.test.js +408 -0
  23. package/dist/__tests__/hazel-response.test.d.ts +2 -0
  24. package/dist/__tests__/hazel-response.test.d.ts.map +1 -0
  25. package/dist/__tests__/hazel-response.test.js +138 -0
  26. package/dist/__tests__/health.test.d.ts +2 -0
  27. package/dist/__tests__/health.test.d.ts.map +1 -0
  28. package/dist/__tests__/health.test.js +147 -0
  29. package/dist/__tests__/index.test.d.ts +2 -0
  30. package/dist/__tests__/index.test.d.ts.map +1 -0
  31. package/dist/__tests__/index.test.js +239 -0
  32. package/dist/__tests__/interceptors/interceptor.test.d.ts +2 -0
  33. package/dist/__tests__/interceptors/interceptor.test.d.ts.map +1 -0
  34. package/dist/__tests__/interceptors/interceptor.test.js +166 -0
  35. package/dist/__tests__/logger.test.d.ts +2 -0
  36. package/dist/__tests__/logger.test.d.ts.map +1 -0
  37. package/dist/__tests__/logger.test.js +141 -0
  38. package/dist/__tests__/middleware/cors.test.d.ts +2 -0
  39. package/dist/__tests__/middleware/cors.test.d.ts.map +1 -0
  40. package/dist/__tests__/middleware/cors.test.js +129 -0
  41. package/dist/__tests__/middleware/csrf.test.d.ts +2 -0
  42. package/dist/__tests__/middleware/csrf.test.d.ts.map +1 -0
  43. package/dist/__tests__/middleware/csrf.test.js +247 -0
  44. package/dist/__tests__/middleware/global-middleware.test.d.ts +2 -0
  45. package/dist/__tests__/middleware/global-middleware.test.d.ts.map +1 -0
  46. package/dist/__tests__/middleware/global-middleware.test.js +259 -0
  47. package/dist/__tests__/middleware/rate-limit.test.d.ts +2 -0
  48. package/dist/__tests__/middleware/rate-limit.test.d.ts.map +1 -0
  49. package/dist/__tests__/middleware/rate-limit.test.js +264 -0
  50. package/dist/__tests__/middleware/security-headers.test.d.ts +2 -0
  51. package/dist/__tests__/middleware/security-headers.test.d.ts.map +1 -0
  52. package/dist/__tests__/middleware/security-headers.test.js +229 -0
  53. package/dist/__tests__/middleware/timeout.test.d.ts +2 -0
  54. package/dist/__tests__/middleware/timeout.test.d.ts.map +1 -0
  55. package/dist/__tests__/middleware/timeout.test.js +132 -0
  56. package/dist/__tests__/middleware.test.d.ts +2 -0
  57. package/dist/__tests__/middleware.test.d.ts.map +1 -0
  58. package/dist/__tests__/middleware.test.js +180 -0
  59. package/dist/__tests__/pipes/pipe.test.d.ts +2 -0
  60. package/dist/__tests__/pipes/pipe.test.d.ts.map +1 -0
  61. package/dist/__tests__/pipes/pipe.test.js +245 -0
  62. package/dist/__tests__/pipes/validation.pipe.test.d.ts +2 -0
  63. package/dist/__tests__/pipes/validation.pipe.test.d.ts.map +1 -0
  64. package/dist/__tests__/pipes/validation.pipe.test.js +297 -0
  65. package/dist/__tests__/request-parser.test.d.ts +2 -0
  66. package/dist/__tests__/request-parser.test.d.ts.map +1 -0
  67. package/dist/__tests__/request-parser.test.js +182 -0
  68. package/dist/__tests__/router.test.d.ts +2 -0
  69. package/dist/__tests__/router.test.d.ts.map +1 -0
  70. package/dist/__tests__/router.test.js +680 -0
  71. package/dist/__tests__/routing/route-matcher.test.d.ts +2 -0
  72. package/dist/__tests__/routing/route-matcher.test.d.ts.map +1 -0
  73. package/dist/__tests__/routing/route-matcher.test.js +219 -0
  74. package/dist/__tests__/routing/version.decorator.test.d.ts +2 -0
  75. package/dist/__tests__/routing/version.decorator.test.d.ts.map +1 -0
  76. package/dist/__tests__/routing/version.decorator.test.js +298 -0
  77. package/dist/__tests__/service.test.d.ts +2 -0
  78. package/dist/__tests__/service.test.d.ts.map +1 -0
  79. package/dist/__tests__/service.test.js +121 -0
  80. package/dist/__tests__/shutdown.test.d.ts +2 -0
  81. package/dist/__tests__/shutdown.test.d.ts.map +1 -0
  82. package/dist/__tests__/shutdown.test.js +250 -0
  83. package/dist/__tests__/testing/testing.module.test.d.ts +2 -0
  84. package/dist/__tests__/testing/testing.module.test.d.ts.map +1 -0
  85. package/dist/__tests__/testing/testing.module.test.js +370 -0
  86. package/dist/__tests__/upload/file-upload.test.d.ts +2 -0
  87. package/dist/__tests__/upload/file-upload.test.d.ts.map +1 -0
  88. package/dist/__tests__/upload/file-upload.test.js +498 -0
  89. package/dist/__tests__/utils/sanitize.test.d.ts +2 -0
  90. package/dist/__tests__/utils/sanitize.test.d.ts.map +1 -0
  91. package/dist/__tests__/utils/sanitize.test.js +291 -0
  92. package/dist/__tests__/validator.test.d.ts +2 -0
  93. package/dist/__tests__/validator.test.d.ts.map +1 -0
  94. package/dist/__tests__/validator.test.js +300 -0
  95. package/dist/container.d.ts +80 -0
  96. package/dist/container.d.ts.map +1 -0
  97. package/dist/container.js +271 -0
  98. package/dist/decorators.d.ts +92 -0
  99. package/dist/decorators.d.ts.map +1 -0
  100. package/dist/decorators.js +343 -0
  101. package/dist/errors/http.error.d.ts +31 -0
  102. package/dist/errors/http.error.d.ts.map +1 -0
  103. package/dist/errors/http.error.js +62 -0
  104. package/dist/filters/exception-filter.d.ts +39 -0
  105. package/dist/filters/exception-filter.d.ts.map +1 -0
  106. package/dist/filters/exception-filter.js +38 -0
  107. package/dist/filters/http-exception.filter.d.ts +9 -0
  108. package/dist/filters/http-exception.filter.d.ts.map +1 -0
  109. package/dist/filters/http-exception.filter.js +42 -0
  110. package/dist/hazel-app.d.ts +78 -0
  111. package/dist/hazel-app.d.ts.map +1 -0
  112. package/dist/hazel-app.js +453 -0
  113. package/dist/hazel-module.d.ts +20 -0
  114. package/dist/hazel-module.d.ts.map +1 -0
  115. package/dist/hazel-module.js +109 -0
  116. package/dist/hazel-response.d.ts +20 -0
  117. package/dist/hazel-response.d.ts.map +1 -0
  118. package/dist/hazel-response.js +68 -0
  119. package/dist/health.d.ts +73 -0
  120. package/dist/health.d.ts.map +1 -0
  121. package/dist/health.js +174 -0
  122. package/dist/index.d.ts +41 -0
  123. package/dist/index.d.ts.map +1 -0
  124. package/dist/index.js +140 -0
  125. package/dist/interceptors/interceptor.d.ts +22 -0
  126. package/dist/interceptors/interceptor.d.ts.map +1 -0
  127. package/dist/interceptors/interceptor.js +46 -0
  128. package/dist/logger.d.ts +8 -0
  129. package/dist/logger.d.ts.map +1 -0
  130. package/dist/logger.js +238 -0
  131. package/dist/middleware/cors.middleware.d.ts +44 -0
  132. package/dist/middleware/cors.middleware.d.ts.map +1 -0
  133. package/dist/middleware/cors.middleware.js +118 -0
  134. package/dist/middleware/csrf.middleware.d.ts +82 -0
  135. package/dist/middleware/csrf.middleware.d.ts.map +1 -0
  136. package/dist/middleware/csrf.middleware.js +183 -0
  137. package/dist/middleware/global-middleware.d.ts +111 -0
  138. package/dist/middleware/global-middleware.d.ts.map +1 -0
  139. package/dist/middleware/global-middleware.js +179 -0
  140. package/dist/middleware/rate-limit.middleware.d.ts +73 -0
  141. package/dist/middleware/rate-limit.middleware.d.ts.map +1 -0
  142. package/dist/middleware/rate-limit.middleware.js +124 -0
  143. package/dist/middleware/security-headers.middleware.d.ts +76 -0
  144. package/dist/middleware/security-headers.middleware.d.ts.map +1 -0
  145. package/dist/middleware/security-headers.middleware.js +123 -0
  146. package/dist/middleware/timeout.middleware.d.ts +25 -0
  147. package/dist/middleware/timeout.middleware.d.ts.map +1 -0
  148. package/dist/middleware/timeout.middleware.js +74 -0
  149. package/dist/middleware.d.ts +13 -0
  150. package/dist/middleware.d.ts.map +1 -0
  151. package/dist/middleware.js +47 -0
  152. package/dist/pipes/pipe.d.ts +50 -0
  153. package/dist/pipes/pipe.d.ts.map +1 -0
  154. package/dist/pipes/pipe.js +96 -0
  155. package/dist/pipes/validation.pipe.d.ts +6 -0
  156. package/dist/pipes/validation.pipe.d.ts.map +1 -0
  157. package/dist/pipes/validation.pipe.js +61 -0
  158. package/dist/request-context.d.ts +17 -0
  159. package/dist/request-context.d.ts.map +1 -0
  160. package/dist/request-context.js +2 -0
  161. package/dist/request-parser.d.ts +7 -0
  162. package/dist/request-parser.d.ts.map +1 -0
  163. package/dist/request-parser.js +60 -0
  164. package/dist/router.d.ts +33 -0
  165. package/dist/router.d.ts.map +1 -0
  166. package/dist/router.js +426 -0
  167. package/dist/routing/route-matcher.d.ts +39 -0
  168. package/dist/routing/route-matcher.d.ts.map +1 -0
  169. package/dist/routing/route-matcher.js +93 -0
  170. package/dist/routing/version.decorator.d.ts +36 -0
  171. package/dist/routing/version.decorator.d.ts.map +1 -0
  172. package/dist/routing/version.decorator.js +89 -0
  173. package/dist/service.d.ts +9 -0
  174. package/dist/service.d.ts.map +1 -0
  175. package/dist/service.js +39 -0
  176. package/dist/shutdown.d.ts +32 -0
  177. package/dist/shutdown.d.ts.map +1 -0
  178. package/dist/shutdown.js +109 -0
  179. package/dist/testing/testing.module.d.ts +83 -0
  180. package/dist/testing/testing.module.d.ts.map +1 -0
  181. package/dist/testing/testing.module.js +164 -0
  182. package/dist/types.d.ts +76 -0
  183. package/dist/types.d.ts.map +1 -0
  184. package/dist/types.js +2 -0
  185. package/dist/upload/file-upload.d.ts +75 -0
  186. package/dist/upload/file-upload.d.ts.map +1 -0
  187. package/dist/upload/file-upload.js +261 -0
  188. package/dist/utils/sanitize.d.ts +45 -0
  189. package/dist/utils/sanitize.d.ts.map +1 -0
  190. package/dist/utils/sanitize.js +165 -0
  191. package/dist/validator.d.ts +7 -0
  192. package/dist/validator.d.ts.map +1 -0
  193. package/dist/validator.js +119 -0
  194. package/package.json +65 -0
@@ -0,0 +1,259 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const global_middleware_1 = require("../../middleware/global-middleware");
4
+ // Mock logger
5
+ jest.mock('../../logger', () => ({
6
+ info: jest.fn(),
7
+ debug: jest.fn(),
8
+ warn: jest.fn(),
9
+ error: jest.fn(),
10
+ }));
11
+ describe('GlobalMiddlewareManager', () => {
12
+ let manager;
13
+ let mockReq;
14
+ let mockRes;
15
+ beforeEach(() => {
16
+ manager = new global_middleware_1.GlobalMiddlewareManager();
17
+ mockReq = {
18
+ method: 'GET',
19
+ url: '/test',
20
+ headers: {},
21
+ };
22
+ mockRes = {
23
+ setHeader: jest.fn(),
24
+ status: jest.fn().mockReturnThis(),
25
+ json: jest.fn(),
26
+ };
27
+ });
28
+ describe('use', () => {
29
+ it('should register global middleware function', () => {
30
+ const middleware = jest.fn((req, res, next) => next());
31
+ manager.use(middleware);
32
+ expect(manager).toBeDefined();
33
+ });
34
+ it('should register global middleware class', () => {
35
+ class TestMiddleware {
36
+ use(req, res, next) {
37
+ next();
38
+ }
39
+ }
40
+ manager.use(new TestMiddleware());
41
+ expect(manager).toBeDefined();
42
+ });
43
+ });
44
+ describe('useFor', () => {
45
+ it('should register middleware for specific routes', () => {
46
+ const middleware = jest.fn((req, res, next) => next());
47
+ manager.useFor(middleware, ['/users', '/posts']);
48
+ expect(manager).toBeDefined();
49
+ });
50
+ it('should register middleware for route with method', () => {
51
+ const middleware = jest.fn((req, res, next) => next());
52
+ manager.useFor(middleware, [{ path: '/users', method: 'POST' }]);
53
+ expect(manager).toBeDefined();
54
+ });
55
+ it('should register middleware for mixed route types', () => {
56
+ const middleware = jest.fn((req, res, next) => next());
57
+ manager.useFor(middleware, ['/users', { path: '/posts', method: 'GET' }]);
58
+ expect(manager).toBeDefined();
59
+ });
60
+ });
61
+ describe('useExcept', () => {
62
+ it('should register middleware with exclusions', () => {
63
+ const middleware = jest.fn((req, res, next) => next());
64
+ manager.useExcept(middleware, ['/health', '/metrics']);
65
+ expect(manager).toBeDefined();
66
+ });
67
+ it('should register middleware excluding specific methods', () => {
68
+ const middleware = jest.fn((req, res, next) => next());
69
+ manager.useExcept(middleware, [{ path: '/admin', method: 'DELETE' }]);
70
+ expect(manager).toBeDefined();
71
+ });
72
+ });
73
+ describe('execute', () => {
74
+ it('should execute global middleware', async () => {
75
+ const middleware = jest.fn((req, res, next) => next());
76
+ manager.use(middleware);
77
+ await manager.execute(mockReq, mockRes);
78
+ expect(middleware).toHaveBeenCalled();
79
+ });
80
+ it('should execute middleware in order', async () => {
81
+ const order = [];
82
+ const middleware1 = jest.fn((req, res, next) => {
83
+ order.push(1);
84
+ next();
85
+ });
86
+ const middleware2 = jest.fn((req, res, next) => {
87
+ order.push(2);
88
+ next();
89
+ });
90
+ manager.use(middleware1);
91
+ manager.use(middleware2);
92
+ await manager.execute(mockReq, mockRes);
93
+ expect(order).toEqual([1, 2]);
94
+ });
95
+ it('should execute route-specific middleware only for matching routes', async () => {
96
+ const middleware = jest.fn((req, res, next) => next());
97
+ manager.useFor(middleware, ['/test']);
98
+ mockReq.url = '/test';
99
+ await manager.execute(mockReq, mockRes);
100
+ expect(middleware).toHaveBeenCalled();
101
+ });
102
+ it('should not execute middleware for non-matching routes', async () => {
103
+ const middleware = jest.fn((req, res, next) => next());
104
+ manager.useFor(middleware, ['/users']);
105
+ mockReq.url = '/posts';
106
+ await manager.execute(mockReq, mockRes);
107
+ expect(middleware).not.toHaveBeenCalled();
108
+ });
109
+ it('should not execute middleware for excluded routes', async () => {
110
+ const middleware = jest.fn((req, res, next) => next());
111
+ manager.useExcept(middleware, ['/health']);
112
+ mockReq.url = '/health';
113
+ await manager.execute(mockReq, mockRes);
114
+ expect(middleware).not.toHaveBeenCalled();
115
+ });
116
+ it('should execute middleware for non-excluded routes', async () => {
117
+ const middleware = jest.fn((req, res, next) => next());
118
+ manager.useExcept(middleware, ['/health']);
119
+ mockReq.url = '/users';
120
+ await manager.execute(mockReq, mockRes);
121
+ expect(middleware).toHaveBeenCalled();
122
+ });
123
+ it('should execute class-based middleware', async () => {
124
+ class TestMiddleware {
125
+ use(req, res, next) {
126
+ next();
127
+ }
128
+ }
129
+ const instance = new TestMiddleware();
130
+ const spy = jest.spyOn(instance, 'use');
131
+ manager.use(instance);
132
+ await manager.execute(mockReq, mockRes);
133
+ expect(spy).toHaveBeenCalled();
134
+ });
135
+ it('should handle middleware errors', async () => {
136
+ const middleware = jest.fn(() => {
137
+ throw new Error('Middleware error');
138
+ });
139
+ manager.use(middleware);
140
+ await expect(manager.execute(mockReq, mockRes)).rejects.toThrow('Middleware error');
141
+ });
142
+ it('should stop execution if middleware does not call next', async () => {
143
+ const middleware1 = jest.fn((_req, _res, _next) => {
144
+ // Don't call next
145
+ });
146
+ const middleware2 = jest.fn((req, res, next) => next());
147
+ manager.use(middleware1);
148
+ manager.use(middleware2);
149
+ await manager.execute(mockReq, mockRes);
150
+ expect(middleware1).toHaveBeenCalled();
151
+ expect(middleware2).not.toHaveBeenCalled();
152
+ });
153
+ it('should handle async middleware', async () => {
154
+ const middleware = jest.fn(async (req, res, next) => {
155
+ await new Promise(resolve => setTimeout(resolve, 10));
156
+ next();
157
+ });
158
+ manager.use(middleware);
159
+ await manager.execute(mockReq, mockRes);
160
+ expect(middleware).toHaveBeenCalled();
161
+ });
162
+ });
163
+ describe('route matching', () => {
164
+ it('should match exact paths', async () => {
165
+ const middleware = jest.fn((req, res, next) => next());
166
+ manager.useFor(middleware, ['/users']);
167
+ mockReq.url = '/users';
168
+ await manager.execute(mockReq, mockRes);
169
+ expect(middleware).toHaveBeenCalled();
170
+ });
171
+ it('should match paths with query strings', async () => {
172
+ const middleware = jest.fn((req, res, next) => next());
173
+ manager.useFor(middleware, ['/users']);
174
+ mockReq.url = '/users?page=1';
175
+ await manager.execute(mockReq, mockRes);
176
+ expect(middleware).toHaveBeenCalled();
177
+ });
178
+ it('should match method-specific routes', async () => {
179
+ const middleware = jest.fn((req, res, next) => next());
180
+ manager.useFor(middleware, [{ path: '/users', method: 'POST' }]);
181
+ mockReq.url = '/users';
182
+ mockReq.method = 'POST';
183
+ await manager.execute(mockReq, mockRes);
184
+ expect(middleware).toHaveBeenCalled();
185
+ });
186
+ it('should not match different methods', async () => {
187
+ const middleware = jest.fn((req, res, next) => next());
188
+ manager.useFor(middleware, [{ path: '/users', method: 'POST' }]);
189
+ mockReq.url = '/users';
190
+ mockReq.method = 'GET';
191
+ await manager.execute(mockReq, mockRes);
192
+ expect(middleware).not.toHaveBeenCalled();
193
+ });
194
+ it('should match wildcard routes', async () => {
195
+ const middleware = jest.fn((req, res, next) => next());
196
+ manager.useFor(middleware, ['/api/*']);
197
+ mockReq.url = '/api/users';
198
+ await manager.execute(mockReq, mockRes);
199
+ expect(middleware).toHaveBeenCalled();
200
+ });
201
+ });
202
+ describe('edge cases', () => {
203
+ it('should handle empty middleware list', async () => {
204
+ await expect(manager.execute(mockReq, mockRes)).resolves.not.toThrow();
205
+ });
206
+ it('should handle undefined URL', async () => {
207
+ mockReq.url = undefined;
208
+ await expect(manager.execute(mockReq, mockRes)).resolves.not.toThrow();
209
+ });
210
+ it('should handle undefined method', async () => {
211
+ mockReq.method = undefined;
212
+ const middleware = jest.fn((req, res, next) => next());
213
+ manager.use(middleware);
214
+ await manager.execute(mockReq, mockRes);
215
+ expect(middleware).toHaveBeenCalled();
216
+ });
217
+ it('should handle multiple middleware with same route', async () => {
218
+ const middleware1 = jest.fn((req, res, next) => next());
219
+ const middleware2 = jest.fn((req, res, next) => next());
220
+ manager.useFor(middleware1, ['/users']);
221
+ manager.useFor(middleware2, ['/users']);
222
+ mockReq.url = '/users';
223
+ await manager.execute(mockReq, mockRes);
224
+ expect(middleware1).toHaveBeenCalled();
225
+ expect(middleware2).toHaveBeenCalled();
226
+ });
227
+ });
228
+ describe('clear', () => {
229
+ it('should clear all middleware', () => {
230
+ const middleware = jest.fn((req, res, next) => next());
231
+ manager.use(middleware);
232
+ manager.clear();
233
+ const allMiddleware = manager.getMiddleware();
234
+ expect(allMiddleware).toHaveLength(0);
235
+ });
236
+ });
237
+ describe('getMiddleware', () => {
238
+ it('should return all registered middleware', () => {
239
+ const middleware1 = jest.fn((req, res, next) => next());
240
+ const middleware2 = jest.fn((req, res, next) => next());
241
+ manager.use(middleware1);
242
+ manager.use(middleware2);
243
+ const allMiddleware = manager.getMiddleware();
244
+ expect(allMiddleware).toHaveLength(2);
245
+ });
246
+ it('should return copy of middleware array', () => {
247
+ const middleware = jest.fn((req, res, next) => next());
248
+ manager.use(middleware);
249
+ const allMiddleware = manager.getMiddleware();
250
+ allMiddleware.push({
251
+ handler: jest.fn(),
252
+ routes: [],
253
+ excludedRoutes: [],
254
+ });
255
+ // Original should not be affected
256
+ expect(manager.getMiddleware()).toHaveLength(1);
257
+ });
258
+ });
259
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=rate-limit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/middleware/rate-limit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,264 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const rate_limit_middleware_1 = require("../../middleware/rate-limit.middleware");
4
+ const http_error_1 = require("../../errors/http.error");
5
+ // Mock logger
6
+ jest.mock('../../logger', () => ({
7
+ warn: jest.fn(),
8
+ error: jest.fn(),
9
+ }));
10
+ describe('RateLimitMiddleware', () => {
11
+ let mockReq;
12
+ let mockRes;
13
+ let nextFn;
14
+ beforeEach(() => {
15
+ jest.useFakeTimers();
16
+ mockReq = {
17
+ method: 'GET',
18
+ url: '/test',
19
+ headers: {},
20
+ };
21
+ mockReq.socket = { remoteAddress: '127.0.0.1' };
22
+ mockRes = {
23
+ setHeader: jest.fn(),
24
+ status: jest.fn().mockReturnThis(),
25
+ json: jest.fn(),
26
+ };
27
+ nextFn = jest.fn().mockResolvedValue(undefined);
28
+ });
29
+ afterEach(() => {
30
+ jest.useRealTimers();
31
+ });
32
+ describe('constructor', () => {
33
+ it('should create middleware with default options', () => {
34
+ const middleware = new rate_limit_middleware_1.RateLimitMiddleware({
35
+ max: 10,
36
+ windowMs: 60,
37
+ });
38
+ expect(middleware).toBeDefined();
39
+ });
40
+ it('should use custom key generator', () => {
41
+ const customKeyGen = jest.fn().mockReturnValue('custom-key');
42
+ const middleware = new rate_limit_middleware_1.RateLimitMiddleware({
43
+ max: 10,
44
+ windowMs: 60,
45
+ keyGenerator: customKeyGen,
46
+ });
47
+ expect(middleware).toBeDefined();
48
+ });
49
+ });
50
+ describe('use', () => {
51
+ it('should allow requests within limit', async () => {
52
+ const middleware = new rate_limit_middleware_1.RateLimitMiddleware({
53
+ max: 5,
54
+ windowMs: 60,
55
+ });
56
+ await middleware.use(mockReq, mockRes, nextFn);
57
+ expect(nextFn).toHaveBeenCalled();
58
+ expect(mockRes.setHeader).toHaveBeenCalledWith('RateLimit-Limit', '5');
59
+ expect(mockRes.setHeader).toHaveBeenCalledWith('RateLimit-Remaining', '4');
60
+ });
61
+ it('should set standard rate limit headers', async () => {
62
+ const middleware = new rate_limit_middleware_1.RateLimitMiddleware({
63
+ max: 10,
64
+ windowMs: 60,
65
+ standardHeaders: true,
66
+ });
67
+ await middleware.use(mockReq, mockRes, nextFn);
68
+ expect(mockRes.setHeader).toHaveBeenCalledWith('RateLimit-Limit', '10');
69
+ expect(mockRes.setHeader).toHaveBeenCalledWith('RateLimit-Remaining', '9');
70
+ expect(mockRes.setHeader).toHaveBeenCalledWith('RateLimit-Reset', expect.any(String));
71
+ });
72
+ it('should set legacy rate limit headers', async () => {
73
+ const middleware = new rate_limit_middleware_1.RateLimitMiddleware({
74
+ max: 10,
75
+ windowMs: 60,
76
+ legacyHeaders: true,
77
+ });
78
+ await middleware.use(mockReq, mockRes, nextFn);
79
+ expect(mockRes.setHeader).toHaveBeenCalledWith('X-RateLimit-Limit', '10');
80
+ expect(mockRes.setHeader).toHaveBeenCalledWith('X-RateLimit-Remaining', '9');
81
+ expect(mockRes.setHeader).toHaveBeenCalledWith('X-RateLimit-Reset', expect.any(String));
82
+ });
83
+ it('should throw error when limit exceeded', async () => {
84
+ const middleware = new rate_limit_middleware_1.RateLimitMiddleware({
85
+ max: 2,
86
+ windowMs: 60,
87
+ });
88
+ // Make 3 requests
89
+ await middleware.use(mockReq, mockRes, nextFn);
90
+ await middleware.use(mockReq, mockRes, nextFn);
91
+ await expect(middleware.use(mockReq, mockRes, nextFn)).rejects.toThrow(http_error_1.HttpError);
92
+ });
93
+ it('should use custom error message', async () => {
94
+ const middleware = new rate_limit_middleware_1.RateLimitMiddleware({
95
+ max: 1,
96
+ windowMs: 60,
97
+ message: 'Custom rate limit message',
98
+ });
99
+ await middleware.use(mockReq, mockRes, nextFn);
100
+ try {
101
+ await middleware.use(mockReq, mockRes, nextFn);
102
+ }
103
+ catch (error) {
104
+ expect(error).toBeInstanceOf(http_error_1.HttpError);
105
+ expect(error.message).toBe('Custom rate limit message');
106
+ }
107
+ });
108
+ it('should use custom status code', async () => {
109
+ const middleware = new rate_limit_middleware_1.RateLimitMiddleware({
110
+ max: 1,
111
+ windowMs: 60,
112
+ statusCode: 503,
113
+ });
114
+ await middleware.use(mockReq, mockRes, nextFn);
115
+ try {
116
+ await middleware.use(mockReq, mockRes, nextFn);
117
+ }
118
+ catch (error) {
119
+ expect(error.statusCode).toBe(503);
120
+ }
121
+ });
122
+ it('should use custom key generator', async () => {
123
+ const customKeyGen = jest.fn().mockReturnValue('user-123');
124
+ const middleware = new rate_limit_middleware_1.RateLimitMiddleware({
125
+ max: 5,
126
+ windowMs: 60,
127
+ keyGenerator: customKeyGen,
128
+ });
129
+ await middleware.use(mockReq, mockRes, nextFn);
130
+ expect(customKeyGen).toHaveBeenCalledWith(mockReq);
131
+ });
132
+ it('should handle x-forwarded-for header', async () => {
133
+ mockReq.headers = {
134
+ 'x-forwarded-for': '192.168.1.1, 10.0.0.1',
135
+ };
136
+ const middleware = new rate_limit_middleware_1.RateLimitMiddleware({
137
+ max: 5,
138
+ windowMs: 60,
139
+ });
140
+ await middleware.use(mockReq, mockRes, nextFn);
141
+ expect(nextFn).toHaveBeenCalled();
142
+ });
143
+ it('should handle x-forwarded-for as array', async () => {
144
+ mockReq.headers = {
145
+ 'x-forwarded-for': ['192.168.1.1', '10.0.0.1'],
146
+ };
147
+ const middleware = new rate_limit_middleware_1.RateLimitMiddleware({
148
+ max: 5,
149
+ windowMs: 60,
150
+ });
151
+ await middleware.use(mockReq, mockRes, nextFn);
152
+ expect(nextFn).toHaveBeenCalled();
153
+ });
154
+ it('should reset count after window expires', async () => {
155
+ const middleware = new rate_limit_middleware_1.RateLimitMiddleware({
156
+ max: 2,
157
+ windowMs: 1, // 1 second
158
+ });
159
+ // First request
160
+ await middleware.use(mockReq, mockRes, nextFn);
161
+ await middleware.use(mockReq, mockRes, nextFn);
162
+ // Advance time past window
163
+ jest.advanceTimersByTime(2000);
164
+ // Should allow new requests
165
+ await middleware.use(mockReq, mockRes, nextFn);
166
+ expect(nextFn).toHaveBeenCalledTimes(3);
167
+ });
168
+ it('should show remaining count decreasing', async () => {
169
+ const middleware = new rate_limit_middleware_1.RateLimitMiddleware({
170
+ max: 3,
171
+ windowMs: 60,
172
+ });
173
+ await middleware.use(mockReq, mockRes, nextFn);
174
+ expect(mockRes.setHeader).toHaveBeenCalledWith('RateLimit-Remaining', '2');
175
+ jest.clearAllMocks();
176
+ await middleware.use(mockReq, mockRes, nextFn);
177
+ expect(mockRes.setHeader).toHaveBeenCalledWith('RateLimit-Remaining', '1');
178
+ jest.clearAllMocks();
179
+ await middleware.use(mockReq, mockRes, nextFn);
180
+ expect(mockRes.setHeader).toHaveBeenCalledWith('RateLimit-Remaining', '0');
181
+ });
182
+ });
183
+ describe('destroy', () => {
184
+ it('should cleanup resources', () => {
185
+ const middleware = new rate_limit_middleware_1.RateLimitMiddleware({
186
+ max: 10,
187
+ windowMs: 60,
188
+ });
189
+ middleware.destroy();
190
+ // Should not throw
191
+ expect(true).toBe(true);
192
+ });
193
+ });
194
+ describe('edge cases', () => {
195
+ it('should handle missing socket', async () => {
196
+ const middleware = new rate_limit_middleware_1.RateLimitMiddleware({
197
+ max: 10,
198
+ windowMs: 60,
199
+ });
200
+ const reqWithoutSocket = {
201
+ method: 'GET',
202
+ url: '/test',
203
+ headers: {},
204
+ };
205
+ await middleware.use(reqWithoutSocket, mockRes, nextFn);
206
+ expect(nextFn).toHaveBeenCalled();
207
+ });
208
+ it('should handle missing remote address', async () => {
209
+ const middleware = new rate_limit_middleware_1.RateLimitMiddleware({
210
+ max: 10,
211
+ windowMs: 60,
212
+ });
213
+ const reqWithoutAddress = {
214
+ method: 'GET',
215
+ url: '/test',
216
+ headers: {},
217
+ socket: {},
218
+ };
219
+ await middleware.use(reqWithoutAddress, mockRes, nextFn);
220
+ expect(nextFn).toHaveBeenCalled();
221
+ });
222
+ it('should use X-Forwarded-For header when available', async () => {
223
+ const middleware = new rate_limit_middleware_1.RateLimitMiddleware({
224
+ max: 2,
225
+ windowMs: 60,
226
+ });
227
+ const reqWithForwardedFor = {
228
+ method: 'GET',
229
+ url: '/test',
230
+ headers: { 'x-forwarded-for': '192.168.1.1' },
231
+ socket: { remoteAddress: '127.0.0.1' },
232
+ };
233
+ await middleware.use(reqWithForwardedFor, mockRes, nextFn);
234
+ await middleware.use(reqWithForwardedFor, mockRes, nextFn);
235
+ // Third request should be rate limited
236
+ await expect(middleware.use(reqWithForwardedFor, mockRes, nextFn)).rejects.toThrow(http_error_1.HttpError);
237
+ });
238
+ it('should handle rate limit exceeded', async () => {
239
+ const middleware = new rate_limit_middleware_1.RateLimitMiddleware({
240
+ max: 1,
241
+ windowMs: 60,
242
+ });
243
+ await middleware.use(mockReq, mockRes, nextFn);
244
+ // Second request should be rate limited
245
+ await expect(middleware.use(mockReq, mockRes, nextFn)).rejects.toThrow(http_error_1.HttpError);
246
+ });
247
+ it('should use custom message when provided', async () => {
248
+ const customMessage = 'Custom rate limit message';
249
+ const middleware = new rate_limit_middleware_1.RateLimitMiddleware({
250
+ max: 1,
251
+ windowMs: 60,
252
+ message: customMessage,
253
+ });
254
+ await middleware.use(mockReq, mockRes, nextFn);
255
+ try {
256
+ await middleware.use(mockReq, mockRes, nextFn);
257
+ }
258
+ catch (error) {
259
+ expect(error).toBeInstanceOf(http_error_1.HttpError);
260
+ expect(error.message).toBe(customMessage);
261
+ }
262
+ });
263
+ });
264
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=security-headers.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security-headers.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/middleware/security-headers.test.ts"],"names":[],"mappings":""}