@5minds/node-red-contrib-processcube-tools 1.0.1-feature-df88cf-mfdx610k → 1.0.1-feature-050c1a-mfe18hnk
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/package.json
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
const should = require('should');
|
|
2
2
|
const helper = require('node-red-node-test-helper');
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
const {
|
|
4
|
+
createMockImap,
|
|
5
|
+
createMockMailparser,
|
|
6
|
+
createMockNodeRED,
|
|
7
|
+
setupModuleMocks,
|
|
8
|
+
testConfigs,
|
|
9
|
+
testFlows,
|
|
10
|
+
testUtils
|
|
11
|
+
} = require('../helpers/email-receiver.mocks.js');
|
|
12
|
+
|
|
13
|
+
describe('Email Receiver Node - Integration Tests with Helpers', function() {
|
|
5
14
|
// Set a reasonable timeout for integration tests
|
|
6
15
|
this.timeout(10000);
|
|
7
16
|
|
|
8
17
|
let emailReceiverNode;
|
|
9
|
-
let
|
|
18
|
+
let cleanupMocks;
|
|
10
19
|
|
|
11
20
|
before(function(done) {
|
|
12
|
-
// Set up mocks
|
|
13
|
-
|
|
21
|
+
// Set up mocks using helper
|
|
22
|
+
cleanupMocks = setupModuleMocks();
|
|
14
23
|
|
|
15
24
|
// Load the node with mocked dependencies
|
|
16
25
|
emailReceiverNode = require('../../email-receiver/email-receiver.js');
|
|
@@ -21,10 +30,9 @@ describe('Email Receiver Node - Integration Tests', function() {
|
|
|
21
30
|
});
|
|
22
31
|
|
|
23
32
|
after(function() {
|
|
24
|
-
//
|
|
25
|
-
if (
|
|
26
|
-
|
|
27
|
-
Module._load = originalLoad;
|
|
33
|
+
// Clean up mocks using helper cleanup function
|
|
34
|
+
if (cleanupMocks) {
|
|
35
|
+
cleanupMocks();
|
|
28
36
|
}
|
|
29
37
|
});
|
|
30
38
|
|
|
@@ -37,105 +45,18 @@ describe('Email Receiver Node - Integration Tests', function() {
|
|
|
37
45
|
helper.stopServer(done);
|
|
38
46
|
});
|
|
39
47
|
|
|
40
|
-
function setupMocks() {
|
|
41
|
-
// Create mock IMAP module
|
|
42
|
-
const mockImap = function(config) {
|
|
43
|
-
this.config = config;
|
|
44
|
-
this.connect = () => {
|
|
45
|
-
// Simulate a successful connection by immediately emitting 'ready'
|
|
46
|
-
if (this.events && this.events.ready) {
|
|
47
|
-
this.events.ready();
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
this.openBox = (folder, readOnly, callback) => {
|
|
51
|
-
callback(null, { messages: { total: 1 } });
|
|
52
|
-
};
|
|
53
|
-
this.search = (criteria, callback) => {
|
|
54
|
-
callback(null, [123]);
|
|
55
|
-
};
|
|
56
|
-
this.fetch = (results, options) => {
|
|
57
|
-
return {
|
|
58
|
-
on: (event, cb) => {
|
|
59
|
-
if (event === 'message') {
|
|
60
|
-
cb({ on: (e, bodyCb) => { if (e === 'body') bodyCb({}); } });
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
once: (event, cb) => {
|
|
64
|
-
if (event === 'end') { cb(); }
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
};
|
|
68
|
-
this.end = () => {};
|
|
69
|
-
this.once = (event, callback) => {
|
|
70
|
-
if (!this.events) this.events = {};
|
|
71
|
-
this.events[event] = callback;
|
|
72
|
-
};
|
|
73
|
-
return this;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
// Create mock mailparser module
|
|
77
|
-
const mockMailparser = {
|
|
78
|
-
simpleParser: function() {
|
|
79
|
-
return Promise.resolve({
|
|
80
|
-
subject: 'test integration email',
|
|
81
|
-
text: 'test integration body',
|
|
82
|
-
html: '<p>test integration</p>',
|
|
83
|
-
from: { text: 'integration@test.com' },
|
|
84
|
-
date: new Date(),
|
|
85
|
-
headers: new Map(),
|
|
86
|
-
attachments: []
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const mockModules = {
|
|
92
|
-
'node-imap': mockImap,
|
|
93
|
-
'mailparser': mockMailparser
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
// Override require to use mocks
|
|
97
|
-
const Module = require('module');
|
|
98
|
-
originalLoad = Module._load;
|
|
99
|
-
Module._load = function(request, parent) {
|
|
100
|
-
if (mockModules[request]) {
|
|
101
|
-
return mockModules[request];
|
|
102
|
-
}
|
|
103
|
-
return originalLoad.apply(this, arguments);
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
48
|
describe('Node Loading', function() {
|
|
108
49
|
it('should load in Node-RED test environment', function(done) {
|
|
109
|
-
// ARRANGE:
|
|
110
|
-
const flow = [
|
|
111
|
-
{
|
|
112
|
-
id: "n1",
|
|
113
|
-
type: "email-receiver",
|
|
114
|
-
name: "test node",
|
|
115
|
-
host: "imap.test.com",
|
|
116
|
-
hostType: "str",
|
|
117
|
-
port: "993",
|
|
118
|
-
portType: "str",
|
|
119
|
-
tls: true,
|
|
120
|
-
tlsType: "bool",
|
|
121
|
-
user: "test@example.com",
|
|
122
|
-
userType: "str",
|
|
123
|
-
password: "testpass",
|
|
124
|
-
passwordType: "str",
|
|
125
|
-
folder: "INBOX",
|
|
126
|
-
folderType: "str",
|
|
127
|
-
markseen: true,
|
|
128
|
-
markseenType: "bool"
|
|
129
|
-
}
|
|
130
|
-
];
|
|
50
|
+
// ARRANGE: Use test flow from helpers
|
|
51
|
+
const flow = [testConfigs.valid];
|
|
131
52
|
|
|
132
53
|
// ACT: Load the node in the test helper environment
|
|
133
54
|
helper.load(emailReceiverNode, flow, function() {
|
|
134
55
|
try {
|
|
135
56
|
// ASSERT: Verify the node loaded correctly
|
|
136
|
-
const n1 = helper.getNode(
|
|
57
|
+
const n1 = helper.getNode(testConfigs.valid.id);
|
|
137
58
|
should.exist(n1);
|
|
138
|
-
n1.should.have.property('name',
|
|
59
|
+
n1.should.have.property('name', testConfigs.valid.name);
|
|
139
60
|
n1.should.have.property('type', 'email-receiver');
|
|
140
61
|
done();
|
|
141
62
|
} catch (err) {
|
|
@@ -145,29 +66,14 @@ describe('Email Receiver Node - Integration Tests', function() {
|
|
|
145
66
|
});
|
|
146
67
|
|
|
147
68
|
it('should load with minimal configuration', function(done) {
|
|
148
|
-
// ARRANGE:
|
|
149
|
-
const flow = [
|
|
150
|
-
{
|
|
151
|
-
id: "n1",
|
|
152
|
-
type: "email-receiver",
|
|
153
|
-
host: "imap.minimal.com",
|
|
154
|
-
hostType: "str",
|
|
155
|
-
port: "993",
|
|
156
|
-
portType: "str",
|
|
157
|
-
user: "minimal@test.com",
|
|
158
|
-
userType: "str",
|
|
159
|
-
password: "minimalpass",
|
|
160
|
-
passwordType: "str",
|
|
161
|
-
folder: "INBOX",
|
|
162
|
-
folderType: "str"
|
|
163
|
-
}
|
|
164
|
-
];
|
|
69
|
+
// ARRANGE: Use minimal test config from helpers
|
|
70
|
+
const flow = [testConfigs.minimal];
|
|
165
71
|
|
|
166
72
|
// ACT: Load the node
|
|
167
73
|
helper.load(emailReceiverNode, flow, function() {
|
|
168
74
|
try {
|
|
169
75
|
// ASSERT: Verify the node loaded with minimal config
|
|
170
|
-
const n1 = helper.getNode(
|
|
76
|
+
const n1 = helper.getNode(testConfigs.minimal.id);
|
|
171
77
|
should.exist(n1);
|
|
172
78
|
n1.should.have.property('type', 'email-receiver');
|
|
173
79
|
done();
|
|
@@ -176,46 +82,60 @@ describe('Email Receiver Node - Integration Tests', function() {
|
|
|
176
82
|
}
|
|
177
83
|
});
|
|
178
84
|
});
|
|
85
|
+
|
|
86
|
+
it('should load with string folders configuration', function(done) {
|
|
87
|
+
// ARRANGE: Use string folders config from helpers
|
|
88
|
+
const flow = [testConfigs.stringFolders];
|
|
89
|
+
|
|
90
|
+
// ACT: Load the node
|
|
91
|
+
helper.load(emailReceiverNode, flow, function() {
|
|
92
|
+
try {
|
|
93
|
+
// ASSERT: Verify the node loaded with string folders
|
|
94
|
+
const n1 = helper.getNode(testConfigs.stringFolders.id);
|
|
95
|
+
should.exist(n1);
|
|
96
|
+
n1.should.have.property('name', testConfigs.stringFolders.name);
|
|
97
|
+
done();
|
|
98
|
+
} catch (err) {
|
|
99
|
+
done(err);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should load with array folders configuration', function(done) {
|
|
105
|
+
// ARRANGE: Use array folders config from helpers
|
|
106
|
+
const flow = [testConfigs.arrayFolders];
|
|
107
|
+
|
|
108
|
+
// ACT: Load the node
|
|
109
|
+
helper.load(emailReceiverNode, flow, function() {
|
|
110
|
+
try {
|
|
111
|
+
// ASSERT: Verify the node loaded with array folders
|
|
112
|
+
const n1 = helper.getNode(testConfigs.arrayFolders.id);
|
|
113
|
+
should.exist(n1);
|
|
114
|
+
n1.should.have.property('name', testConfigs.arrayFolders.name);
|
|
115
|
+
done();
|
|
116
|
+
} catch (err) {
|
|
117
|
+
done(err);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
});
|
|
179
121
|
});
|
|
180
122
|
|
|
181
123
|
describe('Node Connections', function() {
|
|
182
124
|
it('should create wired connections correctly', function(done) {
|
|
183
|
-
// ARRANGE:
|
|
184
|
-
const flow =
|
|
185
|
-
{
|
|
186
|
-
id: "n1",
|
|
187
|
-
type: "email-receiver",
|
|
188
|
-
name: "test node",
|
|
189
|
-
host: "imap.test.com",
|
|
190
|
-
hostType: "str",
|
|
191
|
-
port: "993",
|
|
192
|
-
portType: "str",
|
|
193
|
-
tls: true,
|
|
194
|
-
tlsType: "bool",
|
|
195
|
-
user: "test@example.com",
|
|
196
|
-
userType: "str",
|
|
197
|
-
password: "testpass",
|
|
198
|
-
passwordType: "str",
|
|
199
|
-
folder: "INBOX",
|
|
200
|
-
folderType: "str",
|
|
201
|
-
markseen: true,
|
|
202
|
-
markseenType: "bool",
|
|
203
|
-
wires: [["n2"]]
|
|
204
|
-
},
|
|
205
|
-
{ id: "n2", type: "helper" }
|
|
206
|
-
];
|
|
125
|
+
// ARRANGE: Use connected test flow from helpers
|
|
126
|
+
const flow = testFlows.connected;
|
|
207
127
|
|
|
208
128
|
// ACT: Load nodes and verify connections
|
|
209
129
|
helper.load(emailReceiverNode, flow, function() {
|
|
210
130
|
try {
|
|
211
|
-
const n1 = helper.getNode(
|
|
212
|
-
const
|
|
131
|
+
const n1 = helper.getNode(testConfigs.valid.id);
|
|
132
|
+
const h1 = helper.getNode('h1');
|
|
213
133
|
|
|
214
134
|
// ASSERT: Both nodes should exist and be connected
|
|
215
135
|
should.exist(n1);
|
|
216
|
-
should.exist(
|
|
217
|
-
n1.should.have.property('name',
|
|
218
|
-
|
|
136
|
+
should.exist(h1);
|
|
137
|
+
n1.should.have.property('name', testConfigs.valid.name);
|
|
138
|
+
h1.should.have.property('type', 'helper');
|
|
219
139
|
|
|
220
140
|
done();
|
|
221
141
|
} catch (err) {
|
|
@@ -225,42 +145,21 @@ describe('Email Receiver Node - Integration Tests', function() {
|
|
|
225
145
|
});
|
|
226
146
|
|
|
227
147
|
it('should handle multiple output connections', function(done) {
|
|
228
|
-
// ARRANGE:
|
|
229
|
-
const flow =
|
|
230
|
-
{
|
|
231
|
-
id: "n1",
|
|
232
|
-
type: "email-receiver",
|
|
233
|
-
name: "multi-output node",
|
|
234
|
-
host: "imap.test.com",
|
|
235
|
-
hostType: "str",
|
|
236
|
-
port: "993",
|
|
237
|
-
portType: "str",
|
|
238
|
-
user: "test@example.com",
|
|
239
|
-
userType: "str",
|
|
240
|
-
password: "testpass",
|
|
241
|
-
passwordType: "str",
|
|
242
|
-
folder: "INBOX",
|
|
243
|
-
folderType: "str",
|
|
244
|
-
markseen: true,
|
|
245
|
-
markseenType: "bool",
|
|
246
|
-
wires: [["n2", "n3"]]
|
|
247
|
-
},
|
|
248
|
-
{ id: "n2", type: "helper" },
|
|
249
|
-
{ id: "n3", type: "helper" }
|
|
250
|
-
];
|
|
148
|
+
// ARRANGE: Use multi-output test flow from helpers
|
|
149
|
+
const flow = testFlows.multiOutput;
|
|
251
150
|
|
|
252
151
|
// ACT: Load nodes
|
|
253
152
|
helper.load(emailReceiverNode, flow, function() {
|
|
254
153
|
try {
|
|
255
|
-
const n1 = helper.getNode(
|
|
256
|
-
const
|
|
257
|
-
const
|
|
154
|
+
const n1 = helper.getNode(testConfigs.valid.id);
|
|
155
|
+
const h1 = helper.getNode('h1');
|
|
156
|
+
const h2 = helper.getNode('h2');
|
|
258
157
|
|
|
259
158
|
// ASSERT: All nodes should exist
|
|
260
159
|
should.exist(n1);
|
|
261
|
-
should.exist(
|
|
262
|
-
should.exist(
|
|
263
|
-
n1.should.have.property('name',
|
|
160
|
+
should.exist(h1);
|
|
161
|
+
should.exist(h2);
|
|
162
|
+
n1.should.have.property('name', testConfigs.valid.name);
|
|
264
163
|
|
|
265
164
|
done();
|
|
266
165
|
} catch (err) {
|
|
@@ -271,87 +170,53 @@ describe('Email Receiver Node - Integration Tests', function() {
|
|
|
271
170
|
});
|
|
272
171
|
|
|
273
172
|
describe('Message Flow', function() {
|
|
274
|
-
it('should handle input without crashing', function(
|
|
275
|
-
// ARRANGE:
|
|
276
|
-
const flow =
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
}
|
|
296
|
-
];
|
|
297
|
-
|
|
298
|
-
// ACT: Load node and send input
|
|
299
|
-
helper.load(emailReceiverNode, flow, function() {
|
|
300
|
-
try {
|
|
301
|
-
const n1 = helper.getNode("n1");
|
|
302
|
-
should.exist(n1);
|
|
303
|
-
|
|
304
|
-
// Send input - this should not crash due to mocked IMAP
|
|
305
|
-
n1.receive({ payload: "test input" });
|
|
306
|
-
|
|
307
|
-
// ASSERT: If we reach here, the node handled input gracefully
|
|
308
|
-
setTimeout(() => {
|
|
309
|
-
done(); // Success if no errors thrown
|
|
310
|
-
}, 500);
|
|
311
|
-
|
|
312
|
-
} catch (err) {
|
|
313
|
-
done(err);
|
|
314
|
-
}
|
|
173
|
+
it('should handle input without crashing', async function() {
|
|
174
|
+
// ARRANGE: Use test flow from helpers
|
|
175
|
+
const flow = testFlows.single;
|
|
176
|
+
|
|
177
|
+
return new Promise((resolve, reject) => {
|
|
178
|
+
helper.load(emailReceiverNode, flow, function() {
|
|
179
|
+
try {
|
|
180
|
+
const n1 = helper.getNode(testConfigs.valid.id);
|
|
181
|
+
should.exist(n1);
|
|
182
|
+
|
|
183
|
+
// Send input - this should not crash due to mocked IMAP
|
|
184
|
+
n1.receive({ payload: "test input" });
|
|
185
|
+
|
|
186
|
+
// ASSERT: If we reach here, the node handled input gracefully
|
|
187
|
+
testUtils.wait(500).then(() => {
|
|
188
|
+
resolve(); // Success if no errors thrown
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
} catch (err) {
|
|
192
|
+
reject(err);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
315
195
|
});
|
|
316
196
|
});
|
|
317
197
|
|
|
318
198
|
it('should process messages through connected nodes', function(done) {
|
|
319
|
-
// ARRANGE:
|
|
320
|
-
const flow =
|
|
321
|
-
{
|
|
322
|
-
id: "n1",
|
|
323
|
-
type: "email-receiver",
|
|
324
|
-
name: "sender node",
|
|
325
|
-
host: "imap.test.com",
|
|
326
|
-
hostType: "str",
|
|
327
|
-
port: "993",
|
|
328
|
-
portType: "str",
|
|
329
|
-
user: "test@example.com",
|
|
330
|
-
userType: "str",
|
|
331
|
-
password: "testpass",
|
|
332
|
-
passwordType: "str",
|
|
333
|
-
folder: "INBOX",
|
|
334
|
-
folderType: "str",
|
|
335
|
-
markseen: true,
|
|
336
|
-
markseenType: "bool",
|
|
337
|
-
wires: [["n2"]]
|
|
338
|
-
},
|
|
339
|
-
{ id: "n2", type: "helper" }
|
|
340
|
-
];
|
|
199
|
+
// ARRANGE: Use connected test flow from helpers
|
|
200
|
+
const flow = testFlows.connected;
|
|
341
201
|
|
|
342
202
|
// ACT: Load nodes and set up message listener
|
|
343
203
|
helper.load(emailReceiverNode, flow, function() {
|
|
344
204
|
try {
|
|
345
|
-
const n1 = helper.getNode(
|
|
346
|
-
const
|
|
205
|
+
const n1 = helper.getNode(testConfigs.valid.id);
|
|
206
|
+
const h1 = helper.getNode('h1');
|
|
347
207
|
|
|
348
208
|
// Set up listener for messages from email receiver
|
|
349
|
-
|
|
209
|
+
h1.on("input", function(msg) {
|
|
350
210
|
try {
|
|
351
211
|
// ASSERT: Should receive a message with expected properties
|
|
352
212
|
should.exist(msg);
|
|
353
213
|
should.exist(msg.payload);
|
|
354
|
-
|
|
214
|
+
|
|
215
|
+
// Use helper utility to verify message
|
|
216
|
+
testUtils.verifyMessage(msg, {
|
|
217
|
+
payload: 'This is a mock email body for testing purposes.'
|
|
218
|
+
});
|
|
219
|
+
|
|
355
220
|
done();
|
|
356
221
|
} catch (err) {
|
|
357
222
|
done(err);
|
|
@@ -366,33 +231,50 @@ describe('Email Receiver Node - Integration Tests', function() {
|
|
|
366
231
|
}
|
|
367
232
|
});
|
|
368
233
|
});
|
|
234
|
+
|
|
235
|
+
it('should handle message timeout gracefully', async function() {
|
|
236
|
+
// ARRANGE: Use connected test flow
|
|
237
|
+
const flow = testFlows.connected;
|
|
238
|
+
|
|
239
|
+
return new Promise((resolve, reject) => {
|
|
240
|
+
helper.load(emailReceiverNode, flow, function() {
|
|
241
|
+
try {
|
|
242
|
+
const n1 = helper.getNode(testConfigs.valid.id);
|
|
243
|
+
const h1 = helper.getNode('h1');
|
|
244
|
+
|
|
245
|
+
// Use testUtils.waitForMessage with timeout
|
|
246
|
+
testUtils.waitForMessage(h1, 1000)
|
|
247
|
+
.then((msg) => {
|
|
248
|
+
// ASSERT: Should receive message within timeout
|
|
249
|
+
should.exist(msg);
|
|
250
|
+
resolve();
|
|
251
|
+
})
|
|
252
|
+
.catch((err) => {
|
|
253
|
+
// ASSERT: Should handle timeout appropriately
|
|
254
|
+
err.message.should.containEql('Timeout waiting for message');
|
|
255
|
+
resolve(); // This is expected behavior for this test
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Trigger the node but don't send a message (to test timeout)
|
|
259
|
+
// n1.receive({ payload: "trigger" }); // Commented out to test timeout
|
|
260
|
+
|
|
261
|
+
} catch (err) {
|
|
262
|
+
reject(err);
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
});
|
|
369
267
|
});
|
|
370
268
|
|
|
371
269
|
describe('Configuration Validation', function() {
|
|
372
270
|
it('should handle invalid configuration gracefully', function(done) {
|
|
373
|
-
// ARRANGE:
|
|
374
|
-
const flow = [
|
|
375
|
-
{
|
|
376
|
-
id: "n1",
|
|
377
|
-
type: "email-receiver",
|
|
378
|
-
name: "invalid config node",
|
|
379
|
-
host: "", // Missing host
|
|
380
|
-
hostType: "str",
|
|
381
|
-
port: "993",
|
|
382
|
-
portType: "str",
|
|
383
|
-
user: "test@example.com",
|
|
384
|
-
userType: "str",
|
|
385
|
-
password: "testpass",
|
|
386
|
-
passwordType: "str",
|
|
387
|
-
folder: "INBOX",
|
|
388
|
-
folderType: "str"
|
|
389
|
-
}
|
|
390
|
-
];
|
|
271
|
+
// ARRANGE: Use invalid config from helpers
|
|
272
|
+
const flow = [testConfigs.invalidConfig];
|
|
391
273
|
|
|
392
274
|
// ACT: Load node with invalid config
|
|
393
275
|
helper.load(emailReceiverNode, flow, function() {
|
|
394
276
|
try {
|
|
395
|
-
const n1 = helper.getNode(
|
|
277
|
+
const n1 = helper.getNode(testConfigs.invalidConfig.id);
|
|
396
278
|
should.exist(n1);
|
|
397
279
|
|
|
398
280
|
// ASSERT: Node should exist but handle invalid config appropriately
|
|
@@ -400,9 +282,9 @@ describe('Email Receiver Node - Integration Tests', function() {
|
|
|
400
282
|
n1.receive({ payload: "test" });
|
|
401
283
|
|
|
402
284
|
// If we get here without crashing, the validation worked
|
|
403
|
-
|
|
285
|
+
testUtils.wait(300).then(() => {
|
|
404
286
|
done();
|
|
405
|
-
}
|
|
287
|
+
});
|
|
406
288
|
|
|
407
289
|
} catch (err) {
|
|
408
290
|
done(err);
|
|
@@ -410,78 +292,183 @@ describe('Email Receiver Node - Integration Tests', function() {
|
|
|
410
292
|
});
|
|
411
293
|
});
|
|
412
294
|
|
|
413
|
-
it('should
|
|
414
|
-
// ARRANGE:
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
type: "email-receiver",
|
|
419
|
-
name: "array folder node",
|
|
420
|
-
host: "imap.test.com",
|
|
421
|
-
hostType: "str",
|
|
422
|
-
port: "993",
|
|
423
|
-
portType: "str",
|
|
424
|
-
user: "test@example.com",
|
|
425
|
-
userType: "str",
|
|
426
|
-
password: "testpass",
|
|
427
|
-
passwordType: "str",
|
|
428
|
-
folder: ["INBOX", "Sent", "Drafts"],
|
|
429
|
-
folderType: "json",
|
|
430
|
-
markseen: true,
|
|
431
|
-
markseenType: "bool"
|
|
432
|
-
}
|
|
295
|
+
it('should validate folder configurations properly', async function() {
|
|
296
|
+
// ARRANGE: Test different folder configurations
|
|
297
|
+
const folderConfigs = [
|
|
298
|
+
testConfigs.stringFolders,
|
|
299
|
+
testConfigs.arrayFolders
|
|
433
300
|
];
|
|
434
301
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
const n1 = helper.getNode("n1");
|
|
302
|
+
for (const config of folderConfigs) {
|
|
303
|
+
await new Promise((resolve, reject) => {
|
|
304
|
+
const flow = [config];
|
|
439
305
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
306
|
+
helper.load(emailReceiverNode, flow, function() {
|
|
307
|
+
try {
|
|
308
|
+
const n1 = helper.getNode(config.id);
|
|
309
|
+
|
|
310
|
+
// ASSERT: Node should load successfully with different folder configs
|
|
311
|
+
should.exist(n1);
|
|
312
|
+
n1.should.have.property('name', config.name);
|
|
313
|
+
|
|
314
|
+
helper.unload();
|
|
315
|
+
resolve();
|
|
316
|
+
} catch (err) {
|
|
317
|
+
reject(err);
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
describe('Mock Integration Verification', function() {
|
|
326
|
+
it('should work with createMockImap from helpers', function(done) {
|
|
327
|
+
// ARRANGE: Create IMAP mock instance directly
|
|
328
|
+
const mockImap = createMockImap();
|
|
329
|
+
const imapInstance = new mockImap({
|
|
330
|
+
host: testConfigs.valid.host,
|
|
331
|
+
port: testConfigs.valid.port,
|
|
332
|
+
secure: true
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// ACT: Test IMAP mock behavior
|
|
336
|
+
let readyFired = false;
|
|
337
|
+
imapInstance.once('ready', () => {
|
|
338
|
+
readyFired = true;
|
|
339
|
+
|
|
340
|
+
// Test openBox functionality
|
|
341
|
+
imapInstance.openBox('INBOX', false, (err, box) => {
|
|
342
|
+
should.not.exist(err);
|
|
343
|
+
should.exist(box);
|
|
344
|
+
box.should.have.property('messages');
|
|
345
|
+
|
|
346
|
+
// ASSERT: Mock IMAP should work as expected
|
|
347
|
+
readyFired.should.be.true();
|
|
443
348
|
done();
|
|
349
|
+
});
|
|
350
|
+
});
|
|
444
351
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
352
|
+
imapInstance.connect();
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('should work with createMockMailparser from helpers', async function() {
|
|
356
|
+
// ARRANGE: Create mailparser mock instance directly
|
|
357
|
+
const mockMailparser = createMockMailparser();
|
|
358
|
+
|
|
359
|
+
// ACT: Test mailparser mock behavior
|
|
360
|
+
const result = await mockMailparser.simpleParser('test content', {
|
|
361
|
+
subject: 'Integration Test Email',
|
|
362
|
+
from: 'integration@test.com'
|
|
448
363
|
});
|
|
364
|
+
|
|
365
|
+
// ASSERT: Mock mailparser should return expected structure
|
|
366
|
+
result.should.have.property('subject', 'Integration Test Email');
|
|
367
|
+
result.should.have.property('from');
|
|
368
|
+
result.from.should.have.property('text', 'integration@test.com');
|
|
369
|
+
result.should.have.property('headers');
|
|
370
|
+
result.headers.should.be.instanceOf(Map);
|
|
449
371
|
});
|
|
450
372
|
});
|
|
451
373
|
|
|
452
374
|
describe('Node Lifecycle', function() {
|
|
453
|
-
it('should clean up properly on unload', function(
|
|
454
|
-
// ARRANGE:
|
|
455
|
-
const flow =
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
375
|
+
it('should clean up properly on unload', async function() {
|
|
376
|
+
// ARRANGE: Use test flow from helpers
|
|
377
|
+
const flow = testFlows.single;
|
|
378
|
+
|
|
379
|
+
return new Promise((resolve, reject) => {
|
|
380
|
+
helper.load(emailReceiverNode, flow, function() {
|
|
381
|
+
try {
|
|
382
|
+
const n1 = helper.getNode(testConfigs.valid.id);
|
|
383
|
+
should.exist(n1);
|
|
384
|
+
|
|
385
|
+
// Simulate some activity
|
|
386
|
+
n1.receive({ payload: "test" });
|
|
387
|
+
|
|
388
|
+
// Wait a bit for any async operations
|
|
389
|
+
testUtils.wait(100).then(() => {
|
|
390
|
+
// ASSERT: Unloading should not throw errors
|
|
391
|
+
helper.unload();
|
|
392
|
+
resolve();
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
} catch (err) {
|
|
396
|
+
reject(err);
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it('should handle multiple load/unload cycles', async function() {
|
|
403
|
+
// ARRANGE: Test multiple cycles
|
|
404
|
+
const flow = testFlows.single;
|
|
405
|
+
const cycles = 3;
|
|
406
|
+
|
|
407
|
+
for (let i = 0; i < cycles; i++) {
|
|
408
|
+
await new Promise((resolve, reject) => {
|
|
409
|
+
helper.load(emailReceiverNode, flow, function() {
|
|
410
|
+
try {
|
|
411
|
+
const n1 = helper.getNode(testConfigs.valid.id);
|
|
412
|
+
should.exist(n1);
|
|
413
|
+
|
|
414
|
+
// Quick activity simulation
|
|
415
|
+
n1.receive({ payload: `test cycle ${i}` });
|
|
416
|
+
|
|
417
|
+
testUtils.wait(50).then(() => {
|
|
418
|
+
helper.unload();
|
|
419
|
+
resolve();
|
|
420
|
+
});
|
|
421
|
+
} catch (err) {
|
|
422
|
+
reject(err);
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// ASSERT: If we complete all cycles without error, lifecycle handling works
|
|
429
|
+
// This assertion is implicit in the successful completion of the loop
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
describe('Advanced Flow Testing', function() {
|
|
434
|
+
it('should handle complex message flows with multiple helpers', function(done) {
|
|
435
|
+
// ARRANGE: Use multi-output flow from helpers
|
|
436
|
+
const flow = testFlows.multiOutput;
|
|
437
|
+
let receivedMessages = [];
|
|
472
438
|
|
|
473
|
-
// ACT: Load and then unload the node
|
|
474
439
|
helper.load(emailReceiverNode, flow, function() {
|
|
475
440
|
try {
|
|
476
|
-
const n1 = helper.getNode(
|
|
477
|
-
|
|
441
|
+
const n1 = helper.getNode(testConfigs.valid.id);
|
|
442
|
+
const h1 = helper.getNode('h1');
|
|
443
|
+
const h2 = helper.getNode('h2');
|
|
444
|
+
|
|
445
|
+
// Set up listeners for both helper nodes
|
|
446
|
+
h1.on("input", function(msg) {
|
|
447
|
+
receivedMessages.push({ node: 'h1', msg: msg });
|
|
448
|
+
checkCompletion();
|
|
449
|
+
});
|
|
478
450
|
|
|
479
|
-
|
|
480
|
-
|
|
451
|
+
h2.on("input", function(msg) {
|
|
452
|
+
receivedMessages.push({ node: 'h2', msg: msg });
|
|
453
|
+
checkCompletion();
|
|
454
|
+
});
|
|
481
455
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
456
|
+
function checkCompletion() {
|
|
457
|
+
if (receivedMessages.length >= 2) {
|
|
458
|
+
// ASSERT: Both helpers should receive messages
|
|
459
|
+
receivedMessages.length.should.equal(2);
|
|
460
|
+
|
|
461
|
+
receivedMessages.forEach(item => {
|
|
462
|
+
should.exist(item.msg);
|
|
463
|
+
should.exist(item.msg.payload);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
done();
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Trigger the email receiver
|
|
471
|
+
n1.receive({ payload: "multi-trigger" });
|
|
485
472
|
|
|
486
473
|
} catch (err) {
|
|
487
474
|
done(err);
|
|
@@ -1,332 +1,279 @@
|
|
|
1
1
|
const should = require('should');
|
|
2
|
+
const {
|
|
3
|
+
createMockImap,
|
|
4
|
+
createMockMailparser,
|
|
5
|
+
createMockNodeRED,
|
|
6
|
+
setupModuleMocks,
|
|
7
|
+
testConfigs,
|
|
8
|
+
testUtils
|
|
9
|
+
} = require('../helpers/email-receiver.mocks.js');
|
|
2
10
|
|
|
3
|
-
describe('Email Receiver Node - Unit Tests', function() {
|
|
4
|
-
// Set a reasonable timeout
|
|
11
|
+
describe('Email Receiver Node - Unit Tests with Helpers', function() {
|
|
5
12
|
this.timeout(10000);
|
|
6
13
|
|
|
7
|
-
// Module and mocking setup
|
|
8
14
|
let emailReceiverNode;
|
|
9
|
-
let
|
|
10
|
-
let mockImap;
|
|
11
|
-
let mockMailparser;
|
|
15
|
+
let cleanupMocks;
|
|
12
16
|
|
|
13
17
|
before(function() {
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
this.config = config;
|
|
17
|
-
this.connect = () => {
|
|
18
|
-
// Simulate a successful connection by immediately emitting 'ready'
|
|
19
|
-
if (this.events && this.events.ready) {
|
|
20
|
-
this.events.ready();
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
this.openBox = (folder, readOnly, callback) => { callback(null, { messages: { total: 1 } }); };
|
|
24
|
-
this.search = (criteria, callback) => { callback(null, [123]); };
|
|
25
|
-
this.fetch = (results, options) => {
|
|
26
|
-
return {
|
|
27
|
-
on: (event, cb) => {
|
|
28
|
-
if (event === 'message') {
|
|
29
|
-
cb({ on: (e, bodyCb) => { if (e === 'body') bodyCb({}); } });
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
once: (event, cb) => {
|
|
33
|
-
if (event === 'end') { cb(); }
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
|
-
this.end = () => {};
|
|
38
|
-
this.once = (event, callback) => {
|
|
39
|
-
if (!this.events) this.events = {};
|
|
40
|
-
this.events[event] = callback;
|
|
41
|
-
};
|
|
42
|
-
return this;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
mockMailparser = {
|
|
46
|
-
simpleParser: function() {
|
|
47
|
-
return Promise.resolve({
|
|
48
|
-
subject: 'test',
|
|
49
|
-
text: 'test body',
|
|
50
|
-
html: '<p>test</p>',
|
|
51
|
-
from: { text: 'test@test.com' },
|
|
52
|
-
date: new Date(),
|
|
53
|
-
headers: new Map(),
|
|
54
|
-
attachments: []
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const mockModules = {
|
|
60
|
-
'node-imap': mockImap,
|
|
61
|
-
'mailparser': mockMailparser
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
// Override require
|
|
65
|
-
const Module = require('module');
|
|
66
|
-
originalLoad = Module._load;
|
|
67
|
-
Module._load = function(request, parent) {
|
|
68
|
-
if (mockModules[request]) {
|
|
69
|
-
return mockModules[request];
|
|
70
|
-
}
|
|
71
|
-
return originalLoad.apply(this, arguments);
|
|
72
|
-
};
|
|
18
|
+
// Set up module mocks using helper
|
|
19
|
+
cleanupMocks = setupModuleMocks();
|
|
73
20
|
|
|
74
21
|
// Load the node with mocked dependencies
|
|
75
22
|
emailReceiverNode = require('../../email-receiver/email-receiver.js');
|
|
76
23
|
});
|
|
77
24
|
|
|
78
25
|
after(function() {
|
|
79
|
-
//
|
|
80
|
-
if (
|
|
81
|
-
|
|
82
|
-
Module._load = originalLoad;
|
|
26
|
+
// Clean up mocks
|
|
27
|
+
if (cleanupMocks) {
|
|
28
|
+
cleanupMocks();
|
|
83
29
|
}
|
|
84
30
|
});
|
|
85
31
|
|
|
86
32
|
describe('Module Export', function() {
|
|
87
33
|
it('should export a function', function() {
|
|
88
|
-
// ARRANGE: Node module is already loaded
|
|
89
|
-
|
|
90
|
-
// ACT: Check the type of the exported module
|
|
91
|
-
|
|
92
|
-
// ASSERT: Should be a function
|
|
93
34
|
emailReceiverNode.should.be.type('function');
|
|
94
35
|
});
|
|
95
36
|
});
|
|
96
37
|
|
|
97
38
|
describe('Node Registration', function() {
|
|
98
39
|
it('should register node type without errors', function() {
|
|
99
|
-
// ARRANGE:
|
|
100
|
-
|
|
101
|
-
let registeredConstructor;
|
|
102
|
-
|
|
103
|
-
const mockRED = {
|
|
104
|
-
nodes: {
|
|
105
|
-
createNode: function(node, config) {
|
|
106
|
-
node.id = config.id;
|
|
107
|
-
node.type = config.type;
|
|
108
|
-
node.name = config.name;
|
|
109
|
-
node.on = function() {};
|
|
110
|
-
node.status = function() {};
|
|
111
|
-
node.error = function() {};
|
|
112
|
-
node.send = function() {};
|
|
113
|
-
return node;
|
|
114
|
-
},
|
|
115
|
-
registerType: function(type, constructor) {
|
|
116
|
-
registeredType = type;
|
|
117
|
-
registeredConstructor = constructor;
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
|
-
util: {
|
|
121
|
-
evaluateNodeProperty: function(value, type) {
|
|
122
|
-
return value;
|
|
123
|
-
},
|
|
124
|
-
encrypt: function(value) {
|
|
125
|
-
return 'encrypted:' + value;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
};
|
|
40
|
+
// ARRANGE: Create mock Node-RED with tracking
|
|
41
|
+
const mockRED = createMockNodeRED();
|
|
129
42
|
|
|
130
|
-
// ACT:
|
|
43
|
+
// ACT: Register the node
|
|
131
44
|
emailReceiverNode(mockRED);
|
|
132
45
|
|
|
133
|
-
// ASSERT: Verify registration
|
|
134
|
-
|
|
135
|
-
|
|
46
|
+
// ASSERT: Verify registration
|
|
47
|
+
mockRED.nodes.lastRegisteredType.should.equal('email-receiver');
|
|
48
|
+
mockRED.nodes.lastRegisteredConstructor.should.be.type('function');
|
|
136
49
|
});
|
|
137
50
|
});
|
|
138
51
|
|
|
139
52
|
describe('Node Instantiation', function() {
|
|
140
53
|
it('should handle node instantiation with valid config', function() {
|
|
141
|
-
// ARRANGE:
|
|
142
|
-
let
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
createNode: function(node, config) {
|
|
147
|
-
nodeInstance = node;
|
|
148
|
-
node.id = config.id;
|
|
149
|
-
node.type = config.type;
|
|
150
|
-
node.name = config.name;
|
|
151
|
-
node.on = function() {};
|
|
152
|
-
node.status = function() {};
|
|
153
|
-
node.error = function() {};
|
|
154
|
-
node.send = function() {};
|
|
155
|
-
return node;
|
|
156
|
-
},
|
|
157
|
-
registerType: function(type, NodeConstructor) {
|
|
158
|
-
// Simulate creating a node instance with valid config
|
|
159
|
-
const config = {
|
|
160
|
-
id: 'test-node',
|
|
161
|
-
type: 'email-receiver',
|
|
162
|
-
name: 'Test Email Receiver',
|
|
163
|
-
host: 'imap.test.com',
|
|
164
|
-
hostType: 'str',
|
|
165
|
-
port: 993,
|
|
166
|
-
portType: 'num',
|
|
167
|
-
user: 'test@test.com',
|
|
168
|
-
userType: 'str',
|
|
169
|
-
password: 'testpass',
|
|
170
|
-
passwordType: 'str',
|
|
171
|
-
folder: ["INBOX"],
|
|
172
|
-
folderType: 'json',
|
|
173
|
-
markseen: true,
|
|
174
|
-
markseenType: 'bool'
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
new NodeConstructor(config);
|
|
178
|
-
}
|
|
179
|
-
},
|
|
180
|
-
util: {
|
|
181
|
-
evaluateNodeProperty: function(value, type) {
|
|
182
|
-
return value;
|
|
183
|
-
},
|
|
184
|
-
encrypt: function(value) {
|
|
185
|
-
return 'encrypted:' + value;
|
|
186
|
-
}
|
|
54
|
+
// ARRANGE: Track node creation
|
|
55
|
+
let createdNode = null;
|
|
56
|
+
const mockRED = createMockNodeRED({
|
|
57
|
+
onHandler: function(event, callback) {
|
|
58
|
+
createdNode = this;
|
|
187
59
|
}
|
|
188
|
-
};
|
|
60
|
+
});
|
|
189
61
|
|
|
190
|
-
// ACT: Register
|
|
62
|
+
// ACT: Register and create node instance
|
|
191
63
|
emailReceiverNode(mockRED);
|
|
64
|
+
new mockRED.nodes.lastRegisteredConstructor(testConfigs.valid);
|
|
192
65
|
|
|
193
|
-
// ASSERT: Verify
|
|
194
|
-
should.exist(
|
|
195
|
-
|
|
66
|
+
// ASSERT: Verify node was created with correct properties
|
|
67
|
+
should.exist(createdNode);
|
|
68
|
+
createdNode.should.have.property('name', testConfigs.valid.name);
|
|
69
|
+
createdNode.should.have.property('id', testConfigs.valid.id);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should handle minimal config', function() {
|
|
73
|
+
// ARRANGE: Use minimal test config
|
|
74
|
+
let createdNode = null;
|
|
75
|
+
const mockRED = createMockNodeRED({
|
|
76
|
+
onHandler: function(event, callback) {
|
|
77
|
+
createdNode = this;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// ACT: Register and create node with minimal config
|
|
82
|
+
emailReceiverNode(mockRED);
|
|
83
|
+
new mockRED.nodes.lastRegisteredConstructor(testConfigs.minimal);
|
|
84
|
+
|
|
85
|
+
// ASSERT: Verify node creation
|
|
86
|
+
should.exist(createdNode);
|
|
87
|
+
createdNode.should.have.property('id', testConfigs.minimal.id);
|
|
196
88
|
});
|
|
197
89
|
});
|
|
198
90
|
|
|
199
91
|
describe('Folder Configuration', function() {
|
|
200
|
-
it('should handle
|
|
201
|
-
// ARRANGE:
|
|
202
|
-
let
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const mockRED = {
|
|
208
|
-
nodes: {
|
|
209
|
-
createNode: function(node, config) {
|
|
210
|
-
nodeInstance = node;
|
|
211
|
-
node.on = (event, callback) => { if (event === 'input') inputCallback = callback; };
|
|
212
|
-
node.status = () => {};
|
|
213
|
-
node.error = () => {};
|
|
214
|
-
node.send = (msg) => {
|
|
215
|
-
should.exist(msg);
|
|
216
|
-
msg.payload.should.equal('test body');
|
|
217
|
-
messagesSent++;
|
|
218
|
-
if (messagesSent === expectedMessages) {
|
|
219
|
-
done();
|
|
220
|
-
}
|
|
221
|
-
};
|
|
222
|
-
return node;
|
|
223
|
-
},
|
|
224
|
-
registerType: (type, constructor) => {
|
|
225
|
-
const mockNode = {
|
|
226
|
-
id: 'mock-node-id',
|
|
227
|
-
on: (event, callback) => {
|
|
228
|
-
if (event === 'input') {
|
|
229
|
-
inputCallback = callback;
|
|
230
|
-
}
|
|
231
|
-
},
|
|
232
|
-
status: () => {},
|
|
233
|
-
error: () => {},
|
|
234
|
-
send: (msg) => {
|
|
235
|
-
should.exist(msg);
|
|
236
|
-
msg.payload.should.equal('test body');
|
|
237
|
-
messagesSent++;
|
|
238
|
-
if (messagesSent === expectedMessages) {
|
|
239
|
-
done();
|
|
240
|
-
}
|
|
241
|
-
},
|
|
242
|
-
};
|
|
243
|
-
// Create an instance of the node with the test configuration
|
|
244
|
-
const node = new constructor(mockNode, {
|
|
245
|
-
host: "imap.test.com", hostType: "str",
|
|
246
|
-
port: 993, portType: "num",
|
|
247
|
-
user: "test@test.com", userType: "str",
|
|
248
|
-
password: "testpass", passwordType: "str",
|
|
249
|
-
folder: ["INBOX", "Junk"], folderType: 'json',
|
|
250
|
-
markseen: true, markseenType: 'bool'
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
},
|
|
254
|
-
util: { evaluateNodeProperty: (value) => value },
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
// ACT: Register the node, then simulate input
|
|
258
|
-
emailReceiverNode(mockRED);
|
|
259
|
-
// After the node is registered, simulate an input to trigger the flow
|
|
260
|
-
setTimeout(() => {
|
|
261
|
-
if (inputCallback) {
|
|
262
|
-
inputCallback({});
|
|
263
|
-
} else {
|
|
264
|
-
done(new Error("Input callback not set up."));
|
|
92
|
+
it('should handle array of folders', async function() {
|
|
93
|
+
// ARRANGE: Set up message tracking
|
|
94
|
+
let sentMessage = null;
|
|
95
|
+
const mockRED = createMockNodeRED({
|
|
96
|
+
sendHandler: function(msg) {
|
|
97
|
+
sentMessage = msg;
|
|
265
98
|
}
|
|
266
|
-
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// ACT: Register node and create instance with array folders
|
|
102
|
+
emailReceiverNode(mockRED);
|
|
103
|
+
const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
|
|
104
|
+
const nodeInstance = new nodeConstructor(testConfigs.arrayFolders);
|
|
105
|
+
|
|
106
|
+
// Wait for processing
|
|
107
|
+
await testUtils.wait(50);
|
|
108
|
+
|
|
109
|
+
// ASSERT: Should handle array folders without error
|
|
110
|
+
should.exist(nodeInstance);
|
|
111
|
+
nodeInstance.should.have.property('name', testConfigs.arrayFolders.name);
|
|
112
|
+
});
|
|
267
113
|
});
|
|
268
|
-
});
|
|
269
114
|
|
|
270
115
|
describe('Error Handling', function() {
|
|
271
116
|
it('should call node.error for invalid folder type', function(done) {
|
|
272
|
-
// ARRANGE:
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
status: () => {},
|
|
278
|
-
error: (err) => {
|
|
279
|
-
errorCalled = true;
|
|
280
|
-
err.should.containEql('The \'folders\' property must be an array of strings');
|
|
117
|
+
// ARRANGE: Set up error tracking
|
|
118
|
+
const mockRED = createMockNodeRED({
|
|
119
|
+
errorHandler: function(err) {
|
|
120
|
+
// ASSERT: Should receive appropriate error message
|
|
121
|
+
err.should.containEql("The 'folders' property must be an array of strings.");
|
|
281
122
|
done();
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
};
|
|
285
|
-
const mockRED = {
|
|
286
|
-
nodes: {
|
|
287
|
-
createNode: (node, config) => Object.assign(node, { on: nodeInstance.on, status: nodeInstance.status, error: nodeInstance.error, send: nodeInstance.send }),
|
|
288
|
-
registerType: (type, constructor) => new constructor(nodeInstance.config),
|
|
289
|
-
},
|
|
290
|
-
util: { evaluateNodeProperty: (value, type) => value },
|
|
291
|
-
};
|
|
123
|
+
}
|
|
124
|
+
});
|
|
292
125
|
|
|
293
|
-
// ACT: Register and
|
|
126
|
+
// ACT: Register node and create instance with invalid config
|
|
294
127
|
emailReceiverNode(mockRED);
|
|
295
|
-
|
|
128
|
+
const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
|
|
129
|
+
new nodeConstructor(testConfigs.invalidConfig);
|
|
296
130
|
});
|
|
297
131
|
|
|
298
132
|
it('should call node.error for missing config', function(done) {
|
|
299
|
-
// ARRANGE:
|
|
300
|
-
let errorCalled = false;
|
|
133
|
+
// ARRANGE: Set up error and status tracking
|
|
301
134
|
let statusCalled = false;
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
folder: ["INBOX"], folderType: "json"
|
|
135
|
+
const mockRED = createMockNodeRED({
|
|
136
|
+
statusHandler: function(status) {
|
|
137
|
+
statusCalled = true;
|
|
138
|
+
if (status.fill) {
|
|
139
|
+
status.fill.should.equal('red');
|
|
140
|
+
}
|
|
309
141
|
},
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
error: (err) => {
|
|
313
|
-
errorCalled = true;
|
|
142
|
+
errorHandler: function(err) {
|
|
143
|
+
// ASSERT: Should receive config error
|
|
314
144
|
err.should.containEql('Missing required IMAP config');
|
|
145
|
+
statusCalled.should.be.true();
|
|
146
|
+
done();
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// ACT: Register node and create instance with invalid config
|
|
151
|
+
emailReceiverNode(mockRED);
|
|
152
|
+
const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
|
|
153
|
+
new nodeConstructor(testConfigs.invalidConfig);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe('Message Processing', function() {
|
|
158
|
+
it('should process email message correctly', async function() {
|
|
159
|
+
// ARRANGE: Set up message capture
|
|
160
|
+
let processedMessage = null;
|
|
161
|
+
const mockRED = createMockNodeRED({
|
|
162
|
+
sendHandler: function(msg) {
|
|
163
|
+
processedMessage = msg;
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// ACT: Create node and simulate email processing
|
|
168
|
+
emailReceiverNode(mockRED);
|
|
169
|
+
const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
|
|
170
|
+
const nodeInstance = new nodeConstructor(testConfigs.valid);
|
|
171
|
+
|
|
172
|
+
// Simulate input trigger (this would depend on your node's implementation)
|
|
173
|
+
// The actual trigger mechanism would need to match your node's design
|
|
174
|
+
|
|
175
|
+
await testUtils.wait(100);
|
|
176
|
+
|
|
177
|
+
// ASSERT: Message processing behavior would be verified here
|
|
178
|
+
// The specific assertions depend on your node's output format
|
|
179
|
+
should.exist(nodeInstance);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should handle multiple messages', async function() {
|
|
183
|
+
// ARRANGE: Set up message collection
|
|
184
|
+
const messages = [];
|
|
185
|
+
const mockRED = createMockNodeRED({
|
|
186
|
+
sendHandler: function(msg) {
|
|
187
|
+
messages.push(msg);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// ACT: Create node and simulate multiple emails
|
|
192
|
+
emailReceiverNode(mockRED);
|
|
193
|
+
const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
|
|
194
|
+
const nodeInstance = new nodeConstructor(testConfigs.valid);
|
|
195
|
+
|
|
196
|
+
await testUtils.wait(150);
|
|
197
|
+
|
|
198
|
+
// ASSERT: Should handle multiple messages appropriately
|
|
199
|
+
should.exist(nodeInstance);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
describe('IMAP Connection', function() {
|
|
204
|
+
it('should handle connection success', function(done) {
|
|
205
|
+
// ARRANGE: Set up connection tracking
|
|
206
|
+
const mockRED = createMockNodeRED({
|
|
207
|
+
statusHandler: function(status) {
|
|
208
|
+
if (status.fill === 'green') {
|
|
209
|
+
// ASSERT: Should show connected status
|
|
210
|
+
status.text.should.containEql('connected');
|
|
211
|
+
done();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// ACT: Create node which should attempt connection
|
|
217
|
+
emailReceiverNode(mockRED);
|
|
218
|
+
const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
|
|
219
|
+
new nodeConstructor(testConfigs.valid);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('should handle connection errors', function(done) {
|
|
223
|
+
// ARRANGE: Set up error tracking
|
|
224
|
+
const mockRED = createMockNodeRED({
|
|
225
|
+
errorHandler: function(err) {
|
|
226
|
+
// ASSERT: Should handle connection errors gracefully
|
|
227
|
+
should.exist(err);
|
|
315
228
|
done();
|
|
316
229
|
},
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
util: { evaluateNodeProperty: (value, type) => value },
|
|
325
|
-
};
|
|
230
|
+
statusHandler: function(status) {
|
|
231
|
+
if (status.fill === 'red') {
|
|
232
|
+
// Connection failed status
|
|
233
|
+
status.text.should.containEql('error');
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
});
|
|
326
237
|
|
|
327
|
-
// ACT:
|
|
238
|
+
// ACT: Create node with config that should fail
|
|
328
239
|
emailReceiverNode(mockRED);
|
|
329
|
-
|
|
240
|
+
const nodeConstructor = mockRED.nodes.lastRegisteredConstructor;
|
|
241
|
+
|
|
242
|
+
// Use invalid config to trigger connection error
|
|
243
|
+
const invalidConfig = { ...testConfigs.valid, host: 'invalid.host.com' };
|
|
244
|
+
new nodeConstructor(invalidConfig);
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
describe('Message Verification Utilities', function() {
|
|
249
|
+
it('should verify message properties using testUtils', function() {
|
|
250
|
+
// ARRANGE: Create a test message
|
|
251
|
+
const testMessage = {
|
|
252
|
+
payload: 'test content',
|
|
253
|
+
topic: 'email/received',
|
|
254
|
+
from: 'test@example.com'
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
// ACT & ASSERT: Use helper to verify message properties
|
|
258
|
+
testUtils.verifyMessage(testMessage, {
|
|
259
|
+
payload: 'test content',
|
|
260
|
+
topic: 'email/received'
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// Should not throw any errors if verification passes
|
|
264
|
+
testMessage.should.have.property('from', 'test@example.com');
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('should use wait utility for async operations', async function() {
|
|
268
|
+
// ARRANGE: Record start time
|
|
269
|
+
const startTime = Date.now();
|
|
270
|
+
|
|
271
|
+
// ACT: Use the wait utility
|
|
272
|
+
await testUtils.wait(100);
|
|
273
|
+
|
|
274
|
+
// ASSERT: Should have waited approximately the right amount of time
|
|
275
|
+
const elapsed = Date.now() - startTime;
|
|
276
|
+
elapsed.should.be.approximately(100, 50); // Allow 50ms tolerance
|
|
330
277
|
});
|
|
331
278
|
});
|
|
332
279
|
});
|