@flusys/nestjs-notification 6.0.2 → 6.1.1
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/cjs/adapters/notification.adapter.spec.js +73 -0
- package/cjs/controllers/notification.controller.spec.js +214 -0
- package/cjs/docs/notification-swagger.config.spec.js +68 -0
- package/cjs/entities/index.spec.js +13 -0
- package/cjs/gateways/notification.gateway.spec.js +359 -0
- package/cjs/modules/notification.module.spec.js +152 -0
- package/cjs/services/notification-config.service.spec.js +113 -0
- package/cjs/services/notification-datasource.provider.spec.js +136 -0
- package/cjs/services/notification-helper.service.spec.js +306 -0
- package/cjs/services/notification.service.spec.js +490 -0
- package/fesm/adapters/notification.adapter.spec.js +69 -0
- package/fesm/controllers/notification.controller.spec.js +210 -0
- package/fesm/docs/notification-swagger.config.spec.js +64 -0
- package/fesm/entities/index.spec.js +9 -0
- package/fesm/gateways/notification.gateway.spec.js +355 -0
- package/fesm/modules/notification.module.spec.js +148 -0
- package/fesm/services/notification-config.service.spec.js +109 -0
- package/fesm/services/notification-datasource.provider.spec.js +132 -0
- package/fesm/services/notification-helper.service.spec.js +302 -0
- package/fesm/services/notification.service.spec.js +486 -0
- package/package.json +3 -3
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
const _notificationadapter = require("./notification.adapter");
|
|
6
|
+
describe('NotificationAdapter', ()=>{
|
|
7
|
+
let adapter;
|
|
8
|
+
let mockHelper;
|
|
9
|
+
beforeEach(()=>{
|
|
10
|
+
mockHelper = {
|
|
11
|
+
send: jest.fn(),
|
|
12
|
+
sendToMany: jest.fn(),
|
|
13
|
+
isUserOnline: jest.fn()
|
|
14
|
+
};
|
|
15
|
+
adapter = new _notificationadapter.NotificationAdapter(mockHelper);
|
|
16
|
+
});
|
|
17
|
+
describe('send', ()=>{
|
|
18
|
+
it('delegates to NotificationHelperService.send with the same options', async ()=>{
|
|
19
|
+
const options = {
|
|
20
|
+
userId: 'user-uuid-1',
|
|
21
|
+
title: 'Hello',
|
|
22
|
+
message: 'World'
|
|
23
|
+
};
|
|
24
|
+
mockHelper.send.mockResolvedValue(undefined);
|
|
25
|
+
await adapter.send(options);
|
|
26
|
+
expect(mockHelper.send).toHaveBeenCalledWith(options);
|
|
27
|
+
expect(mockHelper.send).toHaveBeenCalledTimes(1);
|
|
28
|
+
});
|
|
29
|
+
it('propagates errors thrown by the helper service', async ()=>{
|
|
30
|
+
const options = {
|
|
31
|
+
userId: 'user-uuid-1',
|
|
32
|
+
title: 'Hello'
|
|
33
|
+
};
|
|
34
|
+
mockHelper.send.mockRejectedValue(new Error('boom'));
|
|
35
|
+
await expect(adapter.send(options)).rejects.toThrow('boom');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
describe('sendToMany', ()=>{
|
|
39
|
+
it('delegates to NotificationHelperService.sendToMany with the same options', async ()=>{
|
|
40
|
+
const options = {
|
|
41
|
+
userIds: [
|
|
42
|
+
'user-uuid-1',
|
|
43
|
+
'user-uuid-2'
|
|
44
|
+
],
|
|
45
|
+
title: 'Broadcast'
|
|
46
|
+
};
|
|
47
|
+
mockHelper.sendToMany.mockResolvedValue(undefined);
|
|
48
|
+
await adapter.sendToMany(options);
|
|
49
|
+
expect(mockHelper.sendToMany).toHaveBeenCalledWith(options);
|
|
50
|
+
expect(mockHelper.sendToMany).toHaveBeenCalledTimes(1);
|
|
51
|
+
});
|
|
52
|
+
it('handles an empty userIds array without throwing', async ()=>{
|
|
53
|
+
const options = {
|
|
54
|
+
userIds: [],
|
|
55
|
+
title: 'Nobody'
|
|
56
|
+
};
|
|
57
|
+
mockHelper.sendToMany.mockResolvedValue(undefined);
|
|
58
|
+
await expect(adapter.sendToMany(options)).resolves.toBeUndefined();
|
|
59
|
+
expect(mockHelper.sendToMany).toHaveBeenCalledWith(options);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
describe('isUserOnline', ()=>{
|
|
63
|
+
it('returns true when the helper reports the user online', ()=>{
|
|
64
|
+
mockHelper.isUserOnline.mockReturnValue(true);
|
|
65
|
+
expect(adapter.isUserOnline('user-uuid-1')).toBe(true);
|
|
66
|
+
expect(mockHelper.isUserOnline).toHaveBeenCalledWith('user-uuid-1');
|
|
67
|
+
});
|
|
68
|
+
it('returns false when the helper reports the user offline', ()=>{
|
|
69
|
+
mockHelper.isUserOnline.mockReturnValue(false);
|
|
70
|
+
expect(adapter.isUserOnline('user-uuid-2')).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
const _enums = require("@flusys/nestjs-shared/enums");
|
|
6
|
+
const _loggedusermock = require("@test-utils/mocks/logged-user.mock");
|
|
7
|
+
const _notificationcontroller = require("./notification.controller");
|
|
8
|
+
const _config = require("../config");
|
|
9
|
+
function buildNotification(overrides = {}) {
|
|
10
|
+
return {
|
|
11
|
+
id: 'notif-1',
|
|
12
|
+
userId: 'user-uuid-1',
|
|
13
|
+
type: _enums.NotificationType.INFO,
|
|
14
|
+
title: 'Title',
|
|
15
|
+
message: 'Message',
|
|
16
|
+
data: null,
|
|
17
|
+
isRead: false,
|
|
18
|
+
readAt: null,
|
|
19
|
+
createdAt: new Date(),
|
|
20
|
+
updatedAt: new Date(),
|
|
21
|
+
...overrides
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
describe('NotificationController', ()=>{
|
|
25
|
+
let controller;
|
|
26
|
+
let mockService;
|
|
27
|
+
let mockHelper;
|
|
28
|
+
beforeEach(()=>{
|
|
29
|
+
mockService = {
|
|
30
|
+
markAsRead: jest.fn(),
|
|
31
|
+
markAllAsRead: jest.fn(),
|
|
32
|
+
getUnreadCount: jest.fn(),
|
|
33
|
+
insert: jest.fn(),
|
|
34
|
+
insertMany: jest.fn(),
|
|
35
|
+
findById: jest.fn(),
|
|
36
|
+
findByIds: jest.fn(),
|
|
37
|
+
update: jest.fn(),
|
|
38
|
+
updateMany: jest.fn(),
|
|
39
|
+
bulkUpsert: jest.fn(),
|
|
40
|
+
getAll: jest.fn(),
|
|
41
|
+
delete: jest.fn()
|
|
42
|
+
};
|
|
43
|
+
mockHelper = {
|
|
44
|
+
send: jest.fn(),
|
|
45
|
+
sendToMany: jest.fn(),
|
|
46
|
+
isUserOnline: jest.fn(),
|
|
47
|
+
getOnlineUserCount: jest.fn()
|
|
48
|
+
};
|
|
49
|
+
controller = new _notificationcontroller.NotificationController(mockService, mockHelper);
|
|
50
|
+
});
|
|
51
|
+
describe('markAsRead', ()=>{
|
|
52
|
+
it('marks the notification as read and returns the mapped response', async ()=>{
|
|
53
|
+
const user = (0, _loggedusermock.buildMockUser)();
|
|
54
|
+
const notification = buildNotification({
|
|
55
|
+
isRead: true
|
|
56
|
+
});
|
|
57
|
+
mockService.markAsRead.mockResolvedValue(notification);
|
|
58
|
+
const result = await controller.markAsRead({
|
|
59
|
+
id: 'notif-1'
|
|
60
|
+
}, user);
|
|
61
|
+
expect(mockService.markAsRead).toHaveBeenCalledWith('notif-1', user);
|
|
62
|
+
expect(result.success).toBe(true);
|
|
63
|
+
expect(result.messageKey).toBe(_config.NOTIFICATION_MESSAGES.MARK_READ_SUCCESS);
|
|
64
|
+
expect(result.data?.id).toBe('notif-1');
|
|
65
|
+
expect(result.data?.isRead).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
it('propagates a not-found error from the service', async ()=>{
|
|
68
|
+
mockService.markAsRead.mockRejectedValue(new Error(_config.NOTIFICATION_MESSAGES.NOT_FOUND));
|
|
69
|
+
await expect(controller.markAsRead({
|
|
70
|
+
id: 'missing'
|
|
71
|
+
}, (0, _loggedusermock.buildMockUser)())).rejects.toThrow();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
describe('markAllAsRead', ()=>{
|
|
75
|
+
it('returns the count of notifications marked as read', async ()=>{
|
|
76
|
+
mockService.markAllAsRead.mockResolvedValue(3);
|
|
77
|
+
const result = await controller.markAllAsRead((0, _loggedusermock.buildMockUser)());
|
|
78
|
+
expect(result.success).toBe(true);
|
|
79
|
+
expect(result.messageKey).toBe(_config.NOTIFICATION_MESSAGES.MARK_ALL_READ_SUCCESS);
|
|
80
|
+
expect(result.messageVariables).toEqual({
|
|
81
|
+
count: 3
|
|
82
|
+
});
|
|
83
|
+
expect(result.message).toBe('3 notifications marked as read');
|
|
84
|
+
});
|
|
85
|
+
it('handles a user with zero unread notifications gracefully', async ()=>{
|
|
86
|
+
mockService.markAllAsRead.mockResolvedValue(0);
|
|
87
|
+
const result = await controller.markAllAsRead((0, _loggedusermock.buildMockUser)());
|
|
88
|
+
expect(result.messageVariables).toEqual({
|
|
89
|
+
count: 0
|
|
90
|
+
});
|
|
91
|
+
expect(result.message).toBe('0 notifications marked as read');
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
describe('getUnreadCount', ()=>{
|
|
95
|
+
it('returns the unread count wrapped in a single response', async ()=>{
|
|
96
|
+
mockService.getUnreadCount.mockResolvedValue(5);
|
|
97
|
+
const result = await controller.getUnreadCount((0, _loggedusermock.buildMockUser)());
|
|
98
|
+
expect(result.success).toBe(true);
|
|
99
|
+
expect(result.messageKey).toBe(_config.NOTIFICATION_MESSAGES.UNREAD_COUNT_SUCCESS);
|
|
100
|
+
expect(result.data).toEqual({
|
|
101
|
+
count: 5
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
it('returns zero for a user with no unread notifications', async ()=>{
|
|
105
|
+
mockService.getUnreadCount.mockResolvedValue(0);
|
|
106
|
+
const result = await controller.getUnreadCount((0, _loggedusermock.buildMockUser)());
|
|
107
|
+
expect(result.data).toEqual({
|
|
108
|
+
count: 0
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
describe('sendNotification', ()=>{
|
|
113
|
+
it('sends a notification via the helper service and returns the mapped response', async ()=>{
|
|
114
|
+
const notification = buildNotification({
|
|
115
|
+
id: 'notif-2'
|
|
116
|
+
});
|
|
117
|
+
mockHelper.send.mockResolvedValue(notification);
|
|
118
|
+
const result = await controller.sendNotification({
|
|
119
|
+
userId: 'user-uuid-1',
|
|
120
|
+
title: 'Hello',
|
|
121
|
+
message: 'World'
|
|
122
|
+
});
|
|
123
|
+
expect(mockHelper.send).toHaveBeenCalledWith({
|
|
124
|
+
userId: 'user-uuid-1',
|
|
125
|
+
title: 'Hello',
|
|
126
|
+
message: 'World',
|
|
127
|
+
type: undefined,
|
|
128
|
+
data: undefined,
|
|
129
|
+
companyId: undefined
|
|
130
|
+
});
|
|
131
|
+
expect(result.success).toBe(true);
|
|
132
|
+
expect(result.messageKey).toBe(_config.NOTIFICATION_MESSAGES.SEND_SUCCESS);
|
|
133
|
+
expect(result.data?.id).toBe('notif-2');
|
|
134
|
+
});
|
|
135
|
+
it('propagates errors from the helper service', async ()=>{
|
|
136
|
+
mockHelper.send.mockRejectedValue(new Error('db down'));
|
|
137
|
+
await expect(controller.sendNotification({
|
|
138
|
+
userId: 'user-1',
|
|
139
|
+
title: 'X'
|
|
140
|
+
})).rejects.toThrow('db down');
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
describe('sendBulkNotification', ()=>{
|
|
144
|
+
it('sends notifications to multiple users and returns bulk meta', async ()=>{
|
|
145
|
+
const notifications = [
|
|
146
|
+
buildNotification({
|
|
147
|
+
id: 'n1',
|
|
148
|
+
userId: 'user-1'
|
|
149
|
+
}),
|
|
150
|
+
buildNotification({
|
|
151
|
+
id: 'n2',
|
|
152
|
+
userId: 'user-2'
|
|
153
|
+
})
|
|
154
|
+
];
|
|
155
|
+
mockHelper.sendToMany.mockResolvedValue(notifications);
|
|
156
|
+
const result = await controller.sendBulkNotification({
|
|
157
|
+
userIds: [
|
|
158
|
+
'user-1',
|
|
159
|
+
'user-2'
|
|
160
|
+
],
|
|
161
|
+
title: 'Broadcast'
|
|
162
|
+
});
|
|
163
|
+
expect(mockHelper.sendToMany).toHaveBeenCalledWith({
|
|
164
|
+
userIds: [
|
|
165
|
+
'user-1',
|
|
166
|
+
'user-2'
|
|
167
|
+
],
|
|
168
|
+
title: 'Broadcast',
|
|
169
|
+
message: undefined,
|
|
170
|
+
type: undefined,
|
|
171
|
+
data: undefined,
|
|
172
|
+
companyId: undefined
|
|
173
|
+
});
|
|
174
|
+
expect(result.success).toBe(true);
|
|
175
|
+
expect(result.messageKey).toBe(_config.NOTIFICATION_MESSAGES.BROADCAST_SUCCESS);
|
|
176
|
+
expect(result.data).toHaveLength(2);
|
|
177
|
+
expect(result.meta).toEqual({
|
|
178
|
+
count: 2,
|
|
179
|
+
total: 2
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
it('reports a mismatch between requested and sent counts in the meta', async ()=>{
|
|
183
|
+
mockHelper.sendToMany.mockResolvedValue([
|
|
184
|
+
buildNotification({
|
|
185
|
+
id: 'n1'
|
|
186
|
+
})
|
|
187
|
+
]);
|
|
188
|
+
const result = await controller.sendBulkNotification({
|
|
189
|
+
userIds: [
|
|
190
|
+
'user-1',
|
|
191
|
+
'user-2',
|
|
192
|
+
'user-3'
|
|
193
|
+
],
|
|
194
|
+
title: 'Partial broadcast'
|
|
195
|
+
});
|
|
196
|
+
expect(result.meta).toEqual({
|
|
197
|
+
count: 1,
|
|
198
|
+
total: 3
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
it('handles an empty userIds bulk send without throwing', async ()=>{
|
|
202
|
+
mockHelper.sendToMany.mockResolvedValue([]);
|
|
203
|
+
const result = await controller.sendBulkNotification({
|
|
204
|
+
userIds: [],
|
|
205
|
+
title: 'Nobody'
|
|
206
|
+
});
|
|
207
|
+
expect(result.data).toEqual([]);
|
|
208
|
+
expect(result.meta).toEqual({
|
|
209
|
+
count: 0,
|
|
210
|
+
total: 0
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
const _notificationswaggerconfig = require("./notification-swagger.config");
|
|
6
|
+
describe('notificationSwaggerConfig', ()=>{
|
|
7
|
+
it('should default to company feature enabled and single-database mode when no config is given', ()=>{
|
|
8
|
+
const config = (0, _notificationswaggerconfig.notificationSwaggerConfig)();
|
|
9
|
+
expect(config.title).toBe('Notification API');
|
|
10
|
+
expect(config.path).toBe('api/docs/notification');
|
|
11
|
+
expect(config.bearerAuth).toBe(true);
|
|
12
|
+
expect(config.excludeSchemaProperties).toBeUndefined();
|
|
13
|
+
expect(config.description).not.toContain('Multi-Tenant Mode');
|
|
14
|
+
});
|
|
15
|
+
it('should exclude companyId schema properties across all notification DTOs when the company feature is disabled', ()=>{
|
|
16
|
+
const config = (0, _notificationswaggerconfig.notificationSwaggerConfig)({
|
|
17
|
+
enableCompanyFeature: false
|
|
18
|
+
});
|
|
19
|
+
expect(config.excludeSchemaProperties).toEqual([
|
|
20
|
+
{
|
|
21
|
+
schemaName: 'NotificationResponseDto',
|
|
22
|
+
properties: [
|
|
23
|
+
'companyId'
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
schemaName: 'CreateNotificationDto',
|
|
28
|
+
properties: [
|
|
29
|
+
'companyId'
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
schemaName: 'SendNotificationDto',
|
|
34
|
+
properties: [
|
|
35
|
+
'companyId'
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
schemaName: 'SendBulkNotificationDto',
|
|
40
|
+
properties: [
|
|
41
|
+
'companyId'
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
schemaName: 'UpdateNotificationDto',
|
|
46
|
+
properties: [
|
|
47
|
+
'companyId'
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
]);
|
|
51
|
+
});
|
|
52
|
+
it('should not exclude any schema properties when the company feature is enabled', ()=>{
|
|
53
|
+
const config = (0, _notificationswaggerconfig.notificationSwaggerConfig)({
|
|
54
|
+
enableCompanyFeature: true
|
|
55
|
+
});
|
|
56
|
+
expect(config.excludeSchemaProperties).toBeUndefined();
|
|
57
|
+
});
|
|
58
|
+
it('should mention multi-tenant mode in the description only for multi-tenant database mode', ()=>{
|
|
59
|
+
const multiTenant = (0, _notificationswaggerconfig.notificationSwaggerConfig)({
|
|
60
|
+
databaseMode: 'multi-tenant'
|
|
61
|
+
});
|
|
62
|
+
const single = (0, _notificationswaggerconfig.notificationSwaggerConfig)({
|
|
63
|
+
databaseMode: 'single'
|
|
64
|
+
});
|
|
65
|
+
expect(multiTenant.description).toContain('Multi-Tenant Mode');
|
|
66
|
+
expect(single.description).not.toContain('Multi-Tenant Mode');
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
const _index = require("./index");
|
|
6
|
+
describe('getNotificationEntitiesByConfig', ()=>{
|
|
7
|
+
it('returns the core entities when the company feature is disabled', ()=>{
|
|
8
|
+
expect((0, _index.getNotificationEntitiesByConfig)(false)).toBe(_index.NotificationCoreEntities);
|
|
9
|
+
});
|
|
10
|
+
it('returns the company-scoped entities when the company feature is enabled', ()=>{
|
|
11
|
+
expect((0, _index.getNotificationEntitiesByConfig)(true)).toBe(_index.NotificationCompanyEntities);
|
|
12
|
+
});
|
|
13
|
+
});
|