@smartsoft001/mongo 1.1.91 → 1.3.0
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/.eslintrc +13 -0
- package/jest.config.ts +27 -0
- package/package.json +2 -26
- package/project.json +48 -0
- package/src/index.ts +3 -0
- package/src/lib/mongo.config.ts +10 -0
- package/src/lib/mongo.module.ts +28 -0
- package/src/lib/mongo.unitofwork.spec.ts +84 -0
- package/src/lib/mongo.unitofwork.ts +58 -0
- package/src/lib/mongo.utils.spec.ts +54 -0
- package/src/lib/mongo.utils.ts +17 -0
- package/src/lib/repositories/attachment.repository.spec.ts +249 -0
- package/src/lib/repositories/attachment.repository.ts +113 -0
- package/src/lib/repositories/interfaces.ts +28 -0
- package/src/lib/repositories/item.repository.spec.ts +1269 -0
- package/src/lib/repositories/item.repository.ts +582 -0
- package/tsconfig.json +13 -0
- package/tsconfig.lib.json +11 -0
- package/tsconfig.spec.json +20 -0
- package/src/index.d.ts +0 -3
- package/src/index.js +0 -7
- package/src/index.js.map +0 -1
- package/src/lib/mongo.config.d.ts +0 -9
- package/src/lib/mongo.config.js +0 -7
- package/src/lib/mongo.config.js.map +0 -1
- package/src/lib/mongo.module.d.ts +0 -5
- package/src/lib/mongo.module.js +0 -25
- package/src/lib/mongo.module.js.map +0 -1
- package/src/lib/mongo.unitofwork.d.ts +0 -12
- package/src/lib/mongo.unitofwork.js +0 -56
- package/src/lib/mongo.unitofwork.js.map +0 -1
- package/src/lib/mongo.utils.d.ts +0 -2
- package/src/lib/mongo.utils.js +0 -13
- package/src/lib/mongo.utils.js.map +0 -1
- package/src/lib/repositories/attachment.repository.d.ts +0 -28
- package/src/lib/repositories/attachment.repository.js +0 -88
- package/src/lib/repositories/attachment.repository.js.map +0 -1
- package/src/lib/repositories/item.repository.d.ts +0 -51
- package/src/lib/repositories/item.repository.js +0 -411
- package/src/lib/repositories/item.repository.js.map +0 -1
- package/test-setup.js +0 -1
- package/test-setup.js.map +0 -1
- /package/{test-setup.d.ts → test-setup.ts} +0 -0
|
@@ -0,0 +1,1269 @@
|
|
|
1
|
+
import { IItemRepositoryOptions } from '@smartsoft001/domain-core';
|
|
2
|
+
import { MongoConfig, MongoItemRepository } from '@smartsoft001/mongo';
|
|
3
|
+
import { IUser } from '@smartsoft001/users';
|
|
4
|
+
|
|
5
|
+
import { IMongoTransaction } from '../mongo.unitofwork';
|
|
6
|
+
|
|
7
|
+
describe('shared-mongo: MongoItemRepository create function', () => {
|
|
8
|
+
let repository: MongoItemRepository<any>;
|
|
9
|
+
let mockCollection: any;
|
|
10
|
+
let mockLogChange: jest.Mock;
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
mockCollection = {
|
|
14
|
+
insertOne: jest.fn(),
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
mockLogChange = jest.fn().mockResolvedValue(undefined);
|
|
18
|
+
|
|
19
|
+
repository = new MongoItemRepository(new MongoConfig());
|
|
20
|
+
jest
|
|
21
|
+
.spyOn(repository as any, 'collectionContext')
|
|
22
|
+
.mockImplementation((callback: any) => {
|
|
23
|
+
return callback(mockCollection);
|
|
24
|
+
});
|
|
25
|
+
jest
|
|
26
|
+
.spyOn(repository as any, 'logChange')
|
|
27
|
+
.mockImplementation(mockLogChange);
|
|
28
|
+
jest
|
|
29
|
+
.spyOn(repository as any, 'getModelToCreate')
|
|
30
|
+
.mockImplementation((item: Record<string, any>, user: IUser) => ({
|
|
31
|
+
...item,
|
|
32
|
+
_id: item.id,
|
|
33
|
+
__info: { create: { username: user.username, date: new Date() } },
|
|
34
|
+
}));
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should call insertOne with the transformed item', async () => {
|
|
38
|
+
const item = { id: '123', name: 'test item' };
|
|
39
|
+
const user = { username: 'testuser', permissions: [''] };
|
|
40
|
+
|
|
41
|
+
await repository.create(item, user);
|
|
42
|
+
|
|
43
|
+
expect(mockCollection.insertOne).toHaveBeenCalledWith(
|
|
44
|
+
{
|
|
45
|
+
id: '123',
|
|
46
|
+
name: 'test item',
|
|
47
|
+
_id: '123',
|
|
48
|
+
__info: {
|
|
49
|
+
create: {
|
|
50
|
+
username: 'testuser',
|
|
51
|
+
date: expect.any(Date),
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{ session: undefined },
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should call logChange with correct parameters on success', async () => {
|
|
60
|
+
const item = { id: '123', name: 'test item' };
|
|
61
|
+
const user = { username: 'testuser', permissions: [''] };
|
|
62
|
+
const repoOptions = {
|
|
63
|
+
transaction: { session: 'mockSession', connection: 'mockConnection' },
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
await repository.create(item, user, repoOptions);
|
|
67
|
+
|
|
68
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
69
|
+
'create',
|
|
70
|
+
item,
|
|
71
|
+
repoOptions,
|
|
72
|
+
user,
|
|
73
|
+
null,
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should call logChange with error on failure and rethrow the error', async () => {
|
|
78
|
+
const item = { id: '123', name: 'test item' };
|
|
79
|
+
const user = { username: 'testuser', permissions: [''] };
|
|
80
|
+
const error = new Error('Insert failed');
|
|
81
|
+
mockCollection.insertOne.mockRejectedValue(error);
|
|
82
|
+
|
|
83
|
+
await expect(repository.create(item, user)).rejects.toThrow(error);
|
|
84
|
+
|
|
85
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
86
|
+
'create',
|
|
87
|
+
item,
|
|
88
|
+
undefined,
|
|
89
|
+
user,
|
|
90
|
+
error,
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe('shared-mongo: MongoItemRepository clear function', () => {
|
|
96
|
+
let repository: MongoItemRepository<any>;
|
|
97
|
+
let mockCollection: any;
|
|
98
|
+
let mockLogChange: jest.Mock;
|
|
99
|
+
|
|
100
|
+
beforeEach(() => {
|
|
101
|
+
mockCollection = {
|
|
102
|
+
deleteMany: jest.fn(),
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
mockLogChange = jest.fn().mockResolvedValue(undefined);
|
|
106
|
+
|
|
107
|
+
repository = new MongoItemRepository<any>(null as any);
|
|
108
|
+
|
|
109
|
+
// Mock collectionContext
|
|
110
|
+
jest
|
|
111
|
+
.spyOn(repository as any, 'collectionContext')
|
|
112
|
+
.mockImplementation(async (callback: any) => {
|
|
113
|
+
return callback(mockCollection);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Mock logChange
|
|
117
|
+
jest
|
|
118
|
+
.spyOn(repository as any, 'logChange')
|
|
119
|
+
.mockImplementation(mockLogChange);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should call deleteMany and logChange on success', async () => {
|
|
123
|
+
const mockUser: IUser = { username: 'testUser', permissions: [''] }; // Replace with actual IUser fields
|
|
124
|
+
const mockRepoOptions: IItemRepositoryOptions = {
|
|
125
|
+
transaction: { session: {} } as IMongoTransaction,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
mockCollection.deleteMany.mockResolvedValueOnce({});
|
|
129
|
+
|
|
130
|
+
await repository.clear(mockUser, mockRepoOptions);
|
|
131
|
+
|
|
132
|
+
expect(mockCollection.deleteMany).toHaveBeenCalledWith(
|
|
133
|
+
{},
|
|
134
|
+
{ session: (mockRepoOptions.transaction as IMongoTransaction).session },
|
|
135
|
+
);
|
|
136
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
137
|
+
'clear',
|
|
138
|
+
null,
|
|
139
|
+
mockRepoOptions,
|
|
140
|
+
mockUser,
|
|
141
|
+
null,
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should call logChange with error if deleteMany fails', async () => {
|
|
146
|
+
const mockUser: IUser = { username: 'testUser', permissions: [''] };
|
|
147
|
+
const mockRepoOptions: IItemRepositoryOptions = {
|
|
148
|
+
transaction: { session: {} } as IMongoTransaction,
|
|
149
|
+
};
|
|
150
|
+
const mockError = new Error('DeleteMany failed');
|
|
151
|
+
|
|
152
|
+
mockCollection.deleteMany.mockRejectedValueOnce(mockError);
|
|
153
|
+
|
|
154
|
+
await expect(repository.clear(mockUser, mockRepoOptions)).rejects.toThrow(
|
|
155
|
+
mockError,
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
expect(mockCollection.deleteMany).toHaveBeenCalledWith(
|
|
159
|
+
{},
|
|
160
|
+
{ session: (mockRepoOptions.transaction as IMongoTransaction).session },
|
|
161
|
+
);
|
|
162
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
163
|
+
'clear',
|
|
164
|
+
null,
|
|
165
|
+
mockRepoOptions,
|
|
166
|
+
mockUser,
|
|
167
|
+
mockError,
|
|
168
|
+
);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('should work without repoOptions', async () => {
|
|
172
|
+
const mockUser: IUser = { username: 'testUser', permissions: [''] };
|
|
173
|
+
|
|
174
|
+
mockCollection.deleteMany.mockResolvedValueOnce({});
|
|
175
|
+
|
|
176
|
+
await repository.clear(mockUser);
|
|
177
|
+
|
|
178
|
+
expect(mockCollection.deleteMany).toHaveBeenCalledWith(
|
|
179
|
+
{},
|
|
180
|
+
{ session: undefined },
|
|
181
|
+
);
|
|
182
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
183
|
+
'clear',
|
|
184
|
+
null,
|
|
185
|
+
undefined,
|
|
186
|
+
mockUser,
|
|
187
|
+
null,
|
|
188
|
+
);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe('shared-mongo: MongoItemRepository createMany function', () => {
|
|
193
|
+
let repository: MongoItemRepository<any>;
|
|
194
|
+
let mockCollection: any;
|
|
195
|
+
let mockLogChange: jest.Mock;
|
|
196
|
+
let mockGetModelToCreate: jest.Mock;
|
|
197
|
+
|
|
198
|
+
beforeEach(() => {
|
|
199
|
+
mockCollection = {
|
|
200
|
+
insertMany: jest.fn(),
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
mockLogChange = jest.fn().mockResolvedValue(undefined);
|
|
204
|
+
mockGetModelToCreate = jest.fn((item, user) => ({
|
|
205
|
+
...item,
|
|
206
|
+
createdBy: user.username,
|
|
207
|
+
}));
|
|
208
|
+
|
|
209
|
+
repository = new MongoItemRepository<any>(null as any);
|
|
210
|
+
|
|
211
|
+
// Mock collectionContext
|
|
212
|
+
jest
|
|
213
|
+
.spyOn(repository as any, 'collectionContext')
|
|
214
|
+
.mockImplementation(async (callback: any) => {
|
|
215
|
+
return callback(mockCollection);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// Mock logChange
|
|
219
|
+
jest
|
|
220
|
+
.spyOn(repository as any, 'logChange')
|
|
221
|
+
.mockImplementation(mockLogChange);
|
|
222
|
+
|
|
223
|
+
// Mock getModelToCreate
|
|
224
|
+
jest
|
|
225
|
+
.spyOn(repository as any, 'getModelToCreate')
|
|
226
|
+
.mockImplementation(mockGetModelToCreate);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('should call insertMany and logChange on success', async () => {
|
|
230
|
+
const mockUser: IUser = { username: 'testUser', permissions: [''] };
|
|
231
|
+
const mockRepoOptions: IItemRepositoryOptions = {
|
|
232
|
+
transaction: { session: {} } as IMongoTransaction,
|
|
233
|
+
};
|
|
234
|
+
const mockList = [{ id: 1 }, { id: 2 }];
|
|
235
|
+
|
|
236
|
+
mockCollection.insertMany.mockResolvedValueOnce({});
|
|
237
|
+
|
|
238
|
+
await repository.createMany(mockList, mockUser, mockRepoOptions);
|
|
239
|
+
|
|
240
|
+
expect(mockCollection.insertMany).toHaveBeenCalledWith(
|
|
241
|
+
mockList.map((item) => ({ ...item, createdBy: mockUser.username })),
|
|
242
|
+
{ session: (mockRepoOptions.transaction as IMongoTransaction).session },
|
|
243
|
+
);
|
|
244
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
245
|
+
'createMany',
|
|
246
|
+
null,
|
|
247
|
+
mockRepoOptions,
|
|
248
|
+
mockUser,
|
|
249
|
+
null,
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('should call logChange with error if insertMany fails', async () => {
|
|
254
|
+
const mockUser: IUser = { username: 'testUser', permissions: [''] };
|
|
255
|
+
const mockRepoOptions: IItemRepositoryOptions = {
|
|
256
|
+
transaction: { session: {} } as IMongoTransaction,
|
|
257
|
+
};
|
|
258
|
+
const mockList = [{ id: 1 }, { id: 2 }];
|
|
259
|
+
const mockError = new Error('InsertMany failed');
|
|
260
|
+
|
|
261
|
+
mockCollection.insertMany.mockRejectedValueOnce(mockError);
|
|
262
|
+
|
|
263
|
+
await expect(
|
|
264
|
+
repository.createMany(mockList, mockUser, mockRepoOptions),
|
|
265
|
+
).rejects.toThrow(mockError);
|
|
266
|
+
|
|
267
|
+
expect(mockCollection.insertMany).toHaveBeenCalledWith(
|
|
268
|
+
mockList.map((item) => ({ ...item, createdBy: mockUser.username })),
|
|
269
|
+
{ session: (mockRepoOptions.transaction as IMongoTransaction).session },
|
|
270
|
+
);
|
|
271
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
272
|
+
'createMany',
|
|
273
|
+
null,
|
|
274
|
+
mockRepoOptions,
|
|
275
|
+
mockUser,
|
|
276
|
+
mockError,
|
|
277
|
+
);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('should work without repoOptions', async () => {
|
|
281
|
+
const mockUser: IUser = { username: 'testUser', permissions: [''] };
|
|
282
|
+
const mockList = [{ id: 1 }, { id: 2 }];
|
|
283
|
+
|
|
284
|
+
mockCollection.insertMany.mockResolvedValueOnce({});
|
|
285
|
+
|
|
286
|
+
await repository.createMany(mockList, mockUser);
|
|
287
|
+
|
|
288
|
+
expect(mockCollection.insertMany).toHaveBeenCalledWith(
|
|
289
|
+
mockList.map((item) => ({ ...item, createdBy: mockUser.username })),
|
|
290
|
+
{ session: undefined },
|
|
291
|
+
);
|
|
292
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
293
|
+
'createMany',
|
|
294
|
+
null,
|
|
295
|
+
undefined,
|
|
296
|
+
mockUser,
|
|
297
|
+
null,
|
|
298
|
+
);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('should handle an empty list without calling insertMany', async () => {
|
|
302
|
+
const mockUser: IUser = { username: 'testUser', permissions: [''] };
|
|
303
|
+
|
|
304
|
+
await repository.createMany([], mockUser);
|
|
305
|
+
|
|
306
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
307
|
+
'createMany',
|
|
308
|
+
null,
|
|
309
|
+
undefined,
|
|
310
|
+
mockUser,
|
|
311
|
+
null,
|
|
312
|
+
);
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
describe('shared-mongo: MongoItemRepository update function', () => {
|
|
317
|
+
let repository: MongoItemRepository<any>;
|
|
318
|
+
let mockCollection: any;
|
|
319
|
+
let mockLogChange: jest.Mock;
|
|
320
|
+
let mockGetInfo: jest.Mock;
|
|
321
|
+
let mockGetModelToUpdate: jest.Mock;
|
|
322
|
+
|
|
323
|
+
beforeEach(() => {
|
|
324
|
+
mockCollection = {
|
|
325
|
+
replaceOne: jest.fn(),
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
mockLogChange = jest.fn().mockResolvedValue(undefined);
|
|
329
|
+
mockGetInfo = jest.fn().mockResolvedValue({ existingData: 'value' });
|
|
330
|
+
mockGetModelToUpdate = jest.fn((item, user, info) => ({
|
|
331
|
+
...item,
|
|
332
|
+
updatedBy: user.username,
|
|
333
|
+
previousInfo: info,
|
|
334
|
+
}));
|
|
335
|
+
|
|
336
|
+
repository = new MongoItemRepository<any>(null as any);
|
|
337
|
+
|
|
338
|
+
// Mock collectionContext
|
|
339
|
+
jest
|
|
340
|
+
.spyOn(repository as any, 'collectionContext')
|
|
341
|
+
.mockImplementation(async (callback: any) => {
|
|
342
|
+
return callback(mockCollection);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// Mock logChange
|
|
346
|
+
jest
|
|
347
|
+
.spyOn(repository as any, 'logChange')
|
|
348
|
+
.mockImplementation(mockLogChange);
|
|
349
|
+
|
|
350
|
+
// Mock getInfo
|
|
351
|
+
jest.spyOn(repository as any, 'getInfo').mockImplementation(mockGetInfo);
|
|
352
|
+
|
|
353
|
+
// Mock getModelToUpdate
|
|
354
|
+
jest
|
|
355
|
+
.spyOn(repository as any, 'getModelToUpdate')
|
|
356
|
+
.mockImplementation(mockGetModelToUpdate);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('should call replaceOne and logChange on success', async () => {
|
|
360
|
+
const mockUser: IUser = { username: 'testUser', permissions: [''] };
|
|
361
|
+
const mockRepoOptions: IItemRepositoryOptions = {
|
|
362
|
+
transaction: { session: {} } as IMongoTransaction,
|
|
363
|
+
};
|
|
364
|
+
const mockItem = { id: 'item1', name: 'Updated Item' };
|
|
365
|
+
|
|
366
|
+
mockCollection.replaceOne.mockResolvedValueOnce({});
|
|
367
|
+
|
|
368
|
+
await repository.update(mockItem, mockUser, mockRepoOptions);
|
|
369
|
+
|
|
370
|
+
expect(mockGetInfo).toHaveBeenCalledWith(mockItem.id, mockCollection);
|
|
371
|
+
expect(mockCollection.replaceOne).toHaveBeenCalledWith(
|
|
372
|
+
{ _id: mockItem.id },
|
|
373
|
+
{
|
|
374
|
+
...mockItem,
|
|
375
|
+
updatedBy: mockUser.username,
|
|
376
|
+
previousInfo: { existingData: 'value' },
|
|
377
|
+
},
|
|
378
|
+
{ session: (mockRepoOptions.transaction as IMongoTransaction).session },
|
|
379
|
+
);
|
|
380
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
381
|
+
'update',
|
|
382
|
+
mockItem,
|
|
383
|
+
mockRepoOptions,
|
|
384
|
+
mockUser,
|
|
385
|
+
null,
|
|
386
|
+
);
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it('should call logChange with error if replaceOne fails', async () => {
|
|
390
|
+
const mockUser: IUser = { username: 'testUser', permissions: [''] };
|
|
391
|
+
const mockRepoOptions: IItemRepositoryOptions = {
|
|
392
|
+
transaction: { session: {} } as IMongoTransaction,
|
|
393
|
+
};
|
|
394
|
+
const mockItem = { id: 'item1', name: 'Updated Item' };
|
|
395
|
+
const mockError = new Error('ReplaceOne failed');
|
|
396
|
+
|
|
397
|
+
mockCollection.replaceOne.mockRejectedValueOnce(mockError);
|
|
398
|
+
|
|
399
|
+
await expect(
|
|
400
|
+
repository.update(mockItem, mockUser, mockRepoOptions),
|
|
401
|
+
).rejects.toThrow(mockError);
|
|
402
|
+
|
|
403
|
+
expect(mockGetInfo).toHaveBeenCalledWith(mockItem.id, mockCollection);
|
|
404
|
+
expect(mockCollection.replaceOne).toHaveBeenCalledWith(
|
|
405
|
+
{ _id: mockItem.id },
|
|
406
|
+
{
|
|
407
|
+
...mockItem,
|
|
408
|
+
updatedBy: mockUser.username,
|
|
409
|
+
previousInfo: { existingData: 'value' },
|
|
410
|
+
},
|
|
411
|
+
{ session: (mockRepoOptions.transaction as IMongoTransaction).session },
|
|
412
|
+
);
|
|
413
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
414
|
+
'update',
|
|
415
|
+
mockItem,
|
|
416
|
+
mockRepoOptions,
|
|
417
|
+
mockUser,
|
|
418
|
+
mockError,
|
|
419
|
+
);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it('should work without repoOptions', async () => {
|
|
423
|
+
const mockUser: IUser = { username: 'testUser', permissions: [''] };
|
|
424
|
+
const mockItem = { id: 'item1', name: 'Updated Item' };
|
|
425
|
+
|
|
426
|
+
mockCollection.replaceOne.mockResolvedValueOnce({});
|
|
427
|
+
|
|
428
|
+
await repository.update(mockItem, mockUser);
|
|
429
|
+
|
|
430
|
+
expect(mockGetInfo).toHaveBeenCalledWith(mockItem.id, mockCollection);
|
|
431
|
+
expect(mockCollection.replaceOne).toHaveBeenCalledWith(
|
|
432
|
+
{ _id: mockItem.id },
|
|
433
|
+
{
|
|
434
|
+
...mockItem,
|
|
435
|
+
updatedBy: mockUser.username,
|
|
436
|
+
previousInfo: { existingData: 'value' },
|
|
437
|
+
},
|
|
438
|
+
{ session: undefined },
|
|
439
|
+
);
|
|
440
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
441
|
+
'update',
|
|
442
|
+
mockItem,
|
|
443
|
+
undefined,
|
|
444
|
+
mockUser,
|
|
445
|
+
null,
|
|
446
|
+
);
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it('should throw if getInfo fails', async () => {
|
|
450
|
+
const mockUser: IUser = { username: 'testUser', permissions: [''] };
|
|
451
|
+
const mockItem = { id: 'item1', name: 'Updated Item' };
|
|
452
|
+
const mockError = new Error('GetInfo failed');
|
|
453
|
+
|
|
454
|
+
mockGetInfo.mockRejectedValueOnce(mockError);
|
|
455
|
+
|
|
456
|
+
await expect(repository.update(mockItem, mockUser)).rejects.toThrow(
|
|
457
|
+
mockError,
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
expect(mockGetInfo).toHaveBeenCalledWith(mockItem.id, mockCollection);
|
|
461
|
+
expect(mockCollection.replaceOne).not.toHaveBeenCalled();
|
|
462
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
463
|
+
'update',
|
|
464
|
+
mockItem,
|
|
465
|
+
undefined,
|
|
466
|
+
mockUser,
|
|
467
|
+
mockError,
|
|
468
|
+
);
|
|
469
|
+
});
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
describe('shared-mongo: MongoItemRepository updatePartial function', () => {
|
|
473
|
+
let repository: MongoItemRepository<any>;
|
|
474
|
+
let mockCollection: any;
|
|
475
|
+
let mockLogChange: jest.Mock;
|
|
476
|
+
let mockGetInfo: jest.Mock;
|
|
477
|
+
let mockGetModelToUpdate: jest.Mock;
|
|
478
|
+
|
|
479
|
+
beforeEach(() => {
|
|
480
|
+
mockCollection = {
|
|
481
|
+
updateOne: jest.fn(),
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
mockLogChange = jest.fn().mockResolvedValue(undefined);
|
|
485
|
+
mockGetInfo = jest.fn().mockResolvedValue({ existingData: 'value' });
|
|
486
|
+
mockGetModelToUpdate = jest.fn((item, user, info) => ({
|
|
487
|
+
...item,
|
|
488
|
+
updatedBy: user.username,
|
|
489
|
+
previousInfo: info,
|
|
490
|
+
}));
|
|
491
|
+
|
|
492
|
+
repository = new MongoItemRepository<any>(null as any);
|
|
493
|
+
|
|
494
|
+
// Mock collectionContext
|
|
495
|
+
jest
|
|
496
|
+
.spyOn(repository as any, 'collectionContext')
|
|
497
|
+
.mockImplementation(async (callback: any) => {
|
|
498
|
+
return callback(mockCollection);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
// Mock logChange
|
|
502
|
+
jest
|
|
503
|
+
.spyOn(repository as any, 'logChange')
|
|
504
|
+
.mockImplementation(mockLogChange);
|
|
505
|
+
|
|
506
|
+
// Mock getInfo
|
|
507
|
+
jest.spyOn(repository as any, 'getInfo').mockImplementation(mockGetInfo);
|
|
508
|
+
|
|
509
|
+
// Mock getModelToUpdate
|
|
510
|
+
jest
|
|
511
|
+
.spyOn(repository as any, 'getModelToUpdate')
|
|
512
|
+
.mockImplementation(mockGetModelToUpdate);
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
it('should call updateOne and logChange on success', async () => {
|
|
516
|
+
const mockUser: IUser = { username: 'testUser', permissions: [''] };
|
|
517
|
+
const mockRepoOptions: IItemRepositoryOptions = {
|
|
518
|
+
transaction: { session: {} } as IMongoTransaction,
|
|
519
|
+
};
|
|
520
|
+
const mockItem = { id: 'item1', name: 'Partial Update' };
|
|
521
|
+
|
|
522
|
+
mockCollection.updateOne.mockResolvedValueOnce({});
|
|
523
|
+
|
|
524
|
+
await repository.updatePartial(mockItem, mockUser, mockRepoOptions);
|
|
525
|
+
|
|
526
|
+
expect(mockGetInfo).toHaveBeenCalledWith(mockItem.id, mockCollection);
|
|
527
|
+
expect(mockCollection.updateOne).toHaveBeenCalledWith(
|
|
528
|
+
{ _id: mockItem.id },
|
|
529
|
+
{
|
|
530
|
+
$set: {
|
|
531
|
+
...mockItem,
|
|
532
|
+
updatedBy: mockUser.username,
|
|
533
|
+
previousInfo: { existingData: 'value' },
|
|
534
|
+
},
|
|
535
|
+
},
|
|
536
|
+
{ session: (mockRepoOptions.transaction as IMongoTransaction).session },
|
|
537
|
+
);
|
|
538
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
539
|
+
'updatePartial',
|
|
540
|
+
mockItem,
|
|
541
|
+
mockRepoOptions,
|
|
542
|
+
mockUser,
|
|
543
|
+
null,
|
|
544
|
+
);
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
it('should call logChange with error if updateOne fails', async () => {
|
|
548
|
+
const mockUser: IUser = { username: 'testUser', permissions: [''] };
|
|
549
|
+
const mockRepoOptions: IItemRepositoryOptions = {
|
|
550
|
+
transaction: { session: {} } as IMongoTransaction,
|
|
551
|
+
};
|
|
552
|
+
const mockItem = { id: 'item1', name: 'Partial Update' };
|
|
553
|
+
const mockError = new Error('UpdateOne failed');
|
|
554
|
+
|
|
555
|
+
mockCollection.updateOne.mockRejectedValueOnce(mockError);
|
|
556
|
+
|
|
557
|
+
await expect(
|
|
558
|
+
repository.updatePartial(mockItem, mockUser, mockRepoOptions),
|
|
559
|
+
).rejects.toThrow(mockError);
|
|
560
|
+
|
|
561
|
+
expect(mockGetInfo).toHaveBeenCalledWith(mockItem.id, mockCollection);
|
|
562
|
+
expect(mockCollection.updateOne).toHaveBeenCalledWith(
|
|
563
|
+
{ _id: mockItem.id },
|
|
564
|
+
{
|
|
565
|
+
$set: {
|
|
566
|
+
...mockItem,
|
|
567
|
+
updatedBy: mockUser.username,
|
|
568
|
+
previousInfo: { existingData: 'value' },
|
|
569
|
+
},
|
|
570
|
+
},
|
|
571
|
+
{ session: (mockRepoOptions.transaction as IMongoTransaction).session },
|
|
572
|
+
);
|
|
573
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
574
|
+
'updatePartial',
|
|
575
|
+
mockItem,
|
|
576
|
+
mockRepoOptions,
|
|
577
|
+
mockUser,
|
|
578
|
+
mockError,
|
|
579
|
+
);
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
it('should work without repoOptions', async () => {
|
|
583
|
+
const mockUser: IUser = { username: 'testUser', permissions: [''] };
|
|
584
|
+
const mockItem = { id: 'item1', name: 'Partial Update' };
|
|
585
|
+
|
|
586
|
+
mockCollection.updateOne.mockResolvedValueOnce({});
|
|
587
|
+
|
|
588
|
+
await repository.updatePartial(mockItem, mockUser);
|
|
589
|
+
|
|
590
|
+
expect(mockGetInfo).toHaveBeenCalledWith(mockItem.id, mockCollection);
|
|
591
|
+
expect(mockCollection.updateOne).toHaveBeenCalledWith(
|
|
592
|
+
{ _id: mockItem.id },
|
|
593
|
+
{
|
|
594
|
+
$set: {
|
|
595
|
+
...mockItem,
|
|
596
|
+
updatedBy: mockUser.username,
|
|
597
|
+
previousInfo: { existingData: 'value' },
|
|
598
|
+
},
|
|
599
|
+
},
|
|
600
|
+
{ session: undefined },
|
|
601
|
+
);
|
|
602
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
603
|
+
'updatePartial',
|
|
604
|
+
mockItem,
|
|
605
|
+
undefined,
|
|
606
|
+
mockUser,
|
|
607
|
+
null,
|
|
608
|
+
);
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
it('should throw if getInfo fails', async () => {
|
|
612
|
+
const mockUser: IUser = { username: 'testUser', permissions: [''] };
|
|
613
|
+
const mockItem = { id: 'item1', name: 'Partial Update' };
|
|
614
|
+
const mockError = new Error('GetInfo failed');
|
|
615
|
+
|
|
616
|
+
mockGetInfo.mockRejectedValueOnce(mockError);
|
|
617
|
+
|
|
618
|
+
await expect(repository.updatePartial(mockItem, mockUser)).rejects.toThrow(
|
|
619
|
+
mockError,
|
|
620
|
+
);
|
|
621
|
+
|
|
622
|
+
expect(mockGetInfo).toHaveBeenCalledWith(mockItem.id, mockCollection);
|
|
623
|
+
expect(mockCollection.updateOne).not.toHaveBeenCalled();
|
|
624
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
625
|
+
'updatePartial',
|
|
626
|
+
mockItem,
|
|
627
|
+
undefined,
|
|
628
|
+
mockUser,
|
|
629
|
+
mockError,
|
|
630
|
+
);
|
|
631
|
+
});
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
describe('shared-mongo: MongoItemRepository updatePartialManyByCriteria function', () => {
|
|
635
|
+
let repository: MongoItemRepository<any>;
|
|
636
|
+
let mockCollection: any;
|
|
637
|
+
let mockLogChange: jest.Mock;
|
|
638
|
+
let mockConvertIdInCriteria: jest.Mock;
|
|
639
|
+
|
|
640
|
+
beforeEach(() => {
|
|
641
|
+
mockCollection = {
|
|
642
|
+
updateMany: jest.fn(),
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
mockLogChange = jest.fn().mockResolvedValue(undefined);
|
|
646
|
+
mockConvertIdInCriteria = jest.fn((criteria) => criteria);
|
|
647
|
+
|
|
648
|
+
repository = new MongoItemRepository<any>(null as any);
|
|
649
|
+
|
|
650
|
+
// Mock collectionContext
|
|
651
|
+
jest
|
|
652
|
+
.spyOn(repository as any, 'collectionContext')
|
|
653
|
+
.mockImplementation(async (callback: any) => {
|
|
654
|
+
return callback(mockCollection);
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
// Mock logChange
|
|
658
|
+
jest
|
|
659
|
+
.spyOn(repository as any, 'logChange')
|
|
660
|
+
.mockImplementation(mockLogChange);
|
|
661
|
+
|
|
662
|
+
// Mock convertIdInCriteria
|
|
663
|
+
jest
|
|
664
|
+
.spyOn(repository as any, 'convertIdInCriteria')
|
|
665
|
+
.mockImplementation(mockConvertIdInCriteria);
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
it('should call updateMany and logChange on success', async () => {
|
|
669
|
+
const mockUser: IUser = { username: 'testUser', permissions: [''] };
|
|
670
|
+
const mockRepoOptions: IItemRepositoryOptions = {
|
|
671
|
+
transaction: { session: {} } as IMongoTransaction,
|
|
672
|
+
};
|
|
673
|
+
const mockCriteria = { status: 'pending' };
|
|
674
|
+
const mockSet = { fieldToUpdate: 'newValue' };
|
|
675
|
+
|
|
676
|
+
mockCollection.updateMany.mockResolvedValueOnce({});
|
|
677
|
+
|
|
678
|
+
await repository.updatePartialManyByCriteria(
|
|
679
|
+
mockCriteria,
|
|
680
|
+
mockSet,
|
|
681
|
+
mockUser,
|
|
682
|
+
mockRepoOptions,
|
|
683
|
+
);
|
|
684
|
+
|
|
685
|
+
expect(mockConvertIdInCriteria).toHaveBeenCalledWith(mockCriteria);
|
|
686
|
+
expect(mockCollection.updateMany).toHaveBeenCalledWith(
|
|
687
|
+
mockCriteria,
|
|
688
|
+
{
|
|
689
|
+
$set: {
|
|
690
|
+
...mockSet,
|
|
691
|
+
'__info.update': {
|
|
692
|
+
username: mockUser.username,
|
|
693
|
+
date: expect.any(Date),
|
|
694
|
+
},
|
|
695
|
+
},
|
|
696
|
+
},
|
|
697
|
+
{ session: (mockRepoOptions.transaction as IMongoTransaction).session },
|
|
698
|
+
);
|
|
699
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
700
|
+
'updatePartialManyByCriteria',
|
|
701
|
+
{ ...mockCriteria, set: mockSet },
|
|
702
|
+
mockRepoOptions,
|
|
703
|
+
mockUser,
|
|
704
|
+
null,
|
|
705
|
+
);
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
it('should call logChange with error if updateMany fails', async () => {
|
|
709
|
+
const mockUser: IUser = { username: 'testUser', permissions: [''] };
|
|
710
|
+
const mockRepoOptions: IItemRepositoryOptions = {
|
|
711
|
+
transaction: { session: {} } as IMongoTransaction,
|
|
712
|
+
};
|
|
713
|
+
const mockCriteria = { status: 'pending' };
|
|
714
|
+
const mockSet = { fieldToUpdate: 'newValue' };
|
|
715
|
+
const mockError = new Error('updateMany failed');
|
|
716
|
+
|
|
717
|
+
mockCollection.updateMany.mockRejectedValueOnce(mockError);
|
|
718
|
+
|
|
719
|
+
await expect(
|
|
720
|
+
repository.updatePartialManyByCriteria(
|
|
721
|
+
mockCriteria,
|
|
722
|
+
mockSet,
|
|
723
|
+
mockUser,
|
|
724
|
+
mockRepoOptions,
|
|
725
|
+
),
|
|
726
|
+
).rejects.toThrow(mockError);
|
|
727
|
+
|
|
728
|
+
expect(mockConvertIdInCriteria).toHaveBeenCalledWith(mockCriteria);
|
|
729
|
+
expect(mockCollection.updateMany).toHaveBeenCalledWith(
|
|
730
|
+
mockCriteria,
|
|
731
|
+
{
|
|
732
|
+
$set: {
|
|
733
|
+
...mockSet,
|
|
734
|
+
'__info.update': {
|
|
735
|
+
username: mockUser.username,
|
|
736
|
+
date: expect.any(Date),
|
|
737
|
+
},
|
|
738
|
+
},
|
|
739
|
+
},
|
|
740
|
+
{ session: (mockRepoOptions.transaction as IMongoTransaction).session },
|
|
741
|
+
);
|
|
742
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
743
|
+
'updatePartialManyByCriteria',
|
|
744
|
+
{ ...mockCriteria, set: mockSet },
|
|
745
|
+
mockRepoOptions,
|
|
746
|
+
mockUser,
|
|
747
|
+
mockError,
|
|
748
|
+
);
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
it('should work without repoOptions', async () => {
|
|
752
|
+
const mockUser: IUser = { username: 'testUser', permissions: [''] };
|
|
753
|
+
const mockCriteria = { status: 'pending' };
|
|
754
|
+
const mockSet = { fieldToUpdate: 'newValue' };
|
|
755
|
+
|
|
756
|
+
mockCollection.updateMany.mockResolvedValueOnce({});
|
|
757
|
+
|
|
758
|
+
await repository.updatePartialManyByCriteria(
|
|
759
|
+
mockCriteria,
|
|
760
|
+
mockSet,
|
|
761
|
+
mockUser,
|
|
762
|
+
);
|
|
763
|
+
|
|
764
|
+
expect(mockConvertIdInCriteria).toHaveBeenCalledWith(mockCriteria);
|
|
765
|
+
expect(mockCollection.updateMany).toHaveBeenCalledWith(
|
|
766
|
+
mockCriteria,
|
|
767
|
+
{
|
|
768
|
+
$set: {
|
|
769
|
+
...mockSet,
|
|
770
|
+
'__info.update': {
|
|
771
|
+
username: mockUser.username,
|
|
772
|
+
date: expect.any(Date),
|
|
773
|
+
},
|
|
774
|
+
},
|
|
775
|
+
},
|
|
776
|
+
{ session: undefined },
|
|
777
|
+
);
|
|
778
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
779
|
+
'updatePartialManyByCriteria',
|
|
780
|
+
{ ...mockCriteria, set: mockSet },
|
|
781
|
+
undefined,
|
|
782
|
+
mockUser,
|
|
783
|
+
null,
|
|
784
|
+
);
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
it('should throw if convertIdInCriteria fails', async () => {
|
|
788
|
+
const mockUser: IUser = { username: 'testUser', permissions: [''] };
|
|
789
|
+
const mockCriteria = { status: 'pending' };
|
|
790
|
+
const mockSet = { fieldToUpdate: 'newValue' };
|
|
791
|
+
const mockError = new Error('convertIdInCriteria failed');
|
|
792
|
+
|
|
793
|
+
mockConvertIdInCriteria.mockImplementationOnce(() => {
|
|
794
|
+
throw mockError;
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
await expect(
|
|
798
|
+
repository.updatePartialManyByCriteria(mockCriteria, mockSet, mockUser),
|
|
799
|
+
).rejects.toThrow(mockError);
|
|
800
|
+
|
|
801
|
+
expect(mockConvertIdInCriteria).toHaveBeenCalledWith(mockCriteria);
|
|
802
|
+
expect(mockCollection.updateMany).not.toHaveBeenCalled();
|
|
803
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
804
|
+
'updatePartialManyByCriteria',
|
|
805
|
+
{ ...mockCriteria, set: mockSet },
|
|
806
|
+
undefined,
|
|
807
|
+
mockUser,
|
|
808
|
+
mockError,
|
|
809
|
+
);
|
|
810
|
+
});
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
describe('shared-mongo: MongoItemRepository delete function', () => {
|
|
814
|
+
let repository: MongoItemRepository<any>;
|
|
815
|
+
let mockCollection: any;
|
|
816
|
+
let mockLogChange: jest.Mock;
|
|
817
|
+
|
|
818
|
+
beforeEach(() => {
|
|
819
|
+
mockCollection = {
|
|
820
|
+
deleteOne: jest.fn(),
|
|
821
|
+
};
|
|
822
|
+
|
|
823
|
+
mockLogChange = jest.fn().mockResolvedValue(undefined);
|
|
824
|
+
|
|
825
|
+
repository = new MongoItemRepository<any>(null as any);
|
|
826
|
+
|
|
827
|
+
jest
|
|
828
|
+
.spyOn(repository as any, 'collectionContext')
|
|
829
|
+
.mockImplementation(async (callback: any) => {
|
|
830
|
+
return callback(mockCollection);
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
jest
|
|
834
|
+
.spyOn(repository as any, 'logChange')
|
|
835
|
+
.mockImplementation(mockLogChange);
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
it('should call deleteOne and logChange on success', async () => {
|
|
839
|
+
const mockUser: IUser = { username: 'testUser', permissions: [''] };
|
|
840
|
+
const mockRepoOptions: IItemRepositoryOptions = {
|
|
841
|
+
transaction: { session: {} } as IMongoTransaction,
|
|
842
|
+
};
|
|
843
|
+
const mockId = '123';
|
|
844
|
+
|
|
845
|
+
mockCollection.deleteOne.mockResolvedValueOnce({});
|
|
846
|
+
|
|
847
|
+
await repository.delete(mockId, mockUser, mockRepoOptions);
|
|
848
|
+
|
|
849
|
+
expect(mockCollection.deleteOne).toHaveBeenCalledWith(
|
|
850
|
+
{ _id: mockId },
|
|
851
|
+
{ session: (mockRepoOptions.transaction as IMongoTransaction).session },
|
|
852
|
+
);
|
|
853
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
854
|
+
'delete',
|
|
855
|
+
{ id: mockId },
|
|
856
|
+
mockRepoOptions,
|
|
857
|
+
mockUser,
|
|
858
|
+
null,
|
|
859
|
+
);
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
it('should call logChange with error if deleteOne fails', async () => {
|
|
863
|
+
const mockUser: IUser = { username: 'testUser', permissions: [''] };
|
|
864
|
+
const mockRepoOptions: IItemRepositoryOptions = {
|
|
865
|
+
transaction: { session: {} } as IMongoTransaction,
|
|
866
|
+
};
|
|
867
|
+
const mockId = '123';
|
|
868
|
+
const mockError = new Error('deleteOne failed');
|
|
869
|
+
|
|
870
|
+
mockCollection.deleteOne.mockRejectedValueOnce(mockError);
|
|
871
|
+
|
|
872
|
+
await expect(
|
|
873
|
+
repository.delete(mockId, mockUser, mockRepoOptions),
|
|
874
|
+
).rejects.toThrow(mockError);
|
|
875
|
+
|
|
876
|
+
expect(mockCollection.deleteOne).toHaveBeenCalledWith(
|
|
877
|
+
{ _id: mockId },
|
|
878
|
+
{ session: (mockRepoOptions.transaction as IMongoTransaction).session },
|
|
879
|
+
);
|
|
880
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
881
|
+
'delete',
|
|
882
|
+
{ id: mockId },
|
|
883
|
+
mockRepoOptions,
|
|
884
|
+
mockUser,
|
|
885
|
+
mockError,
|
|
886
|
+
);
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
it('should work without repoOptions', async () => {
|
|
890
|
+
const mockUser: IUser = { username: 'testUser', permissions: [''] };
|
|
891
|
+
const mockId = '123';
|
|
892
|
+
|
|
893
|
+
mockCollection.deleteOne.mockResolvedValueOnce({});
|
|
894
|
+
|
|
895
|
+
await repository.delete(mockId, mockUser);
|
|
896
|
+
|
|
897
|
+
expect(mockCollection.deleteOne).toHaveBeenCalledWith(
|
|
898
|
+
{ _id: mockId },
|
|
899
|
+
{ session: undefined },
|
|
900
|
+
);
|
|
901
|
+
expect(mockLogChange).toHaveBeenCalledWith(
|
|
902
|
+
'delete',
|
|
903
|
+
{ id: mockId },
|
|
904
|
+
undefined,
|
|
905
|
+
mockUser,
|
|
906
|
+
null,
|
|
907
|
+
);
|
|
908
|
+
});
|
|
909
|
+
});
|
|
910
|
+
|
|
911
|
+
describe('shared-mongo: MongoItemRepository getById function', () => {
|
|
912
|
+
let repository: MongoItemRepository<any>;
|
|
913
|
+
let mockCollection: any;
|
|
914
|
+
|
|
915
|
+
beforeEach(() => {
|
|
916
|
+
mockCollection = {
|
|
917
|
+
findOne: jest.fn(),
|
|
918
|
+
};
|
|
919
|
+
|
|
920
|
+
repository = new MongoItemRepository<any>(null as any);
|
|
921
|
+
|
|
922
|
+
jest
|
|
923
|
+
.spyOn(repository as any, 'collectionContext')
|
|
924
|
+
.mockImplementation(async (callback: any) => {
|
|
925
|
+
return callback(mockCollection);
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
jest
|
|
929
|
+
.spyOn(repository as any, 'getModelToResult')
|
|
930
|
+
.mockImplementation((item: any) => item);
|
|
931
|
+
});
|
|
932
|
+
|
|
933
|
+
it('should call findOne and return the result', async () => {
|
|
934
|
+
const mockId = '123';
|
|
935
|
+
const mockRepoOptions: IItemRepositoryOptions = {
|
|
936
|
+
transaction: { session: {} } as IMongoTransaction,
|
|
937
|
+
};
|
|
938
|
+
const mockItem = { _id: mockId, name: 'testItem' };
|
|
939
|
+
|
|
940
|
+
mockCollection.findOne.mockResolvedValueOnce(mockItem);
|
|
941
|
+
|
|
942
|
+
const result = await repository.getById(mockId, mockRepoOptions);
|
|
943
|
+
|
|
944
|
+
expect(mockCollection.findOne).toHaveBeenCalledWith(
|
|
945
|
+
{ _id: mockId },
|
|
946
|
+
{ session: (mockRepoOptions.transaction as IMongoTransaction).session },
|
|
947
|
+
);
|
|
948
|
+
expect(result).toEqual(mockItem);
|
|
949
|
+
});
|
|
950
|
+
|
|
951
|
+
it('should return null if no item is found', async () => {
|
|
952
|
+
const mockId = '123';
|
|
953
|
+
const mockRepoOptions: IItemRepositoryOptions = {
|
|
954
|
+
transaction: { session: {} } as IMongoTransaction,
|
|
955
|
+
};
|
|
956
|
+
|
|
957
|
+
mockCollection.findOne.mockResolvedValueOnce(null);
|
|
958
|
+
|
|
959
|
+
const result = await repository.getById(mockId, mockRepoOptions);
|
|
960
|
+
|
|
961
|
+
expect(mockCollection.findOne).toHaveBeenCalledWith(
|
|
962
|
+
{ _id: mockId },
|
|
963
|
+
{ session: (mockRepoOptions.transaction as IMongoTransaction).session },
|
|
964
|
+
);
|
|
965
|
+
expect(result).toBeNull();
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
it('should work without repoOptions', async () => {
|
|
969
|
+
const mockId = '123';
|
|
970
|
+
const mockItem = { _id: mockId, name: 'testItem' };
|
|
971
|
+
|
|
972
|
+
mockCollection.findOne.mockResolvedValueOnce(mockItem);
|
|
973
|
+
|
|
974
|
+
const result = await repository.getById(mockId);
|
|
975
|
+
|
|
976
|
+
expect(mockCollection.findOne).toHaveBeenCalledWith(
|
|
977
|
+
{ _id: mockId },
|
|
978
|
+
{ session: undefined },
|
|
979
|
+
);
|
|
980
|
+
expect(result).toEqual(mockItem);
|
|
981
|
+
});
|
|
982
|
+
});
|
|
983
|
+
|
|
984
|
+
describe('shared-mongo: MongoItemRepository getByCriteria function', () => {
|
|
985
|
+
let repository: MongoItemRepository<any>;
|
|
986
|
+
let mockCollection: any;
|
|
987
|
+
|
|
988
|
+
beforeEach(() => {
|
|
989
|
+
mockCollection = {
|
|
990
|
+
aggregate: jest.fn(),
|
|
991
|
+
};
|
|
992
|
+
|
|
993
|
+
repository = new MongoItemRepository<any>(null as any);
|
|
994
|
+
|
|
995
|
+
jest
|
|
996
|
+
.spyOn(repository as any, 'collectionContext')
|
|
997
|
+
.mockImplementation(async (callback: any) => {
|
|
998
|
+
return callback(mockCollection);
|
|
999
|
+
});
|
|
1000
|
+
|
|
1001
|
+
jest
|
|
1002
|
+
.spyOn(repository as any, 'convertIdInCriteria')
|
|
1003
|
+
.mockImplementation(jest.fn());
|
|
1004
|
+
jest
|
|
1005
|
+
.spyOn(repository as any, 'generateSearch')
|
|
1006
|
+
.mockImplementation(jest.fn());
|
|
1007
|
+
jest.spyOn(repository as any, 'getCount').mockResolvedValue(42);
|
|
1008
|
+
jest
|
|
1009
|
+
.spyOn(repository as any, 'getModelToResult')
|
|
1010
|
+
.mockImplementation((item: any) => item);
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
it('should aggregate data based on criteria and options', async () => {
|
|
1014
|
+
const criteria = { field: 'value' };
|
|
1015
|
+
const options = { sort: { field: 1 }, skip: 10, limit: 5 };
|
|
1016
|
+
const mockData = [{ id: 1 }, { id: 2 }];
|
|
1017
|
+
|
|
1018
|
+
mockCollection.aggregate.mockReturnValueOnce({
|
|
1019
|
+
toArray: jest.fn().mockResolvedValueOnce(mockData),
|
|
1020
|
+
});
|
|
1021
|
+
|
|
1022
|
+
const result = await repository.getByCriteria(criteria, options);
|
|
1023
|
+
|
|
1024
|
+
expect(repository['convertIdInCriteria']).toHaveBeenCalledWith(criteria);
|
|
1025
|
+
expect(repository['generateSearch']).toHaveBeenCalledWith(criteria);
|
|
1026
|
+
expect(repository['getCount']).toHaveBeenCalledWith(
|
|
1027
|
+
criteria,
|
|
1028
|
+
mockCollection,
|
|
1029
|
+
);
|
|
1030
|
+
|
|
1031
|
+
expect(mockCollection.aggregate).toHaveBeenCalledWith(
|
|
1032
|
+
[
|
|
1033
|
+
{ $match: criteria },
|
|
1034
|
+
{ $sort: options.sort },
|
|
1035
|
+
{ $skip: options.skip },
|
|
1036
|
+
{ $limit: options.limit },
|
|
1037
|
+
],
|
|
1038
|
+
{ allowDiskUse: undefined, session: undefined },
|
|
1039
|
+
);
|
|
1040
|
+
|
|
1041
|
+
expect(result).toEqual({ data: mockData, totalCount: 42 });
|
|
1042
|
+
});
|
|
1043
|
+
|
|
1044
|
+
it('should handle no criteria or options', async () => {
|
|
1045
|
+
const mockData = [{ id: 1 }];
|
|
1046
|
+
|
|
1047
|
+
mockCollection.aggregate.mockReturnValueOnce({
|
|
1048
|
+
toArray: jest.fn().mockResolvedValueOnce(mockData),
|
|
1049
|
+
});
|
|
1050
|
+
|
|
1051
|
+
const result = await repository.getByCriteria(undefined, undefined);
|
|
1052
|
+
|
|
1053
|
+
expect(repository['convertIdInCriteria']).toHaveBeenCalledWith(undefined);
|
|
1054
|
+
expect(repository['generateSearch']).toHaveBeenCalledWith(undefined);
|
|
1055
|
+
expect(repository['getCount']).toHaveBeenCalledWith(
|
|
1056
|
+
undefined,
|
|
1057
|
+
mockCollection,
|
|
1058
|
+
);
|
|
1059
|
+
|
|
1060
|
+
expect(mockCollection.aggregate).toHaveBeenCalledWith([], {
|
|
1061
|
+
allowDiskUse: undefined,
|
|
1062
|
+
session: undefined,
|
|
1063
|
+
});
|
|
1064
|
+
|
|
1065
|
+
expect(result).toEqual({ data: mockData, totalCount: 42 });
|
|
1066
|
+
});
|
|
1067
|
+
|
|
1068
|
+
it('should include additional pipeline stages when options are provided', async () => {
|
|
1069
|
+
const criteria = { field: 'value' };
|
|
1070
|
+
const options = {
|
|
1071
|
+
project: { field: 1 },
|
|
1072
|
+
group: { _id: '$field', count: { $sum: 1 } },
|
|
1073
|
+
};
|
|
1074
|
+
const mockData = [{ _id: 'field1', count: 10 }];
|
|
1075
|
+
|
|
1076
|
+
mockCollection.aggregate.mockReturnValueOnce({
|
|
1077
|
+
toArray: jest.fn().mockResolvedValueOnce(mockData),
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
const result = await repository.getByCriteria(criteria, options);
|
|
1081
|
+
|
|
1082
|
+
expect(mockCollection.aggregate).toHaveBeenCalledWith(
|
|
1083
|
+
[
|
|
1084
|
+
{ $match: criteria },
|
|
1085
|
+
{ $project: options.project },
|
|
1086
|
+
{ $group: options.group },
|
|
1087
|
+
],
|
|
1088
|
+
{ allowDiskUse: undefined, session: undefined },
|
|
1089
|
+
);
|
|
1090
|
+
|
|
1091
|
+
expect(result).toEqual({ data: mockData, totalCount: 42 });
|
|
1092
|
+
});
|
|
1093
|
+
});
|
|
1094
|
+
|
|
1095
|
+
// Mock MongoDB components
|
|
1096
|
+
// jest.mock('mongodb', () => {
|
|
1097
|
+
// const originalModule = jest.requireActual('mongodb');
|
|
1098
|
+
//
|
|
1099
|
+
// const mockGridFSBucket = jest.fn().mockImplementation(() => ({}));
|
|
1100
|
+
// const mockDb = jest.fn().mockReturnValue({
|
|
1101
|
+
// GridFSBucket: mockGridFSBucket,
|
|
1102
|
+
// });
|
|
1103
|
+
// const mockConnect = jest.fn().mockResolvedValue({
|
|
1104
|
+
// db: jest.fn(() => mockDb()),
|
|
1105
|
+
// });
|
|
1106
|
+
//
|
|
1107
|
+
// return {
|
|
1108
|
+
// ...originalModule,
|
|
1109
|
+
// MongoClient: {
|
|
1110
|
+
// connect: mockConnect,
|
|
1111
|
+
// },
|
|
1112
|
+
// };
|
|
1113
|
+
// });
|
|
1114
|
+
|
|
1115
|
+
// describe('shared-mongo: MongoItemRepository changesByCriteria function', () => {
|
|
1116
|
+
// let mongoItemRepository: MongoItemRepository<any>;
|
|
1117
|
+
// let mockClient: any;
|
|
1118
|
+
// let mockDb: any;
|
|
1119
|
+
// let mockCollection: any;
|
|
1120
|
+
// let mockChangeStream: any;
|
|
1121
|
+
// const mockMongoConfig: MongoConfig = {
|
|
1122
|
+
// database: 'testDb',
|
|
1123
|
+
// collection: 'testCollection',
|
|
1124
|
+
// type: null,
|
|
1125
|
+
// host: 'host',
|
|
1126
|
+
// port: 4200
|
|
1127
|
+
// };
|
|
1128
|
+
//
|
|
1129
|
+
// const mongodbMock = jest.requireMock("mongodb");
|
|
1130
|
+
// const mockConnect = mongodbMock.MongoClient.connect;
|
|
1131
|
+
//
|
|
1132
|
+
// beforeEach(() => {
|
|
1133
|
+
// mockClient = { db: jest.fn().mockReturnThis(), close: jest.fn() };
|
|
1134
|
+
// mockDb = { collection: jest.fn().mockReturnThis() };
|
|
1135
|
+
// mockCollection = { watch: jest.fn() };
|
|
1136
|
+
// mockChangeStream = { on: jest.fn() };
|
|
1137
|
+
// mockClient.db.mockReturnValue(mockDb);
|
|
1138
|
+
// mockDb.collection.mockReturnValue(mockCollection);
|
|
1139
|
+
// mockCollection.watch.mockReturnValue(mockChangeStream);
|
|
1140
|
+
// mockConnect.mockResolvedValue(mockClient);
|
|
1141
|
+
//
|
|
1142
|
+
// mongoItemRepository = new MongoItemRepository(mockMongoConfig);
|
|
1143
|
+
// });
|
|
1144
|
+
//
|
|
1145
|
+
// afterEach(() => {
|
|
1146
|
+
// jest.clearAllMocks();
|
|
1147
|
+
// });
|
|
1148
|
+
//
|
|
1149
|
+
// it('should establish a MongoDB connection and emit changes', (done) => {
|
|
1150
|
+
// const mockResult = {
|
|
1151
|
+
// 'documentKey': { '_id': '123' },
|
|
1152
|
+
// 'operationType': 'insert',
|
|
1153
|
+
// 'fullDocument': { id: '123', name: 'Item 1' },
|
|
1154
|
+
// };
|
|
1155
|
+
//
|
|
1156
|
+
// mockChangeStream.on.mockImplementationOnce((event, callback) => {
|
|
1157
|
+
// if (event === 'change') {
|
|
1158
|
+
// callback(mockResult); // Simulate a change event
|
|
1159
|
+
// }
|
|
1160
|
+
// });
|
|
1161
|
+
//
|
|
1162
|
+
// const criteria = { id: '123' };
|
|
1163
|
+
//
|
|
1164
|
+
// mongoItemRepository.changesByCriteria(criteria).subscribe({
|
|
1165
|
+
// next: (itemChangedData: ItemChangedData) => {
|
|
1166
|
+
// expect(itemChangedData).toEqual({
|
|
1167
|
+
// id: '123',
|
|
1168
|
+
// type: 'create', // 'insert' should be mapped to 'create'
|
|
1169
|
+
// data: mockResult.fullDocument,
|
|
1170
|
+
// });
|
|
1171
|
+
// done();
|
|
1172
|
+
// },
|
|
1173
|
+
// error: done.fail,
|
|
1174
|
+
// });
|
|
1175
|
+
//
|
|
1176
|
+
// expect(mockConnect).toHaveBeenCalledTimes(1);
|
|
1177
|
+
// expect(mockCollection.watch).toHaveBeenCalledWith([
|
|
1178
|
+
// { $match: { 'documentKey._id': '123' } },
|
|
1179
|
+
// ]);
|
|
1180
|
+
// });
|
|
1181
|
+
//
|
|
1182
|
+
// it('should establish a MongoDB connection and emit changes for general updates', (done) => {
|
|
1183
|
+
// const mockResult = {
|
|
1184
|
+
// 'documentKey': { '_id': '123' },
|
|
1185
|
+
// 'operationType': 'update',
|
|
1186
|
+
// 'updateDescription': { updatedFields: { name: 'Updated Item' } },
|
|
1187
|
+
// };
|
|
1188
|
+
//
|
|
1189
|
+
// mockChangeStream.on.mockImplementationOnce((event, callback) => {
|
|
1190
|
+
// if (event === 'change') {
|
|
1191
|
+
// callback(mockResult); // Simulate a change event
|
|
1192
|
+
// }
|
|
1193
|
+
// });
|
|
1194
|
+
//
|
|
1195
|
+
// const criteria = { id: '123' };
|
|
1196
|
+
//
|
|
1197
|
+
// mongoItemRepository.changesByCriteria(criteria).subscribe({
|
|
1198
|
+
// next: (itemChangedData: ItemChangedData) => {
|
|
1199
|
+
// expect(itemChangedData).toEqual({
|
|
1200
|
+
// id: '123',
|
|
1201
|
+
// type: 'update', // 'update' should be mapped to 'update'
|
|
1202
|
+
// data: mockResult.updateDescription,
|
|
1203
|
+
// });
|
|
1204
|
+
// done();
|
|
1205
|
+
// },
|
|
1206
|
+
// error: done.fail,
|
|
1207
|
+
// });
|
|
1208
|
+
//
|
|
1209
|
+
// expect(mockConnect).toHaveBeenCalledTimes(1);
|
|
1210
|
+
// expect(mockCollection.watch).toHaveBeenCalledWith([
|
|
1211
|
+
// { $match: { 'documentKey._id': '123' } },
|
|
1212
|
+
// ]);
|
|
1213
|
+
// });
|
|
1214
|
+
//
|
|
1215
|
+
// it('should handle errors properly and close the connection', (done) => {
|
|
1216
|
+
// const mockError = new Error('Connection error');
|
|
1217
|
+
//
|
|
1218
|
+
// mockChangeStream.on.mockImplementationOnce((event, callback) => {
|
|
1219
|
+
// if (event === 'change') {
|
|
1220
|
+
// callback(mockError); // Simulate an error during the change event
|
|
1221
|
+
// }
|
|
1222
|
+
// });
|
|
1223
|
+
//
|
|
1224
|
+
// const criteria = { id: '123' };
|
|
1225
|
+
//
|
|
1226
|
+
// mongoItemRepository.changesByCriteria(criteria).subscribe({
|
|
1227
|
+
// next: () => {},
|
|
1228
|
+
// error: (error) => {
|
|
1229
|
+
// expect(error).toEqual(mockError);
|
|
1230
|
+
// expect(mockConnect).toHaveBeenCalledTimes(1);
|
|
1231
|
+
// expect(mockClient.close).toHaveBeenCalledTimes(1);
|
|
1232
|
+
// done();
|
|
1233
|
+
// },
|
|
1234
|
+
// });
|
|
1235
|
+
// });
|
|
1236
|
+
//
|
|
1237
|
+
// it('should finalize the stream and close the connection', (done) => {
|
|
1238
|
+
// const mockResult = {
|
|
1239
|
+
// 'documentKey': { '_id': '123' },
|
|
1240
|
+
// 'operationType': 'insert',
|
|
1241
|
+
// 'fullDocument': { id: '123', name: 'Item 1' },
|
|
1242
|
+
// };
|
|
1243
|
+
//
|
|
1244
|
+
// mockChangeStream.on.mockImplementationOnce((event, callback) => {
|
|
1245
|
+
// if (event === 'change') {
|
|
1246
|
+
// callback(mockResult); // Simulate a change event
|
|
1247
|
+
// }
|
|
1248
|
+
// });
|
|
1249
|
+
//
|
|
1250
|
+
// const criteria = { id: '123' };
|
|
1251
|
+
//
|
|
1252
|
+
// const subscription = mongoItemRepository.changesByCriteria(criteria).subscribe({
|
|
1253
|
+
// next: (itemChangedData: ItemChangedData) => {
|
|
1254
|
+
// expect(itemChangedData).toEqual({
|
|
1255
|
+
// id: '123',
|
|
1256
|
+
// type: 'create',
|
|
1257
|
+
// data: mockResult.fullDocument,
|
|
1258
|
+
// });
|
|
1259
|
+
// subscription.unsubscribe(); // Unsubscribe to finalize the stream
|
|
1260
|
+
// done();
|
|
1261
|
+
// },
|
|
1262
|
+
// error: done.fail,
|
|
1263
|
+
// });
|
|
1264
|
+
//
|
|
1265
|
+
// subscription.add(() => {
|
|
1266
|
+
// expect(mockClient.close).toHaveBeenCalledTimes(1); // Ensure the connection is closed after unsubscribe
|
|
1267
|
+
// });
|
|
1268
|
+
// });
|
|
1269
|
+
// });
|