@5minds/node-red-contrib-processcube-tools 1.0.1 → 1.0.2-develop-e3d5d9-mfm8oea8

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.
@@ -0,0 +1,570 @@
1
+ const { expect } = require('chai');
2
+ const {
3
+ createMockNodeRED,
4
+ setupModuleMocks,
5
+ emailSenderConfigs,
6
+ createMockNodemailer,
7
+ } = require('../helpers/email-sender.mocks.js');
8
+
9
+ describe('E-Mail Sender Node - Unit Tests', function () {
10
+ this.timeout(10000);
11
+
12
+ let emailSenderNode;
13
+ let cleanupMocks;
14
+
15
+ before(function () {
16
+ // Set up module mocks using helper
17
+ cleanupMocks = setupModuleMocks();
18
+
19
+ // Load the node with mocked dependencies
20
+ emailSenderNode = require('../../email-sender/email-sender.js');
21
+ });
22
+
23
+ after(function () {
24
+ // Clean up mocks
25
+ if (cleanupMocks) {
26
+ cleanupMocks();
27
+ }
28
+ });
29
+
30
+ // A separate describe block for module export
31
+ describe('Module Export', function () {
32
+ it('should export a function', function () {
33
+ expect(emailSenderNode).to.be.a('function');
34
+ });
35
+ });
36
+
37
+ describe('Node Registration', function () {
38
+ it('should register node type without errors', function () {
39
+ // ARRANGE: Create mock Node-RED with tracking
40
+ const mockRED = createMockNodeRED();
41
+
42
+ // ACT: Register the node
43
+ emailSenderNode(mockRED);
44
+
45
+ // ASSERT: Verify registration
46
+ expect(mockRED.nodes.lastRegisteredType).to.equal('email-sender');
47
+ expect(mockRED.nodes.lastRegisteredConstructor).to.be.a('function');
48
+ });
49
+ });
50
+
51
+ describe('Node Instantiation', function () {
52
+ it('should handle node instantiation with valid config', function () {
53
+ // ARRANGE: Track node creation
54
+ let createdNode = null;
55
+ const mockRED = createMockNodeRED({
56
+ onHandler: function (event, callback) {
57
+ createdNode = this;
58
+ },
59
+ });
60
+
61
+ // ACT: Register and create node instance
62
+ emailSenderNode(mockRED);
63
+ new mockRED.nodes.lastRegisteredConstructor(emailSenderConfigs.valid);
64
+
65
+ // ASSERT: Verify node was created with correct properties
66
+ expect(createdNode).to.exist;
67
+ expect(createdNode).to.have.property('name', emailSenderConfigs.valid.name);
68
+ expect(createdNode).to.have.property('id', emailSenderConfigs.valid.id);
69
+ });
70
+
71
+ it('should handle minimal config', function () {
72
+ // ARRANGE: Use minimal test config
73
+ let createdNode = null;
74
+ const mockRED = createMockNodeRED({
75
+ onHandler: function (event, callback) {
76
+ createdNode = this;
77
+ },
78
+ });
79
+
80
+ // ACT: Register and create node with minimal config
81
+ emailSenderNode(mockRED);
82
+ new mockRED.nodes.lastRegisteredConstructor(emailSenderConfigs.minimal);
83
+
84
+ // ASSERT: Verify node creation
85
+ expect(createdNode).to.exist;
86
+ expect(createdNode).to.have.property('id', emailSenderConfigs.minimal.id);
87
+ });
88
+ });
89
+
90
+ describe('Node Functionality', function () {
91
+ beforeEach(function () {
92
+ // Clear the module cache BEFORE requiring anything
93
+ delete require.cache[require.resolve('nodemailer')];
94
+ });
95
+
96
+ afterEach(function () {
97
+ delete require.cache[require.resolve('nodemailer')];
98
+ });
99
+
100
+ it('should send email successfully and set status to "sent"', function (done) {
101
+ let statusSet = false;
102
+
103
+ // ARRANGE: Initialize mockNodemailer
104
+ const mockNodemailer = createMockNodemailer();
105
+
106
+ // Mock the nodemailer module
107
+ delete require.cache[require.resolve('nodemailer')];
108
+ require.cache[require.resolve('nodemailer')] = {
109
+ exports: mockNodemailer,
110
+ };
111
+
112
+ // ARRANGE: Create mock Node-RED environment
113
+ const mockRED = createMockNodeRED({
114
+ onHandler: function (event, callback) {
115
+ if (event === 'input') {
116
+ this.inputCallback = callback;
117
+ }
118
+ },
119
+ statusHandler: function (status) {
120
+ if (status.fill === 'green') {
121
+ expect(status.text).to.include('sent');
122
+ expect(status.shape).to.equal('dot');
123
+ statusSet = true;
124
+ done();
125
+ }
126
+ },
127
+ errorHandler: function (err) {
128
+ done(err || new Error('Unexpected error handler called'));
129
+ },
130
+ });
131
+
132
+ // ACT: Initialize the email sender node
133
+ const emailSenderNode = require('../../email-sender/email-sender.js');
134
+ emailSenderNode(mockRED);
135
+
136
+ const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
137
+ const nodeInstance = new nodeConstructor(emailSenderConfigs.valid);
138
+
139
+ nodeInstance.inputCallback({
140
+ payload: 'test',
141
+ topic: 'test message',
142
+ });
143
+ });
144
+
145
+ it('should handle sendMail error and set status to "error sending"', function (done) {
146
+ let errorHandlerCalled = false;
147
+ let redStatusSet = false;
148
+
149
+ function checkDone() {
150
+ if (errorHandlerCalled && redStatusSet) {
151
+ done();
152
+ }
153
+ }
154
+
155
+ // Explicitly set shouldFail to true
156
+ const mockOptions = { shouldFail: true };
157
+ const mockNodemailer = createMockNodemailer(mockOptions);
158
+
159
+ // Mock the nodemailer module
160
+ delete require.cache[require.resolve('nodemailer')];
161
+ require.cache[require.resolve('nodemailer')] = {
162
+ exports: mockNodemailer,
163
+ };
164
+
165
+ const mockRED = createMockNodeRED({
166
+ onHandler: function (event, callback) {
167
+ if (event === 'input') {
168
+ this.inputCallback = callback;
169
+ }
170
+ },
171
+ statusHandler: function (status) {
172
+ if (status.fill === 'red' && status.text === 'error sending') {
173
+ redStatusSet = true;
174
+ checkDone();
175
+ }
176
+ },
177
+ errorHandler: function (err) {
178
+ expect(err.message).to.equal('Mock sendMail error');
179
+ errorHandlerCalled = true;
180
+ checkDone();
181
+ },
182
+ });
183
+
184
+ const emailSenderNode = require('../../email-sender/email-sender.js');
185
+ emailSenderNode(mockRED);
186
+
187
+ const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
188
+ const nodeInstance = new nodeConstructor(emailSenderConfigs.valid);
189
+
190
+ nodeInstance.inputCallback({
191
+ payload: 'test',
192
+ topic: 'test message',
193
+ });
194
+ });
195
+
196
+ it('should handle an array of attachments correctly', function (done) {
197
+ let attachmentsChecked = false;
198
+ let statusSet = false;
199
+
200
+ function checkDone() {
201
+ if (attachmentsChecked && statusSet) {
202
+ done();
203
+ }
204
+ }
205
+
206
+ // ARRANGE: Configure test attachments
207
+ const attachments = [
208
+ {
209
+ filename: 'test1.txt',
210
+ content: 'This is the first test file.',
211
+ },
212
+ {
213
+ filename: 'test2.txt',
214
+ content: 'This is the second test file.',
215
+ },
216
+ ];
217
+
218
+ const mockNodemailer = createMockNodemailer({
219
+ onSendMail: (mailOptions) => {
220
+ expect(mailOptions.attachments).to.be.an('array').with.lengthOf(2);
221
+ expect(mailOptions.attachments[0].filename).to.equal('test1.txt');
222
+ expect(mailOptions.attachments[1].content).to.equal('This is the second test file.');
223
+ attachmentsChecked = true;
224
+ checkDone();
225
+ },
226
+ });
227
+
228
+ // Mock the nodemailer module
229
+ delete require.cache[require.resolve('nodemailer')];
230
+ require.cache[require.resolve('nodemailer')] = {
231
+ exports: mockNodemailer,
232
+ };
233
+
234
+ const mockRED = createMockNodeRED({
235
+ onHandler: function (event, callback) {
236
+ if (event === 'input') {
237
+ this.inputCallback = callback;
238
+ }
239
+ },
240
+ statusHandler: function (status) {
241
+ if (status.fill === 'green') {
242
+ expect(status.text).to.include('sent');
243
+ expect(status.shape).to.equal('dot');
244
+ statusSet = true;
245
+ checkDone();
246
+ }
247
+ },
248
+ errorHandler: function (err) {
249
+ done(err || new Error('Unexpected error handler called'));
250
+ },
251
+ });
252
+
253
+ const emailSenderNode = require('../../email-sender/email-sender.js');
254
+ emailSenderNode(mockRED);
255
+
256
+ const config = { ...emailSenderConfigs.valid };
257
+ config.attachments = JSON.stringify(attachments);
258
+ config.attachmentsType = 'json';
259
+
260
+ const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
261
+ const nodeInstance = new nodeConstructor(config);
262
+
263
+ setTimeout(() => {
264
+ nodeInstance.inputCallback({
265
+ payload: 'test',
266
+ topic: 'test message',
267
+ });
268
+ }, 100);
269
+ });
270
+
271
+ it('should throw error for malformed attachments', function (done) {
272
+ let errorHandlerCalled = false;
273
+ let redStatusSet = false;
274
+
275
+ function checkDone() {
276
+ if (errorHandlerCalled && redStatusSet) {
277
+ done();
278
+ }
279
+ }
280
+
281
+ // ARRANGE: Configure the node with a JSON string containing a malformed attachment
282
+ const malformedAttachments = [
283
+ {
284
+ filename: 'test.txt',
285
+ content: 'This is a test file.',
286
+ },
287
+ {
288
+ // Malformed attachment with missing content
289
+ filename: 'invalid.txt',
290
+ },
291
+ ];
292
+
293
+ const mockNodemailer = createMockNodemailer();
294
+
295
+ // Mock the nodemailer module
296
+ delete require.cache[require.resolve('nodemailer')];
297
+ require.cache[require.resolve('nodemailer')] = {
298
+ exports: mockNodemailer,
299
+ };
300
+
301
+ const mockRED = createMockNodeRED({
302
+ onHandler: function (event, callback) {
303
+ if (event === 'input') {
304
+ this.inputCallback = callback;
305
+ }
306
+ },
307
+ statusHandler: function (status) {
308
+ if (status.fill === 'red') {
309
+ redStatusSet = true;
310
+ checkDone();
311
+ }
312
+ },
313
+ errorHandler: function (err) {
314
+ expect(err).to.equal("Attachment object is missing 'filename' or 'content' property.");
315
+ errorHandlerCalled = true;
316
+ checkDone();
317
+ },
318
+ });
319
+
320
+ const emailSenderNode = require('../../email-sender/email-sender.js');
321
+ emailSenderNode(mockRED);
322
+
323
+ const config = { ...emailSenderConfigs.valid };
324
+ config.attachments = JSON.stringify(malformedAttachments);
325
+ config.attachmentsType = 'json';
326
+
327
+ const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
328
+ const nodeInstance = new nodeConstructor(config);
329
+
330
+ nodeInstance.inputCallback({
331
+ payload: 'test',
332
+ topic: 'test message',
333
+ });
334
+ });
335
+
336
+ it('should handle rejected emails and set status to rejected', function (done) {
337
+ let errorHandlerCalled = false;
338
+ let redStatusSet = false;
339
+
340
+ function checkDone() {
341
+ if (errorHandlerCalled && redStatusSet) {
342
+ done();
343
+ }
344
+ }
345
+
346
+ // ARRANGE: Configure mock to simulate rejected emails
347
+ const mockOptions = {
348
+ rejectedEmails: ['recipient@example.com'],
349
+ acceptedEmails: [], // Ensure no emails are accepted
350
+ };
351
+
352
+ const mockNodemailer = createMockNodemailer(mockOptions);
353
+
354
+ // Mock the nodemailer module
355
+ delete require.cache[require.resolve('nodemailer')];
356
+ require.cache[require.resolve('nodemailer')] = {
357
+ exports: mockNodemailer,
358
+ };
359
+
360
+ const mockRED = createMockNodeRED({
361
+ onHandler: function (event, callback) {
362
+ if (event === 'input') {
363
+ this.inputCallback = callback;
364
+ }
365
+ },
366
+ statusHandler: function (status) {
367
+ if (status.fill === 'red' && status.text === 'rejected') {
368
+ redStatusSet = true;
369
+ checkDone();
370
+ }
371
+ },
372
+ errorHandler: function (err) {
373
+ expect(err.message).to.include('Email rejected: recipient@example.com');
374
+ errorHandlerCalled = true;
375
+ checkDone();
376
+ },
377
+ });
378
+
379
+ const emailSenderNode = require('../../email-sender/email-sender.js');
380
+ emailSenderNode(mockRED);
381
+
382
+ const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
383
+ const nodeInstance = new nodeConstructor(emailSenderConfigs.valid);
384
+
385
+ nodeInstance.inputCallback({
386
+ payload: 'test',
387
+ topic: 'test message',
388
+ });
389
+ });
390
+
391
+ it('should handle pending emails and set status to pending', function (done) {
392
+ let errorHandlerCalled = false;
393
+ let yellowStatusSet = false;
394
+
395
+ function checkDone() {
396
+ if (errorHandlerCalled && yellowStatusSet) {
397
+ done();
398
+ }
399
+ }
400
+
401
+ // ARRANGE: Configure mock to simulate pending emails
402
+ const mockOptions = {
403
+ pendingEmails: ['recipient@example.com'],
404
+ acceptedEmails: [], // Ensure no emails are accepted
405
+ };
406
+
407
+ const mockNodemailer = createMockNodemailer(mockOptions);
408
+
409
+ // Mock the nodemailer module
410
+ delete require.cache[require.resolve('nodemailer')];
411
+ require.cache[require.resolve('nodemailer')] = {
412
+ exports: mockNodemailer,
413
+ };
414
+
415
+ const mockRED = createMockNodeRED({
416
+ onHandler: function (event, callback) {
417
+ if (event === 'input') {
418
+ this.inputCallback = callback;
419
+ }
420
+ },
421
+ statusHandler: function (status) {
422
+ if (status.fill === 'yellow' && status.text === 'pending') {
423
+ yellowStatusSet = true;
424
+ checkDone();
425
+ }
426
+ },
427
+ errorHandler: function (err) {
428
+ expect(err.message).to.include('Email pending: recipient@example.com');
429
+ errorHandlerCalled = true;
430
+ checkDone();
431
+ },
432
+ });
433
+
434
+ const emailSenderNode = require('../../email-sender/email-sender.js');
435
+ emailSenderNode(mockRED);
436
+
437
+ const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
438
+ const nodeInstance = new nodeConstructor(emailSenderConfigs.valid);
439
+
440
+ nodeInstance.inputCallback({
441
+ payload: 'test',
442
+ topic: 'test message',
443
+ });
444
+ });
445
+
446
+ it('should handle a single attachment object correctly', function (done) {
447
+ let attachmentChecked = false;
448
+ let statusSet = false;
449
+
450
+ function checkDone() {
451
+ if (attachmentChecked && statusSet) {
452
+ done();
453
+ }
454
+ }
455
+
456
+ // ARRANGE: Configure test with single attachment object
457
+ const singleAttachment = {
458
+ filename: 'single-test.txt',
459
+ content: 'This is a single test file.',
460
+ };
461
+
462
+ const mockNodemailer = createMockNodemailer({
463
+ onSendMail: (mailOptions) => {
464
+ expect(mailOptions.attachments).to.be.an('array').with.lengthOf(1);
465
+ expect(mailOptions.attachments[0].filename).to.equal('single-test.txt');
466
+ expect(mailOptions.attachments[0].content).to.equal('This is a single test file.');
467
+ attachmentChecked = true;
468
+ checkDone();
469
+ },
470
+ });
471
+
472
+ // Mock the nodemailer module
473
+ delete require.cache[require.resolve('nodemailer')];
474
+ require.cache[require.resolve('nodemailer')] = {
475
+ exports: mockNodemailer,
476
+ };
477
+
478
+ const mockRED = createMockNodeRED({
479
+ onHandler: function (event, callback) {
480
+ if (event === 'input') {
481
+ this.inputCallback = callback;
482
+ }
483
+ },
484
+ statusHandler: function (status) {
485
+ if (status.fill === 'green') {
486
+ expect(status.text).to.include('sent');
487
+ expect(status.shape).to.equal('dot');
488
+ statusSet = true;
489
+ checkDone();
490
+ }
491
+ },
492
+ errorHandler: function (err) {
493
+ done(err || new Error('Unexpected error handler called'));
494
+ },
495
+ });
496
+
497
+ const emailSenderNode = require('../../email-sender/email-sender.js');
498
+ emailSenderNode(mockRED);
499
+
500
+ const config = { ...emailSenderConfigs.valid };
501
+ config.attachments = JSON.stringify(singleAttachment);
502
+ config.attachmentsType = 'json';
503
+
504
+ const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
505
+ const nodeInstance = new nodeConstructor(config);
506
+
507
+ setTimeout(() => {
508
+ nodeInstance.inputCallback({
509
+ payload: 'test',
510
+ topic: 'test message',
511
+ });
512
+ }, 100);
513
+ });
514
+
515
+ it('should handle an empty attachments string without error', function (done) {
516
+ let statusSet = false;
517
+
518
+ // ARRANGE: Create mock nodemailer to verify no attachments are processed
519
+ const mockNodemailer = createMockNodemailer({
520
+ onSendMail: (mailOptions) => {
521
+ // Should be an empty array when no attachments are provided
522
+ expect(mailOptions.attachments).to.be.an('array').with.lengthOf(0);
523
+ },
524
+ });
525
+
526
+ // Mock the nodemailer module
527
+ delete require.cache[require.resolve('nodemailer')];
528
+ require.cache[require.resolve('nodemailer')] = {
529
+ exports: mockNodemailer,
530
+ };
531
+
532
+ const mockRED = createMockNodeRED({
533
+ onHandler: function (event, callback) {
534
+ if (event === 'input') {
535
+ this.inputCallback = callback;
536
+ }
537
+ },
538
+ statusHandler: function (status) {
539
+ if (status.fill === 'green') {
540
+ expect(status.text).to.include('sent');
541
+ expect(status.shape).to.equal('dot');
542
+ statusSet = true;
543
+ done();
544
+ }
545
+ },
546
+ errorHandler: function (err) {
547
+ done(err || new Error('Unexpected error handler called'));
548
+ },
549
+ });
550
+
551
+ const emailSenderNode = require('../../email-sender/email-sender.js');
552
+ emailSenderNode(mockRED);
553
+
554
+ const config = { ...emailSenderConfigs.valid };
555
+ // Set attachments to empty string to test this scenario
556
+ config.attachments = '';
557
+ config.attachmentsType = 'str';
558
+
559
+ const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
560
+ const nodeInstance = new nodeConstructor(config);
561
+
562
+ setTimeout(() => {
563
+ nodeInstance.inputCallback({
564
+ payload: 'test',
565
+ topic: 'test message',
566
+ });
567
+ }, 100);
568
+ });
569
+ });
570
+ });