@bopen-io/messagebox-server 1.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +372 -0
  2. package/knexfile.d.ts +5 -0
  3. package/knexfile.ts +29 -0
  4. package/out/knexfile.js +25 -0
  5. package/out/knexfile.js.map +1 -0
  6. package/out/src/app.js +160 -0
  7. package/out/src/app.js.map +1 -0
  8. package/out/src/config/firebase.js +150 -0
  9. package/out/src/config/firebase.js.map +1 -0
  10. package/out/src/index.js +295 -0
  11. package/out/src/index.js.map +1 -0
  12. package/out/src/migrations/2022-12-28-001-initial-migration.js +28 -0
  13. package/out/src/migrations/2022-12-28-001-initial-migration.js.map +1 -0
  14. package/out/src/migrations/2023-01-17-messages-update.js +11 -0
  15. package/out/src/migrations/2023-01-17-messages-update.js.map +1 -0
  16. package/out/src/migrations/2024-03-05-001-messageID-upgrade.js +21 -0
  17. package/out/src/migrations/2024-03-05-001-messageID-upgrade.js.map +1 -0
  18. package/out/src/migrations/2025-01-31-001-notification-permissions.js +59 -0
  19. package/out/src/migrations/2025-01-31-001-notification-permissions.js.map +1 -0
  20. package/out/src/migrations/2025-01-31-002-device-registrations.js +24 -0
  21. package/out/src/migrations/2025-01-31-002-device-registrations.js.map +1 -0
  22. package/out/src/routes/__tests/acknowledgeMessage.test.js +129 -0
  23. package/out/src/routes/__tests/acknowledgeMessage.test.js.map +1 -0
  24. package/out/src/routes/__tests/listMessages.test.js +211 -0
  25. package/out/src/routes/__tests/listMessages.test.js.map +1 -0
  26. package/out/src/routes/__tests/sendMessage.test.js +307 -0
  27. package/out/src/routes/__tests/sendMessage.test.js.map +1 -0
  28. package/out/src/routes/acknowledgeMessage.js +144 -0
  29. package/out/src/routes/acknowledgeMessage.js.map +1 -0
  30. package/out/src/routes/index.js +17 -0
  31. package/out/src/routes/index.js.map +1 -0
  32. package/out/src/routes/listDevices.js +74 -0
  33. package/out/src/routes/listDevices.js.map +1 -0
  34. package/out/src/routes/listMessages.js +186 -0
  35. package/out/src/routes/listMessages.js.map +1 -0
  36. package/out/src/routes/permissions/getPermission.js +149 -0
  37. package/out/src/routes/permissions/getPermission.js.map +1 -0
  38. package/out/src/routes/permissions/getQuote.js +165 -0
  39. package/out/src/routes/permissions/getQuote.js.map +1 -0
  40. package/out/src/routes/permissions/index.js +12 -0
  41. package/out/src/routes/permissions/index.js.map +1 -0
  42. package/out/src/routes/permissions/listPermissions.js +190 -0
  43. package/out/src/routes/permissions/listPermissions.js.map +1 -0
  44. package/out/src/routes/permissions/setPermission.js +137 -0
  45. package/out/src/routes/permissions/setPermission.js.map +1 -0
  46. package/out/src/routes/registerDevice.js +150 -0
  47. package/out/src/routes/registerDevice.js.map +1 -0
  48. package/out/src/routes/sendMessage.js +451 -0
  49. package/out/src/routes/sendMessage.js.map +1 -0
  50. package/out/src/swagger.js +49 -0
  51. package/out/src/swagger.js.map +1 -0
  52. package/out/src/types/messagePermissions.js +2 -0
  53. package/out/src/types/messagePermissions.js.map +1 -0
  54. package/out/src/types/notifications.js +2 -0
  55. package/out/src/types/notifications.js.map +1 -0
  56. package/out/src/utils/logger.js +24 -0
  57. package/out/src/utils/logger.js.map +1 -0
  58. package/out/src/utils/messagePermissions.js +126 -0
  59. package/out/src/utils/messagePermissions.js.map +1 -0
  60. package/out/src/utils/sendFCMNotification.js +121 -0
  61. package/out/src/utils/sendFCMNotification.js.map +1 -0
  62. package/out/tsconfig.tsbuildinfo +1 -0
  63. package/package.json +88 -0
  64. package/src/migrations/2022-12-28-001-initial-migration.ts +31 -0
  65. package/src/migrations/2023-01-17-messages-update.ts +13 -0
  66. package/src/migrations/2024-03-05-001-messageID-upgrade.ts +25 -0
  67. package/src/migrations/2025-01-31-001-notification-permissions.ts +68 -0
  68. package/src/migrations/2025-01-31-002-device-registrations.ts +28 -0
  69. package/src/templates/documentation.ejs +62 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"2025-01-31-002-device-registrations.js","sourceRoot":"","sources":["../../../src/migrations/2025-01-31-002-device-registrations.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC,IAAU;IACjC,+DAA+D;IAC/D,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,sBAAsB,EAAE,CAAC,KAAK,EAAE,EAAE;QAC9D,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAA;QAChC,KAAK,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAA;QACtD,KAAK,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAA;QACtD,KAAK,CAAC,MAAM,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAA,CAAC,sBAAsB;QACtE,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAA,CAAC,+CAA+C;QAC5F,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAA,CAAC,6BAA6B;QACvE,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAA,CAAC,gCAAgC;QACxE,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,QAAQ,EAAE,CAAA,CAAC,6CAA6C;QACrF,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA,CAAC,qCAAqC;QAE7E,6DAA6D;QAC7D,KAAK,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,CAAC,CAAA;QAE3B,8CAA8C;QAC9C,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,CAAA;QAC3B,KAAK,CAAC,KAAK,CAAC,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC,CAAA;QACvC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;IAC1B,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,IAAU;IACnC,MAAM,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,CAAC,CAAA;AAC7D,CAAC"}
