@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,368 @@
1
+ // Helper functions and mocks for email-sender tests
2
+ const { EventEmitter } = require('events');
3
+
4
+ /**
5
+ * Create mock Node-RED object for unit testing
6
+ */
7
+ function createMockNodeRED(options = {}) {
8
+ const mockRED = {
9
+ nodes: {
10
+ createNode: function (node, config) {
11
+ nodeInstance = node; // Capture the node instance
12
+
13
+ // Apply config properties to node
14
+ Object.assign(node, {
15
+ id: config.id || 'mock-node-id',
16
+ type: config.type || 'email-sender',
17
+ name: config.name || 'Mock Node',
18
+ on: function (event, callback) {
19
+ if (event === 'input') {
20
+ storedInputCallback = callback;
21
+ // Store the callback on the node instance for easy access
22
+ node.inputCallback = callback;
23
+ }
24
+ // Call the original onHandler if provided
25
+ if (options.onHandler) {
26
+ options.onHandler.call(node, event, callback);
27
+ }
28
+ },
29
+ status: options.statusHandler || function () {},
30
+ error: options.errorHandler || function () {},
31
+ send: options.sendHandler || function () {},
32
+ log: options.logHandler || function () {},
33
+ warn: options.warnHandler || function () {},
34
+ debug: options.debugHandler || function () {},
35
+ });
36
+ return node;
37
+ },
38
+ registerType: function (type, constructor) {
39
+ // Store registration for verification in tests
40
+ this.lastRegisteredType = type;
41
+ this.lastRegisteredConstructor = constructor;
42
+ },
43
+ // Helper method to get the stored input callback
44
+ getInputCallback: function () {
45
+ return storedInputCallback;
46
+ },
47
+ // Helper method to get the node instance
48
+ getNodeInstance: function () {
49
+ return nodeInstance;
50
+ },
51
+ },
52
+ util: {
53
+ evaluateNodeProperty: function (value, type, node, msg, callback) {
54
+ if (type === 'json') {
55
+ try {
56
+ // Simulate parsing a JSON string into an object
57
+ return JSON.parse(JSON.stringify(value));
58
+ } catch (e) {
59
+ if (callback) {
60
+ callback(e, null);
61
+ }
62
+ return null;
63
+ }
64
+ }
65
+
66
+ // Simple mock implementation
67
+ if (callback) {
68
+ callback(null, value);
69
+ }
70
+ return value;
71
+ },
72
+ encrypt: function (value) {
73
+ return 'encrypted:' + value;
74
+ },
75
+ decrypt: function (value) {
76
+ return value.replace('encrypted:', '');
77
+ },
78
+ },
79
+ log: {
80
+ info: options.logInfo || function () {},
81
+ warn: options.logWarn || function () {},
82
+ error: options.logError || function () {},
83
+ debug: options.logDebug || function () {},
84
+ },
85
+ };
86
+
87
+ return mockRED;
88
+ }
89
+
90
+ function createMockNodemailer(options = {}) {
91
+ const settings = Object.assign(
92
+ {
93
+ shouldFail: false,
94
+ // New options for different email statuses
95
+ rejectedEmails: [], // Array of emails to mark as rejected
96
+ pendingEmails: [], // Array of emails to mark as pending
97
+ acceptedEmails: [], // Array of emails to mark as accepted (overrides default)
98
+ },
99
+ options,
100
+ );
101
+
102
+ return {
103
+ createTransport: () => ({
104
+ sendMail: (mailOptions, callback) => {
105
+ if (settings.onSendMail) {
106
+ settings.onSendMail(mailOptions);
107
+ }
108
+
109
+ if (settings.shouldFail === true) {
110
+ const error = new Error('Mock sendMail error');
111
+ error.code = 'ECONNREFUSED';
112
+ return callback(error);
113
+ }
114
+
115
+ // Determine email status based on configuration
116
+ const toEmail = Array.isArray(mailOptions.to) ? mailOptions.to[0] : mailOptions.to;
117
+ let accepted = [];
118
+ let rejected = [];
119
+ let pending = [];
120
+
121
+ if (
122
+ settings.rejectedEmails.length > 0 ||
123
+ settings.pendingEmails.length > 0 ||
124
+ settings.acceptedEmails.length > 0
125
+ ) {
126
+ // Use explicit configuration
127
+ if (settings.rejectedEmails.includes(toEmail)) {
128
+ rejected = [toEmail];
129
+ } else if (settings.pendingEmails.includes(toEmail)) {
130
+ pending = [toEmail];
131
+ } else if (settings.acceptedEmails.includes(toEmail)) {
132
+ accepted = [toEmail];
133
+ } else {
134
+ // Default behavior - accept if not explicitly configured
135
+ accepted = [toEmail];
136
+ }
137
+ } else {
138
+ // Original behavior - accept all emails (backwards compatibility)
139
+ accepted = [mailOptions.to];
140
+ }
141
+
142
+ // Set appropriate response message based on status
143
+ let responseMessage = '250 OK: Message accepted';
144
+ if (rejected.length > 0) {
145
+ responseMessage = '550 Mailbox unavailable';
146
+ } else if (pending.length > 0) {
147
+ responseMessage = '451 Requested action aborted: local error';
148
+ }
149
+
150
+ callback(null, {
151
+ messageId: '<mock-message-id@test.com>',
152
+ response: responseMessage,
153
+ accepted: accepted,
154
+ rejected: rejected,
155
+ pending: pending,
156
+ });
157
+ },
158
+ }),
159
+ restore: function () {
160
+ // Cleanup method
161
+ },
162
+ };
163
+ }
164
+
165
+ // Aktualisiere setupModuleMocks um die neue Implementation zu nutzen
166
+ function setupModuleMocks() {
167
+ const mockNodemailerModule = createMockNodemailer();
168
+
169
+ delete require.cache[require.resolve('nodemailer')];
170
+ require.cache[require.resolve('nodemailer')] = {
171
+ exports: mockNodemailerModule,
172
+ };
173
+
174
+ return function cleanup() {
175
+ delete require.cache[require.resolve('nodemailer')];
176
+ if (mockNodemailerModule.restore) {
177
+ mockNodemailerModule.restore();
178
+ }
179
+ };
180
+ }
181
+
182
+ // Custom mock node
183
+ function getMockNode() {
184
+ // Create an EventEmitter instance to get the .on and .emit methods
185
+ const mock = Object.assign(new EventEmitter(), {
186
+ status: () => {},
187
+ error: () => {},
188
+ warn: () => {},
189
+ log: () => {},
190
+ send: () => {},
191
+ });
192
+
193
+ mock.status = (...args) => {
194
+ mock.status.called = true;
195
+ mock.status.args = args;
196
+ };
197
+ mock.status.called = false;
198
+ mock.status.args = [];
199
+ mock.status.calledWith = (expectedArgs) => {
200
+ return mock.status.called && JSON.stringify(mock.status.args) === JSON.stringify(expectedArgs);
201
+ };
202
+
203
+ mock.send = (...args) => {
204
+ mock.send.called = true;
205
+ mock.send.args = args;
206
+ mock.send.callCount++;
207
+ };
208
+ mock.send.called = false;
209
+ mock.send.args = [];
210
+ mock.send.callCount = 0;
211
+ mock.send.calledWith = (expectedArgs) => {
212
+ return mock.send.called && JSON.stringify(mock.send.args) === JSON.stringify([expectedArgs]);
213
+ };
214
+
215
+ return mock;
216
+ }
217
+
218
+ /**
219
+ * Test configurations for the email sender node.
220
+ */
221
+ const emailSenderConfigs = {
222
+ valid: {
223
+ id: 'test-node-6',
224
+ type: 'email-sender',
225
+ name: 'Test Email Sender',
226
+ sender: 'Test Sender',
227
+ senderType: 'str',
228
+ address: 'test.sender@example.com',
229
+ addressType: 'str',
230
+ to: 'recipient@example.com',
231
+ toType: 'str',
232
+ cc: '',
233
+ ccType: 'str',
234
+ bcc: '',
235
+ bccType: 'str',
236
+ subject: 'Test Subject',
237
+ subjectType: 'str',
238
+ htmlContent: '<b>Hello World</b>',
239
+ htmlContentType: 'str',
240
+ attachments: '',
241
+ attachmentsType: 'str',
242
+ host: 'smtp.example.com',
243
+ hostType: 'str',
244
+ port: 587,
245
+ portType: 'num',
246
+ user: 'user',
247
+ userType: 'str',
248
+ password: 'password',
249
+ passwordType: 'str',
250
+ secure: false,
251
+ secureType: 'bool',
252
+ rejectUnauthorized: true,
253
+ rejectUnauthorizedType: 'bool',
254
+ },
255
+
256
+ invalid: {
257
+ id: 'test-node-7',
258
+ type: 'email-sender',
259
+ name: 'Invalid Email Sender',
260
+ sender: '', // Missing sender
261
+ senderType: 'str',
262
+ address: 'test.sender@example.com',
263
+ addressType: 'str',
264
+ to: 'recipient@example.com',
265
+ toType: 'str',
266
+ subject: 'Invalid Test Subject',
267
+ subjectType: 'str',
268
+ htmlContent: 'Invalid Test Content',
269
+ htmlContentType: 'str',
270
+ host: '', // Missing host
271
+ hostType: 'str',
272
+ port: 587,
273
+ portType: 'str', // Incorrect type
274
+ user: 'user',
275
+ userType: 'str',
276
+ password: '', // Missing password
277
+ passwordType: 'str',
278
+ },
279
+
280
+ minimal: {
281
+ id: 'test-node-8',
282
+ type: 'email-sender',
283
+ name: 'Minimal Email Sender',
284
+ to: 'recipient@example.com',
285
+ toType: 'str',
286
+ subject: 'Minimal Subject',
287
+ subjectType: 'str',
288
+ htmlContent: 'Minimal content.',
289
+ htmlContentType: 'str',
290
+ host: 'smtp.minimal.com',
291
+ hostType: 'str',
292
+ port: 587,
293
+ portType: 'num',
294
+ user: 'minimal-user',
295
+ userType: 'str',
296
+ password: 'minimal-password',
297
+ passwordType: 'str',
298
+ },
299
+ };
300
+
301
+ /**
302
+ * Create test flows for Node-RED integration tests
303
+ */
304
+ const testFlows = {
305
+ single: [emailSenderConfigs.valid],
306
+
307
+ withHelper: [emailSenderConfigs.valid, { id: 'h1', type: 'helper' }],
308
+
309
+ connected: [
310
+ { ...emailSenderConfigs.valid, wires: [['h1']] },
311
+ { id: 'h1', type: 'helper' },
312
+ ],
313
+
314
+ multiOutput: [
315
+ { ...emailSenderConfigs.valid, wires: [['h1', 'h2']] },
316
+ { id: 'h1', type: 'helper' },
317
+ { id: 'h2', type: 'helper' },
318
+ ],
319
+ };
320
+
321
+ /**
322
+ * Utility functions for test assertions and email simulation
323
+ */
324
+ const testUtils = {
325
+ /**
326
+ * Wait for a specified amount of time
327
+ */
328
+ wait: (ms = 100) => new Promise((resolve) => setTimeout(resolve, ms)),
329
+
330
+ /**
331
+ * Create a promise that resolves when a node receives a message
332
+ */
333
+ waitForMessage: (node, timeout = 1000) => {
334
+ return new Promise((resolve, reject) => {
335
+ const timer = setTimeout(() => {
336
+ reject(new Error('Timeout waiting for message'));
337
+ }, timeout);
338
+
339
+ node.on('input', (msg) => {
340
+ clearTimeout(timer);
341
+ resolve(msg);
342
+ });
343
+ });
344
+ },
345
+ /**
346
+ * Verify that a message has expected properties
347
+ */
348
+ verifyMessage: (msg, expectedProps = {}) => {
349
+ const should = require('should');
350
+ should.exist(msg);
351
+
352
+ Object.keys(expectedProps).forEach((prop) => {
353
+ if (expectedProps[prop] !== undefined) {
354
+ msg.should.have.property(prop, expectedProps[prop]);
355
+ }
356
+ });
357
+ },
358
+ };
359
+
360
+ module.exports = {
361
+ createMockNodeRED,
362
+ setupModuleMocks,
363
+ getMockNode,
364
+ emailSenderConfigs,
365
+ createMockNodemailer,
366
+ testFlows,
367
+ testUtils,
368
+ };