imapflow 1.2.4 → 1.2.6

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.
@@ -946,3 +946,521 @@ module.exports['Connection Edge: Pending requests and locks both rejected on clo
946
946
  test.done();
947
947
  });
948
948
  };
949
+
950
+ module.exports['Connection Edge: exec throws NoConnection when in LOGOUT state'] = async test => {
951
+ let client = new ImapFlow({
952
+ host: 'imap.example.com',
953
+ port: 993,
954
+ auth: { user: 'test', pass: 'test' }
955
+ });
956
+
957
+ // Set state to LOGOUT
958
+ client.state = client.states.LOGOUT;
959
+ client.socket = { destroyed: false };
960
+
961
+ try {
962
+ await client.exec('NOOP', []);
963
+ test.ok(false, 'Should have thrown');
964
+ } catch (err) {
965
+ test.equal(err.code, 'NoConnection');
966
+ test.ok(err.message.includes('Connection not available'));
967
+ }
968
+
969
+ test.done();
970
+ };
971
+
972
+ module.exports['Connection Edge: exec throws NoConnection when isClosed is true'] = async test => {
973
+ let client = new ImapFlow({
974
+ host: 'imap.example.com',
975
+ port: 993,
976
+ auth: { user: 'test', pass: 'test' }
977
+ });
978
+
979
+ // Set isClosed flag
980
+ client.isClosed = true;
981
+ client.socket = { destroyed: false };
982
+
983
+ try {
984
+ await client.exec('NOOP', []);
985
+ test.ok(false, 'Should have thrown');
986
+ } catch (err) {
987
+ test.equal(err.code, 'NoConnection');
988
+ test.ok(err.message.includes('Connection not available'));
989
+ }
990
+
991
+ test.done();
992
+ };
993
+
994
+ module.exports['Connection Edge: getLogger emits log events when emitLogs is true'] = test => {
995
+ let client = new ImapFlow({
996
+ host: 'imap.example.com',
997
+ port: 993,
998
+ auth: { user: 'test', pass: 'test' },
999
+ emitLogs: true
1000
+ });
1001
+
1002
+ let logEvents = [];
1003
+ client.on('log', entry => {
1004
+ logEvents.push(entry);
1005
+ });
1006
+
1007
+ // Trigger logging through the logger
1008
+ client.log.info({ msg: 'Test message', extra: 'data' });
1009
+
1010
+ test.ok(logEvents.length > 0, 'Log event should be emitted');
1011
+ test.equal(logEvents[0].level, 'info');
1012
+ test.equal(logEvents[0].msg, 'Test message');
1013
+ test.equal(logEvents[0].extra, 'data');
1014
+ test.ok(logEvents[0].cid, 'Should have connection id');
1015
+ test.ok(logEvents[0].t, 'Should have timestamp');
1016
+ test.done();
1017
+ };
1018
+
1019
+ module.exports['Connection Edge: getLogger emits log with error object'] = test => {
1020
+ let client = new ImapFlow({
1021
+ host: 'imap.example.com',
1022
+ port: 993,
1023
+ auth: { user: 'test', pass: 'test' },
1024
+ emitLogs: true
1025
+ });
1026
+
1027
+ let logEvents = [];
1028
+ client.on('log', entry => {
1029
+ logEvents.push(entry);
1030
+ });
1031
+
1032
+ // Trigger error logging
1033
+ let testError = new Error('Test error');
1034
+ testError.code = 'TEST_CODE';
1035
+ client.log.error({ msg: 'Error occurred', err: testError });
1036
+
1037
+ test.ok(logEvents.length > 0, 'Log event should be emitted');
1038
+ test.equal(logEvents[0].level, 'error');
1039
+ test.ok(logEvents[0].err, 'Should have error object');
1040
+ test.ok(logEvents[0].err.stack, 'Error should have stack');
1041
+ test.equal(logEvents[0].err.code, 'TEST_CODE', 'Error should have code');
1042
+ test.done();
1043
+ };
1044
+
1045
+ module.exports['Connection Edge: unbind removes socket listeners and returns sockets'] = test => {
1046
+ let client = new ImapFlow({
1047
+ host: 'imap.example.com',
1048
+ port: 993,
1049
+ auth: { user: 'test', pass: 'test' }
1050
+ });
1051
+
1052
+ // Create mock socket with unpipe method
1053
+ client.socket = new EventEmitter();
1054
+ client.socket.unpipe = () => {};
1055
+ client.writeSocket = client.socket;
1056
+
1057
+ // Set up socket handlers first
1058
+ client.setSocketHandlers();
1059
+
1060
+ // Verify handlers are set
1061
+ test.equal(client.socket.listenerCount('error'), 1);
1062
+ test.equal(client.socket.listenerCount('close'), 1);
1063
+
1064
+ // Call unbind
1065
+ let result = client.unbind();
1066
+
1067
+ // Verify handlers are removed
1068
+ test.equal(client.socket.listenerCount('error'), 0);
1069
+ test.equal(client.socket.listenerCount('close'), 0);
1070
+ test.equal(client.socket.listenerCount('end'), 0);
1071
+ test.equal(client.socket.listenerCount('timeout'), 0);
1072
+
1073
+ // Verify return value
1074
+ test.ok(result.readSocket, 'Should return readSocket');
1075
+ test.ok(result.writeSocket, 'Should return writeSocket');
1076
+ test.equal(result.readSocket, client.socket);
1077
+ test.equal(result.writeSocket, client.socket);
1078
+
1079
+ test.done();
1080
+ };
1081
+
1082
+ module.exports['Connection Edge: unbind with inflate stream'] = test => {
1083
+ let client = new ImapFlow({
1084
+ host: 'imap.example.com',
1085
+ port: 993,
1086
+ auth: { user: 'test', pass: 'test' }
1087
+ });
1088
+
1089
+ // Create mock socket
1090
+ client.socket = new EventEmitter();
1091
+ client.socket.unpipe = () => {};
1092
+
1093
+ // Create a separate writeSocket (also needs to be EventEmitter for setSocketHandlers)
1094
+ client.writeSocket = new EventEmitter();
1095
+ client.writeSocket.customProp = 'writeSocket';
1096
+
1097
+ // Create mock inflate stream
1098
+ client._inflate = new EventEmitter();
1099
+ client._inflate.unpipe = () => {};
1100
+
1101
+ // Set up socket handlers
1102
+ client.setSocketHandlers();
1103
+
1104
+ // Call unbind
1105
+ let result = client.unbind();
1106
+
1107
+ // Verify return value uses inflate for read and writeSocket for write
1108
+ test.equal(result.readSocket, client._inflate);
1109
+ test.equal(result.writeSocket.customProp, 'writeSocket');
1110
+
1111
+ test.done();
1112
+ };
1113
+
1114
+ module.exports['Connection Edge: resolveRange with number input'] = async test => {
1115
+ let client = new ImapFlow({
1116
+ host: 'imap.example.com',
1117
+ port: 993,
1118
+ auth: { user: 'test', pass: 'test' }
1119
+ });
1120
+
1121
+ client.mailbox = { exists: 10 };
1122
+ let options = {};
1123
+
1124
+ let result = await client.resolveRange(123, options);
1125
+ test.equal(result, '123');
1126
+ test.done();
1127
+ };
1128
+
1129
+ module.exports['Connection Edge: resolveRange with bigint input'] = async test => {
1130
+ let client = new ImapFlow({
1131
+ host: 'imap.example.com',
1132
+ port: 993,
1133
+ auth: { user: 'test', pass: 'test' }
1134
+ });
1135
+
1136
+ client.mailbox = { exists: 10 };
1137
+ let options = {};
1138
+
1139
+ let result = await client.resolveRange(BigInt(456), options);
1140
+ test.equal(result, '456');
1141
+ test.done();
1142
+ };
1143
+
1144
+ module.exports['Connection Edge: resolveRange with star and existing messages'] = async test => {
1145
+ let client = new ImapFlow({
1146
+ host: 'imap.example.com',
1147
+ port: 993,
1148
+ auth: { user: 'test', pass: 'test' }
1149
+ });
1150
+
1151
+ client.mailbox = { exists: 50 };
1152
+ let options = { uid: true };
1153
+
1154
+ let result = await client.resolveRange('*', options);
1155
+ test.equal(result, '50');
1156
+ test.equal(options.uid, false); // should be changed to sequence query
1157
+ test.done();
1158
+ };
1159
+
1160
+ module.exports['Connection Edge: resolveRange with star and no messages'] = async test => {
1161
+ let client = new ImapFlow({
1162
+ host: 'imap.example.com',
1163
+ port: 993,
1164
+ auth: { user: 'test', pass: 'test' }
1165
+ });
1166
+
1167
+ client.mailbox = { exists: 0 };
1168
+ let options = {};
1169
+
1170
+ let result = await client.resolveRange('*', options);
1171
+ test.equal(result, false);
1172
+ test.done();
1173
+ };
1174
+
1175
+ module.exports['Connection Edge: resolveRange with {all: true}'] = async test => {
1176
+ let client = new ImapFlow({
1177
+ host: 'imap.example.com',
1178
+ port: 993,
1179
+ auth: { user: 'test', pass: 'test' }
1180
+ });
1181
+
1182
+ client.mailbox = { exists: 10 };
1183
+ let options = {};
1184
+
1185
+ let result = await client.resolveRange({ all: true }, options);
1186
+ test.equal(result, '1:*');
1187
+ test.done();
1188
+ };
1189
+
1190
+ module.exports['Connection Edge: resolveRange with {uid: value}'] = async test => {
1191
+ let client = new ImapFlow({
1192
+ host: 'imap.example.com',
1193
+ port: 993,
1194
+ auth: { user: 'test', pass: 'test' }
1195
+ });
1196
+
1197
+ client.mailbox = { exists: 10 };
1198
+ let options = {};
1199
+
1200
+ let result = await client.resolveRange({ uid: '1:100' }, options);
1201
+ test.equal(result, '1:100');
1202
+ test.equal(options.uid, true);
1203
+ test.done();
1204
+ };
1205
+
1206
+ module.exports['Connection Edge: resolveRange with array input'] = async test => {
1207
+ let client = new ImapFlow({
1208
+ host: 'imap.example.com',
1209
+ port: 993,
1210
+ auth: { user: 'test', pass: 'test' }
1211
+ });
1212
+
1213
+ client.mailbox = { exists: 10 };
1214
+ let options = {};
1215
+
1216
+ let result = await client.resolveRange(['1', '2', '3'], options);
1217
+ test.equal(result, '1,2,3');
1218
+ test.done();
1219
+ };
1220
+
1221
+ module.exports['Connection Edge: resolveRange with empty/falsy input'] = async test => {
1222
+ let client = new ImapFlow({
1223
+ host: 'imap.example.com',
1224
+ port: 993,
1225
+ auth: { user: 'test', pass: 'test' }
1226
+ });
1227
+
1228
+ client.mailbox = { exists: 10 };
1229
+ let options = {};
1230
+
1231
+ let result = await client.resolveRange('', options);
1232
+ test.equal(result, false);
1233
+
1234
+ result = await client.resolveRange(null, options);
1235
+ test.equal(result, false);
1236
+
1237
+ test.done();
1238
+ };
1239
+
1240
+ module.exports['Connection Edge: ensureSelectedMailbox with no path'] = async test => {
1241
+ let client = new ImapFlow({
1242
+ host: 'imap.example.com',
1243
+ port: 993,
1244
+ auth: { user: 'test', pass: 'test' }
1245
+ });
1246
+
1247
+ let result = await client.ensureSelectedMailbox('');
1248
+ test.equal(result, false);
1249
+
1250
+ result = await client.ensureSelectedMailbox(null);
1251
+ test.equal(result, false);
1252
+
1253
+ test.done();
1254
+ };
1255
+
1256
+ module.exports['Connection Edge: ensureSelectedMailbox when already selected'] = async test => {
1257
+ let client = new ImapFlow({
1258
+ host: 'imap.example.com',
1259
+ port: 993,
1260
+ auth: { user: 'test', pass: 'test' }
1261
+ });
1262
+
1263
+ client.mailbox = { path: 'INBOX' };
1264
+
1265
+ let result = await client.ensureSelectedMailbox('INBOX');
1266
+ test.equal(result, true);
1267
+ test.done();
1268
+ };
1269
+
1270
+ module.exports['Connection Edge: autoidle clears existing timer'] = test => {
1271
+ let client = new ImapFlow({
1272
+ host: 'imap.example.com',
1273
+ port: 993,
1274
+ auth: { user: 'test', pass: 'test' }
1275
+ });
1276
+
1277
+ // Set a fake timer
1278
+ client.idleStartTimer = setTimeout(() => {}, 10000);
1279
+ client.state = client.states.NOT_AUTHENTICATED;
1280
+
1281
+ // Call autoidle - should clear the timer but not set new one (wrong state)
1282
+ client.autoidle();
1283
+
1284
+ // Should have been cleared
1285
+ test.ok(true);
1286
+ test.done();
1287
+ };
1288
+
1289
+ module.exports['Connection Edge: autoidle disabled by option'] = test => {
1290
+ let client = new ImapFlow({
1291
+ host: 'imap.example.com',
1292
+ port: 993,
1293
+ auth: { user: 'test', pass: 'test' },
1294
+ disableAutoIdle: true
1295
+ });
1296
+
1297
+ client.state = client.states.SELECTED;
1298
+
1299
+ // Call autoidle - should return early due to disableAutoIdle
1300
+ client.autoidle();
1301
+
1302
+ test.equal(client.idleStartTimer, undefined);
1303
+ test.done();
1304
+ };
1305
+
1306
+ module.exports['Connection Edge: getUntaggedHandler with numeric command'] = test => {
1307
+ let client = new ImapFlow({
1308
+ host: 'imap.example.com',
1309
+ port: 993,
1310
+ auth: { user: 'test', pass: 'test' }
1311
+ });
1312
+
1313
+ // Set up a current request with untagged handler
1314
+ client.currentRequest = {
1315
+ options: {
1316
+ untagged: {
1317
+ FETCH: () => 'fetch handler'
1318
+ }
1319
+ }
1320
+ };
1321
+
1322
+ // Test with numeric command and FETCH attribute
1323
+ let handler = client.getUntaggedHandler('123', [{ value: 'FETCH' }]);
1324
+ test.equal(handler(), 'fetch handler');
1325
+
1326
+ test.done();
1327
+ };
1328
+
1329
+ module.exports['Connection Edge: getUntaggedHandler from untaggedHandlers'] = test => {
1330
+ let client = new ImapFlow({
1331
+ host: 'imap.example.com',
1332
+ port: 993,
1333
+ auth: { user: 'test', pass: 'test' }
1334
+ });
1335
+
1336
+ // Set up global untagged handler
1337
+ client.untaggedHandlers = {
1338
+ CAPABILITY: () => 'capability handler'
1339
+ };
1340
+
1341
+ let handler = client.getUntaggedHandler('CAPABILITY', []);
1342
+ test.equal(handler(), 'capability handler');
1343
+
1344
+ test.done();
1345
+ };
1346
+
1347
+ module.exports['Connection Edge: getSectionHandler returns handler'] = test => {
1348
+ let client = new ImapFlow({
1349
+ host: 'imap.example.com',
1350
+ port: 993,
1351
+ auth: { user: 'test', pass: 'test' }
1352
+ });
1353
+
1354
+ // Set up section handler
1355
+ client.sectionHandlers = {
1356
+ CAPABILITY: () => 'section handler'
1357
+ };
1358
+
1359
+ let handler = client.getSectionHandler('CAPABILITY');
1360
+ test.equal(handler(), 'section handler');
1361
+
1362
+ // Test non-existent handler
1363
+ handler = client.getSectionHandler('NONEXISTENT');
1364
+ test.equal(handler, undefined);
1365
+
1366
+ test.done();
1367
+ };
1368
+
1369
+ module.exports['Connection Edge: constructor with custom clientInfo'] = test => {
1370
+ let client = new ImapFlow({
1371
+ host: 'imap.example.com',
1372
+ port: 993,
1373
+ auth: { user: 'test', pass: 'test' },
1374
+ clientInfo: {
1375
+ name: 'CustomClient',
1376
+ version: '1.0.0'
1377
+ }
1378
+ });
1379
+
1380
+ test.equal(client.clientInfo.name, 'CustomClient');
1381
+ test.equal(client.clientInfo.version, '1.0.0');
1382
+ test.ok(client.clientInfo.vendor); // Should still have default vendor
1383
+ test.done();
1384
+ };
1385
+
1386
+ module.exports['Connection Edge: constructor with secure port 993 detection'] = test => {
1387
+ // When port is 993 and secure is undefined, should default to secure
1388
+ let client = new ImapFlow({
1389
+ host: 'imap.example.com',
1390
+ port: 993,
1391
+ auth: { user: 'test', pass: 'test' }
1392
+ });
1393
+
1394
+ test.equal(client.secureConnection, true);
1395
+ test.done();
1396
+ };
1397
+
1398
+ module.exports['Connection Edge: constructor normalizes clientInfo diacritics'] = test => {
1399
+ let client = new ImapFlow({
1400
+ host: 'imap.example.com',
1401
+ port: 993,
1402
+ auth: { user: 'test', pass: 'test' },
1403
+ clientInfo: {
1404
+ name: 'Café Clïent',
1405
+ version: '1.0.0'
1406
+ }
1407
+ });
1408
+
1409
+ // Diacritics should be removed (NFD normalization splits é into e + accent, then accent is removed)
1410
+ test.equal(client.clientInfo.name, 'Cafe Client');
1411
+ test.done();
1412
+ };
1413
+
1414
+ module.exports['Connection Edge: constructor with servername option'] = test => {
1415
+ let client = new ImapFlow({
1416
+ host: '192.168.1.1',
1417
+ port: 993,
1418
+ auth: { user: 'test', pass: 'test' },
1419
+ servername: 'mail.example.com'
1420
+ });
1421
+
1422
+ test.equal(client.servername, 'mail.example.com');
1423
+ test.done();
1424
+ };
1425
+
1426
+ module.exports['Connection Edge: constructor servername from host when not IP'] = test => {
1427
+ let client = new ImapFlow({
1428
+ host: 'imap.example.com',
1429
+ port: 993,
1430
+ auth: { user: 'test', pass: 'test' }
1431
+ });
1432
+
1433
+ test.equal(client.servername, 'imap.example.com');
1434
+ test.done();
1435
+ };
1436
+
1437
+ module.exports['Connection Edge: constructor servername false for IP host'] = test => {
1438
+ let client = new ImapFlow({
1439
+ host: '192.168.1.1',
1440
+ port: 993,
1441
+ auth: { user: 'test', pass: 'test' }
1442
+ });
1443
+
1444
+ test.equal(client.servername, false);
1445
+ test.done();
1446
+ };
1447
+
1448
+ module.exports['Connection Edge: connect throws if called twice'] = async test => {
1449
+ let client = new ImapFlow({
1450
+ host: 'imap.example.com',
1451
+ port: 993,
1452
+ auth: { user: 'test', pass: 'test' }
1453
+ });
1454
+
1455
+ // Mark as already called
1456
+ client._connectCalled = true;
1457
+
1458
+ try {
1459
+ await client.connect();
1460
+ test.ok(false, 'Should have thrown');
1461
+ } catch (err) {
1462
+ test.ok(err.message.includes('re-use'));
1463
+ }
1464
+
1465
+ test.done();
1466
+ };