xcally-nest-library 0.0.36 → 0.0.37-XM3778-bae91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.jest/jestEnv.js +3 -0
- package/.nvmrc +1 -1
- package/.prettierrc +18 -1
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/jest.config.d.ts +3 -0
- package/dist/jest.config.js +61 -0
- package/dist/jest.config.js.map +1 -0
- package/dist/src/core/application/__tests__/cached-base.service.spec.d.ts +1 -0
- package/dist/src/core/application/__tests__/cached-base.service.spec.js +224 -0
- package/dist/src/core/application/__tests__/cached-base.service.spec.js.map +1 -0
- package/dist/src/core/application/__tests__/mock-types.d.ts +66 -0
- package/dist/src/core/application/__tests__/mock-types.js +9 -0
- package/dist/src/core/application/__tests__/mock-types.js.map +1 -0
- package/dist/src/core/application/__tests__/mocks/paginated-query.mock.d.ts +2 -0
- package/dist/src/core/application/__tests__/mocks/paginated-query.mock.js +12 -0
- package/dist/src/core/application/__tests__/mocks/paginated-query.mock.js.map +1 -0
- package/dist/src/core/application/__tests__/mocks/paginated-result.mock.d.ts +31 -0
- package/dist/src/core/application/__tests__/mocks/paginated-result.mock.js +169 -0
- package/dist/src/core/application/__tests__/mocks/paginated-result.mock.js.map +1 -0
- package/dist/src/core/application/base.interface.d.ts +4 -4
- package/dist/src/core/application/base.mongo.interface.d.ts +16 -0
- package/dist/src/core/application/base.mongo.interface.js +3 -0
- package/dist/src/core/application/base.mongo.interface.js.map +1 -0
- package/dist/src/core/application/base.mongo.service.d.ts +23 -0
- package/dist/src/core/application/base.mongo.service.js +91 -0
- package/dist/src/core/application/base.mongo.service.js.map +1 -0
- package/dist/src/core/application/base.service.d.ts +2 -2
- package/dist/src/core/application/cached-base.service.d.ts +26 -0
- package/dist/src/core/application/cached-base.service.js +52 -0
- package/dist/src/core/application/cached-base.service.js.map +1 -0
- package/dist/src/core/application/cached.service.d.ts +13 -0
- package/dist/src/core/application/cached.service.js +55 -0
- package/dist/src/core/application/cached.service.js.map +1 -0
- package/dist/src/core/domain/interfaces/base.mongo.repository.interface.d.ts +12 -0
- package/dist/src/core/domain/interfaces/base.mongo.repository.interface.js +3 -0
- package/dist/src/core/domain/interfaces/base.mongo.repository.interface.js.map +1 -0
- package/dist/src/core/domain/interfaces/cache-configuration.interface.d.ts +8 -0
- package/dist/src/core/domain/interfaces/cache-configuration.interface.js +11 -0
- package/dist/src/core/domain/interfaces/cache-configuration.interface.js.map +1 -0
- package/dist/src/core/infrastracture/databases/base.mongo.entity.d.ts +14 -0
- package/dist/src/core/infrastracture/databases/base.mongo.entity.interface.d.ts +10 -0
- package/dist/src/core/infrastracture/databases/base.mongo.entity.interface.js +3 -0
- package/dist/src/core/infrastracture/databases/base.mongo.entity.interface.js.map +1 -0
- package/dist/src/core/infrastracture/databases/base.mongo.entity.js +66 -0
- package/dist/src/core/infrastracture/databases/base.mongo.entity.js.map +1 -0
- package/dist/src/core/infrastracture/databases/base.mongo.repository.d.ts +20 -0
- package/dist/src/core/infrastracture/databases/base.mongo.repository.js +275 -0
- package/dist/src/core/infrastracture/databases/base.mongo.repository.js.map +1 -0
- package/dist/src/core/infrastracture/databases/base.repository.d.ts +1 -1
- package/dist/src/modules/logger/pino/logger.module.js +1 -1
- package/dist/src/modules/logger/pino/logger.module.js.map +1 -1
- package/dist/src/modules/logger/pino/logger.service.js +4 -4
- package/dist/src/modules/logger/pino/logger.service.js.map +1 -1
- package/dist/tsconfig.json +38 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/eslint.config.mjs +49 -0
- package/index.ts +9 -0
- package/jest.config.ts +219 -0
- package/package.json +18 -6
- package/scripts/publish-feat.sh +67 -0
- package/src/core/application/__tests__/cached-base.service.spec.ts +367 -0
- package/src/core/application/__tests__/mock-types.ts +73 -0
- package/src/core/application/__tests__/mocks/paginated-query.mock.ts +10 -0
- package/src/core/application/__tests__/mocks/paginated-result.mock.ts +166 -0
- package/src/core/application/base.interface.ts +4 -5
- package/src/core/application/base.mongo.interface.ts +15 -0
- package/src/core/application/base.mongo.service.ts +170 -0
- package/src/core/application/base.service.ts +2 -2
- package/src/core/application/cached-base.service.ts +119 -0
- package/src/core/application/cached.service.ts +96 -0
- package/src/core/domain/interfaces/base.mongo.repository.interface.ts +14 -0
- package/src/core/domain/interfaces/base.repository.interface.ts +1 -0
- package/src/core/domain/interfaces/cache-configuration.interface.ts +10 -0
- package/src/core/infrastracture/databases/base.mongo.entity.interface.ts +12 -0
- package/src/core/infrastracture/databases/base.mongo.entity.ts +65 -0
- package/src/core/infrastracture/databases/base.mongo.repository.ts +371 -0
- package/src/core/infrastracture/databases/base.repository.ts +1 -1
- package/src/modules/logger/pino/logger.module.ts +1 -1
- package/src/modules/logger/pino/logger.service.ts +4 -4
- package/tsconfig.json +1 -0
- package/dist/src/modules/db-hooks-subscriber/db-hooks-subscriber.module.d.ts +0 -2
- package/dist/src/modules/db-hooks-subscriber/db-hooks-subscriber.module.js +0 -22
- package/dist/src/modules/db-hooks-subscriber/db-hooks-subscriber.module.js.map +0 -1
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import { Test, TestingModule } from '@nestjs/testing';
|
|
2
|
+
import { Cache } from 'cache-manager';
|
|
3
|
+
import { createHash } from 'crypto';
|
|
4
|
+
import { CachedBaseService } from '../cached-base.service';
|
|
5
|
+
import {
|
|
6
|
+
CacheConfiguration,
|
|
7
|
+
InvalidationStrategy,
|
|
8
|
+
TestMessage,
|
|
9
|
+
TestCreateMessage,
|
|
10
|
+
TestUpdateMessage,
|
|
11
|
+
} from './mock-types';
|
|
12
|
+
import { dataResultMock, paginatedResultMock } from './mocks/paginated-result.mock';
|
|
13
|
+
import { paginatedQueryMock } from './mocks/paginated-query.mock';
|
|
14
|
+
import { IBaseService } from '../base.interface';
|
|
15
|
+
|
|
16
|
+
const mockLoggerService = {
|
|
17
|
+
debug: jest.fn(),
|
|
18
|
+
error: jest.fn(),
|
|
19
|
+
log: jest.fn(),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const mockCacheManager = {
|
|
23
|
+
get: jest.fn(),
|
|
24
|
+
set: jest.fn(),
|
|
25
|
+
mdel: jest.fn(),
|
|
26
|
+
|
|
27
|
+
stores: [
|
|
28
|
+
{
|
|
29
|
+
iterator: jest.fn(async function* (namespace: string) {
|
|
30
|
+
yield ['cachedbase:findOne:12345', '{"id":1, "name":"cached"}'];
|
|
31
|
+
yield ['cachedbase:findAll:all', '{"data":[]}'];
|
|
32
|
+
yield ['another-service:findAll:all', '{"data":[]}'];
|
|
33
|
+
}),
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const mockService: IBaseService<TestCreateMessage, TestUpdateMessage, TestMessage> = {
|
|
39
|
+
findOne: jest.fn(),
|
|
40
|
+
findAll: jest.fn(),
|
|
41
|
+
getMany: jest.fn(),
|
|
42
|
+
create: jest.fn(),
|
|
43
|
+
update: jest.fn(),
|
|
44
|
+
remove: jest.fn(),
|
|
45
|
+
count: jest.fn(),
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
describe('CachedBaseService', () => {
|
|
49
|
+
let cachedService: CachedBaseService<TestMessage, TestCreateMessage, TestUpdateMessage, TestMessage>;
|
|
50
|
+
let service: jest.Mocked<IBaseService<TestCreateMessage, TestUpdateMessage, TestMessage>>;
|
|
51
|
+
let cacheManager: jest.Mocked<Cache>;
|
|
52
|
+
|
|
53
|
+
const testId = 1;
|
|
54
|
+
const testMessage: TestMessage = { id: testId, name: 'Test Entity' };
|
|
55
|
+
const testCreateDTO: TestCreateMessage = { name: 'Test Entity' };
|
|
56
|
+
const testUpdateDTO: TestUpdateMessage = { id: testId, name: 'Updated Entity' };
|
|
57
|
+
const cachedData = JSON.stringify(testMessage);
|
|
58
|
+
|
|
59
|
+
const generateHash = (params: any) => createHash('md5').update(JSON.stringify(params)).digest('hex');
|
|
60
|
+
|
|
61
|
+
const cacheConfigOnChange: CacheConfiguration = { invalidationStrategy: InvalidationStrategy.OnChange };
|
|
62
|
+
const cacheConfigTTL: CacheConfiguration = { invalidationStrategy: InvalidationStrategy.TTL };
|
|
63
|
+
|
|
64
|
+
const setupModule = async (config: CacheConfiguration) => {
|
|
65
|
+
const module: TestingModule = await Test.createTestingModule({
|
|
66
|
+
providers: [
|
|
67
|
+
{
|
|
68
|
+
provide: CachedBaseService,
|
|
69
|
+
useFactory: () =>
|
|
70
|
+
new CachedBaseService<TestMessage, TestCreateMessage, TestUpdateMessage, TestMessage>(
|
|
71
|
+
mockLoggerService as any,
|
|
72
|
+
mockService as unknown as jest.Mocked<IBaseService<TestCreateMessage, TestUpdateMessage, TestMessage>>,
|
|
73
|
+
mockCacheManager as unknown as jest.Mocked<Cache>,
|
|
74
|
+
config,
|
|
75
|
+
),
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
}).compile();
|
|
79
|
+
|
|
80
|
+
cachedService = module.get<CachedBaseService<TestMessage, TestCreateMessage, TestUpdateMessage, TestMessage>>(
|
|
81
|
+
CachedBaseService,
|
|
82
|
+
);
|
|
83
|
+
service = mockService as unknown as jest.Mocked<IBaseService<TestCreateMessage, TestUpdateMessage, TestMessage>>;
|
|
84
|
+
cacheManager = mockCacheManager as unknown as jest.Mocked<Cache>;
|
|
85
|
+
|
|
86
|
+
jest.clearAllMocks();
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
describe('Read operations (findOne, findAll, getMany, count)', () => {
|
|
90
|
+
beforeEach(async () => {
|
|
91
|
+
await setupModule(cacheConfigTTL);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should return cached data on cache hit (findOne)', async () => {
|
|
95
|
+
// Arrange
|
|
96
|
+
cacheManager.get.mockResolvedValue(cachedData);
|
|
97
|
+
|
|
98
|
+
// Act
|
|
99
|
+
const result = await cachedService.findOne(testId);
|
|
100
|
+
|
|
101
|
+
// Assert
|
|
102
|
+
expect(cacheManager.get).toHaveBeenCalledWith(
|
|
103
|
+
`cachedbase:findOne:${generateHash({ id: testId })}`,
|
|
104
|
+
);
|
|
105
|
+
expect(service.findOne).not.toHaveBeenCalled();
|
|
106
|
+
expect(cacheManager.set).not.toHaveBeenCalled();
|
|
107
|
+
expect(result).toEqual(testMessage);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should fetch data from service and cache it on cache miss (findOne)', async () => {
|
|
111
|
+
// Arrange
|
|
112
|
+
cacheManager.get.mockResolvedValue(undefined);
|
|
113
|
+
service.findOne.mockResolvedValue(testMessage);
|
|
114
|
+
|
|
115
|
+
// Act
|
|
116
|
+
const result = await cachedService.findOne(testId);
|
|
117
|
+
|
|
118
|
+
// Assert
|
|
119
|
+
expect(cacheManager.get).toHaveBeenCalledTimes(1);
|
|
120
|
+
expect(service.findOne).toHaveBeenCalledWith(testId);
|
|
121
|
+
expect(cacheManager.set).toHaveBeenCalledWith(
|
|
122
|
+
`cachedbase:findOne:${generateHash({ id: testId })}`,
|
|
123
|
+
cachedData,
|
|
124
|
+
);
|
|
125
|
+
expect(result).toEqual(testMessage);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should return cached data on cache hit (getMany)', async () => {
|
|
129
|
+
// Arrange
|
|
130
|
+
cacheManager.get.mockResolvedValue(cachedData);
|
|
131
|
+
|
|
132
|
+
// Act
|
|
133
|
+
const result = await cachedService.getMany(paginatedQueryMock);
|
|
134
|
+
|
|
135
|
+
// Assert
|
|
136
|
+
expect(cacheManager.get).toHaveBeenCalledWith(
|
|
137
|
+
`cachedbase:getMany:${generateHash(paginatedQueryMock)}`,
|
|
138
|
+
);
|
|
139
|
+
expect(service.getMany).not.toHaveBeenCalled();
|
|
140
|
+
expect(cacheManager.set).not.toHaveBeenCalled();
|
|
141
|
+
expect(result).toEqual(testMessage);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should fetch data from service and cache it on cache miss (getMany)', async () => {
|
|
145
|
+
// Arrange
|
|
146
|
+
cacheManager.get.mockResolvedValue(undefined);
|
|
147
|
+
service.getMany.mockResolvedValue(paginatedResultMock as any);
|
|
148
|
+
|
|
149
|
+
// Act
|
|
150
|
+
const result = await cachedService.getMany(paginatedQueryMock);
|
|
151
|
+
|
|
152
|
+
// Assert
|
|
153
|
+
expect(cacheManager.get).toHaveBeenCalledTimes(1);
|
|
154
|
+
expect(service.getMany).toHaveBeenCalledWith(paginatedQueryMock);
|
|
155
|
+
expect(cacheManager.set).toHaveBeenCalledWith(
|
|
156
|
+
`cachedbase:getMany:${generateHash(paginatedQueryMock)}`,
|
|
157
|
+
JSON.stringify(paginatedResultMock),
|
|
158
|
+
);
|
|
159
|
+
expect(result).toEqual(paginatedResultMock);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('should return cached data on cache hit (findAll)', async () => {
|
|
163
|
+
// Arrange
|
|
164
|
+
cacheManager.get.mockResolvedValue(JSON.stringify({ data: dataResultMock }));
|
|
165
|
+
|
|
166
|
+
// Act
|
|
167
|
+
const result = await cachedService.findAll();
|
|
168
|
+
|
|
169
|
+
// Assert
|
|
170
|
+
expect(cacheManager.get).toHaveBeenCalledWith(
|
|
171
|
+
`cachedbase:findAll:all`,
|
|
172
|
+
);
|
|
173
|
+
expect(service.findAll).not.toHaveBeenCalled();
|
|
174
|
+
expect(cacheManager.set).not.toHaveBeenCalled();
|
|
175
|
+
expect(result).toEqual({ data: dataResultMock });
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('should fetch data from service and cache it on cache miss (findAll)', async () => {
|
|
179
|
+
// Arrange
|
|
180
|
+
cacheManager.get.mockResolvedValue(undefined);
|
|
181
|
+
service.findAll.mockResolvedValue({ data: dataResultMock });
|
|
182
|
+
|
|
183
|
+
// Act
|
|
184
|
+
const result = await cachedService.findAll();
|
|
185
|
+
|
|
186
|
+
// Assert
|
|
187
|
+
expect(cacheManager.get).toHaveBeenCalledTimes(1);
|
|
188
|
+
expect(service.findAll).toHaveBeenCalledWith();
|
|
189
|
+
expect(cacheManager.set).toHaveBeenCalledWith(
|
|
190
|
+
`cachedbase:findAll:all`,
|
|
191
|
+
JSON.stringify({ data: dataResultMock }),
|
|
192
|
+
);
|
|
193
|
+
expect(result).toEqual({ data: dataResultMock });
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('should fetch data from service when an error occurred retrieving data from cache', async () => {
|
|
197
|
+
// Arrange
|
|
198
|
+
cacheManager.get.mockRejectedValue(new Error('Cache not reachable'));
|
|
199
|
+
service.findAll.mockResolvedValue({ data: dataResultMock });
|
|
200
|
+
|
|
201
|
+
// Act
|
|
202
|
+
const result = await cachedService.findAll();
|
|
203
|
+
|
|
204
|
+
// Assert
|
|
205
|
+
expect(cacheManager.get).toHaveBeenCalledTimes(1);
|
|
206
|
+
expect(service.findAll).toHaveBeenCalledWith();
|
|
207
|
+
expect(cacheManager.set).not.toHaveBeenCalled();
|
|
208
|
+
expect(result).toEqual({ data: dataResultMock });
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('should handle getMany correctly (pagination and query hashing)', async () => {
|
|
212
|
+
// Arrange
|
|
213
|
+
const stringifiedResult = JSON.stringify(paginatedResultMock);
|
|
214
|
+
cacheManager.get.mockResolvedValue(undefined);
|
|
215
|
+
service.getMany.mockResolvedValue(paginatedResultMock as any);
|
|
216
|
+
|
|
217
|
+
// Act
|
|
218
|
+
await cachedService.getMany(paginatedQueryMock);
|
|
219
|
+
|
|
220
|
+
// Assert
|
|
221
|
+
const expectedKey = `cachedbase:getMany:${generateHash(paginatedQueryMock)}`;
|
|
222
|
+
expect(service.getMany).toHaveBeenCalledWith(paginatedQueryMock);
|
|
223
|
+
expect(cacheManager.set).toHaveBeenCalledWith(expectedKey, stringifiedResult);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('should return cached data on cache hit (count)', async () => {
|
|
227
|
+
// Arrange
|
|
228
|
+
cacheManager.get.mockResolvedValue(JSON.stringify({ count: 42 }));
|
|
229
|
+
|
|
230
|
+
// Act
|
|
231
|
+
const result = await cachedService.count();
|
|
232
|
+
|
|
233
|
+
// Assert
|
|
234
|
+
expect(cacheManager.get).toHaveBeenCalledWith(
|
|
235
|
+
`cachedbase:count:all`,
|
|
236
|
+
);
|
|
237
|
+
expect(service.count).not.toHaveBeenCalled();
|
|
238
|
+
expect(cacheManager.set).not.toHaveBeenCalled();
|
|
239
|
+
expect(result).toEqual({ count: 42 });
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('should fetch data from service and cache it on cache miss (count)', async () => {
|
|
243
|
+
// Arrange
|
|
244
|
+
cacheManager.get.mockResolvedValue(undefined);
|
|
245
|
+
service.count.mockResolvedValue({ count: 42 });
|
|
246
|
+
|
|
247
|
+
// Act
|
|
248
|
+
const result = await cachedService.count();
|
|
249
|
+
|
|
250
|
+
// Assert
|
|
251
|
+
expect(cacheManager.get).toHaveBeenCalledWith(
|
|
252
|
+
`cachedbase:count:all`,
|
|
253
|
+
);
|
|
254
|
+
expect(service.count).toHaveBeenCalledTimes(1);
|
|
255
|
+
expect(cacheManager.set).toHaveBeenCalledWith(
|
|
256
|
+
`cachedbase:count:all`,
|
|
257
|
+
JSON.stringify({ count: 42 }),
|
|
258
|
+
);
|
|
259
|
+
expect(result).toEqual({ count: 42 });
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
describe('Write operations (create, update, remove)', () => {
|
|
264
|
+
it('should invalidate cache after create when strategy is OnChange', async () => {
|
|
265
|
+
// Arrange
|
|
266
|
+
await setupModule(cacheConfigOnChange);
|
|
267
|
+
service.create.mockResolvedValue(testMessage);
|
|
268
|
+
|
|
269
|
+
// Act
|
|
270
|
+
const result = await cachedService.create(testCreateDTO);
|
|
271
|
+
|
|
272
|
+
// Assert
|
|
273
|
+
expect(service.create).toHaveBeenCalledWith(testCreateDTO);
|
|
274
|
+
expect(cacheManager.mdel).toHaveBeenCalledWith([
|
|
275
|
+
'cachedbase:findOne:12345',
|
|
276
|
+
'cachedbase:findAll:all',
|
|
277
|
+
]);
|
|
278
|
+
expect(result).toEqual(testMessage);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('should invalidate cache after update when strategy is OnChange', async () => {
|
|
282
|
+
// Arrange
|
|
283
|
+
await setupModule(cacheConfigOnChange);
|
|
284
|
+
service.update.mockResolvedValue(testUpdateDTO);
|
|
285
|
+
|
|
286
|
+
// Act
|
|
287
|
+
const result = await cachedService.update(testUpdateDTO);
|
|
288
|
+
|
|
289
|
+
// Assert
|
|
290
|
+
expect(service.update).toHaveBeenCalledWith(testUpdateDTO);
|
|
291
|
+
expect(cacheManager.mdel).toHaveBeenCalledWith([
|
|
292
|
+
'cachedbase:findOne:12345',
|
|
293
|
+
'cachedbase:findAll:all',
|
|
294
|
+
]);
|
|
295
|
+
expect(result).toEqual(testUpdateDTO);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it('should invalidate cache after remove when strategy is OnChange', async () => {
|
|
299
|
+
await setupModule(cacheConfigOnChange);
|
|
300
|
+
service.remove.mockResolvedValue();
|
|
301
|
+
|
|
302
|
+
await cachedService.remove(testId);
|
|
303
|
+
|
|
304
|
+
expect(service.remove).toHaveBeenCalledWith(testId);
|
|
305
|
+
expect(cacheManager.mdel).toHaveBeenCalledWith([
|
|
306
|
+
'cachedbase:findOne:12345',
|
|
307
|
+
'cachedbase:findAll:all',
|
|
308
|
+
]);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it('should not invalidate cache after create when strategy is TTL', async () => {
|
|
312
|
+
// Arrange
|
|
313
|
+
await setupModule(cacheConfigTTL);
|
|
314
|
+
service.create.mockResolvedValue(testMessage);
|
|
315
|
+
|
|
316
|
+
// Act
|
|
317
|
+
const result = await cachedService.create(testCreateDTO);
|
|
318
|
+
|
|
319
|
+
// Assert
|
|
320
|
+
expect(service.create).toHaveBeenCalledWith(testCreateDTO);
|
|
321
|
+
expect(cacheManager.mdel).not.toHaveBeenCalled();
|
|
322
|
+
expect(result).toEqual(testMessage);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it('should not invalidate cache after update when strategy is TTL', async () => {
|
|
326
|
+
// Arrange
|
|
327
|
+
await setupModule(cacheConfigTTL);
|
|
328
|
+
service.update.mockResolvedValue(testUpdateDTO);
|
|
329
|
+
|
|
330
|
+
// Act
|
|
331
|
+
const result = await cachedService.update(testUpdateDTO);
|
|
332
|
+
|
|
333
|
+
// Assert
|
|
334
|
+
expect(service.update).toHaveBeenCalledWith(testUpdateDTO);
|
|
335
|
+
expect(cacheManager.mdel).not.toHaveBeenCalled();
|
|
336
|
+
expect(result).toEqual(testUpdateDTO);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('should not invalidate cache after remove when strategy is TTL', async () => {
|
|
340
|
+
await setupModule(cacheConfigTTL);
|
|
341
|
+
service.remove.mockResolvedValue();
|
|
342
|
+
|
|
343
|
+
await cachedService.remove(testId);
|
|
344
|
+
|
|
345
|
+
expect(service.remove).toHaveBeenCalledWith(testId);
|
|
346
|
+
expect(cacheManager.mdel).not.toHaveBeenCalled();
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it('should return data in case of error while invalidating cache when strategy is OnChange', async () => {
|
|
351
|
+
// Arrange
|
|
352
|
+
await setupModule(cacheConfigOnChange);
|
|
353
|
+
service.create.mockResolvedValue(testMessage);
|
|
354
|
+
cacheManager.mdel.mockRejectedValue(new Error('Cannot delete data from cache'));
|
|
355
|
+
|
|
356
|
+
// Act
|
|
357
|
+
const result = await cachedService.create(testCreateDTO);
|
|
358
|
+
|
|
359
|
+
// Assert
|
|
360
|
+
expect(service.create).toHaveBeenCalledWith(testCreateDTO);
|
|
361
|
+
expect(cacheManager.mdel).toHaveBeenCalledWith([
|
|
362
|
+
'cachedbase:findOne:12345',
|
|
363
|
+
'cachedbase:findAll:all',
|
|
364
|
+
]);
|
|
365
|
+
expect(result).toEqual(testMessage);
|
|
366
|
+
});
|
|
367
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Paginated } from 'nestjs-paginate';
|
|
2
|
+
|
|
3
|
+
/** Definizioni di tipi mock */
|
|
4
|
+
|
|
5
|
+
// Mock semplice per la paginazione di nestjs-paginate
|
|
6
|
+
export type MockPaginateQuery = PaginateQuery;
|
|
7
|
+
export type MockPaginated<T> = Paginated<T>;
|
|
8
|
+
|
|
9
|
+
// Interfaccia del servizio base (sotto il caching)
|
|
10
|
+
export interface IBaseService<CreateMessage, UpdateMessage extends { id: number | string }, Message> {
|
|
11
|
+
findOne(id: number | string): Promise<Message>;
|
|
12
|
+
findAll(): Promise<{ data: Message[] }>;
|
|
13
|
+
getMany(query?: MockPaginateQuery): Promise<PaginatedServiceMessage | Promise<PaginatedServiceMessage>>;
|
|
14
|
+
create(createDTO: CreateMessage): Promise<Message>;
|
|
15
|
+
update(updateDTO: UpdateMessage): Promise<Message>;
|
|
16
|
+
remove(id: number | string): Promise<void>;
|
|
17
|
+
count(options?: any): Promise<{ count: number }>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Configurazione della cache
|
|
21
|
+
export enum InvalidationStrategy {
|
|
22
|
+
OnChange = 'OnChange',
|
|
23
|
+
TTL = 'TTL',
|
|
24
|
+
// Potresti aggiungere altri metodi (es. TTL)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface CacheConfiguration {
|
|
28
|
+
invalidationStrategy: InvalidationStrategy;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Struttura dei messaggi
|
|
32
|
+
export interface TestMessage {
|
|
33
|
+
id: number;
|
|
34
|
+
name: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface TestCreateMessage {
|
|
38
|
+
name: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface TestUpdateMessage extends TestCreateMessage {
|
|
42
|
+
id: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface ServiceMessage {
|
|
46
|
+
id: number;
|
|
47
|
+
name?: string | undefined;
|
|
48
|
+
description?: string | undefined;
|
|
49
|
+
createdAt?: string | undefined;
|
|
50
|
+
updatedAt?: string | undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface PaginatedServiceMessage {
|
|
54
|
+
data: ServiceMessage[];
|
|
55
|
+
meta: { [key: string]: any } | undefined;
|
|
56
|
+
links: { [key: string]: any } | undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface PaginateQuery {
|
|
60
|
+
page?: number;
|
|
61
|
+
limit?: number;
|
|
62
|
+
sortBy?: [string, string][];
|
|
63
|
+
searchBy?: string[];
|
|
64
|
+
search?: string;
|
|
65
|
+
filter?: {
|
|
66
|
+
[column: string]: string | string[];
|
|
67
|
+
};
|
|
68
|
+
select?: string[];
|
|
69
|
+
cursor?: string;
|
|
70
|
+
cursorColumn?: string;
|
|
71
|
+
cursorDirection?: 'before' | 'after';
|
|
72
|
+
path: string;
|
|
73
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
export const dataResultMock = [
|
|
2
|
+
{
|
|
3
|
+
"id": 1,
|
|
4
|
+
"createdAt": "2023-02-06T10:57:24.000Z",
|
|
5
|
+
"updatedAt": "2023-02-06T10:57:24.000Z",
|
|
6
|
+
"name": "CHOICE",
|
|
7
|
+
"description": "variable for menu auto generated"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"id": 2,
|
|
11
|
+
"createdAt": "2023-02-06T10:57:24.000Z",
|
|
12
|
+
"updatedAt": "2023-02-06T10:57:24.000Z",
|
|
13
|
+
"name": "ORDERNUM",
|
|
14
|
+
"description": "variable for order auto generated"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"id": 3,
|
|
18
|
+
"createdAt": "2023-02-06T10:57:24.000Z",
|
|
19
|
+
"updatedAt": "2023-02-06T10:57:24.000Z",
|
|
20
|
+
"name": "DBRESULT",
|
|
21
|
+
"description": "variable for db query result auto generated"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": 4,
|
|
25
|
+
"createdAt": "2023-02-06T10:57:24.000Z",
|
|
26
|
+
"updatedAt": "2023-02-06T10:57:24.000Z",
|
|
27
|
+
"name": "RATE",
|
|
28
|
+
"description": "variable for rate call auto generated"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"id": 5,
|
|
32
|
+
"createdAt": "2023-02-06T10:57:24.000Z",
|
|
33
|
+
"updatedAt": "2023-02-06T10:57:24.000Z",
|
|
34
|
+
"name": "CALLERID(dnid)",
|
|
35
|
+
"description": "variable dialed number identifier auto generated"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": 6,
|
|
39
|
+
"createdAt": "2023-02-06T10:57:24.000Z",
|
|
40
|
+
"updatedAt": "2023-02-06T10:57:24.000Z",
|
|
41
|
+
"name": "CALLERID(name)",
|
|
42
|
+
"description": "variable name of the caller auto generated"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"id": 7,
|
|
46
|
+
"createdAt": "2023-02-06T10:57:24.000Z",
|
|
47
|
+
"updatedAt": "2023-02-06T10:57:24.000Z",
|
|
48
|
+
"name": "CALLERID(num)",
|
|
49
|
+
"description": "variable number of the caller auto generated"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"id": 8,
|
|
53
|
+
"createdAt": "2023-02-06T10:57:24.000Z",
|
|
54
|
+
"updatedAt": "2023-02-06T10:57:24.000Z",
|
|
55
|
+
"name": "CHANNEL(language)",
|
|
56
|
+
"description": "variable select a natural language file when available auto generated"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"id": 9,
|
|
60
|
+
"createdAt": "2023-02-06T10:57:24.000Z",
|
|
61
|
+
"updatedAt": "2023-02-06T10:57:24.000Z",
|
|
62
|
+
"name": "COUNTER",
|
|
63
|
+
"description": "variable auto generated"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"id": 10,
|
|
67
|
+
"createdAt": "2023-02-06T10:57:24.000Z",
|
|
68
|
+
"updatedAt": "2023-02-06T10:57:24.000Z",
|
|
69
|
+
"name": "EMAIL",
|
|
70
|
+
"description": "variable auto generated"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"id": 11,
|
|
74
|
+
"createdAt": "2023-02-06T10:57:24.000Z",
|
|
75
|
+
"updatedAt": "2023-02-06T10:57:24.000Z",
|
|
76
|
+
"name": "EXTRAVAR",
|
|
77
|
+
"description": "variable useful for agent popup auto generated"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"id": 12,
|
|
81
|
+
"createdAt": "2023-02-06T10:57:24.000Z",
|
|
82
|
+
"updatedAt": "2023-02-06T10:57:24.000Z",
|
|
83
|
+
"name": "FIRSTNAME",
|
|
84
|
+
"description": "variable auto generated"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"id": 13,
|
|
88
|
+
"createdAt": "2023-02-06T10:57:24.000Z",
|
|
89
|
+
"updatedAt": "2023-02-06T10:57:24.000Z",
|
|
90
|
+
"name": "LASTNAME",
|
|
91
|
+
"description": "variable auto generated"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"id": 14,
|
|
95
|
+
"createdAt": "2023-02-06T10:57:24.000Z",
|
|
96
|
+
"updatedAt": "2023-02-06T10:57:24.000Z",
|
|
97
|
+
"name": "INPUT",
|
|
98
|
+
"description": "variable auto generated"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"id": 15,
|
|
102
|
+
"createdAt": "2023-02-06T10:57:24.000Z",
|
|
103
|
+
"updatedAt": "2023-02-06T10:57:24.000Z",
|
|
104
|
+
"name": "OPTION",
|
|
105
|
+
"description": "variable auto generated"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"id": 16,
|
|
109
|
+
"createdAt": "2023-02-06T10:57:24.000Z",
|
|
110
|
+
"updatedAt": "2023-02-06T10:57:24.000Z",
|
|
111
|
+
"name": "ORDER",
|
|
112
|
+
"description": "variable auto generated"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"id": 17,
|
|
116
|
+
"createdAt": "2023-02-06T10:57:24.000Z",
|
|
117
|
+
"updatedAt": "2023-02-06T10:57:24.000Z",
|
|
118
|
+
"name": "PHONE",
|
|
119
|
+
"description": "variable auto generated"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"id": 18,
|
|
123
|
+
"createdAt": "2023-02-06T10:57:24.000Z",
|
|
124
|
+
"updatedAt": "2023-02-06T10:57:24.000Z",
|
|
125
|
+
"name": "RESULT",
|
|
126
|
+
"description": "variable auto generated"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"id": 19,
|
|
130
|
+
"createdAt": "2023-02-06T10:57:24.000Z",
|
|
131
|
+
"updatedAt": "2023-02-06T10:57:24.000Z",
|
|
132
|
+
"name": "UNIQUEID",
|
|
133
|
+
"description": "variable current call unique identifier auto generated"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"id": 20,
|
|
137
|
+
"createdAt": "2023-02-06T10:57:24.000Z",
|
|
138
|
+
"updatedAt": "2023-02-06T10:57:24.000Z",
|
|
139
|
+
"name": "VAR1",
|
|
140
|
+
"description": "generic variable auto generated"
|
|
141
|
+
}
|
|
142
|
+
];
|
|
143
|
+
|
|
144
|
+
export const paginatedResultMock = {
|
|
145
|
+
"meta": {
|
|
146
|
+
"itemsPerPage": 20,
|
|
147
|
+
"totalItems": 25,
|
|
148
|
+
"currentPage": 1,
|
|
149
|
+
"totalPages": 2,
|
|
150
|
+
"sortBy": [
|
|
151
|
+
[
|
|
152
|
+
"id",
|
|
153
|
+
"ASC"
|
|
154
|
+
]
|
|
155
|
+
],
|
|
156
|
+
"searchBy": "",
|
|
157
|
+
"search": "",
|
|
158
|
+
"select": "",
|
|
159
|
+
},
|
|
160
|
+
"links": {
|
|
161
|
+
"current": "/api/v2/variables?page=1&limit=20&sortBy=id:ASC",
|
|
162
|
+
"next": "/api/v2/variables?page=2&limit=20&sortBy=id:ASC",
|
|
163
|
+
"last": "/api/v2/variables?page=2&limit=20&sortBy=id:ASC"
|
|
164
|
+
},
|
|
165
|
+
"data": dataResultMock,
|
|
166
|
+
};
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { Paginated, PaginateQuery } from 'nestjs-paginate';
|
|
2
2
|
|
|
3
3
|
export interface IBaseService<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
UpdateMessage extends Partial<Entity> & { id: number },
|
|
4
|
+
CreateMessage,
|
|
5
|
+
UpdateMessage extends { id: number | string },
|
|
7
6
|
Message,
|
|
8
7
|
> {
|
|
9
|
-
findOne(id: number): Promise<Message>;
|
|
8
|
+
findOne(id: number | string): Promise<Message>;
|
|
10
9
|
findAll(): Promise<{ data: Message[] }>;
|
|
11
10
|
getMany(query?: PaginateQuery): Promise<Paginated<Message>>;
|
|
12
11
|
create(createDTO: CreateMessage): Promise<Message>;
|
|
13
12
|
update(updateDTO: UpdateMessage): Promise<Message>;
|
|
14
|
-
remove(id: number): Promise<void>;
|
|
13
|
+
remove(id: number | string): Promise<void>;
|
|
15
14
|
count(options?: any): Promise<{ count: number }>;
|
|
16
15
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Paginated, PaginateQuery } from 'nestjs-paginate';
|
|
2
|
+
|
|
3
|
+
export interface IBaseMongoService<
|
|
4
|
+
CreateMessage,
|
|
5
|
+
UpdateMessage extends { id: string },
|
|
6
|
+
Message,
|
|
7
|
+
> {
|
|
8
|
+
findOne(id: string): Promise<Message>;
|
|
9
|
+
findAll(): Promise<{ data: Message[] }>;
|
|
10
|
+
getMany(query?: PaginateQuery): Promise<Paginated<Message>>;
|
|
11
|
+
create(createDTO: CreateMessage): Promise<Message>;
|
|
12
|
+
update(updateDTO: UpdateMessage): Promise<Message>;
|
|
13
|
+
remove(id: string): Promise<void>;
|
|
14
|
+
count(options?: any): Promise<{ count: number }>;
|
|
15
|
+
}
|