imapflow 1.0.199 → 1.1.0
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/.eslintrc +1 -2
- package/.github/CODE_OF_CONDUCT.md +13 -13
- package/.github/ISSUE_TEMPLATE/bug_report.md +10 -8
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -1
- package/.github/workflows/release.yaml +16 -17
- package/.github/workflows/test.yml +2 -2
- package/.ncurc.js +1 -1
- package/.prettierignore +4 -0
- package/.release-please-manifest.json +3 -0
- package/CHANGELOG.md +98 -0
- package/Gruntfile.js +1 -1
- package/eslint.config.js +43 -0
- package/lib/imap-flow.js +8 -8
- package/lib/jp-decoder.js +1 -1
- package/lib/tools.js +3 -3
- package/package.json +13 -8
- package/release-please-config.json +9 -0
- package/test/commands-test.js +6 -6
- package/test/connection-edge-cases-test.js +150 -130
- package/test/connection-test.js +17 -17
- package/test/fixtures/serialized-mimetorture.js +1 -2
- package/test/integration-test.js +10 -10
- package/test/search-compiler-test.js +34 -22
- package/test/special-use-test.js +1 -1
|
@@ -11,7 +11,7 @@ module.exports['Connection Edge: Socket error during connection'] = test => {
|
|
|
11
11
|
port: 993,
|
|
12
12
|
auth: { user: 'test', pass: 'test' }
|
|
13
13
|
});
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
// Simulate socket error
|
|
16
16
|
let errorEmitted = false;
|
|
17
17
|
client.on('error', err => {
|
|
@@ -19,12 +19,12 @@ module.exports['Connection Edge: Socket error during connection'] = test => {
|
|
|
19
19
|
test.ok(err);
|
|
20
20
|
test.equal(err._connId, client.id);
|
|
21
21
|
});
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
// Trigger error through emitError
|
|
24
24
|
let testError = new Error('Socket connection failed');
|
|
25
25
|
testError.code = 'ECONNREFUSED';
|
|
26
26
|
client.emitError(testError);
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
test.ok(errorEmitted, 'Error event should be emitted');
|
|
29
29
|
test.done();
|
|
30
30
|
};
|
|
@@ -35,14 +35,18 @@ module.exports['Connection Edge: Write after socket destroyed'] = test => {
|
|
|
35
35
|
port: 993,
|
|
36
36
|
auth: { user: 'test', pass: 'test' }
|
|
37
37
|
});
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
// Simulate destroyed socket
|
|
40
40
|
client.socket = { destroyed: true };
|
|
41
|
-
|
|
42
|
-
test.throws(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
|
|
42
|
+
test.throws(
|
|
43
|
+
() => {
|
|
44
|
+
client.write('TEST');
|
|
45
|
+
},
|
|
46
|
+
/Socket is already closed/,
|
|
47
|
+
'Should throw when writing to destroyed socket'
|
|
48
|
+
);
|
|
49
|
+
|
|
46
50
|
test.done();
|
|
47
51
|
};
|
|
48
52
|
|
|
@@ -52,15 +56,19 @@ module.exports['Connection Edge: Write after logout'] = test => {
|
|
|
52
56
|
port: 993,
|
|
53
57
|
auth: { user: 'test', pass: 'test' }
|
|
54
58
|
});
|
|
55
|
-
|
|
59
|
+
|
|
56
60
|
// Create a mock socket
|
|
57
61
|
client.socket = { destroyed: false };
|
|
58
62
|
client.state = client.states.LOGOUT;
|
|
59
|
-
|
|
60
|
-
test.throws(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
|
|
64
|
+
test.throws(
|
|
65
|
+
() => {
|
|
66
|
+
client.write('TEST');
|
|
67
|
+
},
|
|
68
|
+
/Can not send data after logged out/,
|
|
69
|
+
'Should throw when writing after logout'
|
|
70
|
+
);
|
|
71
|
+
|
|
64
72
|
test.done();
|
|
65
73
|
};
|
|
66
74
|
|
|
@@ -70,24 +78,24 @@ module.exports['Connection Edge: Stats reset functionality'] = test => {
|
|
|
70
78
|
port: 993,
|
|
71
79
|
auth: { user: 'test', pass: 'test' }
|
|
72
80
|
});
|
|
73
|
-
|
|
81
|
+
|
|
74
82
|
// Set some initial values
|
|
75
83
|
client.writeBytesCounter = 100;
|
|
76
84
|
client.streamer.readBytesCounter = 200;
|
|
77
|
-
|
|
85
|
+
|
|
78
86
|
let stats = client.stats(false);
|
|
79
87
|
test.equal(stats.sent, 100);
|
|
80
88
|
test.equal(stats.received, 200);
|
|
81
|
-
|
|
89
|
+
|
|
82
90
|
// Reset stats
|
|
83
91
|
stats = client.stats(true);
|
|
84
92
|
test.equal(stats.sent, 100, 'Should return old value before reset');
|
|
85
93
|
test.equal(stats.received, 200, 'Should return old value before reset');
|
|
86
|
-
|
|
94
|
+
|
|
87
95
|
// Check if reset worked
|
|
88
96
|
test.equal(client.writeBytesCounter, 0, 'Write counter should be reset');
|
|
89
97
|
test.equal(client.streamer.readBytesCounter, 0, 'Read counter should be reset');
|
|
90
|
-
|
|
98
|
+
|
|
91
99
|
test.done();
|
|
92
100
|
};
|
|
93
101
|
|
|
@@ -97,17 +105,17 @@ module.exports['Connection Edge: Multiple error handlers'] = test => {
|
|
|
97
105
|
port: 993,
|
|
98
106
|
auth: { user: 'test', pass: 'test' }
|
|
99
107
|
});
|
|
100
|
-
|
|
108
|
+
|
|
101
109
|
let errorCount = 0;
|
|
102
|
-
|
|
110
|
+
|
|
103
111
|
// Add multiple error handlers
|
|
104
112
|
client.on('error', () => errorCount++);
|
|
105
113
|
client.on('error', () => errorCount++);
|
|
106
114
|
client.on('error', () => errorCount++);
|
|
107
|
-
|
|
115
|
+
|
|
108
116
|
// Emit an error
|
|
109
117
|
client.emitError(new Error('Test error'));
|
|
110
|
-
|
|
118
|
+
|
|
111
119
|
test.equal(errorCount, 3, 'All error handlers should be called');
|
|
112
120
|
test.done();
|
|
113
121
|
};
|
|
@@ -120,7 +128,7 @@ module.exports['Connection Edge: Connection with valid empty auth'] = test => {
|
|
|
120
128
|
port: 993,
|
|
121
129
|
auth: { user: 'test' }
|
|
122
130
|
});
|
|
123
|
-
|
|
131
|
+
|
|
124
132
|
test.ok(client, 'Client should be created even with partial auth');
|
|
125
133
|
test.equal(client.host, 'imap.example.com');
|
|
126
134
|
test.done();
|
|
@@ -132,7 +140,7 @@ module.exports['Connection Edge: Connection with default values'] = test => {
|
|
|
132
140
|
host: 'imap.example.com',
|
|
133
141
|
auth: { user: 'test', pass: 'test' }
|
|
134
142
|
});
|
|
135
|
-
|
|
143
|
+
|
|
136
144
|
test.ok(client, 'Client should be created with defaults');
|
|
137
145
|
test.equal(client.port, 110, 'Should use default port');
|
|
138
146
|
test.equal(client.secureConnection, false, 'Should default to non-secure');
|
|
@@ -147,14 +155,14 @@ module.exports['Connection Edge: Port number handling'] = test => {
|
|
|
147
155
|
auth: { user: 'test', pass: 'test' }
|
|
148
156
|
});
|
|
149
157
|
test.equal(client1.port, 110, 'Default non-secure port');
|
|
150
|
-
|
|
158
|
+
|
|
151
159
|
let client2 = new ImapFlow({
|
|
152
160
|
host: 'imap.example.com',
|
|
153
161
|
port: 65535,
|
|
154
162
|
auth: { user: 'test', pass: 'test' }
|
|
155
163
|
});
|
|
156
164
|
test.equal(client2.port, 65535, 'Custom port accepted');
|
|
157
|
-
|
|
165
|
+
|
|
158
166
|
test.done();
|
|
159
167
|
};
|
|
160
168
|
|
|
@@ -166,7 +174,7 @@ module.exports['Connection Edge: STARTTLS misconfiguration'] = test => {
|
|
|
166
174
|
doSTARTTLS: true,
|
|
167
175
|
auth: { user: 'test', pass: 'test' }
|
|
168
176
|
});
|
|
169
|
-
|
|
177
|
+
|
|
170
178
|
// Test that upgradeToSTARTTLS throws on misconfiguration
|
|
171
179
|
client.upgradeToSTARTTLS().catch(err => {
|
|
172
180
|
test.ok(err);
|
|
@@ -181,27 +189,27 @@ module.exports['Connection Edge: Socket timeout handling'] = test => {
|
|
|
181
189
|
port: 993,
|
|
182
190
|
auth: { user: 'test', pass: 'test' }
|
|
183
191
|
});
|
|
184
|
-
|
|
192
|
+
|
|
185
193
|
// Create a mock socket
|
|
186
194
|
client.socket = new EventEmitter();
|
|
187
195
|
client.socket.destroyed = false;
|
|
188
196
|
client.socket.destroy = () => {};
|
|
189
197
|
client.writeSocket = client.socket;
|
|
190
198
|
client.writeSocket.destroy = () => {};
|
|
191
|
-
|
|
199
|
+
|
|
192
200
|
let errorEmitted = false;
|
|
193
201
|
client.on('error', err => {
|
|
194
202
|
errorEmitted = true;
|
|
195
203
|
test.equal(err.code, 'ETIMEOUT');
|
|
196
204
|
});
|
|
197
|
-
|
|
205
|
+
|
|
198
206
|
// Set up socket handlers
|
|
199
207
|
client.setSocketHandlers();
|
|
200
|
-
|
|
208
|
+
|
|
201
209
|
// Simulate timeout on non-idle connection
|
|
202
210
|
client.idling = false;
|
|
203
211
|
client.socket.emit('timeout');
|
|
204
|
-
|
|
212
|
+
|
|
205
213
|
test.ok(errorEmitted, 'Timeout error should be emitted');
|
|
206
214
|
test.done();
|
|
207
215
|
};
|
|
@@ -212,36 +220,36 @@ module.exports['Connection Edge: Socket timeout during IDLE'] = test => {
|
|
|
212
220
|
port: 993,
|
|
213
221
|
auth: { user: 'test', pass: 'test' }
|
|
214
222
|
});
|
|
215
|
-
|
|
223
|
+
|
|
216
224
|
// Create mock socket and methods
|
|
217
225
|
client.socket = new EventEmitter();
|
|
218
226
|
client.socket.destroyed = false;
|
|
219
227
|
client.writeSocket = client.socket;
|
|
220
228
|
client.usable = true;
|
|
221
229
|
client.idling = true;
|
|
222
|
-
|
|
230
|
+
|
|
223
231
|
// Mock run and idle methods
|
|
224
232
|
let noopCalled = false;
|
|
225
233
|
let idleCalled = false;
|
|
226
|
-
|
|
227
|
-
client.run = async
|
|
234
|
+
|
|
235
|
+
client.run = async command => {
|
|
228
236
|
if (command === 'NOOP') {
|
|
229
237
|
noopCalled = true;
|
|
230
238
|
return Promise.resolve();
|
|
231
239
|
}
|
|
232
240
|
};
|
|
233
|
-
|
|
241
|
+
|
|
234
242
|
client.idle = async () => {
|
|
235
243
|
idleCalled = true;
|
|
236
244
|
return Promise.resolve();
|
|
237
245
|
};
|
|
238
|
-
|
|
246
|
+
|
|
239
247
|
// Set up socket handlers
|
|
240
248
|
client.setSocketHandlers();
|
|
241
|
-
|
|
249
|
+
|
|
242
250
|
// Simulate timeout during IDLE
|
|
243
251
|
client.socket.emit('timeout');
|
|
244
|
-
|
|
252
|
+
|
|
245
253
|
// Give async operations time to complete
|
|
246
254
|
setTimeout(() => {
|
|
247
255
|
test.ok(noopCalled, 'NOOP should be called to recover from IDLE timeout');
|
|
@@ -256,29 +264,29 @@ module.exports['Connection Edge: Clear socket handlers'] = test => {
|
|
|
256
264
|
port: 993,
|
|
257
265
|
auth: { user: 'test', pass: 'test' }
|
|
258
266
|
});
|
|
259
|
-
|
|
267
|
+
|
|
260
268
|
// Create a mock socket
|
|
261
269
|
client.socket = new EventEmitter();
|
|
262
270
|
client.writeSocket = client.socket;
|
|
263
|
-
|
|
271
|
+
|
|
264
272
|
// Set handlers
|
|
265
273
|
client.setSocketHandlers();
|
|
266
|
-
|
|
274
|
+
|
|
267
275
|
// Verify handlers are set
|
|
268
276
|
test.equal(client.socket.listenerCount('error'), 1);
|
|
269
277
|
test.equal(client.socket.listenerCount('close'), 1);
|
|
270
278
|
test.equal(client.socket.listenerCount('end'), 1);
|
|
271
279
|
test.equal(client.socket.listenerCount('timeout'), 1);
|
|
272
|
-
|
|
280
|
+
|
|
273
281
|
// Clear handlers
|
|
274
282
|
client.clearSocketHandlers();
|
|
275
|
-
|
|
283
|
+
|
|
276
284
|
// Verify handlers are removed
|
|
277
285
|
test.equal(client.socket.listenerCount('error'), 0);
|
|
278
286
|
test.equal(client.socket.listenerCount('close'), 0);
|
|
279
287
|
test.equal(client.socket.listenerCount('end'), 0);
|
|
280
288
|
test.equal(client.socket.listenerCount('timeout'), 0);
|
|
281
|
-
|
|
289
|
+
|
|
282
290
|
test.done();
|
|
283
291
|
};
|
|
284
292
|
|
|
@@ -288,14 +296,18 @@ module.exports['Connection Edge: Write with null socket'] = test => {
|
|
|
288
296
|
port: 993,
|
|
289
297
|
auth: { user: 'test', pass: 'test' }
|
|
290
298
|
});
|
|
291
|
-
|
|
299
|
+
|
|
292
300
|
// Set socket to null
|
|
293
301
|
client.socket = null;
|
|
294
|
-
|
|
295
|
-
test.throws(
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
302
|
+
|
|
303
|
+
test.throws(
|
|
304
|
+
() => {
|
|
305
|
+
client.write('TEST');
|
|
306
|
+
},
|
|
307
|
+
/Socket is already closed/,
|
|
308
|
+
'Should throw when socket is null'
|
|
309
|
+
);
|
|
310
|
+
|
|
299
311
|
test.done();
|
|
300
312
|
};
|
|
301
313
|
|
|
@@ -305,33 +317,33 @@ module.exports['Connection Edge: Compression error handling'] = test => {
|
|
|
305
317
|
port: 993,
|
|
306
318
|
auth: { user: 'test', pass: 'test' }
|
|
307
319
|
});
|
|
308
|
-
|
|
320
|
+
|
|
309
321
|
// Create mock socket and streams
|
|
310
322
|
client.socket = new EventEmitter();
|
|
311
323
|
client.socket.pipe = () => client.socket;
|
|
312
324
|
client.socket.unpipe = () => {};
|
|
313
325
|
client.streamer = new EventEmitter();
|
|
314
|
-
|
|
326
|
+
|
|
315
327
|
// Mock run method to simulate successful COMPRESS negotiation
|
|
316
|
-
client.run = async
|
|
328
|
+
client.run = async command => {
|
|
317
329
|
if (command === 'COMPRESS') {
|
|
318
330
|
return true;
|
|
319
331
|
}
|
|
320
332
|
};
|
|
321
|
-
|
|
333
|
+
|
|
322
334
|
let errorEmitted = false;
|
|
323
335
|
client.streamer.on('error', err => {
|
|
324
336
|
errorEmitted = true;
|
|
325
337
|
test.ok(err);
|
|
326
338
|
});
|
|
327
|
-
|
|
339
|
+
|
|
328
340
|
// Call compress
|
|
329
341
|
client.compress().then(() => {
|
|
330
342
|
// Simulate compression error
|
|
331
343
|
if (client._inflate) {
|
|
332
344
|
client._inflate.emit('error', new Error('Compression failed'));
|
|
333
345
|
}
|
|
334
|
-
|
|
346
|
+
|
|
335
347
|
test.ok(errorEmitted, 'Compression error should be propagated');
|
|
336
348
|
test.done();
|
|
337
349
|
});
|
|
@@ -343,14 +355,14 @@ module.exports['Connection Edge: Authentication state after logout'] = test => {
|
|
|
343
355
|
port: 993,
|
|
344
356
|
auth: { user: 'test', pass: 'test' }
|
|
345
357
|
});
|
|
346
|
-
|
|
358
|
+
|
|
347
359
|
// Set initial authenticated state
|
|
348
360
|
client.state = client.states.AUTHENTICATED;
|
|
349
361
|
client.authenticated = true;
|
|
350
|
-
|
|
362
|
+
|
|
351
363
|
// Simulate logout
|
|
352
364
|
client.state = client.states.LOGOUT;
|
|
353
|
-
|
|
365
|
+
|
|
354
366
|
// Try to authenticate
|
|
355
367
|
client.authenticate().catch(err => {
|
|
356
368
|
test.ok(err);
|
|
@@ -365,53 +377,58 @@ module.exports['Connection Edge: Throttling detection'] = test => {
|
|
|
365
377
|
port: 993,
|
|
366
378
|
auth: { user: 'test', pass: 'test' }
|
|
367
379
|
});
|
|
368
|
-
|
|
380
|
+
|
|
369
381
|
// Create mock request
|
|
370
382
|
let errorReceived = null;
|
|
371
383
|
let request = {
|
|
372
384
|
tag: 'A001',
|
|
373
385
|
command: 'FETCH',
|
|
374
386
|
resolve: () => {},
|
|
375
|
-
reject:
|
|
387
|
+
reject: err => {
|
|
376
388
|
errorReceived = err;
|
|
377
389
|
}
|
|
378
390
|
};
|
|
379
|
-
|
|
391
|
+
|
|
380
392
|
client.requestTagMap = new Map();
|
|
381
393
|
client.requestTagMap.set('A001', request);
|
|
382
|
-
|
|
394
|
+
|
|
383
395
|
// Simulate throttling response
|
|
384
396
|
let parsed = {
|
|
385
397
|
tag: 'A001',
|
|
386
398
|
command: 'BAD',
|
|
387
|
-
attributes: [
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
399
|
+
attributes: [
|
|
400
|
+
{
|
|
401
|
+
type: 'TEXT',
|
|
402
|
+
value: 'Request is throttled. Suggested Backoff Time: 5000 milliseconds'
|
|
403
|
+
}
|
|
404
|
+
]
|
|
391
405
|
};
|
|
392
|
-
|
|
406
|
+
|
|
393
407
|
// Mock streamer with proper async iterator
|
|
394
408
|
client.streamer = new EventEmitter();
|
|
395
409
|
client.streamer.read = () => null;
|
|
396
410
|
client.streamer.iterate = async function* () {
|
|
397
|
-
yield {
|
|
411
|
+
yield {
|
|
398
412
|
next: () => {},
|
|
399
|
-
parsed
|
|
413
|
+
parsed
|
|
400
414
|
};
|
|
401
415
|
};
|
|
402
|
-
|
|
416
|
+
|
|
403
417
|
// Process reader
|
|
404
|
-
client
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
418
|
+
client
|
|
419
|
+
.reader()
|
|
420
|
+
.then(() => {
|
|
421
|
+
if (errorReceived) {
|
|
422
|
+
test.ok(errorReceived, 'Should receive throttle error');
|
|
423
|
+
test.equal(errorReceived.code, 'ETHROTTLE');
|
|
424
|
+
test.equal(errorReceived.throttleReset, 5000);
|
|
425
|
+
}
|
|
426
|
+
test.done();
|
|
427
|
+
})
|
|
428
|
+
.catch(() => {
|
|
429
|
+
// Reader might fail in test environment
|
|
430
|
+
test.done();
|
|
431
|
+
});
|
|
415
432
|
};
|
|
416
433
|
|
|
417
434
|
module.exports['Connection Edge: Binary data in write'] = test => {
|
|
@@ -420,32 +437,32 @@ module.exports['Connection Edge: Binary data in write'] = test => {
|
|
|
420
437
|
port: 993,
|
|
421
438
|
auth: { user: 'test', pass: 'test' }
|
|
422
439
|
});
|
|
423
|
-
|
|
440
|
+
|
|
424
441
|
// Mock socket
|
|
425
442
|
let writtenData = null;
|
|
426
443
|
client.socket = { destroyed: false };
|
|
427
444
|
client.writeSocket = {
|
|
428
445
|
destroyed: false,
|
|
429
|
-
write:
|
|
446
|
+
write: data => {
|
|
430
447
|
writtenData = data;
|
|
431
448
|
}
|
|
432
449
|
};
|
|
433
|
-
|
|
450
|
+
|
|
434
451
|
// Test writing Buffer
|
|
435
452
|
let testBuffer = Buffer.from('TEST');
|
|
436
453
|
client.write(testBuffer);
|
|
437
|
-
|
|
454
|
+
|
|
438
455
|
test.ok(Buffer.isBuffer(writtenData));
|
|
439
456
|
test.ok(writtenData.includes(testBuffer));
|
|
440
457
|
test.ok(writtenData.includes(Buffer.from('\r\n')));
|
|
441
|
-
|
|
458
|
+
|
|
442
459
|
// Test writing string
|
|
443
460
|
client.commandParts = [];
|
|
444
461
|
client.write('STRING_TEST');
|
|
445
|
-
|
|
462
|
+
|
|
446
463
|
test.ok(Buffer.isBuffer(writtenData));
|
|
447
464
|
test.ok(writtenData.includes(Buffer.from('STRING_TEST\r\n')));
|
|
448
|
-
|
|
465
|
+
|
|
449
466
|
test.done();
|
|
450
467
|
};
|
|
451
468
|
|
|
@@ -455,24 +472,24 @@ module.exports['Connection Edge: Invalid write data type'] = test => {
|
|
|
455
472
|
port: 993,
|
|
456
473
|
auth: { user: 'test', pass: 'test' }
|
|
457
474
|
});
|
|
458
|
-
|
|
475
|
+
|
|
459
476
|
// Mock socket
|
|
460
477
|
client.socket = { destroyed: false };
|
|
461
478
|
client.writeSocket = { destroyed: false, write: () => {} };
|
|
462
|
-
|
|
479
|
+
|
|
463
480
|
// Test writing invalid data type
|
|
464
481
|
let result = client.write(12345);
|
|
465
482
|
test.equal(result, false, 'Should return false for invalid data type');
|
|
466
|
-
|
|
483
|
+
|
|
467
484
|
result = client.write({ test: 'object' });
|
|
468
485
|
test.equal(result, false, 'Should return false for object');
|
|
469
|
-
|
|
486
|
+
|
|
470
487
|
test.done();
|
|
471
488
|
};
|
|
472
489
|
|
|
473
490
|
module.exports['Connection Edge: Concurrent connections'] = test => {
|
|
474
491
|
let clients = [];
|
|
475
|
-
|
|
492
|
+
|
|
476
493
|
// Create multiple clients
|
|
477
494
|
for (let i = 0; i < 5; i++) {
|
|
478
495
|
let client = new ImapFlow({
|
|
@@ -482,20 +499,20 @@ module.exports['Connection Edge: Concurrent connections'] = test => {
|
|
|
482
499
|
});
|
|
483
500
|
clients.push(client);
|
|
484
501
|
}
|
|
485
|
-
|
|
502
|
+
|
|
486
503
|
// Verify each client has unique ID
|
|
487
504
|
let ids = clients.map(c => c.id);
|
|
488
505
|
let uniqueIds = [...new Set(ids)];
|
|
489
506
|
test.equal(uniqueIds.length, clients.length, 'All client IDs should be unique');
|
|
490
|
-
|
|
507
|
+
|
|
491
508
|
// Verify each client has independent state
|
|
492
509
|
clients[0].state = clients[0].states.AUTHENTICATED;
|
|
493
510
|
clients[1].state = clients[1].states.SELECTED;
|
|
494
|
-
|
|
511
|
+
|
|
495
512
|
test.equal(clients[0].state, clients[0].states.AUTHENTICATED);
|
|
496
513
|
test.equal(clients[1].state, clients[1].states.SELECTED);
|
|
497
514
|
test.equal(clients[2].state, clients[2].states.NOT_AUTHENTICATED);
|
|
498
|
-
|
|
515
|
+
|
|
499
516
|
test.done();
|
|
500
517
|
};
|
|
501
518
|
|
|
@@ -505,20 +522,20 @@ module.exports['Connection Edge: Destroyed write socket'] = test => {
|
|
|
505
522
|
port: 993,
|
|
506
523
|
auth: { user: 'test', pass: 'test' }
|
|
507
524
|
});
|
|
508
|
-
|
|
525
|
+
|
|
509
526
|
// Mock sockets
|
|
510
527
|
client.socket = { destroyed: false };
|
|
511
528
|
client.writeSocket = { destroyed: true };
|
|
512
|
-
|
|
529
|
+
|
|
513
530
|
// Mock close method
|
|
514
531
|
let closeCalled = false;
|
|
515
532
|
client.close = () => {
|
|
516
533
|
closeCalled = true;
|
|
517
534
|
};
|
|
518
|
-
|
|
535
|
+
|
|
519
536
|
// Attempt to write
|
|
520
537
|
client.write('TEST');
|
|
521
|
-
|
|
538
|
+
|
|
522
539
|
test.ok(closeCalled, 'Should call close when write socket is destroyed');
|
|
523
540
|
test.done();
|
|
524
541
|
};
|
|
@@ -529,22 +546,22 @@ module.exports['Connection Edge: Command after logout'] = test => {
|
|
|
529
546
|
port: 993,
|
|
530
547
|
auth: { user: 'test', pass: 'test' }
|
|
531
548
|
});
|
|
532
|
-
|
|
549
|
+
|
|
533
550
|
// Set logout state
|
|
534
551
|
client.state = client.states.LOGOUT;
|
|
535
|
-
|
|
552
|
+
|
|
536
553
|
// Create a request
|
|
537
554
|
let errorReceived = null;
|
|
538
555
|
let request = {
|
|
539
556
|
tag: 'A001',
|
|
540
|
-
reject:
|
|
557
|
+
reject: err => {
|
|
541
558
|
errorReceived = err;
|
|
542
559
|
}
|
|
543
560
|
};
|
|
544
|
-
|
|
561
|
+
|
|
545
562
|
client.requestTagMap = new Map();
|
|
546
563
|
client.requestTagMap.set('A001', request);
|
|
547
|
-
|
|
564
|
+
|
|
548
565
|
// Try to send command
|
|
549
566
|
client.send({ tag: 'A001', command: 'NOOP' }).then(() => {
|
|
550
567
|
test.ok(errorReceived, 'Should reject command after logout');
|
|
@@ -559,10 +576,10 @@ module.exports['Connection Edge: Race condition in mailbox lock'] = test => {
|
|
|
559
576
|
port: 993,
|
|
560
577
|
auth: { user: 'test', pass: 'test' }
|
|
561
578
|
});
|
|
562
|
-
|
|
579
|
+
|
|
563
580
|
// Initialize locks array
|
|
564
581
|
client.locks = [];
|
|
565
|
-
|
|
582
|
+
|
|
566
583
|
// Simulate concurrent lock requests
|
|
567
584
|
let lockPromises = [];
|
|
568
585
|
for (let i = 0; i < 3; i++) {
|
|
@@ -576,14 +593,14 @@ module.exports['Connection Edge: Race condition in mailbox lock'] = test => {
|
|
|
576
593
|
})
|
|
577
594
|
);
|
|
578
595
|
}
|
|
579
|
-
|
|
596
|
+
|
|
580
597
|
test.equal(client.locks.length, 3, 'Should queue multiple lock requests');
|
|
581
|
-
|
|
598
|
+
|
|
582
599
|
// Process locks sequentially
|
|
583
600
|
client.locks.forEach((lock, index) => {
|
|
584
601
|
lock.resolve({ path: 'INBOX', index });
|
|
585
602
|
});
|
|
586
|
-
|
|
603
|
+
|
|
587
604
|
Promise.all(lockPromises).then(results => {
|
|
588
605
|
test.equal(results.length, 3, 'All lock requests should be resolved');
|
|
589
606
|
test.done();
|
|
@@ -596,13 +613,13 @@ module.exports['Connection Edge: Capability update after STARTTLS'] = test => {
|
|
|
596
613
|
port: 143,
|
|
597
614
|
auth: { user: 'test', pass: 'test' }
|
|
598
615
|
});
|
|
599
|
-
|
|
616
|
+
|
|
600
617
|
// Mock capabilities
|
|
601
618
|
client.capabilities = new Map();
|
|
602
619
|
client.capabilities.set('STARTTLS', true);
|
|
603
|
-
|
|
620
|
+
|
|
604
621
|
// Mock run method
|
|
605
|
-
client.run = async
|
|
622
|
+
client.run = async command => {
|
|
606
623
|
if (command === 'STARTTLS') {
|
|
607
624
|
client.expectCapabilityUpdate = true;
|
|
608
625
|
return true;
|
|
@@ -611,19 +628,22 @@ module.exports['Connection Edge: Capability update after STARTTLS'] = test => {
|
|
|
611
628
|
return true;
|
|
612
629
|
}
|
|
613
630
|
};
|
|
614
|
-
|
|
631
|
+
|
|
615
632
|
// Mock socket upgrade
|
|
616
633
|
client.socket = new EventEmitter();
|
|
617
634
|
client.socket.unpipe = () => {};
|
|
618
635
|
client.streamer = new EventEmitter();
|
|
619
|
-
|
|
636
|
+
|
|
620
637
|
// Override the upgradeToSTARTTLS to test capability update
|
|
621
|
-
client
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
638
|
+
client
|
|
639
|
+
.upgradeToSTARTTLS()
|
|
640
|
+
.then(result => {
|
|
641
|
+
test.ok(result, 'Should successfully upgrade to TLS');
|
|
642
|
+
test.ok(client.expectCapabilityUpdate, 'Should expect capability update');
|
|
643
|
+
test.done();
|
|
644
|
+
})
|
|
645
|
+
.catch(() => {
|
|
646
|
+
// Expected for this mock setup
|
|
647
|
+
test.done();
|
|
648
|
+
});
|
|
649
|
+
};
|