@flusys/nestjs-notification 7.1.0 → 8.0.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.
Files changed (103) hide show
  1. package/adapters/index.d.ts +1 -1
  2. package/adapters/notification.adapter.d.ts +2 -2
  3. package/config/index.d.ts +3 -3
  4. package/controllers/index.d.ts +1 -1
  5. package/controllers/notification.controller.d.ts +4 -4
  6. package/docs/index.d.ts +1 -1
  7. package/docs/notification-swagger.config.d.ts +2 -2
  8. package/dtos/index.d.ts +1 -1
  9. package/entities/index.d.ts +5 -5
  10. package/entities/notification-with-company.entity.d.ts +1 -1
  11. package/fesm/244.js +816 -0
  12. package/fesm/446.js +939 -0
  13. package/fesm/adapters/index.js +79 -1
  14. package/fesm/config/index.js +84 -3
  15. package/fesm/controllers/index.js +13 -1
  16. package/fesm/docs/index.js +112 -1
  17. package/fesm/dtos/index.js +311 -1
  18. package/fesm/entities/index.js +211 -8
  19. package/fesm/gateways/index.js +286 -1
  20. package/fesm/index.js +467 -10
  21. package/fesm/interfaces/index.js +16 -2
  22. package/fesm/modules/index.js +269 -1
  23. package/fesm/runtime.js +85 -0
  24. package/fesm/services/index.js +357 -4
  25. package/gateways/index.d.ts +1 -1
  26. package/gateways/notification.gateway.d.ts +2 -2
  27. package/index.d.ts +10 -10
  28. package/interfaces/index.d.ts +2 -2
  29. package/interfaces/notification-module.interface.d.ts +3 -3
  30. package/interfaces/notification.interface.d.ts +1 -1
  31. package/modules/index.d.ts +1 -1
  32. package/modules/notification.module.d.ts +2 -2
  33. package/package.json +33 -44
  34. package/services/index.d.ts +4 -4
  35. package/services/notification-config.service.d.ts +3 -3
  36. package/services/notification-datasource.provider.d.ts +3 -3
  37. package/services/notification-helper.service.d.ts +5 -5
  38. package/services/notification.service.d.ts +8 -8
  39. package/cjs/adapters/index.js +0 -18
  40. package/cjs/adapters/notification.adapter.js +0 -62
  41. package/cjs/adapters/notification.adapter.spec.js +0 -73
  42. package/cjs/config/event-actions.js +0 -33
  43. package/cjs/config/index.js +0 -20
  44. package/cjs/config/message-keys.js +0 -19
  45. package/cjs/config/notification.constants.js +0 -11
  46. package/cjs/controllers/index.js +0 -18
  47. package/cjs/controllers/notification.controller.js +0 -299
  48. package/cjs/controllers/notification.controller.spec.js +0 -214
  49. package/cjs/docs/index.js +0 -18
  50. package/cjs/docs/notification-swagger.config.js +0 -97
  51. package/cjs/docs/notification-swagger.config.spec.js +0 -68
  52. package/cjs/dtos/index.js +0 -18
  53. package/cjs/dtos/notification.dto.js +0 -295
  54. package/cjs/entities/index.js +0 -48
  55. package/cjs/entities/index.spec.js +0 -13
  56. package/cjs/entities/notification-with-company.entity.js +0 -57
  57. package/cjs/entities/notification.entity.js +0 -112
  58. package/cjs/gateways/index.js +0 -18
  59. package/cjs/gateways/notification.gateway.js +0 -172
  60. package/cjs/gateways/notification.gateway.spec.js +0 -359
  61. package/cjs/index.js +0 -28
  62. package/cjs/interfaces/index.js +0 -19
  63. package/cjs/interfaces/notification-module.interface.js +0 -4
  64. package/cjs/interfaces/notification.interface.js +0 -4
  65. package/cjs/modules/index.js +0 -18
  66. package/cjs/modules/notification.module.js +0 -139
  67. package/cjs/modules/notification.module.spec.js +0 -152
  68. package/cjs/services/index.js +0 -21
  69. package/cjs/services/notification-config.service.js +0 -69
  70. package/cjs/services/notification-config.service.spec.js +0 -113
  71. package/cjs/services/notification-datasource.provider.js +0 -133
  72. package/cjs/services/notification-datasource.provider.spec.js +0 -136
  73. package/cjs/services/notification-helper.service.js +0 -197
  74. package/cjs/services/notification-helper.service.spec.js +0 -392
  75. package/cjs/services/notification.service.js +0 -281
  76. package/cjs/services/notification.service.spec.js +0 -560
  77. package/fesm/adapters/notification.adapter.js +0 -52
  78. package/fesm/adapters/notification.adapter.spec.js +0 -69
  79. package/fesm/config/event-actions.js +0 -12
  80. package/fesm/config/message-keys.js +0 -9
  81. package/fesm/config/notification.constants.js +0 -1
  82. package/fesm/controllers/notification.controller.js +0 -289
  83. package/fesm/controllers/notification.controller.spec.js +0 -210
  84. package/fesm/docs/notification-swagger.config.js +0 -87
  85. package/fesm/docs/notification-swagger.config.spec.js +0 -64
  86. package/fesm/dtos/notification.dto.js +0 -262
  87. package/fesm/entities/index.spec.js +0 -9
  88. package/fesm/entities/notification-with-company.entity.js +0 -47
  89. package/fesm/entities/notification.entity.js +0 -102
  90. package/fesm/gateways/notification.gateway.js +0 -162
  91. package/fesm/gateways/notification.gateway.spec.js +0 -355
  92. package/fesm/interfaces/notification-module.interface.js +0 -1
  93. package/fesm/interfaces/notification.interface.js +0 -1
  94. package/fesm/modules/notification.module.js +0 -129
  95. package/fesm/modules/notification.module.spec.js +0 -148
  96. package/fesm/services/notification-config.service.js +0 -59
  97. package/fesm/services/notification-config.service.spec.js +0 -109
  98. package/fesm/services/notification-datasource.provider.js +0 -82
  99. package/fesm/services/notification-datasource.provider.spec.js +0 -132
  100. package/fesm/services/notification-helper.service.js +0 -187
  101. package/fesm/services/notification-helper.service.spec.js +0 -388
  102. package/fesm/services/notification.service.js +0 -271
  103. package/fesm/services/notification.service.spec.js +0 -556
