imapflow 1.7.8 → 2.0.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/CHANGELOG.md +20 -0
- package/README.md +8 -2
- package/dist/cjs/charsets.d.ts +1 -0
- package/dist/cjs/charsets.js +294 -0
- package/dist/cjs/commands/append.d.ts +22 -0
- package/dist/cjs/commands/append.js +151 -0
- package/dist/cjs/commands/authenticate.d.ts +24 -0
- package/dist/cjs/commands/authenticate.js +223 -0
- package/dist/cjs/commands/capability.d.ts +8 -0
- package/dist/cjs/commands/capability.js +32 -0
- package/dist/cjs/commands/close.d.ts +8 -0
- package/dist/cjs/commands/close.js +39 -0
- package/dist/cjs/commands/compress.d.ts +8 -0
- package/dist/cjs/commands/compress.js +56 -0
- package/dist/cjs/commands/copy.d.ts +13 -0
- package/dist/cjs/commands/copy.js +44 -0
- package/dist/cjs/commands/copyuid-parser.d.ts +11 -0
- package/dist/cjs/commands/copyuid-parser.js +32 -0
- package/dist/cjs/commands/create.d.ts +11 -0
- package/dist/cjs/commands/create.js +80 -0
- package/dist/cjs/commands/delete.d.ts +11 -0
- package/dist/cjs/commands/delete.js +40 -0
- package/dist/cjs/commands/enable.d.ts +9 -0
- package/dist/cjs/commands/enable.js +61 -0
- package/dist/cjs/commands/esearch-parser.d.ts +17 -0
- package/dist/cjs/commands/esearch-parser.js +91 -0
- package/dist/cjs/commands/expunge.d.ts +12 -0
- package/dist/cjs/commands/expunge.js +60 -0
- package/dist/cjs/commands/fetch.d.ts +30 -0
- package/dist/cjs/commands/fetch.js +241 -0
- package/dist/cjs/commands/id.d.ts +10 -0
- package/dist/cjs/commands/id.js +80 -0
- package/dist/cjs/commands/idle.d.ts +9 -0
- package/dist/cjs/commands/idle.js +347 -0
- package/dist/cjs/commands/list.d.ts +16 -0
- package/dist/cjs/commands/list.js +518 -0
- package/dist/cjs/commands/login.d.ts +11 -0
- package/dist/cjs/commands/login.js +42 -0
- package/dist/cjs/commands/logout.d.ts +8 -0
- package/dist/cjs/commands/logout.js +47 -0
- package/dist/cjs/commands/move.d.ts +13 -0
- package/dist/cjs/commands/move.js +57 -0
- package/dist/cjs/commands/namespace.d.ts +25 -0
- package/dist/cjs/commands/namespace.js +139 -0
- package/dist/cjs/commands/noop.d.ts +8 -0
- package/dist/cjs/commands/noop.js +22 -0
- package/dist/cjs/commands/quota.d.ts +10 -0
- package/dist/cjs/commands/quota.js +119 -0
- package/dist/cjs/commands/rename.d.ts +12 -0
- package/dist/cjs/commands/rename.js +48 -0
- package/dist/cjs/commands/search.d.ts +15 -0
- package/dist/cjs/commands/search.js +228 -0
- package/dist/cjs/commands/select.d.ts +25 -0
- package/dist/cjs/commands/select.js +250 -0
- package/dist/cjs/commands/starttls.d.ts +8 -0
- package/dist/cjs/commands/starttls.js +30 -0
- package/dist/cjs/commands/status-fields.d.ts +14 -0
- package/dist/cjs/commands/status-fields.js +61 -0
- package/dist/cjs/commands/status.d.ts +12 -0
- package/dist/cjs/commands/status.js +108 -0
- package/dist/cjs/commands/store.d.ts +19 -0
- package/dist/cjs/commands/store.js +93 -0
- package/dist/cjs/commands/subscribe.d.ts +9 -0
- package/dist/cjs/commands/subscribe.js +31 -0
- package/dist/cjs/commands/unsubscribe.d.ts +9 -0
- package/dist/cjs/commands/unsubscribe.js +31 -0
- package/dist/cjs/connection-deadline.d.ts +49 -0
- package/dist/cjs/connection-deadline.js +91 -0
- package/dist/cjs/errors.d.ts +83 -0
- package/dist/cjs/errors.js +13 -0
- package/dist/cjs/handler/imap-compiler.d.ts +24 -0
- package/dist/cjs/handler/imap-compiler.js +285 -0
- package/dist/cjs/handler/imap-formal-syntax.d.ts +28 -0
- package/dist/cjs/handler/imap-formal-syntax.js +121 -0
- package/dist/cjs/handler/imap-handler.d.ts +9 -0
- package/dist/cjs/handler/imap-handler.js +10 -0
- package/dist/cjs/handler/imap-parser.d.ts +16 -0
- package/dist/cjs/handler/imap-parser.js +90 -0
- package/dist/cjs/handler/imap-stream.d.ts +181 -0
- package/dist/cjs/handler/imap-stream.js +446 -0
- package/dist/cjs/handler/limits.d.ts +25 -0
- package/dist/cjs/handler/limits.js +51 -0
- package/dist/cjs/handler/parser-instance.d.ts +68 -0
- package/dist/cjs/handler/parser-instance.js +223 -0
- package/dist/cjs/handler/token-parser.d.ts +91 -0
- package/dist/cjs/handler/token-parser.js +673 -0
- package/dist/cjs/handler/types.d.ts +91 -0
- package/dist/cjs/handler/types.js +4 -0
- package/dist/cjs/imap-commands.d.ts +16 -0
- package/dist/cjs/imap-commands.js +74 -0
- package/dist/cjs/imap-flow.d.ts +676 -0
- package/dist/cjs/imap-flow.js +3949 -0
- package/dist/cjs/jp-decoder.d.ts +12 -0
- package/dist/cjs/jp-decoder.js +79 -0
- package/dist/cjs/limited-passthrough.d.ts +25 -0
- package/dist/cjs/limited-passthrough.js +54 -0
- package/dist/cjs/logger.d.ts +3 -0
- package/dist/cjs/logger.js +11 -0
- package/dist/cjs/package-info.d.ts +3 -0
- package/dist/cjs/package-info.js +7 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/proxy-connection.d.ts +33 -0
- package/dist/cjs/proxy-connection.js +392 -0
- package/dist/cjs/search-compiler.d.ts +34 -0
- package/dist/cjs/search-compiler.js +476 -0
- package/dist/cjs/special-use.d.ts +22 -0
- package/dist/cjs/special-use.js +911 -0
- package/dist/cjs/tools.d.ts +427 -0
- package/dist/cjs/tools.js +1496 -0
- package/{lib/imap-flow.d.ts → dist/cjs/types.d.ts} +386 -516
- package/dist/cjs/types.js +5 -0
- package/dist/esm/charsets.d.ts +1 -0
- package/{lib → dist/esm}/charsets.js +1 -6
- package/dist/esm/commands/append.d.ts +22 -0
- package/{lib → dist/esm}/commands/append.js +22 -52
- package/dist/esm/commands/authenticate.d.ts +24 -0
- package/{lib → dist/esm}/commands/authenticate.js +62 -87
- package/dist/esm/commands/capability.d.ts +8 -0
- package/{lib → dist/esm}/commands/capability.js +6 -9
- package/dist/esm/commands/close.d.ts +8 -0
- package/{lib → dist/esm}/commands/close.js +6 -10
- package/dist/esm/commands/compress.d.ts +8 -0
- package/{lib → dist/esm}/commands/compress.js +7 -11
- package/dist/esm/commands/copy.d.ts +13 -0
- package/{lib → dist/esm}/commands/copy.js +12 -20
- package/dist/esm/commands/copyuid-parser.d.ts +11 -0
- package/{lib → dist/esm}/commands/copyuid-parser.js +9 -15
- package/dist/esm/commands/create.d.ts +11 -0
- package/{lib → dist/esm}/commands/create.js +13 -27
- package/dist/esm/commands/delete.d.ts +11 -0
- package/{lib → dist/esm}/commands/delete.js +9 -14
- package/dist/esm/commands/enable.d.ts +9 -0
- package/{lib → dist/esm}/commands/enable.js +23 -30
- package/dist/esm/commands/esearch-parser.d.ts +17 -0
- package/dist/esm/commands/esearch-parser.js +88 -0
- package/dist/esm/commands/expunge.d.ts +12 -0
- package/{lib → dist/esm}/commands/expunge.js +17 -22
- package/dist/esm/commands/fetch.d.ts +30 -0
- package/{lib → dist/esm}/commands/fetch.js +32 -64
- package/dist/esm/commands/id.d.ts +10 -0
- package/{lib → dist/esm}/commands/id.js +17 -23
- package/dist/esm/commands/idle.d.ts +9 -0
- package/{lib → dist/esm}/commands/idle.js +47 -81
- package/dist/esm/commands/list.d.ts +16 -0
- package/{lib → dist/esm}/commands/list.js +56 -121
- package/dist/esm/commands/login.d.ts +11 -0
- package/{lib → dist/esm}/commands/login.js +10 -15
- package/dist/esm/commands/logout.d.ts +8 -0
- package/{lib → dist/esm}/commands/logout.js +9 -11
- package/dist/esm/commands/move.d.ts +13 -0
- package/{lib → dist/esm}/commands/move.js +13 -21
- package/dist/esm/commands/namespace.d.ts +25 -0
- package/{lib → dist/esm}/commands/namespace.js +34 -44
- package/dist/esm/commands/noop.d.ts +8 -0
- package/{lib → dist/esm}/commands/noop.js +6 -7
- package/dist/esm/commands/quota.d.ts +10 -0
- package/{lib → dist/esm}/commands/quota.js +18 -36
- package/dist/esm/commands/rename.d.ts +12 -0
- package/{lib → dist/esm}/commands/rename.js +10 -15
- package/dist/esm/commands/search.d.ts +15 -0
- package/{lib → dist/esm}/commands/search.js +36 -135
- package/dist/esm/commands/select.d.ts +25 -0
- package/{lib → dist/esm}/commands/select.js +33 -64
- package/dist/esm/commands/starttls.d.ts +8 -0
- package/{lib → dist/esm}/commands/starttls.js +6 -8
- package/dist/esm/commands/status-fields.d.ts +14 -0
- package/{lib → dist/esm}/commands/status-fields.js +5 -16
- package/dist/esm/commands/status.d.ts +12 -0
- package/{lib → dist/esm}/commands/status.js +18 -29
- package/dist/esm/commands/store.d.ts +19 -0
- package/{lib → dist/esm}/commands/store.js +24 -37
- package/dist/esm/commands/subscribe.d.ts +9 -0
- package/{lib → dist/esm}/commands/subscribe.js +8 -12
- package/dist/esm/commands/unsubscribe.d.ts +9 -0
- package/{lib → dist/esm}/commands/unsubscribe.js +8 -12
- package/dist/esm/connection-deadline.d.ts +49 -0
- package/{lib → dist/esm}/connection-deadline.js +14 -25
- package/dist/esm/errors.d.ts +83 -0
- package/dist/esm/errors.js +9 -0
- package/dist/esm/handler/imap-compiler.d.ts +24 -0
- package/{lib → dist/esm}/handler/imap-compiler.js +22 -80
- package/dist/esm/handler/imap-formal-syntax.d.ts +28 -0
- package/dist/esm/handler/imap-formal-syntax.js +117 -0
- package/dist/esm/handler/imap-handler.d.ts +9 -0
- package/dist/esm/handler/imap-handler.js +9 -0
- package/dist/esm/handler/imap-parser.d.ts +16 -0
- package/{lib → dist/esm}/handler/imap-parser.js +31 -44
- package/dist/esm/handler/imap-stream.d.ts +181 -0
- package/{lib → dist/esm}/handler/imap-stream.js +29 -121
- package/dist/esm/handler/limits.d.ts +25 -0
- package/{lib → dist/esm}/handler/limits.js +13 -22
- package/dist/esm/handler/parser-instance.d.ts +68 -0
- package/{lib → dist/esm}/handler/parser-instance.js +19 -47
- package/dist/esm/handler/token-parser.d.ts +91 -0
- package/{lib → dist/esm}/handler/token-parser.js +71 -155
- package/dist/esm/handler/types.d.ts +91 -0
- package/dist/esm/handler/types.js +3 -0
- package/dist/esm/imap-commands.d.ts +16 -0
- package/dist/esm/imap-commands.js +67 -0
- package/dist/esm/imap-flow.d.ts +676 -0
- package/{lib → dist/esm}/imap-flow.js +761 -1789
- package/dist/esm/jp-decoder.d.ts +12 -0
- package/{lib → dist/esm}/jp-decoder.js +6 -21
- package/dist/esm/limited-passthrough.d.ts +25 -0
- package/{lib → dist/esm}/limited-passthrough.js +7 -20
- package/dist/esm/logger.d.ts +3 -0
- package/dist/esm/logger.js +4 -0
- package/dist/esm/package-info.d.ts +3 -0
- package/dist/esm/package-info.js +4 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/proxy-connection.d.ts +33 -0
- package/{lib → dist/esm}/proxy-connection.js +56 -127
- package/dist/esm/search-compiler.d.ts +34 -0
- package/{lib → dist/esm}/search-compiler.js +54 -110
- package/dist/esm/special-use.d.ts +22 -0
- package/dist/esm/special-use.js +907 -0
- package/dist/esm/tools.d.ts +427 -0
- package/dist/esm/tools.js +1446 -0
- package/dist/esm/types.d.ts +828 -0
- package/dist/esm/types.js +4 -0
- package/package.json +60 -20
- package/.gitattributes +0 -1
- package/.github/CODE_OF_CONDUCT.md +0 -76
- package/.github/FUNDING.yml +0 -4
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
- package/.github/contributing.md +0 -17
- package/.github/workflows/release.yaml +0 -36
- package/.github/workflows/stale.yml +0 -29
- package/.github/workflows/test.yml +0 -51
- package/.ncurc.js +0 -4
- package/.prettierignore +0 -4
- package/.prettierrc.js +0 -8
- package/.release-please-manifest.json +0 -3
- package/CLAUDE.md +0 -104
- package/Gruntfile.js +0 -23
- package/eslint.config.js +0 -45
- package/lib/handler/imap-formal-syntax.js +0 -189
- package/lib/handler/imap-handler.js +0 -17
- package/lib/imap-commands.js +0 -45
- package/lib/logger.js +0 -5
- package/lib/special-use.js +0 -923
- package/lib/tools.js +0 -1612
- package/release-please-config.json +0 -10
- package/test/authentication-test.js +0 -101
- package/test/auto-idle-test.js +0 -470
- package/test/bodystructure-test.js +0 -899
- package/test/charsets-test.js +0 -161
- package/test/commands-branches-test.js +0 -1095
- package/test/commands-integration-test.js +0 -11124
- package/test/commands-test.js +0 -73
- package/test/connection-edge-cases-test.js +0 -1828
- package/test/connection-test.js +0 -162
- package/test/copyuid-parser-test.js +0 -173
- package/test/fetch-generator-test.js +0 -218
- package/test/fixtures/fake-timers.js +0 -115
- package/test/fixtures/serialized-mimetorture.js +0 -2738
- package/test/fixtures/test-client.js +0 -101
- package/test/fixtures/test-tls.js +0 -8
- package/test/handler-branches-test.js +0 -310
- package/test/idle-polling-test.js +0 -518
- package/test/imap-compiler-test.js +0 -809
- package/test/imap-flow-compress-test.js +0 -166
- package/test/imap-flow-coverage-test.js +0 -612
- package/test/imap-flow-fetch-download-test.js +0 -909
- package/test/imap-flow-internals-test.js +0 -725
- package/test/imap-flow-methods-test.js +0 -889
- package/test/imap-flow-proxy-paths-test.js +0 -366
- package/test/imap-flow-secure-test.js +0 -573
- package/test/imap-flow-server-test.js +0 -1474
- package/test/imap-formal-syntax-test.js +0 -293
- package/test/imap-parser-test.js +0 -1474
- package/test/imap-stream-edge-cases-test.js +0 -666
- package/test/imap-stream-test.js +0 -177
- package/test/imapflow-test.js +0 -258
- package/test/integration/README.md +0 -52
- package/test/integration/dovecot-test.conf +0 -27
- package/test/integration/rev2-live-test.js +0 -431
- package/test/integration/run-rev2-tests.sh +0 -75
- package/test/integration-test.js +0 -83
- package/test/jp-decoder-test.js +0 -304
- package/test/limited-passthrough-test.js +0 -299
- package/test/memory-cleanup-test.js +0 -144
- package/test/memory-leak-test.js +0 -667
- package/test/parser-limits-test.js +0 -292
- package/test/proxy-connection-test.js +0 -738
- package/test/reliability-improvements-test.js +0 -548
- package/test/search-compiler-test.js +0 -1300
- package/test/search-test.js +0 -329
- package/test/special-use-test.js +0 -418
- package/test/starttls-injection-test.js +0 -181
- package/test/tag-correlation-test.js +0 -333
- package/test/timer-policy-test.js +0 -227
- package/test/token-parser-test.js +0 -456
- package/test/tools-test.js +0 -2013
- package/test/unhandled-rejection-test.js +0 -661
|
@@ -1,612 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Targeted coverage for ImapFlow internals that need precise state setups:
|
|
4
|
-
// the lock queue processor (processLocks), authentication fallbacks, and a few
|
|
5
|
-
// reader/handler error branches.
|
|
6
|
-
|
|
7
|
-
const { ImapFlow } = require('../lib/imap-flow');
|
|
8
|
-
const { makeSocketStub } = require('./fixtures/test-client');
|
|
9
|
-
|
|
10
|
-
const makeClient = (overrides = {}) => {
|
|
11
|
-
let client = new ImapFlow({
|
|
12
|
-
host: 'imap.example.com',
|
|
13
|
-
port: 993,
|
|
14
|
-
auth: { user: 'test', pass: 'test' },
|
|
15
|
-
logger: false,
|
|
16
|
-
...overrides
|
|
17
|
-
});
|
|
18
|
-
client.socket = { destroyed: false, destroy: () => {} };
|
|
19
|
-
client.usable = true;
|
|
20
|
-
return client;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const drain = () => new Promise(resolve => setImmediate(resolve));
|
|
24
|
-
|
|
25
|
-
// ============================================================================
|
|
26
|
-
// processLocks
|
|
27
|
-
// ============================================================================
|
|
28
|
-
|
|
29
|
-
module.exports['Coverage: processLocks is re-entrant safe (already processing)'] = async test => {
|
|
30
|
-
let client = makeClient();
|
|
31
|
-
let resolveOpen;
|
|
32
|
-
// mailboxOpen blocks until we resolve it, keeping processingLock busy
|
|
33
|
-
client.mailboxOpen = () =>
|
|
34
|
-
new Promise(resolve => {
|
|
35
|
-
resolveOpen = () => resolve({ path: 'A' });
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
let lockAPromise = client.getMailboxLock('A');
|
|
39
|
-
await drain(); // let processLocks start and await mailboxOpen
|
|
40
|
-
|
|
41
|
-
// Second request while the first is still being processed -> hits the
|
|
42
|
-
// "already processing" early return.
|
|
43
|
-
let lockBPromise = client.getMailboxLock('B');
|
|
44
|
-
await drain();
|
|
45
|
-
|
|
46
|
-
// Now let the first mailbox open complete
|
|
47
|
-
client.mailbox = { path: 'A', readOnly: false };
|
|
48
|
-
resolveOpen();
|
|
49
|
-
let lockA = await lockAPromise;
|
|
50
|
-
test.ok(lockA);
|
|
51
|
-
|
|
52
|
-
// Releasing A lets B be processed; B opens a different mailbox
|
|
53
|
-
client.mailboxOpen = async () => {
|
|
54
|
-
client.mailbox = { path: 'B', readOnly: false };
|
|
55
|
-
return { path: 'B' };
|
|
56
|
-
};
|
|
57
|
-
lockA.release();
|
|
58
|
-
let lockB = await lockBPromise;
|
|
59
|
-
test.ok(lockB);
|
|
60
|
-
lockB.release();
|
|
61
|
-
await drain();
|
|
62
|
-
test.done();
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
module.exports['Coverage: processLocks yields to the event loop on many failing locks'] = async test => {
|
|
66
|
-
let client = makeClient();
|
|
67
|
-
client.usable = false; // every lock fails immediately via the NoConnection path
|
|
68
|
-
|
|
69
|
-
let promises = [];
|
|
70
|
-
for (let i = 0; i < 7; i++) {
|
|
71
|
-
promises.push(client.getMailboxLock('M' + i).catch(err => err));
|
|
72
|
-
}
|
|
73
|
-
let results = await Promise.all(promises);
|
|
74
|
-
test.equal(results.length, 7);
|
|
75
|
-
results.forEach(r => test.equal(r.code, 'NoConnection'));
|
|
76
|
-
test.done();
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
module.exports['Coverage: processLocks marks mailbox missing when SELECT NO and LIST verify throws'] = async test => {
|
|
80
|
-
let client = makeClient();
|
|
81
|
-
client.mailboxOpen = async () => {
|
|
82
|
-
let err = new Error('SELECT failed');
|
|
83
|
-
err.responseStatus = 'NO';
|
|
84
|
-
throw err;
|
|
85
|
-
};
|
|
86
|
-
// run('LIST') used to verify existence throws -> inner catch (3849-3850)
|
|
87
|
-
client.run = async () => {
|
|
88
|
-
throw new Error('LIST blew up');
|
|
89
|
-
};
|
|
90
|
-
let err = null;
|
|
91
|
-
try {
|
|
92
|
-
await client.getMailboxLock('Ghost');
|
|
93
|
-
} catch (e) {
|
|
94
|
-
err = e;
|
|
95
|
-
}
|
|
96
|
-
test.ok(err);
|
|
97
|
-
test.equal(err.responseStatus, 'NO');
|
|
98
|
-
test.done();
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
module.exports['Coverage: processLocks marks mailboxMissing when LIST returns empty'] = async test => {
|
|
102
|
-
let client = makeClient();
|
|
103
|
-
client.mailboxOpen = async () => {
|
|
104
|
-
let err = new Error('SELECT failed');
|
|
105
|
-
err.responseStatus = 'NO';
|
|
106
|
-
throw err;
|
|
107
|
-
};
|
|
108
|
-
client.run = async () => []; // empty LIST -> mailbox confirmed missing
|
|
109
|
-
let err = null;
|
|
110
|
-
try {
|
|
111
|
-
await client.getMailboxLock('Ghost');
|
|
112
|
-
} catch (e) {
|
|
113
|
-
err = e;
|
|
114
|
-
}
|
|
115
|
-
test.ok(err.mailboxMissing);
|
|
116
|
-
test.done();
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
module.exports['Coverage: processLocks yields after 5 processed locks'] = async test => {
|
|
120
|
-
let client = makeClient();
|
|
121
|
-
client.usable = false; // each queued lock fails and continues
|
|
122
|
-
let rejects = 0;
|
|
123
|
-
for (let i = 0; i < 6; i++) {
|
|
124
|
-
client.locks.push({
|
|
125
|
-
resolve: () => {},
|
|
126
|
-
reject: () => {
|
|
127
|
-
rejects++;
|
|
128
|
-
},
|
|
129
|
-
path: 'P',
|
|
130
|
-
options: {},
|
|
131
|
-
lockId: i
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
await client.processLocks();
|
|
135
|
-
test.equal(rejects, 6);
|
|
136
|
-
test.done();
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
module.exports['Coverage: getMailboxLock includes description in trace logs'] = async test => {
|
|
140
|
-
let client = makeClient();
|
|
141
|
-
client.mailbox = { path: 'INBOX', readOnly: false };
|
|
142
|
-
// a description on the lock options exercises the `...(options.description && {...})` spreads
|
|
143
|
-
let lock = await client.getMailboxLock('INBOX', { description: 'my work' });
|
|
144
|
-
test.ok(lock);
|
|
145
|
-
lock.release();
|
|
146
|
-
await drain();
|
|
147
|
-
test.done();
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
module.exports['Coverage: processLocks logs active lock and returns when already processing'] = async test => {
|
|
151
|
-
let client = makeClient();
|
|
152
|
-
client.mailbox = { path: 'INBOX' }; // so the log records the active mailbox path
|
|
153
|
-
client.processingLock = true; // simulate a concurrent processor
|
|
154
|
-
client.currentLock = { lockId: 7, options: { description: 'held lock' } };
|
|
155
|
-
// returns immediately after logging the active lock details
|
|
156
|
-
await client.processLocks();
|
|
157
|
-
test.ok(true);
|
|
158
|
-
test.done();
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
// ============================================================================
|
|
162
|
-
// close() internal error handling
|
|
163
|
-
// ============================================================================
|
|
164
|
-
|
|
165
|
-
module.exports['Coverage: close swallows streamer cleanup errors'] = test => {
|
|
166
|
-
let client = makeClient();
|
|
167
|
-
client.streamer = {
|
|
168
|
-
removeListener() {},
|
|
169
|
-
destroyed: false,
|
|
170
|
-
destroy() {
|
|
171
|
-
throw new Error('streamer destroy boom');
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
// Should not throw despite the streamer.destroy failure
|
|
175
|
-
client.close();
|
|
176
|
-
test.ok(true);
|
|
177
|
-
test.done();
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
module.exports['Coverage: close swallows top-level errors'] = test => {
|
|
181
|
-
let client = makeClient();
|
|
182
|
-
// folders.clear() throws -> the outer try/catch in close() handles it
|
|
183
|
-
client.folders = {
|
|
184
|
-
clear() {
|
|
185
|
-
throw new Error('folders boom');
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
client.close();
|
|
189
|
-
test.ok(true);
|
|
190
|
-
test.done();
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
module.exports['Coverage: lock acquired via SELECT logs description'] = async test => {
|
|
194
|
-
let client = makeClient();
|
|
195
|
-
client.mailbox = false; // force the SELECT/EXAMINE path
|
|
196
|
-
client.mailboxOpen = async path => {
|
|
197
|
-
client.mailbox = { path, readOnly: false };
|
|
198
|
-
return { path };
|
|
199
|
-
};
|
|
200
|
-
let lock = await client.getMailboxLock('Work', { description: 'selected-path lock' });
|
|
201
|
-
test.equal(client.mailbox.path, 'Work');
|
|
202
|
-
lock.release();
|
|
203
|
-
await drain();
|
|
204
|
-
test.done();
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
module.exports['Coverage: lock failure logs description'] = async test => {
|
|
208
|
-
let client = makeClient();
|
|
209
|
-
client.mailbox = false;
|
|
210
|
-
client.mailboxOpen = async () => {
|
|
211
|
-
throw new Error('cannot open');
|
|
212
|
-
};
|
|
213
|
-
let err = null;
|
|
214
|
-
try {
|
|
215
|
-
await client.getMailboxLock('Bad', { description: 'failing lock' });
|
|
216
|
-
} catch (e) {
|
|
217
|
-
err = e;
|
|
218
|
-
}
|
|
219
|
-
test.ok(err);
|
|
220
|
-
test.done();
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
module.exports['Coverage: getMailboxLock logs active lock description while one is held'] = test => {
|
|
224
|
-
let client = makeClient();
|
|
225
|
-
// a held lock carrying a description exercises the activeLock spread in the request log
|
|
226
|
-
client.currentLock = { lockId: 1, options: { description: 'currently held' } };
|
|
227
|
-
let p = client.getMailboxLock('Queued', { description: 'queued lock' });
|
|
228
|
-
p.catch(() => {}); // it will never resolve (held lock blocks); just exercise the log
|
|
229
|
-
test.ok(p && typeof p.then === 'function');
|
|
230
|
-
client.close(); // rejects the queued lock and cleans up
|
|
231
|
-
test.done();
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
// ============================================================================
|
|
235
|
-
// initialOK / serverBye fallbacks
|
|
236
|
-
// ============================================================================
|
|
237
|
-
|
|
238
|
-
module.exports['Coverage: initialOK tolerates a greeting with no attributes'] = async test => {
|
|
239
|
-
let client = makeClient();
|
|
240
|
-
client.isClosed = true; // so beginSession short-circuits without starting a session
|
|
241
|
-
await client.initialOK({}); // no .attributes -> (message.attributes || [])
|
|
242
|
-
test.equal(client.greeting, '');
|
|
243
|
-
test.done();
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
module.exports['Coverage: serverBye defaults the reason when none is given'] = async test => {
|
|
247
|
-
let client = makeClient();
|
|
248
|
-
await client.serverBye({}); // no attributes -> reason falls back to the default
|
|
249
|
-
test.equal(client.byeReason, 'Server closed connection');
|
|
250
|
-
test.done();
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
// ============================================================================
|
|
254
|
-
// authenticate fallbacks
|
|
255
|
-
// ============================================================================
|
|
256
|
-
|
|
257
|
-
module.exports['Coverage: authenticate throws when state is LOGOUT'] = async test => {
|
|
258
|
-
let client = makeClient();
|
|
259
|
-
client.state = client.states.LOGOUT;
|
|
260
|
-
let err = null;
|
|
261
|
-
try {
|
|
262
|
-
await client.authenticate();
|
|
263
|
-
} catch (e) {
|
|
264
|
-
err = e;
|
|
265
|
-
}
|
|
266
|
-
test.ok(err);
|
|
267
|
-
test.done();
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
module.exports['Coverage: authenticate returns true when already authenticated'] = async test => {
|
|
271
|
-
let client = makeClient();
|
|
272
|
-
client.state = client.states.AUTHENTICATED;
|
|
273
|
-
let res = await client.authenticate();
|
|
274
|
-
test.equal(res, true);
|
|
275
|
-
test.done();
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
module.exports['Coverage: authenticate throws when run yields falsy auth result'] = async test => {
|
|
279
|
-
let client = makeClient();
|
|
280
|
-
client.state = client.states.NOT_AUTHENTICATED;
|
|
281
|
-
client.capabilities = new Map(); // no AUTH= -> LOGIN path
|
|
282
|
-
client.run = async () => false; // LOGIN returns falsy -> "No matching authentication method"
|
|
283
|
-
let err = null;
|
|
284
|
-
try {
|
|
285
|
-
await client.authenticate();
|
|
286
|
-
} catch (e) {
|
|
287
|
-
err = e;
|
|
288
|
-
}
|
|
289
|
-
test.ok(err);
|
|
290
|
-
test.done();
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
// ============================================================================
|
|
294
|
-
// Socket event handlers (built by setSocketHandlers)
|
|
295
|
-
// ============================================================================
|
|
296
|
-
|
|
297
|
-
module.exports['Coverage: setSocketHandlers removes a lingering connect error handler'] = test => {
|
|
298
|
-
let client = makeClient();
|
|
299
|
-
let removed = null;
|
|
300
|
-
client.socket = {
|
|
301
|
-
destroyed: false,
|
|
302
|
-
once() {},
|
|
303
|
-
on() {},
|
|
304
|
-
removeListener(evt, fn) {
|
|
305
|
-
removed = fn;
|
|
306
|
-
},
|
|
307
|
-
destroy() {}
|
|
308
|
-
};
|
|
309
|
-
client.writeSocket = client.socket;
|
|
310
|
-
let handler = () => {};
|
|
311
|
-
client._connectErrorHandler = handler;
|
|
312
|
-
client.setSocketHandlers();
|
|
313
|
-
test.equal(removed, handler, 'temporary connect error handler removed');
|
|
314
|
-
test.equal(client._connectErrorHandler, null);
|
|
315
|
-
test.done();
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
module.exports['Coverage: initialPREAUTH is a no-op once closed'] = async test => {
|
|
319
|
-
let client = makeClient();
|
|
320
|
-
client.isClosed = true;
|
|
321
|
-
let started = false;
|
|
322
|
-
client.startSession = async () => {
|
|
323
|
-
started = true;
|
|
324
|
-
};
|
|
325
|
-
await client.initialPREAUTH();
|
|
326
|
-
test.equal(started, false);
|
|
327
|
-
test.done();
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
module.exports['Coverage: initialPREAUTH routes startSession failure to onUnhandledError'] = async test => {
|
|
331
|
-
let client = makeClient();
|
|
332
|
-
client.initialResolve = false;
|
|
333
|
-
client.initialReject = false; // no pending connect promise -> fall through to onUnhandledError
|
|
334
|
-
client.startSession = async () => {
|
|
335
|
-
throw new Error('session boom');
|
|
336
|
-
};
|
|
337
|
-
let closedAfter = false;
|
|
338
|
-
client.closeAfter = () => {
|
|
339
|
-
closedAfter = true;
|
|
340
|
-
};
|
|
341
|
-
await client.initialPREAUTH();
|
|
342
|
-
await drain();
|
|
343
|
-
await drain();
|
|
344
|
-
test.equal(client.state, client.states.AUTHENTICATED);
|
|
345
|
-
test.ok(closedAfter, 'closeAfter invoked from the PREAUTH error handler');
|
|
346
|
-
test.done();
|
|
347
|
-
};
|
|
348
|
-
|
|
349
|
-
module.exports['Coverage: _socketError logs and emits the error'] = test => {
|
|
350
|
-
let client = makeClient();
|
|
351
|
-
client.socket = makeSocketStub();
|
|
352
|
-
client.writeSocket = client.socket;
|
|
353
|
-
client.setSocketHandlers();
|
|
354
|
-
let emitted = null;
|
|
355
|
-
client.on('error', err => {
|
|
356
|
-
emitted = err;
|
|
357
|
-
});
|
|
358
|
-
// not connecting/upgrading -> emitError falls through to emit('error')
|
|
359
|
-
client._socketError(new Error('socket failure'));
|
|
360
|
-
test.ok(emitted);
|
|
361
|
-
test.done();
|
|
362
|
-
};
|
|
363
|
-
|
|
364
|
-
module.exports['Coverage: _socketTimeout recovers an IDLE connection with NOOP'] = async test => {
|
|
365
|
-
let client = makeClient();
|
|
366
|
-
client.socket = makeSocketStub();
|
|
367
|
-
client.writeSocket = client.socket;
|
|
368
|
-
client.setSocketHandlers();
|
|
369
|
-
client.idling = true;
|
|
370
|
-
client.usable = true;
|
|
371
|
-
let noopRun = false;
|
|
372
|
-
let idleResumed = false;
|
|
373
|
-
client.run = async cmd => {
|
|
374
|
-
if (cmd === 'NOOP') noopRun = true;
|
|
375
|
-
return true;
|
|
376
|
-
};
|
|
377
|
-
client.idle = async () => {
|
|
378
|
-
idleResumed = true;
|
|
379
|
-
};
|
|
380
|
-
client._socketTimeout();
|
|
381
|
-
await drain();
|
|
382
|
-
await drain();
|
|
383
|
-
test.ok(noopRun, 'NOOP issued to recover IDLE');
|
|
384
|
-
// Restarting IDLE is autoidle()'s decision once the NOOP settles (run() re-arms it);
|
|
385
|
-
// the watchdog handler itself must not bypass the busy guard by calling idle() directly.
|
|
386
|
-
test.equal(idleResumed, false, 'the handler does not restart IDLE by itself');
|
|
387
|
-
test.done();
|
|
388
|
-
};
|
|
389
|
-
|
|
390
|
-
module.exports['Coverage: _socketTimeout emits error when idling but unusable'] = test => {
|
|
391
|
-
let client = makeClient();
|
|
392
|
-
client.socket = makeSocketStub();
|
|
393
|
-
client.writeSocket = client.socket;
|
|
394
|
-
client.setSocketHandlers();
|
|
395
|
-
client.idling = true;
|
|
396
|
-
client.usable = false; // cannot recover -> emitError
|
|
397
|
-
let emitted = null;
|
|
398
|
-
client.on('error', err => {
|
|
399
|
-
emitted = err;
|
|
400
|
-
});
|
|
401
|
-
client._socketTimeout();
|
|
402
|
-
test.ok(emitted);
|
|
403
|
-
test.equal(emitted.code, 'ETIMEOUT');
|
|
404
|
-
test.done();
|
|
405
|
-
};
|
|
406
|
-
|
|
407
|
-
module.exports['Coverage: _socketTimeout emits error for non-IDLE operations'] = test => {
|
|
408
|
-
let client = makeClient();
|
|
409
|
-
client.socket = makeSocketStub();
|
|
410
|
-
client.writeSocket = client.socket;
|
|
411
|
-
client.setSocketHandlers();
|
|
412
|
-
client.idling = false;
|
|
413
|
-
let emitted = null;
|
|
414
|
-
client.on('error', err => {
|
|
415
|
-
emitted = err;
|
|
416
|
-
});
|
|
417
|
-
client._socketTimeout();
|
|
418
|
-
test.ok(emitted);
|
|
419
|
-
test.equal(emitted.code, 'ETIMEOUT');
|
|
420
|
-
test.done();
|
|
421
|
-
};
|
|
422
|
-
|
|
423
|
-
module.exports['Coverage: _socketTimeout IDLE recovery closes on failure'] = async test => {
|
|
424
|
-
let client = makeClient();
|
|
425
|
-
client.socket = makeSocketStub();
|
|
426
|
-
client.writeSocket = client.socket;
|
|
427
|
-
client.setSocketHandlers();
|
|
428
|
-
client.idling = true;
|
|
429
|
-
client.usable = true;
|
|
430
|
-
client.run = async () => {
|
|
431
|
-
throw new Error('NOOP failed');
|
|
432
|
-
};
|
|
433
|
-
let closed = false;
|
|
434
|
-
client.close = () => {
|
|
435
|
-
closed = true;
|
|
436
|
-
};
|
|
437
|
-
client._socketTimeout();
|
|
438
|
-
await drain();
|
|
439
|
-
await drain();
|
|
440
|
-
test.ok(closed, 'connection closed after failed IDLE recovery');
|
|
441
|
-
test.done();
|
|
442
|
-
};
|
|
443
|
-
|
|
444
|
-
// ============================================================================
|
|
445
|
-
// reader() periodic event-loop yield
|
|
446
|
-
// ============================================================================
|
|
447
|
-
|
|
448
|
-
module.exports['Coverage: reader yields after processing many items in one pass'] = async test => {
|
|
449
|
-
let client = makeClient();
|
|
450
|
-
client.mailbox = { path: 'INBOX', exists: 0 };
|
|
451
|
-
// Queue 12 untagged EXISTS responses so a single reader() pass processes >10
|
|
452
|
-
// items and hits the periodic `await setImmediate` yield.
|
|
453
|
-
let items = [];
|
|
454
|
-
for (let i = 1; i <= 12; i++) {
|
|
455
|
-
items.push({ payload: Buffer.from(`* ${i} EXISTS`), literals: [], next: () => {} });
|
|
456
|
-
}
|
|
457
|
-
let idx = 0;
|
|
458
|
-
client.streamer = {
|
|
459
|
-
read: () => (idx < items.length ? items[idx++] : null)
|
|
460
|
-
};
|
|
461
|
-
client.on('exists', () => {});
|
|
462
|
-
await client.reader();
|
|
463
|
-
test.equal(idx, 12, 'all items drained in one reader pass');
|
|
464
|
-
test.done();
|
|
465
|
-
};
|
|
466
|
-
|
|
467
|
-
// ============================================================================
|
|
468
|
-
// reader() error branches (handlers throwing must not break the loop)
|
|
469
|
-
// ============================================================================
|
|
470
|
-
|
|
471
|
-
// Helper: drive reader() over a fixed list of pre-built data items.
|
|
472
|
-
const runReaderWith = async (client, payloads) => {
|
|
473
|
-
let items = payloads.map(p => ({ payload: Buffer.from(p), literals: [], next: () => {} }));
|
|
474
|
-
let idx = 0;
|
|
475
|
-
client.streamer = { read: () => (idx < items.length ? items[idx++] : null) };
|
|
476
|
-
await client.reader();
|
|
477
|
-
return idx;
|
|
478
|
-
};
|
|
479
|
-
|
|
480
|
-
module.exports['Coverage: reader swallows a throwing onPlusTag handler'] = async test => {
|
|
481
|
-
let client = makeClient();
|
|
482
|
-
client.currentRequest = {
|
|
483
|
-
options: {
|
|
484
|
-
onPlusTag: async () => {
|
|
485
|
-
throw new Error('plus boom');
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
};
|
|
489
|
-
let count = await runReaderWith(client, ['+ go ahead']);
|
|
490
|
-
test.equal(count, 1);
|
|
491
|
-
test.done();
|
|
492
|
-
};
|
|
493
|
-
|
|
494
|
-
module.exports['Coverage: reader swallows a throwing section handler'] = async test => {
|
|
495
|
-
let client = makeClient();
|
|
496
|
-
client.sectionHandlers = {
|
|
497
|
-
CAPABILITY: async () => {
|
|
498
|
-
throw new Error('section boom');
|
|
499
|
-
}
|
|
500
|
-
};
|
|
501
|
-
let count = await runReaderWith(client, ['* OK [CAPABILITY FOO] hi']);
|
|
502
|
-
test.equal(count, 1);
|
|
503
|
-
test.done();
|
|
504
|
-
};
|
|
505
|
-
|
|
506
|
-
module.exports['Coverage: reader swallows a throwing untagged handler'] = async test => {
|
|
507
|
-
let client = makeClient();
|
|
508
|
-
client.untaggedHandlers = {
|
|
509
|
-
FOO: async () => {
|
|
510
|
-
throw new Error('untagged boom');
|
|
511
|
-
}
|
|
512
|
-
};
|
|
513
|
-
let count = await runReaderWith(client, ['* FOO bar']);
|
|
514
|
-
test.equal(count, 1);
|
|
515
|
-
test.done();
|
|
516
|
-
};
|
|
517
|
-
|
|
518
|
-
module.exports['Coverage: reader swallows a throwing trySend after a tagged response'] = async test => {
|
|
519
|
-
let client = makeClient();
|
|
520
|
-
let resolved = false;
|
|
521
|
-
client.currentRequest = { tag: 'A1', sent: true };
|
|
522
|
-
client.requestTagMap.set('A1', {
|
|
523
|
-
command: 'NOOP',
|
|
524
|
-
attributes: [],
|
|
525
|
-
options: {},
|
|
526
|
-
resolve: arg => {
|
|
527
|
-
resolved = true;
|
|
528
|
-
// the reader awaits arg.next(); call it so the loop proceeds
|
|
529
|
-
arg.next();
|
|
530
|
-
},
|
|
531
|
-
reject: () => {}
|
|
532
|
-
});
|
|
533
|
-
client.trySend = async () => {
|
|
534
|
-
throw new Error('trySend boom');
|
|
535
|
-
};
|
|
536
|
-
let count = await runReaderWith(client, ['A1 OK done']);
|
|
537
|
-
test.equal(count, 1);
|
|
538
|
-
test.ok(resolved, 'request still resolved despite trySend failure');
|
|
539
|
-
test.done();
|
|
540
|
-
};
|
|
541
|
-
|
|
542
|
-
module.exports['Coverage: reader handles compiler failure when building executedCommand'] = async test => {
|
|
543
|
-
let client = makeClient();
|
|
544
|
-
let rejected = null;
|
|
545
|
-
// A request whose attributes cannot be compiled -> the executedCommand try/catch is hit
|
|
546
|
-
client.currentRequest = { tag: 'A1', sent: true };
|
|
547
|
-
client.requestTagMap.set('A1', {
|
|
548
|
-
command: 'NOOP',
|
|
549
|
-
// a circular/odd attribute that makes the logging compiler throw
|
|
550
|
-
attributes: [
|
|
551
|
-
{
|
|
552
|
-
type: 'LITERAL',
|
|
553
|
-
get value() {
|
|
554
|
-
throw new Error('compile boom');
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
],
|
|
558
|
-
options: {},
|
|
559
|
-
resolve: () => {},
|
|
560
|
-
reject: err => {
|
|
561
|
-
rejected = err;
|
|
562
|
-
}
|
|
563
|
-
});
|
|
564
|
-
await runReaderWith(client, ['A1 NO it failed']);
|
|
565
|
-
test.ok(rejected);
|
|
566
|
-
test.equal(rejected.responseStatus, 'NO');
|
|
567
|
-
test.done();
|
|
568
|
-
};
|
|
569
|
-
|
|
570
|
-
module.exports['Coverage: reader caps very large throttle backoff'] = async test => {
|
|
571
|
-
let client = makeClient();
|
|
572
|
-
let rejected = null;
|
|
573
|
-
client.currentRequest = { tag: 'A1', sent: true };
|
|
574
|
-
client.requestTagMap.set('A1', {
|
|
575
|
-
command: 'FETCH',
|
|
576
|
-
attributes: [],
|
|
577
|
-
options: {},
|
|
578
|
-
resolve: () => {},
|
|
579
|
-
reject: err => {
|
|
580
|
-
rejected = err;
|
|
581
|
-
}
|
|
582
|
-
});
|
|
583
|
-
// Run the reader; immediately abort the throttle wait via close() so the test
|
|
584
|
-
// does not actually wait. The large backoff value exercises the 5-minute cap.
|
|
585
|
-
let readerPromise = runReaderWith(client, ['A1 BAD Request is throttled. Suggested Backoff Time: 999999999 milliseconds']);
|
|
586
|
-
// give the reader a tick to register the throttle timer, then abort it
|
|
587
|
-
await drain();
|
|
588
|
-
for (let entry of client._throttleWaits) {
|
|
589
|
-
clearTimeout(entry.timer);
|
|
590
|
-
entry.resolve(true);
|
|
591
|
-
}
|
|
592
|
-
client._throttleWaits.clear();
|
|
593
|
-
await readerPromise;
|
|
594
|
-
test.ok(rejected);
|
|
595
|
-
test.done();
|
|
596
|
-
};
|
|
597
|
-
|
|
598
|
-
// ============================================================================
|
|
599
|
-
// beginSession when already closed
|
|
600
|
-
// ============================================================================
|
|
601
|
-
|
|
602
|
-
module.exports['Coverage: beginSession is a no-op once closed'] = test => {
|
|
603
|
-
let client = makeClient();
|
|
604
|
-
client.isClosed = true;
|
|
605
|
-
let started = false;
|
|
606
|
-
client.startSession = async () => {
|
|
607
|
-
started = true;
|
|
608
|
-
};
|
|
609
|
-
client.beginSession(() => {});
|
|
610
|
-
test.equal(started, false, 'startSession not invoked when closed');
|
|
611
|
-
test.done();
|
|
612
|
-
};
|