soajs.controller 4.3.4 → 4.3.5

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.
@@ -0,0 +1,461 @@
1
+ "use strict";
2
+
3
+ const helper = require("../../../helper.js");
4
+ const mw = helper.requireModule('./mw/cache/index');
5
+ const assert = require('assert');
6
+
7
+ describe("Unit test for: mw - cache", () => {
8
+
9
+ let mockLog = {
10
+ debug: () => {},
11
+ info: () => {},
12
+ warn: () => {},
13
+ error: () => {}
14
+ };
15
+
16
+ let mockConfig = {
17
+ log: mockLog,
18
+ gatewayDB: {}
19
+ };
20
+
21
+ describe("middleware initialization", () => {
22
+ it("should return a function", (done) => {
23
+ let functionMw = mw(mockConfig);
24
+ assert.strictEqual(typeof functionMw, 'function');
25
+ done();
26
+ });
27
+ });
28
+
29
+ describe("non-GET requests", () => {
30
+ it("should call next() for POST requests", (done) => {
31
+ let req = {
32
+ method: 'POST',
33
+ soajs: {
34
+ tenant: { id: '123' },
35
+ controller: { serviceParams: { name: 'test', serviceInfo: ['', 'test', 'test'] } },
36
+ registry: { custom: {} }
37
+ }
38
+ };
39
+ let res = {};
40
+ let functionMw = mw(mockConfig);
41
+ functionMw(req, res, (error) => {
42
+ assert.ifError(error);
43
+ done();
44
+ });
45
+ });
46
+
47
+ it("should call next() for PUT requests", (done) => {
48
+ let req = {
49
+ method: 'PUT',
50
+ soajs: {
51
+ tenant: { id: '123' },
52
+ controller: { serviceParams: { name: 'test', serviceInfo: ['', 'test', 'test'] } },
53
+ registry: { custom: {} }
54
+ }
55
+ };
56
+ let res = {};
57
+ let functionMw = mw(mockConfig);
58
+ functionMw(req, res, (error) => {
59
+ assert.ifError(error);
60
+ done();
61
+ });
62
+ });
63
+
64
+ it("should call next() for DELETE requests", (done) => {
65
+ let req = {
66
+ method: 'DELETE',
67
+ soajs: {
68
+ tenant: { id: '123' },
69
+ controller: { serviceParams: { name: 'test', serviceInfo: ['', 'test', 'test'] } },
70
+ registry: { custom: {} }
71
+ }
72
+ };
73
+ let res = {};
74
+ let functionMw = mw(mockConfig);
75
+ functionMw(req, res, (error) => {
76
+ assert.ifError(error);
77
+ done();
78
+ });
79
+ });
80
+ });
81
+
82
+ describe("GET requests without configuration", () => {
83
+ it("should call next() when no cache configuration exists", (done) => {
84
+ let req = {
85
+ method: 'GET',
86
+ soajs: {
87
+ tenant: { id: '123' },
88
+ controller: { serviceParams: { name: 'test', serviceInfo: ['', 'test', 'test'] } },
89
+ registry: { custom: {} }
90
+ }
91
+ };
92
+ let res = {};
93
+ let functionMw = mw(mockConfig);
94
+ functionMw(req, res, (error) => {
95
+ assert.ifError(error);
96
+ done();
97
+ });
98
+ });
99
+
100
+ it("should call next() when service not configured", (done) => {
101
+ let req = {
102
+ method: 'GET',
103
+ soajs: {
104
+ tenant: { id: '123' },
105
+ controller: { serviceParams: { name: 'unconfigured', serviceInfo: ['', 'unconfigured', 'test'] } },
106
+ registry: {
107
+ custom: {
108
+ gateway: {
109
+ value: {
110
+ cache: {
111
+ services: {
112
+ other: { enabled: true, apis: { 'GET /test': { enabled: true } } }
113
+ }
114
+ }
115
+ }
116
+ }
117
+ }
118
+ }
119
+ }
120
+ };
121
+ let res = {};
122
+ let functionMw = mw(mockConfig);
123
+ functionMw(req, res, (error) => {
124
+ assert.ifError(error);
125
+ done();
126
+ });
127
+ });
128
+
129
+ it("should call next() when service not enabled", (done) => {
130
+ let req = {
131
+ method: 'GET',
132
+ soajs: {
133
+ tenant: { id: '123' },
134
+ controller: { serviceParams: { name: 'test', serviceInfo: ['', 'test', 'test'] } },
135
+ registry: {
136
+ custom: {
137
+ gateway: {
138
+ value: {
139
+ cache: {
140
+ services: {
141
+ test: { enabled: false, apis: { 'GET /test': { enabled: true } } }
142
+ }
143
+ }
144
+ }
145
+ }
146
+ }
147
+ }
148
+ }
149
+ };
150
+ let res = {};
151
+ let functionMw = mw(mockConfig);
152
+ functionMw(req, res, (error) => {
153
+ assert.ifError(error);
154
+ done();
155
+ });
156
+ });
157
+
158
+ it("should call next() when API not configured", (done) => {
159
+ let req = {
160
+ method: 'GET',
161
+ soajs: {
162
+ tenant: { id: '123' },
163
+ controller: { serviceParams: { name: 'test', serviceInfo: ['', 'test', 'other'] } },
164
+ registry: {
165
+ custom: {
166
+ gateway: {
167
+ value: {
168
+ cache: {
169
+ services: {
170
+ test: { enabled: true, apis: { 'GET /test': { enabled: true } } }
171
+ }
172
+ }
173
+ }
174
+ }
175
+ }
176
+ }
177
+ }
178
+ };
179
+ let res = {};
180
+ let functionMw = mw(mockConfig);
181
+ functionMw(req, res, (error) => {
182
+ assert.ifError(error);
183
+ done();
184
+ });
185
+ });
186
+
187
+ it("should call next() when API not enabled", (done) => {
188
+ let req = {
189
+ method: 'GET',
190
+ soajs: {
191
+ tenant: { id: '123' },
192
+ controller: { serviceParams: { name: 'test', serviceInfo: ['', 'test', 'test'] } },
193
+ registry: {
194
+ custom: {
195
+ gateway: {
196
+ value: {
197
+ cache: {
198
+ services: {
199
+ test: { enabled: true, apis: { 'GET /test': { enabled: false } } }
200
+ }
201
+ }
202
+ }
203
+ }
204
+ }
205
+ }
206
+ }
207
+ };
208
+ let res = {};
209
+ let functionMw = mw(mockConfig);
210
+ functionMw(req, res, (error) => {
211
+ assert.ifError(error);
212
+ done();
213
+ });
214
+ });
215
+ });
216
+
217
+ describe("cache processing", () => {
218
+ it("should process cache miss and call next()", (done) => {
219
+ let req = {
220
+ method: 'GET',
221
+ query: {},
222
+ soajs: {
223
+ tenant: { id: 'tenant-cache-miss' },
224
+ controller: { serviceParams: { name: 'test', serviceInfo: ['', 'test', 'test'] } },
225
+ registry: {
226
+ custom: {
227
+ gateway: {
228
+ value: {
229
+ cache: {
230
+ model: 'memory',
231
+ defaultTTL: 60000,
232
+ services: {
233
+ test: { enabled: true, apis: { 'GET /test': { enabled: true, ttl: 30000 } } }
234
+ }
235
+ }
236
+ }
237
+ }
238
+ }
239
+ }
240
+ }
241
+ };
242
+ let headerSet = false;
243
+ let res = {
244
+ setHeader: function(name, value) {
245
+ if (name === 'X-Cache' && value === 'MISS') {
246
+ headerSet = true;
247
+ }
248
+ },
249
+ writeHead: function() { return this; },
250
+ write: function() { return true; },
251
+ end: function() {}
252
+ };
253
+ let functionMw = mw(mockConfig);
254
+ functionMw(req, res, (error) => {
255
+ assert.ifError(error);
256
+ assert.strictEqual(headerSet, true);
257
+ done();
258
+ });
259
+ });
260
+
261
+ it("should match API with path parameters", (done) => {
262
+ let req = {
263
+ method: 'GET',
264
+ query: {},
265
+ soajs: {
266
+ tenant: { id: 'tenant-param-cache' },
267
+ controller: { serviceParams: { name: 'test', serviceInfo: ['', 'test', 'users', '123'] } },
268
+ registry: {
269
+ custom: {
270
+ gateway: {
271
+ value: {
272
+ cache: {
273
+ model: 'memory',
274
+ services: {
275
+ test: { enabled: true, apis: { 'GET /users/:id': { enabled: true, ttl: 30000 } } }
276
+ }
277
+ }
278
+ }
279
+ }
280
+ }
281
+ }
282
+ }
283
+ };
284
+ let headerSet = false;
285
+ let res = {
286
+ setHeader: function(name, value) {
287
+ if (name === 'X-Cache') {
288
+ headerSet = true;
289
+ }
290
+ },
291
+ writeHead: function() { return this; },
292
+ write: function() { return true; },
293
+ end: function() {}
294
+ };
295
+ let functionMw = mw(mockConfig);
296
+ functionMw(req, res, (error) => {
297
+ assert.ifError(error);
298
+ assert.strictEqual(headerSet, true);
299
+ done();
300
+ });
301
+ });
302
+
303
+ it("should use defaultTTL when API TTL not specified", (done) => {
304
+ let req = {
305
+ method: 'GET',
306
+ query: {},
307
+ soajs: {
308
+ tenant: { id: 'tenant-default-ttl' },
309
+ controller: { serviceParams: { name: 'test', serviceInfo: ['', 'test', 'test'] } },
310
+ registry: {
311
+ custom: {
312
+ gateway: {
313
+ value: {
314
+ cache: {
315
+ model: 'memory',
316
+ defaultTTL: 300000,
317
+ services: {
318
+ test: { enabled: true, apis: { 'GET /test': { enabled: true } } }
319
+ }
320
+ }
321
+ }
322
+ }
323
+ }
324
+ }
325
+ }
326
+ };
327
+ let res = {
328
+ setHeader: function() {},
329
+ writeHead: function() { return this; },
330
+ write: function() { return true; },
331
+ end: function() {}
332
+ };
333
+ let functionMw = mw(mockConfig);
334
+ functionMw(req, res, (error) => {
335
+ assert.ifError(error);
336
+ done();
337
+ });
338
+ });
339
+ });
340
+
341
+ describe("query parameter handling", () => {
342
+ it("should include query params in cache key", (done) => {
343
+ let req = {
344
+ method: 'GET',
345
+ query: { page: '1', limit: '10' },
346
+ soajs: {
347
+ tenant: { id: 'tenant-query' },
348
+ controller: { serviceParams: { name: 'test', serviceInfo: ['', 'test', 'test'] } },
349
+ registry: {
350
+ custom: {
351
+ gateway: {
352
+ value: {
353
+ cache: {
354
+ model: 'memory',
355
+ services: {
356
+ test: { enabled: true, apis: { 'GET /test': { enabled: true, ttl: 30000 } } }
357
+ }
358
+ }
359
+ }
360
+ }
361
+ }
362
+ }
363
+ }
364
+ };
365
+ let res = {
366
+ setHeader: function() {},
367
+ writeHead: function() { return this; },
368
+ write: function() { return true; },
369
+ end: function() {}
370
+ };
371
+ let functionMw = mw(mockConfig);
372
+ functionMw(req, res, (error) => {
373
+ assert.ifError(error);
374
+ done();
375
+ });
376
+ });
377
+
378
+ it("should handle empty query params", (done) => {
379
+ let req = {
380
+ method: 'GET',
381
+ query: {},
382
+ soajs: {
383
+ tenant: { id: 'tenant-empty-query' },
384
+ controller: { serviceParams: { name: 'test', serviceInfo: ['', 'test', 'test'] } },
385
+ registry: {
386
+ custom: {
387
+ gateway: {
388
+ value: {
389
+ cache: {
390
+ model: 'memory',
391
+ services: {
392
+ test: { enabled: true, apis: { 'GET /test': { enabled: true, ttl: 30000 } } }
393
+ }
394
+ }
395
+ }
396
+ }
397
+ }
398
+ }
399
+ }
400
+ };
401
+ let res = {
402
+ setHeader: function() {},
403
+ writeHead: function() { return this; },
404
+ write: function() { return true; },
405
+ end: function() {}
406
+ };
407
+ let functionMw = mw(mockConfig);
408
+ functionMw(req, res, (error) => {
409
+ assert.ifError(error);
410
+ done();
411
+ });
412
+ });
413
+ });
414
+
415
+ describe("missing prerequisites", () => {
416
+ it("should call next() when soajs object is missing", (done) => {
417
+ let req = {
418
+ method: 'GET'
419
+ };
420
+ let res = {};
421
+ let functionMw = mw(mockConfig);
422
+ functionMw(req, res, (error) => {
423
+ assert.ifError(error);
424
+ done();
425
+ });
426
+ });
427
+
428
+ it("should call next() when tenant is missing", (done) => {
429
+ let req = {
430
+ method: 'GET',
431
+ soajs: {
432
+ controller: { serviceParams: { name: 'test', serviceInfo: ['', 'test', 'test'] } },
433
+ registry: { custom: {} }
434
+ }
435
+ };
436
+ let res = {};
437
+ let functionMw = mw(mockConfig);
438
+ functionMw(req, res, (error) => {
439
+ assert.ifError(error);
440
+ done();
441
+ });
442
+ });
443
+
444
+ it("should call next() when controller is missing", (done) => {
445
+ let req = {
446
+ method: 'GET',
447
+ soajs: {
448
+ tenant: { id: '123' },
449
+ registry: { custom: {} }
450
+ }
451
+ };
452
+ let res = {};
453
+ let functionMw = mw(mockConfig);
454
+ functionMw(req, res, (error) => {
455
+ assert.ifError(error);
456
+ done();
457
+ });
458
+ });
459
+ });
460
+
461
+ });
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+
3
+ const helper = require("../../../helper.js");
4
+ const model = helper.requireModule('./mw/cache/model/memory');
5
+ const assert = require('assert');
6
+
7
+ describe("Unit test for: mw - cache/model/memory", () => {
8
+
9
+ before((done) => {
10
+ model.init({
11
+ debug: () => {},
12
+ info: () => {},
13
+ error: () => {}
14
+ });
15
+ done();
16
+ });
17
+
18
+ afterEach((done) => {
19
+ // Clean up between tests
20
+ model.invalidate({ l1: 'tenant1', l2: 'key1' });
21
+ model.invalidate({ l1: 'tenant1', l2: 'key2' });
22
+ model.invalidate({ l1: 'tenant2', l2: 'key1' });
23
+ done();
24
+ });
25
+
26
+ describe("get", () => {
27
+ it("should return null for non-existent key", (done) => {
28
+ let result = model.get({ l1: 'tenant1', l2: 'nonexistent' });
29
+ assert.strictEqual(result, null);
30
+ done();
31
+ });
32
+
33
+ it("should return entry after set", (done) => {
34
+ let key = { l1: 'tenant1', l2: 'key1' };
35
+ let response = { statusCode: 200, body: 'test' };
36
+ model.set(key, response, 60000);
37
+ let result = model.get(key);
38
+ assert.notStrictEqual(result, null);
39
+ assert.deepStrictEqual(result.response, response);
40
+ done();
41
+ });
42
+
43
+ it("should return null for expired entry", (done) => {
44
+ let key = { l1: 'tenant1', l2: 'key1' };
45
+ model.set(key, { statusCode: 200 }, 1); // 1ms TTL
46
+ setTimeout(() => {
47
+ let result = model.get(key);
48
+ assert.strictEqual(result, null);
49
+ done();
50
+ }, 10);
51
+ });
52
+ });
53
+
54
+ describe("set", () => {
55
+ it("should store response with TTL", (done) => {
56
+ let key = { l1: 'tenant1', l2: 'key1' };
57
+ let response = {
58
+ statusCode: 200,
59
+ headers: { 'Content-Type': 'application/json' },
60
+ body: '{"data": []}'
61
+ };
62
+ model.set(key, response, 60000);
63
+ let result = model.get(key);
64
+ assert.notStrictEqual(result, null);
65
+ assert.deepStrictEqual(result.response, response);
66
+ assert.ok(result.cachedAt);
67
+ assert.ok(result.expiresAt);
68
+ assert.ok(result.expiresAt > result.cachedAt);
69
+ done();
70
+ });
71
+
72
+ it("should overwrite existing entry", (done) => {
73
+ let key = { l1: 'tenant1', l2: 'key1' };
74
+ model.set(key, { statusCode: 200, body: 'first' }, 60000);
75
+ model.set(key, { statusCode: 200, body: 'second' }, 60000);
76
+ let result = model.get(key);
77
+ assert.strictEqual(result.response.body, 'second');
78
+ done();
79
+ });
80
+
81
+ it("should isolate keys by tenant", (done) => {
82
+ let key1 = { l1: 'tenant1', l2: 'key1' };
83
+ let key2 = { l1: 'tenant2', l2: 'key1' };
84
+ model.set(key1, { statusCode: 200, body: 'tenant1' }, 60000);
85
+ model.set(key2, { statusCode: 200, body: 'tenant2' }, 60000);
86
+ let result1 = model.get(key1);
87
+ let result2 = model.get(key2);
88
+ assert.strictEqual(result1.response.body, 'tenant1');
89
+ assert.strictEqual(result2.response.body, 'tenant2');
90
+ done();
91
+ });
92
+ });
93
+
94
+ describe("invalidate", () => {
95
+ it("should remove cached entry", (done) => {
96
+ let key = { l1: 'tenant1', l2: 'key1' };
97
+ model.set(key, { statusCode: 200 }, 60000);
98
+ model.invalidate(key);
99
+ let result = model.get(key);
100
+ assert.strictEqual(result, null);
101
+ done();
102
+ });
103
+
104
+ it("should handle invalidate of non-existent key", (done) => {
105
+ let key = { l1: 'tenant1', l2: 'nonexistent' };
106
+ // Should not throw
107
+ model.invalidate(key);
108
+ done();
109
+ });
110
+ });
111
+
112
+ describe("invalidateTenant", () => {
113
+ it("should remove all entries for tenant", (done) => {
114
+ let key1 = { l1: 'tenant1', l2: 'key1' };
115
+ let key2 = { l1: 'tenant1', l2: 'key2' };
116
+ let key3 = { l1: 'tenant2', l2: 'key1' };
117
+ model.set(key1, { statusCode: 200 }, 60000);
118
+ model.set(key2, { statusCode: 200 }, 60000);
119
+ model.set(key3, { statusCode: 200 }, 60000);
120
+ model.invalidateTenant('tenant1');
121
+ assert.strictEqual(model.get(key1), null);
122
+ assert.strictEqual(model.get(key2), null);
123
+ assert.notStrictEqual(model.get(key3), null);
124
+ done();
125
+ });
126
+ });
127
+
128
+ describe("cleanupExpiredEntries", () => {
129
+ it("should remove expired entries", (done) => {
130
+ let key1 = { l1: 'tenant1', l2: 'key1' };
131
+ let key2 = { l1: 'tenant1', l2: 'key2' };
132
+ model.set(key1, { statusCode: 200 }, 1); // expires immediately
133
+ model.set(key2, { statusCode: 200 }, 60000); // long TTL
134
+ setTimeout(() => {
135
+ model.cleanupExpiredEntries();
136
+ assert.strictEqual(model.get(key1), null);
137
+ assert.notStrictEqual(model.get(key2), null);
138
+ done();
139
+ }, 10);
140
+ });
141
+ });
142
+
143
+ });