@@ -1,556 +0,0 @@
1
- import { Test } from '@nestjs/testing';
2
- import { EventBusRegistry } from '@flusys/nestjs-shared/classes';
3
- import { UtilsService } from '@flusys/nestjs-shared/modules';
4
- import { createMockDataSourceProvider, createMockRepository } from '@test-utils/mocks/repository.mock';
5
- import { buildMockUser } from '@test-utils/mocks/logged-user.mock';
6
- import { NotificationService } from './notification.service';
7
- import { NotificationConfigService } from './notification-config.service';
8
- import { NotificationDataSourceProvider } from './notification-datasource.provider';
9
- import { NotificationGateway } from '../gateways';
10
- import { Notification, NotificationWithCompany } from '../entities';
11
- function buildNotification(overrides = {}) {
12
- return {
13
- id: 'notif-uuid-1',
14
- userId: 'user-uuid-1',
15
- type: 'info',
16
- title: 'Title',
17
- message: 'Message',
18
- data: null,
19
- isRead: false,
20
- readAt: null,
21
- createdAt: new Date(),
22
- updatedAt: new Date(),
23
- deletedAt: null,
24
- createdById: null,
25
- updatedById: null,
26
- deletedById: null,
27
- ...overrides
28
- };
29
- }
30
- function createMockQuery() {
31
- return {
32
- andWhere: jest.fn().mockReturnThis(),
33
- addSelect: jest.fn().mockReturnThis(),
34
- orderBy: jest.fn().mockReturnThis()
35
- };
36
- }
37
- describe('NotificationService', ()=>{
38
- let service;
39
- let mockRepo;
40
- let mockConfig;
41
- let mockGateway;
42
- async function buildService(withGateway = true, companyFeatureEnabled = false) {
43
- mockRepo = createMockRepository();
44
- const mockDataSourceProvider = createMockDataSourceProvider(mockRepo);
45
- mockConfig = {
46
- isCompanyFeatureEnabled: jest.fn().mockReturnValue(companyFeatureEnabled)
47
- };
48
- mockGateway = {
49
- emitToUser: jest.fn(),
50
- emitToUserInCompany: jest.fn()
51
- };
52
- const providers = [
53
- NotificationService,
54
- {
55
- provide: 'CACHE_INSTANCE',
56
- useValue: {}
57
- },
58
- {
59
- provide: UtilsService,
60
- useValue: {
61
- clearCache: jest.fn().mockResolvedValue(undefined)
62
- }
63
- },
64
- {
65
- provide: NotificationConfigService,
66
- useValue: mockConfig
67
- },
68
- {
69
- provide: NotificationDataSourceProvider,
70
- useValue: mockDataSourceProvider
71
- }
72
- ];
73
- if (withGateway) {
74
- providers.push({
75
- provide: NotificationGateway,
76
- useValue: mockGateway
77
- });
78
- }
79
- const module = await Test.createTestingModule({
80
- providers
81
- }).compile();
82
- const resolved = await module.resolve(NotificationService);
83
- resolved.repository = mockRepo;
84
- return resolved;
85
- }
86
- beforeEach(async ()=>{
87
- service = await buildService();
88
- });
89
- describe('entity selection', ()=>{
90
- it('uses Notification when the company feature is disabled', async ()=>{
91
- const built = await buildService(true, false);
92
- expect(built['_entity']).toBe(Notification);
93
- });
94
- it('uses NotificationWithCompany when the company feature is enabled', async ()=>{
95
- const built = await buildService(true, true);
96
- expect(built['_entity']).toBe(NotificationWithCompany);
97
- });
98
- });
99
- describe('convertSingleDtoToEntity', ()=>{
100
- it('builds a new entity for create (no id present)', async ()=>{
101
- const user = buildMockUser();
102
- const dto = {
103
- userId: user.id,
104
- title: 'Hello',
105
- type: 'success'
106
- };
107
- const entity = await service.convertSingleDtoToEntity(dto, user);
108
- expect(entity).toMatchObject({
109
- userId: user.id,
110
- title: 'Hello',
111
- type: 'success',
112
- isRead: false
113
- });
114
- });
115
- it('defaults type to "info" when not provided', async ()=>{
116
- const user = buildMockUser();
117
- const entity = await service.convertSingleDtoToEntity({
118
- userId: user.id,
119
- title: 'Hi'
120
- }, user);
121
- expect(entity.type).toBe('info');
122
- });
123
- it('merges into the existing entity when updating by id', async ()=>{
124
- const existing = buildNotification({
125
- id: 'notif-1',
126
- title: 'Old title'
127
- });
128
- mockRepo.findOne.mockResolvedValue(existing);
129
- const user = buildMockUser();
130
- const entity = await service.convertSingleDtoToEntity({
131
- id: 'notif-1',
132
- title: 'New title'
133
- }, user);
134
- expect(mockRepo.findOne).toHaveBeenCalledWith({
135
- where: {
136
- id: 'notif-1'
137
- }
138
- });
139
- expect(entity.title).toBe('New title');
140
- expect(entity.isRead).toBe(false);
141
- });
142
- it('throws NotFoundException when updating an id that does not exist', async ()=>{
143
- mockRepo.findOne.mockResolvedValue(null);
144
- await expect(service.convertSingleDtoToEntity({
145
- id: 'missing-id',
146
- title: 'X'
147
- }, buildMockUser())).rejects.toThrow();
148
- });
149
- it('includes companyId from the dto when the company feature is enabled', async ()=>{
150
- mockConfig.isCompanyFeatureEnabled.mockReturnValue(true);
151
- const user = buildMockUser({
152
- companyId: 'company-user'
153
- });
154
- const entity = await service.convertSingleDtoToEntity({
155
- userId: user.id,
156
- title: 'Hi',
157
- companyId: 'company-dto'
158
- }, user);
159
- expect(entity.companyId).toBe('company-dto');
160
- });
161
- it('falls back to the user companyId when the dto has none and the company feature is enabled', async ()=>{
162
- mockConfig.isCompanyFeatureEnabled.mockReturnValue(true);
163
- const user = buildMockUser({
164
- companyId: 'company-user'
165
- });
166
- const entity = await service.convertSingleDtoToEntity({
167
- userId: user.id,
168
- title: 'Hi'
169
- }, user);
170
- expect(entity.companyId).toBe('company-user');
171
- });
172
- it('does not set companyId when the company feature is disabled', async ()=>{
173
- const user = buildMockUser({
174
- companyId: 'company-user'
175
- });
176
- const entity = await service.convertSingleDtoToEntity({
177
- userId: user.id,
178
- title: 'Hi',
179
- companyId: 'company-dto'
180
- }, user);
181
- expect(entity.companyId).toBeUndefined();
182
- });
183
- });
184
- describe('getFilterQuery', ()=>{
185
- it('adds an isRead filter when provided as boolean', async ()=>{
186
- const query = createMockQuery();
187
- await service.getFilterQuery(query, {
188
- isRead: true
189
- }, buildMockUser());
190
- expect(query.andWhere).toHaveBeenCalledWith('notification.isRead = :isRead', {
191
- isRead: true
192
- });
193
- });
194
- it('adds a type filter when provided', async ()=>{
195
- const query = createMockQuery();
196
- await service.getFilterQuery(query, {
197
- type: 'success'
198
- }, buildMockUser());
199
- expect(query.andWhere).toHaveBeenCalledWith('notification.type = :type', {
200
- type: 'success'
201
- });
202
- });
203
- it('adds a userId filter when provided', async ()=>{
204
- const query = createMockQuery();
205
- await service.getFilterQuery(query, {
206
- userId: 'user-9'
207
- }, buildMockUser());
208
- expect(query.andWhere).toHaveBeenCalledWith('notification.userId = :userId', {
209
- userId: 'user-9'
210
- });
211
- });
212
- it('ignores unrecognized filter keys', async ()=>{
213
- const query = createMockQuery();
214
- await service.getFilterQuery(query, {
215
- unknownField: 'x'
216
- }, buildMockUser());
217
- expect(query.andWhere).not.toHaveBeenCalled();
218
- });
219
- it('ignores a non-boolean isRead value', async ()=>{
220
- const query = createMockQuery();
221
- await service.getFilterQuery(query, {
222
- isRead: 'true'
223
- }, buildMockUser());
224
- expect(query.andWhere).not.toHaveBeenCalled();
225
- });
226
- });
227
- describe('getSelectQuery', ()=>{
228
- it('always selects createdAt and updatedAt in addition to requested fields', async ()=>{
229
- const query = createMockQuery();
230
- await service.getSelectQuery(query, buildMockUser(), [
231
- 'title',
232
- 'message'
233
- ]);
234
- expect(query.addSelect).toHaveBeenCalledWith([
235
- 'notification.title',
236
- 'notification.message',
237
- 'notification.createdAt',
238
- 'notification.updatedAt'
239
- ]);
240
- });
241
- it('selects only createdAt/updatedAt when no fields are requested', async ()=>{
242
- const query = createMockQuery();
243
- await service.getSelectQuery(query, buildMockUser(), undefined);
244
- expect(query.addSelect).toHaveBeenCalledWith([
245
- 'notification.createdAt',
246
- 'notification.updatedAt'
247
- ]);
248
- });
249
- it('deduplicates fields already included in the always-selected set', async ()=>{
250
- const query = createMockQuery();
251
- await service.getSelectQuery(query, buildMockUser(), [
252
- 'createdAt'
253
- ]);
254
- expect(query.addSelect).toHaveBeenCalledWith([
255
- 'notification.createdAt',
256
- 'notification.updatedAt'
257
- ]);
258
- });
259
- });
260
- describe('getExtraManipulateQuery', ()=>{
261
- it('scopes the query to the current user', async ()=>{
262
- const query = createMockQuery();
263
- const user = buildMockUser();
264
- await service.getExtraManipulateQuery(query, {}, user);
265
- expect(query.andWhere).toHaveBeenCalledWith('notification.userId = :currentUserId', {
266
- currentUserId: user.id
267
- });
268
- });
269
- it('applies the company filter when the company feature is enabled', async ()=>{
270
- mockConfig.isCompanyFeatureEnabled.mockReturnValue(true);
271
- const query = createMockQuery();
272
- const user = buildMockUser({
273
- companyId: 'company-1'
274
- });
275
- await service.getExtraManipulateQuery(query, {}, user);
276
- expect(query.andWhere).toHaveBeenCalledWith('notification.companyId = :companyId', {
277
- companyId: 'company-1'
278
- });
279
- });
280
- it('defaults to ordering by createdAt DESC when no sort is provided', async ()=>{
281
- const query = createMockQuery();
282
- await service.getExtraManipulateQuery(query, {}, buildMockUser());
283
- expect(query.orderBy).toHaveBeenCalledWith('notification.createdAt', 'DESC');
284
- });
285
- it('does not apply a default sort when the caller already specified one', async ()=>{
286
- const query = createMockQuery();
287
- await service.getExtraManipulateQuery(query, {
288
- sort: {
289
- title: 'ASC'
290
- }
291
- }, buildMockUser());
292
- expect(query.orderBy).not.toHaveBeenCalled();
293
- });
294
- it('does not scope by user when no user is provided', async ()=>{
295
- const query = createMockQuery();
296
- await service.getExtraManipulateQuery(query, {}, null);
297
- expect(query.andWhere).not.toHaveBeenCalledWith(expect.stringContaining('currentUserId'), expect.anything());
298
- });
299
- });
300
- describe('afterInsertOperation', ()=>{
301
- it('emits a notification:new event for each inserted entity', async ()=>{
302
- const entities = [
303
- buildNotification({
304
- id: 'n1'
305
- }),
306
- buildNotification({
307
- id: 'n2'
308
- })
309
- ];
310
- await service.afterInsertOperation(entities, {}, buildMockUser(), {});
311
- expect(mockGateway.emitToUser).toHaveBeenCalledTimes(2);
312
- expect(mockGateway.emitToUser).toHaveBeenCalledWith('user-uuid-1', 'notification:new', expect.objectContaining({
313
- id: 'n1'
314
- }));
315
- });
316
- it('uses emitToUserInCompany when the entity has a companyId and the company feature is enabled', async ()=>{
317
- mockConfig.isCompanyFeatureEnabled.mockReturnValue(true);
318
- const entities = [
319
- buildNotification({
320
- id: 'n1',
321
- companyId: 'company-1'
322
- })
323
- ];
324
- await service.afterInsertOperation(entities, {}, buildMockUser(), {});
325
- expect(mockGateway.emitToUserInCompany).toHaveBeenCalledWith('user-uuid-1', 'company-1', 'notification:new', expect.any(Object));
326
- });
327
- });
328
- describe('markAsRead', ()=>{
329
- it('marks a matching notification as read and returns it', async ()=>{
330
- const user = buildMockUser();
331
- const notification = buildNotification({
332
- id: 'notif-1',
333
- userId: user.id,
334
- isRead: false
335
- });
336
- mockRepo.findOne.mockResolvedValue(notification);
337
- mockRepo.save.mockResolvedValue({
338
- ...notification,
339
- isRead: true
340
- });
341
- const result = await service.markAsRead('notif-1', user);
342
- expect(mockRepo.findOne).toHaveBeenCalledWith({
343
- where: {
344
- id: 'notif-1',
345
- userId: user.id
346
- }
347
- });
348
- expect(mockRepo.save).toHaveBeenCalledWith(expect.objectContaining({
349
- isRead: true,
350
- readAt: expect.any(Date)
351
- }));
352
- expect(result.isRead).toBe(true);
353
- expect(mockGateway.emitToUser).toHaveBeenCalledWith(user.id, 'notification:read', {
354
- id: 'notif-1'
355
- });
356
- });
357
- it('throws NotFoundException when the notification does not belong to the user or does not exist', async ()=>{
358
- mockRepo.findOne.mockResolvedValue(null);
359
- await expect(service.markAsRead('missing-id', buildMockUser())).rejects.toThrow();
360
- expect(mockRepo.save).not.toHaveBeenCalled();
361
- });
362
- it('emits to the company-scoped room when the notification has a companyId', async ()=>{
363
- mockConfig.isCompanyFeatureEnabled.mockReturnValue(true);
364
- const user = buildMockUser({
365
- companyId: 'company-1'
366
- });
367
- const notification = buildNotification({
368
- id: 'notif-1',
369
- userId: user.id,
370
- companyId: 'company-1'
371
- });
372
- mockRepo.findOne.mockResolvedValue(notification);
373
- mockRepo.save.mockResolvedValue({
374
- ...notification,
375
- isRead: true
376
- });
377
- await service.markAsRead('notif-1', user);
378
- expect(mockGateway.emitToUserInCompany).toHaveBeenCalledWith(user.id, 'company-1', 'notification:read', {
379
- id: 'notif-1'
380
- });
381
- });
382
- });
383
- describe('markAllAsRead', ()=>{
384
- it('marks all unread notifications as read for the user and returns the affected count', async ()=>{
385
- const user = buildMockUser();
386
- mockRepo.update.mockResolvedValue({
387
- affected: 4,
388
- raw: {},
389
- generatedMaps: []
390
- });
391
- const count = await service.markAllAsRead(user);
392
- expect(mockRepo.update).toHaveBeenCalledWith({
393
- userId: user.id,
394
- isRead: false
395
- }, {
396
- isRead: true,
397
- readAt: expect.any(Date)
398
- });
399
- expect(count).toBe(4);
400
- expect(mockGateway.emitToUser).toHaveBeenCalledWith(user.id, 'notification:read-all', {});
401
- });
402
- it('returns 0 when there is nothing to update (zero unread notifications)', async ()=>{
403
- mockRepo.update.mockResolvedValue({
404
- affected: 0,
405
- raw: {},
406
- generatedMaps: []
407
- });
408
- const count = await service.markAllAsRead(buildMockUser());
409
- expect(count).toBe(0);
410
- });
411
- it('returns 0 when affected is undefined/null', async ()=>{
412
- mockRepo.update.mockResolvedValue({
413
- affected: undefined,
414
- raw: {},
415
- generatedMaps: []
416
- });
417
- const count = await service.markAllAsRead(buildMockUser());
418
- expect(count).toBe(0);
419
- });
420
- it('scopes the update by companyId when the company feature is enabled', async ()=>{
421
- mockConfig.isCompanyFeatureEnabled.mockReturnValue(true);
422
- const user = buildMockUser({
423
- companyId: 'company-1'
424
- });
425
- mockRepo.update.mockResolvedValue({
426
- affected: 1,
427
- raw: {},
428
- generatedMaps: []
429
- });
430
- await service.markAllAsRead(user);
431
- expect(mockRepo.update).toHaveBeenCalledWith({
432
- userId: user.id,
433
- isRead: false,
434
- companyId: 'company-1'
435
- }, expect.any(Object));
436
- });
437
- });
438
- describe('getUnreadCount', ()=>{
439
- it('returns the unread count for the user', async ()=>{
440
- const user = buildMockUser();
441
- mockRepo.count.mockResolvedValue(7);
442
- const count = await service.getUnreadCount(user);
443
- expect(mockRepo.count).toHaveBeenCalledWith({
444
- where: {
445
- userId: user.id,
446
- isRead: false
447
- }
448
- });
449
- expect(count).toBe(7);
450
- });
451
- it('returns 0 for a user with no unread notifications', async ()=>{
452
- mockRepo.count.mockResolvedValue(0);
453
- const count = await service.getUnreadCount(buildMockUser());
454
- expect(count).toBe(0);
455
- });
456
- it('scopes the count by companyId when the company feature is enabled', async ()=>{
457
- mockConfig.isCompanyFeatureEnabled.mockReturnValue(true);
458
- const user = buildMockUser({
459
- companyId: 'company-1'
460
- });
461
- mockRepo.count.mockResolvedValue(2);
462
- await service.getUnreadCount(user);
463
- expect(mockRepo.count).toHaveBeenCalledWith({
464
- where: {
465
- userId: user.id,
466
- isRead: false,
467
- companyId: 'company-1'
468
- }
469
- });
470
- });
471
- });
472
- describe('without a bound gateway', ()=>{
473
- it('does not throw when marking as read and no gateway is registered', async ()=>{
474
- const noGatewayService = await buildService(false);
475
- const user = buildMockUser();
476
- const notification = buildNotification({
477
- id: 'notif-1',
478
- userId: user.id
479
- });
480
- mockRepo.findOne.mockResolvedValue(notification);
481
- mockRepo.save.mockResolvedValue({
482
- ...notification,
483
- isRead: true
484
- });
485
- await expect(noGatewayService.markAsRead('notif-1', user)).resolves.toBeDefined();
486
- });
487
- });
488
- describe('domain events', ()=>{
489
- let publish;
490
- beforeEach(()=>{
491
- publish = jest.fn().mockResolvedValue(undefined);
492
- EventBusRegistry.reset();
493
- EventBusRegistry.setBus({
494
- publish,
495
- subscribe: jest.fn(),
496
- close: jest.fn()
497
- });
498
- EventBusRegistry.configureModule('notification', {
499
- enabled: true
500
- });
501
- });
502
- afterEach(()=>{
503
- EventBusRegistry.reset();
504
- });
505
- it('publishes notification.notification.read when one is marked read', async ()=>{
506
- const user = buildMockUser();
507
- const notification = buildNotification({
508
- id: 'notif-1',
509
- userId: user.id,
510
- isRead: false
511
- });
512
- mockRepo.findOne.mockResolvedValue(notification);
513
- mockRepo.save.mockResolvedValue({
514
- ...notification,
515
- isRead: true
516
- });
517
- await service.markAsRead('notif-1', user);
518
- expect(publish).toHaveBeenCalledWith(expect.objectContaining({
519
- name: 'notification.notification.read',
520
- payload: expect.objectContaining({
521
- ids: [
522
- 'notif-1'
523
- ]
524
- })
525
- }));
526
- });
527
- it('publishes notification.notification.all-read with the affected count', async ()=>{
528
- mockRepo.update.mockResolvedValue({
529
- affected: 4,
530
- raw: {},
531
- generatedMaps: []
532
- });
533
- await service.markAllAsRead(buildMockUser());
534
- expect(publish).toHaveBeenCalledWith(expect.objectContaining({
535
- name: 'notification.notification.all-read',
536
- payload: expect.objectContaining({
537
- metadata: {
538
- count: 4
539
- }
540
- })
541
- }));
542
- });
543
- it('does not publish when the notification module has events disabled', async ()=>{
544
- EventBusRegistry.configureModule('notification', {
545
- enabled: false
546
- });
547
- mockRepo.update.mockResolvedValue({
548
- affected: 1,
549
- raw: {},
550
- generatedMaps: []
551
- });
552
- await service.markAllAsRead(buildMockUser());
553
- expect(publish).not.toHaveBeenCalled();
554
- });
555
- });
556
- });