@@ -0,0 +1,129 @@
1
+ /* eslint-env jest */
2
+ import acknowledgeMessage from '../acknowledgeMessage.js';
3
+ import mockKnex from 'mock-knex';
4
+ const knex = acknowledgeMessage.knex;
5
+ let queryTracker;
6
+ // Define Mock Express Response Object
7
+ const mockRes = {
8
+ status: jest.fn().mockReturnThis(),
9
+ json: jest.fn().mockReturnThis(),
10
+ sendStatus: jest.fn().mockReturnThis(),
11
+ send: jest.fn().mockReturnThis(),
12
+ end: jest.fn().mockReturnThis(),
13
+ setHeader: jest.fn().mockReturnThis(),
14
+ getHeader: jest.fn(),
15
+ getHeaders: jest.fn(),
16
+ header: jest.fn().mockReturnThis(),
17
+ type: jest.fn().mockReturnThis(),
18
+ format: jest.fn(),
19
+ location: jest.fn().mockReturnThis(),
20
+ redirect: jest.fn().mockReturnThis(),
21
+ append: jest.fn().mockReturnThis(),
22
+ render: jest.fn(),
23
+ vary: jest.fn().mockReturnThis(),
24
+ cookie: jest.fn().mockReturnThis(),
25
+ clearCookie: jest.fn().mockReturnThis()
26
+ };
27
+ let validReq;
28
+ describe('acknowledgeMessage', () => {
29
+ beforeAll(() => {
30
+ mockKnex.mock(knex);
31
+ });
32
+ beforeEach(() => {
33
+ jest.spyOn(console, 'error').mockImplementation(() => { });
34
+ queryTracker = mockKnex.getTracker();
35
+ queryTracker.install();
36
+ validReq = {
37
+ auth: {
38
+ identityKey: 'mockIdKey'
39
+ },
40
+ body: {
41
+ messageIds: ['123']
42
+ },
43
+ get: jest.fn(),
44
+ header: jest.fn()
45
+ };
46
+ });
47
+ afterEach(() => {
48
+ jest.clearAllMocks();
49
+ if (queryTracker !== null && queryTracker !== undefined) {
50
+ queryTracker.uninstall();
51
+ }
52
+ });
53
+ afterAll(() => {
54
+ mockKnex.unmock(knex);
55
+ });
56
+ it('Throws an error if messageId is missing', async () => {
57
+ delete validReq.body.messageIds;
58
+ await acknowledgeMessage.func(validReq, mockRes);
59
+ expect(mockRes.status).toHaveBeenCalledWith(400);
60
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
61
+ status: 'error',
62
+ code: 'ERR_MESSAGE_ID_REQUIRED'
63
+ }));
64
+ });
65
+ it('Throws an error if messageIds is not an Array', async () => {
66
+ validReq.body.messageIds = '24';
67
+ await acknowledgeMessage.func(validReq, mockRes);
68
+ expect(mockRes.status).toHaveBeenCalledWith(400);
69
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
70
+ status: 'error',
71
+ code: 'ERR_INVALID_MESSAGE_ID',
72
+ description: 'Message IDs must be formatted as an array of strings!'
73
+ }));
74
+ }, 7000);
75
+ it('Deletes a message', async () => {
76
+ queryTracker.on('query', (q, s) => {
77
+ if (s === 1) {
78
+ expect(q.method).toEqual('del');
79
+ expect(q.sql).toEqual('delete from `messages` where `recipient` = ? and `messageId` in (?)');
80
+ expect(q.bindings).toEqual([
81
+ 'mockIdKey',
82
+ '123'
83
+ ]);
84
+ q.response(true);
85
+ }
86
+ else {
87
+ q.response([]);
88
+ }
89
+ });
90
+ await acknowledgeMessage.func(validReq, mockRes);
91
+ expect(mockRes.status).toHaveBeenCalledWith(200);
92
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
93
+ status: 'success'
94
+ }));
95
+ });
96
+ it('Throws an error if deletion fails', async () => {
97
+ queryTracker.on('query', (q, step) => {
98
+ if (step === 1) {
99
+ expect(q.method).toEqual('del');
100
+ expect(q.sql).toEqual('delete from `messages` where `recipient` = ? and `messageId` in (?)');
101
+ expect(q.bindings).toEqual(['mockIdKey', '123']);
102
+ q.response(0); // Simulate deletion failure
103
+ }
104
+ else {
105
+ q.response([]); // Prevent test from hanging due to unhandled second query
106
+ }
107
+ });
108
+ await acknowledgeMessage.func(validReq, mockRes);
109
+ expect(mockRes.status).toHaveBeenCalledWith(400);
110
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
111
+ status: 'error',
112
+ code: 'ERR_INVALID_ACKNOWLEDGMENT',
113
+ description: 'Message not found!'
114
+ }));
115
+ }, 7000);
116
+ it('Throws unknown errors', async () => {
117
+ queryTracker.on('query', () => {
118
+ throw new Error('Failed');
119
+ });
120
+ await acknowledgeMessage.func(validReq, mockRes);
121
+ expect(mockRes.status).toHaveBeenCalledWith(500);
122
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
123
+ status: 'error',
124
+ code: 'ERR_INTERNAL_ERROR',
125
+ description: 'An internal error has occurred while acknowledging the message'
126
+ }));
127
+ });
128
+ });
129
+ //# sourceMappingURL=acknowledgeMessage.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acknowledgeMessage.test.js","sourceRoot":"","sources":["../../../../src/routes/__tests/acknowledgeMessage.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,211 @@
1
+ /* eslint-env jest */
2
+ import listMessages from '../listMessages.js';
3
+ import mockKnex from 'mock-knex';
4
+ // Ensure proper handling of mock-knex
5
+ const knex = listMessages.knex;
6
+ let queryTracker;
7
+ // Define Mock Express Response Object
8
+ const mockRes = {
9
+ status: jest.fn().mockReturnThis(),
10
+ json: jest.fn().mockReturnThis(),
11
+ sendStatus: jest.fn().mockReturnThis(),
12
+ send: jest.fn().mockReturnThis(),
13
+ end: jest.fn().mockReturnThis(),
14
+ setHeader: jest.fn().mockReturnThis(),
15
+ getHeader: jest.fn(),
16
+ getHeaders: jest.fn(),
17
+ header: jest.fn().mockReturnThis(),
18
+ type: jest.fn().mockReturnThis(),
19
+ format: jest.fn(),
20
+ location: jest.fn().mockReturnThis(),
21
+ redirect: jest.fn().mockReturnThis(),
22
+ append: jest.fn().mockReturnThis(),
23
+ render: jest.fn(),
24
+ vary: jest.fn().mockReturnThis(),
25
+ cookie: jest.fn().mockReturnThis(),
26
+ clearCookie: jest.fn().mockReturnThis()
27
+ };
28
+ let validReq;
29
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
30
+ let validRes;
31
+ let validMessageBoxes;
32
+ let validMessages;
33
+ let expectedMessages;
34
+ describe('listMessages', () => {
35
+ beforeAll(() => {
36
+ mockKnex.mock(knex);
37
+ });
38
+ beforeEach(() => {
39
+ jest.spyOn(console, 'error').mockImplementation(() => { });
40
+ queryTracker = mockKnex.getTracker();
41
+ queryTracker.install();
42
+ validMessages = [{
43
+ sender: '028d37b941208cd6b8a4c28288eda5f2f16c2b3ab0fcb6d13c18b47fe37b971fc1',
44
+ messageId: 'msg-1',
45
+ body: '{}',
46
+ created_at: '2024-01-01',
47
+ updated_at: '2024-01-01'
48
+ }];
49
+ expectedMessages = [{
50
+ sender: '028d37b941208cd6b8a4c28288eda5f2f16c2b3ab0fcb6d13c18b47fe37b971fc1',
51
+ messageId: 'msg-1',
52
+ body: '{}',
53
+ createdAt: '2024-01-01',
54
+ updatedAt: '2024-01-01'
55
+ }];
56
+ // Mock Data
57
+ validRes = {
58
+ status: 'success',
59
+ messages: validMessages
60
+ };
61
+ validMessageBoxes = [
62
+ { messageBoxId: 42 },
63
+ { messageBoxId: 31 }
64
+ ];
65
+ // Fully typed mock request
66
+ validReq = {
67
+ auth: {
68
+ identityKey: 'mockIdKey'
69
+ },
70
+ body: {
71
+ messageBox: 'payment_inbox'
72
+ },
73
+ get: jest.fn(),
74
+ header: jest.fn()
75
+ };
76
+ });
77
+ afterEach(() => {
78
+ jest.clearAllMocks();
79
+ if (queryTracker !== null && queryTracker !== undefined) {
80
+ queryTracker.uninstall();
81
+ }
82
+ });
83
+ afterAll(() => {
84
+ mockKnex.unmock(knex);
85
+ });
86
+ it('Throws an error if a messageBox is not provided', async () => {
87
+ validReq.body.messageBox = undefined;
88
+ queryTracker.on('query', (q) => {
89
+ q.response([]);
90
+ });
91
+ await listMessages.func(validReq, mockRes);
92
+ expect(mockRes.status).toHaveBeenCalledWith(400);
93
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
94
+ status: 'error',
95
+ code: 'ERR_MESSAGEBOX_REQUIRED',
96
+ description: 'Please provide the name of a valid MessageBox!'
97
+ }));
98
+ });
99
+ it('Throws an error if messageBox is not a string', async () => {
100
+ validReq.body.messageBox = 123;
101
+ queryTracker.on('query', (q) => {
102
+ q.response([]);
103
+ });
104
+ await listMessages.func(validReq, mockRes);
105
+ expect(mockRes.status).toHaveBeenCalledWith(400);
106
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
107
+ status: 'error',
108
+ code: 'ERR_INVALID_MESSAGEBOX',
109
+ description: 'MessageBox name must be a string!'
110
+ }));
111
+ });
112
+ it('Throws an error if no matching messageBox is found', async () => {
113
+ validReq.body.messageBox = 'pay_inbox';
114
+ queryTracker.on('query', (q, s) => {
115
+ if (s === 1) {
116
+ expect(q.method).toEqual('select');
117
+ expect(q.sql).toEqual('select `messageBoxId` from `messageBox` where `identityKey` = ? and `type` = ?');
118
+ q.response([undefined]);
119
+ }
120
+ else {
121
+ q.response([]);
122
+ }
123
+ });
124
+ await listMessages.func(validReq, mockRes);
125
+ expect(mockRes.status).toHaveBeenCalledWith(200);
126
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
127
+ status: 'success',
128
+ messages: []
129
+ }));
130
+ });
131
+ it('Returns ID of messageBox', async () => {
132
+ queryTracker.on('query', (q, s) => {
133
+ if (s === 1) {
134
+ expect(q.method).toEqual('select');
135
+ expect(q.sql).toEqual('select `messageBoxId` from `messageBox` where `identityKey` = ? and `type` = ?');
136
+ expect(q.bindings).toEqual([
137
+ 'mockIdKey',
138
+ 'payment_inbox'
139
+ ]);
140
+ q.response([validMessageBoxes[0]]);
141
+ }
142
+ else if (s === 2) {
143
+ q.response(validMessages);
144
+ }
145
+ else {
146
+ q.response([]);
147
+ }
148
+ });
149
+ await listMessages.func(validReq, mockRes);
150
+ expect(mockRes.status).toHaveBeenCalledWith(200);
151
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
152
+ status: 'success',
153
+ messages: expectedMessages
154
+ }));
155
+ });
156
+ it('Returns empty array if no messages found', async () => {
157
+ queryTracker.on('query', (q, s) => {
158
+ if (s === 1) {
159
+ q.response([{ messageBoxId: 123 }]);
160
+ }
161
+ else if (s === 2) {
162
+ expect(q.method).toEqual('select');
163
+ expect(q.sql).toEqual('select `messageId`, `body`, `sender`, `created_at`, `updated_at` from `messages` where `recipient` = ? and `messageBoxId` = ?');
164
+ q.response([]);
165
+ }
166
+ else {
167
+ q.response([]);
168
+ }
169
+ });
170
+ await listMessages.func(validReq, mockRes);
171
+ expect(mockRes.status).toHaveBeenCalledWith(200);
172
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
173
+ status: 'success',
174
+ messages: []
175
+ }));
176
+ });
177
+ it('Returns list of messages found', async () => {
178
+ queryTracker.on('query', (q, s) => {
179
+ if (s === 1) {
180
+ q.response([{ messageBoxId: 123 }]);
181
+ }
182
+ else if (s === 2) {
183
+ expect(q.method).toEqual('select');
184
+ expect(q.sql).toEqual('select `messageId`, `body`, `sender`, `created_at`, `updated_at` from `messages` where `recipient` = ? and `messageBoxId` = ?');
185
+ q.response(validMessages);
186
+ }
187
+ else {
188
+ q.response([]);
189
+ }
190
+ });
191
+ await listMessages.func(validReq, mockRes);
192
+ expect(mockRes.status).toHaveBeenCalledWith(200);
193
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
194
+ status: 'success',
195
+ messages: expectedMessages
196
+ }));
197
+ });
198
+ it('Throws unknown errors', async () => {
199
+ queryTracker.on('query', () => {
200
+ throw new Error('Failed');
201
+ });
202
+ await listMessages.func(validReq, mockRes);
203
+ expect(mockRes.status).toHaveBeenCalledWith(500);
204
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
205
+ status: 'error',
206
+ code: 'ERR_INTERNAL_ERROR',
207
+ description: 'An internal error has occurred while listing messages.'
208
+ }));
209
+ });
210
+ });
211
+ //# sourceMappingURL=listMessages.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listMessages.test.js","sourceRoot":"","sources":["../../../../src/routes/__tests/listMessages.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,307 @@
1
+ /* eslint-env jest */
2
+ import sendMessage, { calculateMessagePrice } from '../sendMessage.js';
3
+ import mockKnex from 'mock-knex';
4
+ import { Logger } from '../../utils/logger.js';
5
+ import axios from 'axios';
6
+ import AxiosMockAdapter from 'axios-mock-adapter';
7
+ global.fetch = jest.fn();
8
+ const knex = sendMessage.knex;
9
+ let queryTracker;
10
+ let axiosMock;
11
+ // Define Mock Express Response Object
12
+ const mockRes = {
13
+ status: jest.fn().mockReturnThis(),
14
+ json: jest.fn().mockReturnThis(),
15
+ sendStatus: jest.fn().mockReturnThis(),
16
+ send: jest.fn().mockReturnThis(),
17
+ end: jest.fn().mockReturnThis(),
18
+ setHeader: jest.fn().mockReturnThis(),
19
+ getHeader: jest.fn(),
20
+ getHeaders: jest.fn(),
21
+ header: jest.fn().mockReturnThis(),
22
+ type: jest.fn().mockReturnThis(),
23
+ format: jest.fn(),
24
+ location: jest.fn().mockReturnThis(),
25
+ redirect: jest.fn().mockReturnThis(),
26
+ append: jest.fn().mockReturnThis(),
27
+ render: jest.fn(),
28
+ vary: jest.fn().mockReturnThis(),
29
+ cookie: jest.fn().mockReturnThis(),
30
+ clearCookie: jest.fn().mockReturnThis()
31
+ };
32
+ let validReq;
33
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
34
+ let validRes;
35
+ let validMessageBox;
36
+ describe('sendMessage', () => {
37
+ // Capture original console methods
38
+ const originalError = console.error;
39
+ const originalLog = console.log;
40
+ const originalWarn = console.warn;
41
+ beforeAll(() => {
42
+ mockKnex.mock(knex);
43
+ });
44
+ beforeEach(() => {
45
+ Logger.enable();
46
+ jest.spyOn(console, 'error').mockImplementation((...args) => originalError(...args));
47
+ jest.spyOn(console, 'log').mockImplementation((...args) => originalLog(...args));
48
+ jest.spyOn(console, 'warn').mockImplementation((...args) => originalWarn(...args));
49
+ const instance = axios;
50
+ // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
51
+ // @ts-ignore
52
+ axiosMock = new AxiosMockAdapter(instance);
53
+ queryTracker = mockKnex.getTracker();
54
+ queryTracker.install();
55
+ // Mock Data
56
+ validRes = {
57
+ status: 'success'
58
+ };
59
+ validMessageBox = {
60
+ messageBoxId: 42,
61
+ type: 'payment_inbox'
62
+ };
63
+ validReq = {
64
+ auth: {
65
+ identityKey: 'mockIdKey'
66
+ },
67
+ body: {
68
+ message: {
69
+ messageId: 'mock-message-id',
70
+ recipient: '028d37b941208cd6b8a4c28288eda5f2f16c2b3ab0fcb6d13c18b47fe37b971fc1',
71
+ messageBox: 'payment_inbox',
72
+ body: JSON.stringify({})
73
+ }
74
+ },
75
+ get: jest.fn(),
76
+ header: jest.fn()
77
+ };
78
+ });
79
+ afterEach(() => {
80
+ jest.clearAllMocks();
81
+ if (queryTracker !== null && queryTracker !== undefined) {
82
+ queryTracker.uninstall();
83
+ }
84
+ axiosMock?.restore();
85
+ });
86
+ afterAll(() => {
87
+ mockKnex.unmock(knex);
88
+ });
89
+ it('Throws an error if message is missing', async () => {
90
+ validReq.body = {}; // Ensure body exists, but message is missing
91
+ await sendMessage.func(validReq, mockRes);
92
+ expect(mockRes.status).toHaveBeenCalledWith(400);
93
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
94
+ status: 'error',
95
+ code: 'ERR_MESSAGE_REQUIRED',
96
+ description: 'Please provide a valid message to send!'
97
+ }));
98
+ });
99
+ it('Throws an error if message is not an object', async () => {
100
+ validReq.body.message = 'My message to send';
101
+ await sendMessage.func(validReq, mockRes);
102
+ expect(mockRes.status).toHaveBeenCalledWith(400);
103
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
104
+ status: 'error',
105
+ code: 'ERR_INVALID_MESSAGEBOX',
106
+ description: 'Invalid message box.'
107
+ }));
108
+ });
109
+ it('Throws an error if recipient is missing', async () => {
110
+ if (validReq.body.message !== null && validReq.body.message !== undefined) {
111
+ validReq.body.message.recipient = undefined;
112
+ }
113
+ await sendMessage.func(validReq, mockRes);
114
+ expect(mockRes.status).toHaveBeenCalledWith(400);
115
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
116
+ status: 'error',
117
+ code: 'ERR_RECIPIENT_REQUIRED',
118
+ description: 'Missing recipient(s). Provide "recipient" or "recipients".'
119
+ }));
120
+ });
121
+ it('Throws an error if recipient is not a string', async () => {
122
+ if (validReq.body.message !== null && validReq.body.message !== undefined) {
123
+ validReq.body.message.recipient = 123;
124
+ }
125
+ await sendMessage.func(validReq, mockRes);
126
+ expect(mockRes.status).toHaveBeenCalledWith(400);
127
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
128
+ status: 'error',
129
+ code: 'ERR_INVALID_RECIPIENT_KEY'
130
+ }));
131
+ });
132
+ it('Returns error if messageBox is missing', async () => {
133
+ if (validReq.body.message !== null && validReq.body.message !== undefined) {
134
+ validReq.body.message.messageBox = undefined;
135
+ }
136
+ await sendMessage.func(validReq, mockRes);
137
+ expect(mockRes.status).toHaveBeenCalledWith(400);
138
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
139
+ status: 'error',
140
+ code: 'ERR_INVALID_MESSAGEBOX'
141
+ }));
142
+ });
143
+ it('Throws an error if messageBox is not a string', async () => {
144
+ if (validReq.body.message !== null && validReq.body.message !== undefined) {
145
+ validReq.body.message.messageBox = 123;
146
+ }
147
+ await sendMessage.func(validReq, mockRes);
148
+ expect(mockRes.status).toHaveBeenCalledWith(400);
149
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
150
+ status: 'error',
151
+ code: 'ERR_INVALID_MESSAGEBOX',
152
+ description: 'Invalid message box.'
153
+ }));
154
+ });
155
+ it('Throws an error if the message body is not a string', async () => {
156
+ if (validReq.body.message !== null && validReq.body.message !== undefined) {
157
+ validReq.body.message.body = 42;
158
+ }
159
+ await sendMessage.func(validReq, mockRes);
160
+ expect(mockRes.status).toHaveBeenCalledWith(400);
161
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
162
+ status: 'error',
163
+ code: 'ERR_INVALID_MESSAGE_BODY',
164
+ description: 'Invalid message body.'
165
+ }));
166
+ });
167
+ it('Returns error if message body is missing', async () => {
168
+ if (validReq.body.message !== null && validReq.body.message !== undefined) {
169
+ validReq.body.message.body = undefined;
170
+ }
171
+ await sendMessage.func(validReq, mockRes);
172
+ expect(mockRes.status).toHaveBeenCalledWith(400);
173
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
174
+ status: 'error',
175
+ code: 'ERR_INVALID_MESSAGE_BODY',
176
+ description: 'Invalid message body.'
177
+ }));
178
+ });
179
+ it('Returns base price for an empty message', () => {
180
+ const price = calculateMessagePrice('');
181
+ expect(price).toBe(2); // Base price only
182
+ });
183
+ it('Calculates price for a small message (below 1KB)', () => {
184
+ const price = calculateMessagePrice('Hello, world!');
185
+ expect(price).toBe(5); // Base price only
186
+ });
187
+ it('Calculates price for a 2KB message', () => {
188
+ const message = 'a'.repeat(2048); // 2KB
189
+ const price = calculateMessagePrice(message);
190
+ expect(price).toBe(8); // Base price + 2KB
191
+ });
192
+ it('Adds priority fee when enabled', () => {
193
+ const price = calculateMessagePrice('Hello', true);
194
+ expect(price).toBe(5); // Base price
195
+ });
196
+ it('Handles large messages (5KB)', () => {
197
+ const message = 'a'.repeat(5120); // 5KB
198
+ const price = calculateMessagePrice(message);
199
+ expect(price).toBe(17); // Base price + 5KB
200
+ });
201
+ it('Handles edge case of exactly 1KB message', () => {
202
+ const message = 'a'.repeat(1024); // Exactly 1KB
203
+ const price = calculateMessagePrice(message);
204
+ expect(price).toBe(5); // Base price + 1KB
205
+ });
206
+ it('Handles edge case of 1KB + 1 byte message', () => {
207
+ const message = 'a'.repeat(1025); // 1KB + 1 byte
208
+ const price = calculateMessagePrice(message);
209
+ expect(price).toBe(8); // Rounded up to 2KB
210
+ });
211
+ it('Handles messages larger than 10KB', () => {
212
+ const message = 'a'.repeat(10240); // 10KB
213
+ const price = calculateMessagePrice(message);
214
+ expect(price).toBe(32); // Base price + 10KB
215
+ });
216
+ it('Returns error if messageId is missing', async () => {
217
+ if (validReq.body.message !== undefined && validReq.body.message !== null) {
218
+ validReq.body.message.messageId = undefined;
219
+ }
220
+ await sendMessage.func(validReq, mockRes);
221
+ expect(mockRes.status).toHaveBeenCalledWith(400);
222
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
223
+ status: 'error',
224
+ code: 'ERR_MESSAGEID_REQUIRED',
225
+ description: 'Missing messageId.'
226
+ }));
227
+ });
228
+ it('Creates a messageBox when it does not exist', async () => {
229
+ queryTracker.on('query', (q, step) => {
230
+ if (step === 1) {
231
+ q.response(0); // Simulate that the messageBox does not exist
232
+ }
233
+ else if (step === 2) {
234
+ q.response([validMessageBox]); // Simulate messageBox being inserted
235
+ }
236
+ else if (step === 3) {
237
+ q.response([validMessageBox]); // Simulate finding a valid messageBoxId
238
+ }
239
+ else {
240
+ q.response([]); // Default response
241
+ }
242
+ });
243
+ await sendMessage.func(validReq, mockRes);
244
+ expect(mockRes.status).toHaveBeenCalledWith(200);
245
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
246
+ status: 'success'
247
+ }));
248
+ });
249
+ it('Silently ignores duplicate messages via onConflict().ignore()', async () => {
250
+ queryTracker.on('query', (q, step) => {
251
+ if (step === 1) {
252
+ q.response({ messageBoxId: 42, type: 'payment_inbox' }); // messageBox exists
253
+ }
254
+ else if (step === 2) {
255
+ q.response({ messageBoxId: 42 }); // get messageBoxId for insert
256
+ }
257
+ else if (step === 3) {
258
+ q.response(0); // insert with onConflict().ignore() returns 0 rows affected
259
+ }
260
+ else {
261
+ q.response([]);
262
+ }
263
+ });
264
+ await sendMessage.func(validReq, mockRes);
265
+ expect(mockRes.status).toHaveBeenCalledWith(200);
266
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
267
+ status: 'success'
268
+ }));
269
+ });
270
+ it('Returns internal error if unexpected error occurs', async () => {
271
+ jest.spyOn(console, 'error').mockImplementation(() => { });
272
+ jest.spyOn(console, 'log').mockImplementation(() => { });
273
+ queryTracker.on('query', () => {
274
+ throw new Error('Unexpected failure'); // Simulating an unexpected database failure
275
+ });
276
+ await sendMessage.func(validReq, mockRes);
277
+ // Ensure the response status is set
278
+ expect(mockRes.status).toHaveBeenCalledTimes(1);
279
+ expect(mockRes.status).toHaveBeenCalledWith(500);
280
+ // Ensure the response body is set
281
+ expect(mockRes.json).toHaveBeenCalledTimes(1);
282
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
283
+ status: 'error',
284
+ code: 'ERR_INTERNAL'
285
+ }));
286
+ });
287
+ it('creates a new messageBox when one does not exist for recipient', async () => {
288
+ queryTracker.on('query', (q, step) => {
289
+ if (step === 1)
290
+ q.response(undefined); // messageBox not found
291
+ else if (step === 2)
292
+ q.response(1); // messageBox insert
293
+ else if (step === 3)
294
+ q.response({ messageBoxId: 42 }); // get messageBoxId for message insert
295
+ else if (step === 4)
296
+ q.response(1); // insert message
297
+ else
298
+ q.response([]);
299
+ });
300
+ await sendMessage.func(validReq, mockRes);
301
+ expect(mockRes.status).toHaveBeenCalledWith(200);
302
+ expect(mockRes.json).toHaveBeenCalledWith(expect.objectContaining({
303
+ status: 'success'
304
+ }));
305
+ });
306
+ });
307
+ //# sourceMappingURL=sendMessage.test.js.map