te.js 2.0.1 → 2.0.3

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.
@@ -1,446 +0,0 @@
1
- import { vi } from 'vitest';
2
- import { createMockRequest, createMockResponse, createMockPair } from './mock-http.js';
3
-
4
- /**
5
- * Create an Ammo instance with mocked request/response
6
- * @param {Object} options - Request options
7
- * @returns {Promise<Object>} Object containing ammo, req, and res
8
- */
9
- export async function createTestAmmo(options = {}) {
10
- const { req, res } = createMockPair(options);
11
-
12
- // Dynamically import Ammo to avoid module initialization issues
13
- const { default: Ammo } = await import('../../server/ammo.js');
14
- const ammo = new Ammo(req, res);
15
-
16
- return { ammo, req, res };
17
- }
18
-
19
- /**
20
- * Create an enhanced Ammo instance (with enhance() already called)
21
- * @param {Object} options - Request options
22
- * @returns {Promise<Object>} Object containing ammo, req, and res
23
- */
24
- export async function createEnhancedAmmo(options = {}) {
25
- const { ammo, req, res } = await createTestAmmo(options);
26
-
27
- // Mock the body parser to avoid actual body parsing
28
- const mockPayload = options.body || {};
29
- ammo.payload = mockPayload;
30
- ammo.method = options.method || 'GET';
31
- ammo.headers = req.headers;
32
- ammo.ip = req.socket?.remoteAddress || '127.0.0.1';
33
- ammo.path = options.url || '/';
34
- ammo.endpoint = options.url || '/';
35
- ammo.protocol = options.protocol || 'http';
36
- ammo.hostname = req.headers?.host || 'localhost';
37
- ammo.fullURL = `${ammo.protocol}://${ammo.hostname}${ammo.path}`;
38
-
39
- // Set HTTP method flags
40
- ammo.GET = ammo.method === 'GET';
41
- ammo.POST = ammo.method === 'POST';
42
- ammo.PUT = ammo.method === 'PUT';
43
- ammo.DELETE = ammo.method === 'DELETE';
44
- ammo.PATCH = ammo.method === 'PATCH';
45
- ammo.HEAD = ammo.method === 'HEAD';
46
- ammo.OPTIONS = ammo.method === 'OPTIONS';
47
-
48
- return { ammo, req, res };
49
- }
50
-
51
- /**
52
- * Create a fresh TargetRegistry for testing
53
- * @returns {Object} Fresh registry instance
54
- */
55
- export function createTestRegistry() {
56
- // Create a new registry instance (bypasses singleton)
57
- return {
58
- targets: [],
59
- globalMiddlewares: [],
60
- addGlobalMiddleware(...middlewares) {
61
- const validMiddlewares = middlewares.filter(
62
- (m) => typeof m === 'function'
63
- );
64
- this.globalMiddlewares = this.globalMiddlewares.concat(validMiddlewares);
65
- },
66
- register(targets) {
67
- if (Array.isArray(targets)) {
68
- this.targets = this.targets.concat(targets);
69
- } else {
70
- this.targets.push(targets);
71
- }
72
- },
73
- aim(endpoint) {
74
- const standardizedEndpoint = endpoint.endsWith('/') && endpoint !== '/'
75
- ? endpoint.slice(0, -1)
76
- : endpoint;
77
-
78
- // Exact match
79
- const exactMatch = this.targets.find(
80
- (target) => target.getPath() === standardizedEndpoint
81
- );
82
- if (exactMatch) {
83
- return { target: exactMatch, params: {} };
84
- }
85
-
86
- // Parameterized route matching
87
- for (const target of this.targets) {
88
- const params = this.matchParameterizedRoute(
89
- target.getPath(),
90
- standardizedEndpoint
91
- );
92
- if (params !== null) {
93
- return { target, params };
94
- }
95
- }
96
-
97
- return null;
98
- },
99
- matchParameterizedRoute(pattern, url) {
100
- if (pattern === '/' && url === '/') return {};
101
-
102
- const patternSegments = pattern.split('/').filter((s) => s.length > 0);
103
- const urlSegments = url.split('/').filter((s) => s.length > 0);
104
-
105
- if (patternSegments.length !== urlSegments.length) return null;
106
- if (patternSegments.length === 0 && urlSegments.length === 0) return {};
107
-
108
- const params = {};
109
- for (let i = 0; i < patternSegments.length; i++) {
110
- const patternSegment = patternSegments[i];
111
- const urlSegment = urlSegments[i];
112
-
113
- if (patternSegment.startsWith(':')) {
114
- params[patternSegment.slice(1)] = urlSegment;
115
- } else if (patternSegment !== urlSegment) {
116
- return null;
117
- }
118
- }
119
-
120
- return params;
121
- },
122
- getAllEndpoints(grouped) {
123
- if (grouped) {
124
- return this.targets.reduce((acc, target) => {
125
- const group = target.getPath().split('/')[1];
126
- if (!acc[group]) acc[group] = [];
127
- acc[group].push(target.getPath());
128
- return acc;
129
- }, {});
130
- }
131
- return this.targets.map((target) => target.getPath());
132
- },
133
- reset() {
134
- this.targets = [];
135
- this.globalMiddlewares = [];
136
- },
137
- };
138
- }
139
-
140
- /**
141
- * Create a mock endpoint for testing
142
- * @param {Object} options - Endpoint options
143
- * @returns {Object} Mock endpoint object
144
- */
145
- export function createMockEndpoint(options = {}) {
146
- const path = options.path || '/test';
147
- const handler = options.handler || vi.fn();
148
- const middlewares = options.middlewares || [];
149
-
150
- return {
151
- getPath: () => path,
152
- getHandler: () => handler,
153
- getMiddlewares: () => middlewares,
154
- setPath: vi.fn().mockReturnThis(),
155
- setHandler: vi.fn().mockReturnThis(),
156
- setMiddlewares: vi.fn().mockReturnThis(),
157
- };
158
- }
159
-
160
- /**
161
- * Create a mock middleware function
162
- * @param {Object} options - Middleware options
163
- * @returns {Function} Mock middleware
164
- */
165
- export function createMockMiddleware(options = {}) {
166
- const {
167
- name = 'mockMiddleware',
168
- callNext = true,
169
- delay = 0,
170
- throwError = null,
171
- modifyAmmo = null,
172
- } = options;
173
-
174
- const middleware = vi.fn(async (ammo, next) => {
175
- if (delay > 0) {
176
- await sleep(delay);
177
- }
178
-
179
- if (throwError) {
180
- throw throwError;
181
- }
182
-
183
- if (modifyAmmo && typeof modifyAmmo === 'function') {
184
- modifyAmmo(ammo);
185
- }
186
-
187
- if (callNext && next) {
188
- await next();
189
- }
190
- });
191
-
192
- // Add name for debugging
193
- Object.defineProperty(middleware, 'name', { value: name });
194
-
195
- return middleware;
196
- }
197
-
198
- /**
199
- * Create a middleware that uses Express-style signature (req, res, next)
200
- * @param {Object} options - Middleware options
201
- * @returns {Function} Mock Express-style middleware
202
- */
203
- export function createExpressStyleMiddleware(options = {}) {
204
- const {
205
- name = 'expressMiddleware',
206
- callNext = true,
207
- delay = 0,
208
- throwError = null,
209
- } = options;
210
-
211
- const middleware = vi.fn(async (req, res, next) => {
212
- if (delay > 0) {
213
- await sleep(delay);
214
- }
215
-
216
- if (throwError) {
217
- throw throwError;
218
- }
219
-
220
- if (callNext && next) {
221
- await next();
222
- }
223
- });
224
-
225
- Object.defineProperty(middleware, 'name', { value: name });
226
-
227
- return middleware;
228
- }
229
-
230
- /**
231
- * Sleep utility for async tests
232
- * @param {number} ms - Milliseconds to sleep
233
- * @returns {Promise<void>}
234
- */
235
- export function sleep(ms) {
236
- return new Promise((resolve) => setTimeout(resolve, ms));
237
- }
238
-
239
- /**
240
- * Wait for a condition to be true
241
- * @param {Function} condition - Function returning boolean
242
- * @param {Object} options - Wait options
243
- * @returns {Promise<void>}
244
- */
245
- export async function waitFor(condition, options = {}) {
246
- const { timeout = 5000, interval = 50 } = options;
247
- const startTime = Date.now();
248
-
249
- while (Date.now() - startTime < timeout) {
250
- if (await condition()) {
251
- return;
252
- }
253
- await sleep(interval);
254
- }
255
-
256
- throw new Error(`waitFor: Condition not met within ${timeout}ms`);
257
- }
258
-
259
- /**
260
- * Create a mock database connection for testing
261
- * @param {string} type - Database type (mongodb, redis)
262
- * @returns {Object} Mock database connection
263
- */
264
- export function createMockDbConnection(type = 'mongodb') {
265
- const mockConnection = {
266
- type,
267
- isConnected: true,
268
- close: vi.fn().mockResolvedValue(undefined),
269
- query: vi.fn().mockResolvedValue([]),
270
- insert: vi.fn().mockResolvedValue({ insertedId: 'mock-id' }),
271
- update: vi.fn().mockResolvedValue({ modifiedCount: 1 }),
272
- delete: vi.fn().mockResolvedValue({ deletedCount: 1 }),
273
- };
274
-
275
- if (type === 'mongodb') {
276
- mockConnection.collection = vi.fn(() => ({
277
- find: vi.fn().mockReturnValue({
278
- toArray: vi.fn().mockResolvedValue([]),
279
- }),
280
- findOne: vi.fn().mockResolvedValue(null),
281
- insertOne: vi.fn().mockResolvedValue({ insertedId: 'mock-id' }),
282
- updateOne: vi.fn().mockResolvedValue({ modifiedCount: 1 }),
283
- deleteOne: vi.fn().mockResolvedValue({ deletedCount: 1 }),
284
- }));
285
- }
286
-
287
- if (type === 'redis') {
288
- mockConnection.get = vi.fn().mockResolvedValue(null);
289
- mockConnection.set = vi.fn().mockResolvedValue('OK');
290
- mockConnection.del = vi.fn().mockResolvedValue(1);
291
- mockConnection.exists = vi.fn().mockResolvedValue(0);
292
- mockConnection.expire = vi.fn().mockResolvedValue(1);
293
- mockConnection.ttl = vi.fn().mockResolvedValue(-1);
294
- }
295
-
296
- return mockConnection;
297
- }
298
-
299
- /**
300
- * Create a mock file object for upload tests
301
- * @param {Object} options - File options
302
- * @returns {Object} Mock file object
303
- */
304
- export function createMockFile(options = {}) {
305
- return {
306
- fieldName: options.fieldName || 'file',
307
- originalFilename: options.filename || 'test.txt',
308
- filepath: options.filepath || '/tmp/upload_test.txt',
309
- mimetype: options.mimetype || 'text/plain',
310
- size: options.size || 1024,
311
- newFilename: options.newFilename || 'upload_test.txt',
312
- hash: options.hash || null,
313
- lastModifiedDate: options.lastModifiedDate || new Date(),
314
- };
315
- }
316
-
317
- /**
318
- * Create a mock storage for rate limiting tests
319
- * @returns {Object} Mock storage object
320
- */
321
- export function createMockStorage() {
322
- const store = new Map();
323
-
324
- return {
325
- get: vi.fn((key) => store.get(key)),
326
- set: vi.fn((key, value, ttl) => {
327
- store.set(key, value);
328
- return true;
329
- }),
330
- increment: vi.fn((key, amount = 1) => {
331
- const current = store.get(key) || 0;
332
- const newValue = current + amount;
333
- store.set(key, newValue);
334
- return newValue;
335
- }),
336
- decrement: vi.fn((key, amount = 1) => {
337
- const current = store.get(key) || 0;
338
- const newValue = Math.max(0, current - amount);
339
- store.set(key, newValue);
340
- return newValue;
341
- }),
342
- delete: vi.fn((key) => store.delete(key)),
343
- has: vi.fn((key) => store.has(key)),
344
- clear: vi.fn(() => store.clear()),
345
- _store: store, // Expose for testing
346
- };
347
- }
348
-
349
- /**
350
- * Assert response has expected status and optional body
351
- * @param {Object} res - Mock response object
352
- * @param {number} expectedStatus - Expected status code
353
- * @param {any} expectedBody - Optional expected body (string or object)
354
- */
355
- export function assertResponse(res, expectedStatus, expectedBody) {
356
- if (expectedStatus !== undefined) {
357
- if (res.statusCode !== expectedStatus) {
358
- throw new Error(
359
- `Expected status ${expectedStatus}, got ${res.statusCode}. Body: ${res.getBody()}`
360
- );
361
- }
362
- }
363
-
364
- if (expectedBody !== undefined) {
365
- const body = res.getBody();
366
- if (typeof expectedBody === 'object') {
367
- const jsonBody = res.getJsonBody();
368
- if (JSON.stringify(jsonBody) !== JSON.stringify(expectedBody)) {
369
- throw new Error(
370
- `Body mismatch. Expected: ${JSON.stringify(expectedBody)}, Got: ${JSON.stringify(jsonBody)}`
371
- );
372
- }
373
- } else if (body !== expectedBody) {
374
- throw new Error(`Body mismatch. Expected: ${expectedBody}, Got: ${body}`);
375
- }
376
- }
377
- }
378
-
379
- /**
380
- * Mock console methods for cleaner test output
381
- * @returns {Object} Object with restore function
382
- */
383
- export function mockConsole() {
384
- const originalConsole = {
385
- log: console.log,
386
- error: console.error,
387
- warn: console.warn,
388
- info: console.info,
389
- };
390
-
391
- console.log = vi.fn();
392
- console.error = vi.fn();
393
- console.warn = vi.fn();
394
- console.info = vi.fn();
395
-
396
- return {
397
- restore: () => {
398
- console.log = originalConsole.log;
399
- console.error = originalConsole.error;
400
- console.warn = originalConsole.warn;
401
- console.info = originalConsole.info;
402
- },
403
- mocks: {
404
- log: console.log,
405
- error: console.error,
406
- warn: console.warn,
407
- info: console.info,
408
- },
409
- };
410
- }
411
-
412
- /**
413
- * Capture all response data for assertion
414
- * @param {Object} res - Mock response object
415
- * @returns {Object} Captured response data
416
- */
417
- export function captureResponse(res) {
418
- return {
419
- statusCode: res.statusCode,
420
- statusMessage: res.statusMessage,
421
- headers: { ...res.headers },
422
- body: res.getBody(),
423
- jsonBody: res.getJsonBody(),
424
- headersSent: res.headersSent,
425
- finished: res.finished,
426
- };
427
- }
428
-
429
- export default {
430
- createTestAmmo,
431
- createEnhancedAmmo,
432
- createTestRegistry,
433
- createMockEndpoint,
434
- createMockMiddleware,
435
- createExpressStyleMiddleware,
436
- sleep,
437
- waitFor,
438
- createMockDbConnection,
439
- createMockFile,
440
- createMockStorage,
441
- assertResponse,
442
- mockConsole,
443
- captureResponse,
444
- };
445
-
446
-
@@ -1,148 +0,0 @@
1
- /**
2
- * Smoke test to verify Vitest setup and test helpers work correctly
3
- */
4
- import { describe, it, expect, vi } from 'vitest';
5
- import {
6
- createMockRequest,
7
- createMockResponse,
8
- createMockPair,
9
- createJsonRequest,
10
- } from './helpers/mock-http.js';
11
- import { createMockStorage, sleep } from './helpers/test-utils.js';
12
-
13
- describe('Test Infrastructure Setup', () => {
14
- describe('Vitest Configuration', () => {
15
- it('should have vitest globals available', () => {
16
- expect(describe).toBeDefined();
17
- expect(it).toBeDefined();
18
- expect(expect).toBeDefined();
19
- expect(vi).toBeDefined();
20
- });
21
-
22
- it('should support async/await', async () => {
23
- const result = await Promise.resolve(42);
24
- expect(result).toBe(42);
25
- });
26
-
27
- it('should support vi.fn() mocking', () => {
28
- const mockFn = vi.fn(() => 'mocked');
29
- expect(mockFn()).toBe('mocked');
30
- expect(mockFn).toHaveBeenCalledTimes(1);
31
- });
32
- });
33
-
34
- describe('Mock HTTP Request', () => {
35
- it('should create a mock request with defaults', () => {
36
- const req = createMockRequest();
37
-
38
- expect(req.method).toBe('GET');
39
- expect(req.url).toBe('/');
40
- expect(req.headers).toBeDefined();
41
- expect(req.socket).toBeDefined();
42
- expect(req.socket.remoteAddress).toBe('127.0.0.1');
43
- });
44
-
45
- it('should create a mock request with custom options', () => {
46
- const req = createMockRequest({
47
- method: 'POST',
48
- url: '/api/users',
49
- headers: { 'content-type': 'application/json' },
50
- ip: '192.168.1.1',
51
- });
52
-
53
- expect(req.method).toBe('POST');
54
- expect(req.url).toBe('/api/users');
55
- expect(req.headers['content-type']).toBe('application/json');
56
- expect(req.socket.remoteAddress).toBe('192.168.1.1');
57
- });
58
-
59
- it('should create a JSON request with proper headers', () => {
60
- const req = createJsonRequest({ method: 'POST', url: '/api/data' });
61
-
62
- expect(req.headers['content-type']).toBe('application/json');
63
- expect(req.headers['accept']).toBe('application/json');
64
- });
65
- });
66
-
67
- describe('Mock HTTP Response', () => {
68
- it('should create a mock response with defaults', () => {
69
- const res = createMockResponse();
70
-
71
- expect(res.statusCode).toBe(200);
72
- expect(res.headers).toEqual({});
73
- expect(res.headersSent).toBe(false);
74
- expect(res.finished).toBe(false);
75
- });
76
-
77
- it('should support writeHead', () => {
78
- const res = createMockResponse();
79
- res.writeHead(201, { 'Content-Type': 'application/json' });
80
-
81
- expect(res.statusCode).toBe(201);
82
- expect(res.headers['content-type']).toBe('application/json');
83
- expect(res.headersSent).toBe(true);
84
- });
85
-
86
- it('should support write and end', () => {
87
- const res = createMockResponse();
88
- res.write('Hello');
89
- res.write(' World');
90
- res.end();
91
-
92
- expect(res.getBody()).toBe('Hello World');
93
- expect(res.finished).toBe(true);
94
- });
95
-
96
- it('should parse JSON body', () => {
97
- const res = createMockResponse();
98
- res.write(JSON.stringify({ message: 'success' }));
99
- res.end();
100
-
101
- expect(res.getJsonBody()).toEqual({ message: 'success' });
102
- });
103
- });
104
-
105
- describe('Mock Request/Response Pair', () => {
106
- it('should create paired req/res objects', () => {
107
- const { req, res } = createMockPair({ method: 'DELETE', url: '/item/1' });
108
-
109
- expect(req.method).toBe('DELETE');
110
- expect(req.url).toBe('/item/1');
111
- expect(res).toBeDefined();
112
- expect(res.statusCode).toBe(200);
113
- });
114
- });
115
-
116
- describe('Mock Storage', () => {
117
- it('should support basic operations', () => {
118
- const storage = createMockStorage();
119
-
120
- storage.set('key1', 'value1');
121
- expect(storage.get('key1')).toBe('value1');
122
-
123
- storage.delete('key1');
124
- expect(storage.get('key1')).toBeUndefined();
125
- });
126
-
127
- it('should support increment/decrement', () => {
128
- const storage = createMockStorage();
129
-
130
- storage.set('counter', 0);
131
- expect(storage.increment('counter')).toBe(1);
132
- expect(storage.increment('counter', 5)).toBe(6);
133
- expect(storage.decrement('counter', 2)).toBe(4);
134
- });
135
- });
136
-
137
- describe('Utility Functions', () => {
138
- it('should support sleep', async () => {
139
- const start = Date.now();
140
- await sleep(50);
141
- const elapsed = Date.now() - start;
142
-
143
- expect(elapsed).toBeGreaterThanOrEqual(45);
144
- });
145
- });
146
- });
147
-
148
-
package/vitest.config.js DELETED
@@ -1,54 +0,0 @@
1
- import { defineConfig } from 'vitest/config';
2
-
3
- export default defineConfig({
4
- test: {
5
- // Use ESM modules
6
- environment: 'node',
7
-
8
- // Test file patterns
9
- include: ['tests/**/*.test.js'],
10
-
11
- // Global test timeout (ms)
12
- testTimeout: 10000,
13
-
14
- // Enable globals (describe, it, expect, etc.)
15
- globals: true,
16
-
17
- // Coverage configuration
18
- coverage: {
19
- provider: 'v8',
20
- reporter: ['text', 'json', 'html'],
21
- include: [
22
- 'server/**/*.js',
23
- 'database/**/*.js',
24
- 'rate-limit/**/*.js',
25
- 'utils/**/*.js',
26
- ],
27
- exclude: [
28
- 'node_modules/**',
29
- 'tests/**',
30
- 'example/**',
31
- 'docs/**',
32
- ],
33
- },
34
-
35
- // Reporter options
36
- reporters: ['verbose'],
37
-
38
- // Isolation mode - each test file runs in its own context
39
- isolate: true,
40
-
41
- // Retry failed tests
42
- retry: 0,
43
-
44
- // Setup files to run before tests
45
- setupFiles: [],
46
-
47
- // Dependencies to inline (necessary for some ESM modules)
48
- deps: {
49
- interopDefault: true,
50
- },
51
- },
52
- });
53
-
54
-