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.
@@ -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
- client.write('TEST');
44
- }, /Socket is already closed/, 'Should throw when writing to destroyed socket');
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
- client.write('TEST');
62
- }, /Can not send data after logged out/, 'Should throw when writing after logout');
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 (command) => {
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
- client.write('TEST');
297
- }, /Socket is already closed/, 'Should throw when socket is null');
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 (command) => {
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: (err) => {
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
- type: 'TEXT',
389
- value: 'Request is throttled. Suggested Backoff Time: 5000 milliseconds'
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.reader().then(() => {
405
- if (errorReceived) {
406
- test.ok(errorReceived, 'Should receive throttle error');
407
- test.equal(errorReceived.code, 'ETHROTTLE');
408
- test.equal(errorReceived.throttleReset, 5000);
409
- }
410
- test.done();
411
- }).catch(() => {
412
- // Reader might fail in test environment
413
- test.done();
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: (data) => {
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: (err) => {
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 (command) => {
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.upgradeToSTARTTLS().then(result => {
622
- test.ok(result, 'Should successfully upgrade to TLS');
623
- test.ok(client.expectCapabilityUpdate, 'Should expect capability update');
624
- test.done();
625
- }).catch(() => {
626
- // Expected for this mock setup
627
- test.done();
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
+ };