Haraka 3.1.4 → 3.1.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.
- package/.prettierignore +1 -1
- package/{Changes.md → CHANGELOG.md} +34 -0
- package/CONTRIBUTORS.md +26 -26
- package/README.md +68 -93
- package/SECURITY.md +178 -0
- package/bin/haraka +7 -14
- package/config/plugins +0 -3
- package/docs/Connection.md +126 -39
- package/docs/CoreConfig.md +92 -74
- package/docs/HAProxy.md +41 -25
- package/docs/Logging.md +68 -38
- package/docs/Outbound.md +124 -179
- package/docs/Plugins.md +38 -59
- package/docs/Transaction.md +78 -83
- package/docs/Tutorial.md +122 -209
- package/docs/plugins/aliases.md +1 -141
- package/docs/plugins/auth/auth_ldap.md +2 -39
- package/docs/plugins/max_unrecognized_commands.md +4 -18
- package/docs/plugins/process_title.md +3 -3
- package/docs/plugins/reseed_rng.md +11 -13
- package/docs/plugins/tls.md +7 -7
- package/docs/plugins/toobusy.md +10 -4
- package/docs/tutorials/SettingUpOutbound.md +40 -48
- package/endpoint.js +32 -2
- package/outbound/hmail.js +3 -2
- package/outbound/index.js +3 -0
- package/package.json +21 -34
- package/plugins/queue/smtp_forward.js +4 -4
- package/run_tests +3 -15
- package/server.js +17 -7
- package/smtp_client.js +8 -6
- package/test/connection.js +234 -0
- package/test/endpoint.js +32 -4
- package/test/host_pool.js +57 -31
- package/test/logger.js +75 -135
- package/test/outbound/bounce_net_errors.js +87 -131
- package/test/outbound/bounce_rfc3464.js +177 -254
- package/test/outbound/hmail.js +19 -0
- package/test/outbound/index.js +189 -0
- package/test/outbound/queue.js +92 -0
- package/test/plugins/auth/auth_base.js +39 -44
- package/test/plugins/auth/auth_vpopmaild.js +8 -9
- package/test/plugins/queue/smtp_forward.js +953 -183
- package/test/plugins/rcpt_to.host_list_base.js +58 -93
- package/test/plugins/rcpt_to.in_host_list.js +126 -175
- package/test/plugins/record_envelope_addresses.js +8 -8
- package/test/plugins/status.js +10 -10
- package/test/plugins/tls.js +9 -19
- package/test/plugins/xclient.js +75 -110
- package/test/plugins.js +10 -13
- package/test/rfc1869.js +50 -70
- package/test/server.js +438 -421
- package/test/smtp_client.js +1192 -218
- package/test/tls_socket.js +242 -0
- package/tls_socket.js +18 -22
package/test/logger.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { describe, it, beforeEach } = require('node:test')
|
|
4
|
+
const assert = require('node:assert/strict')
|
|
2
5
|
const util = require('node:util')
|
|
3
6
|
|
|
4
|
-
const _set_up = (
|
|
7
|
+
const _set_up = () => {
|
|
5
8
|
this.logger = require('../logger')
|
|
6
|
-
done()
|
|
7
9
|
}
|
|
8
10
|
|
|
9
11
|
describe('logger', () => {
|
|
@@ -16,55 +18,23 @@ describe('logger', () => {
|
|
|
16
18
|
})
|
|
17
19
|
|
|
18
20
|
describe('log', () => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
this.logger.plugins = { plugin_list: true }
|
|
28
|
-
this.logger.deferred_logs.push({
|
|
29
|
-
level: 'INFO',
|
|
30
|
-
data: 'log test info',
|
|
21
|
+
const formats = ['DEFAULT', 'LOGFMT', 'JSON']
|
|
22
|
+
|
|
23
|
+
for (const fmt of formats) {
|
|
24
|
+
it(`log in ${fmt} format`, () => {
|
|
25
|
+
this.logger.deferred_logs = []
|
|
26
|
+
this.logger.format = this.logger.formats[fmt]
|
|
27
|
+
assert.ok(this.logger.log('WARN', 'test warning'))
|
|
28
|
+
assert.equal(this.logger.deferred_logs.length, 1)
|
|
31
29
|
})
|
|
32
|
-
assert.ok(this.logger.log('INFO', 'another test info'))
|
|
33
|
-
})
|
|
34
30
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
assert.equal(1, this.logger.deferred_logs.length)
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
it('log in logfmt w/deferred', () => {
|
|
44
|
-
this.logger.plugins = { plugin_list: true }
|
|
45
|
-
this.logger.deferred_logs.push({
|
|
46
|
-
level: 'INFO',
|
|
47
|
-
data: 'log test info',
|
|
31
|
+
it(`log in ${fmt} format w/deferred`, () => {
|
|
32
|
+
this.logger.format = this.logger.formats[fmt]
|
|
33
|
+
this.logger.plugins = { plugin_list: true }
|
|
34
|
+
this.logger.deferred_logs.push({ level: 'INFO', data: 'log test info' })
|
|
35
|
+
assert.ok(this.logger.log('INFO', 'another test info'))
|
|
48
36
|
})
|
|
49
|
-
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
it('log in json', () => {
|
|
53
|
-
this.logger.deferred_logs = []
|
|
54
|
-
this.logger.format = this.logger.formats.JSON
|
|
55
|
-
assert.equal(0, this.logger.deferred_logs.length)
|
|
56
|
-
assert.ok(this.logger.log('WARN', 'test warning'))
|
|
57
|
-
assert.equal(1, this.logger.deferred_logs.length)
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it('log in json w/deferred', () => {
|
|
61
|
-
this.logger.plugins = { plugin_list: true }
|
|
62
|
-
this.logger.deferred_logs.push({
|
|
63
|
-
level: 'INFO',
|
|
64
|
-
data: 'log test info',
|
|
65
|
-
})
|
|
66
|
-
assert.ok(this.logger.log('INFO', 'another test info'))
|
|
67
|
-
})
|
|
37
|
+
}
|
|
68
38
|
})
|
|
69
39
|
|
|
70
40
|
describe('level', () => {
|
|
@@ -75,68 +45,40 @@ describe('logger', () => {
|
|
|
75
45
|
})
|
|
76
46
|
|
|
77
47
|
describe('set_format', () => {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
it('set format to DEFAULT if empty', () => {
|
|
97
|
-
this.logger.format = ''
|
|
98
|
-
this.logger.set_format('')
|
|
99
|
-
assert.equal(this.logger.format, this.logger.formats.DEFAULT)
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
it('set format to DEFAULT if lowercase', () => {
|
|
103
|
-
this.logger.format = ''
|
|
104
|
-
this.logger.set_format('default')
|
|
105
|
-
assert.equal(this.logger.format, this.logger.formats.DEFAULT)
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
it('set format to DEFAULT if invalid', () => {
|
|
109
|
-
this.logger.format = ''
|
|
110
|
-
this.logger.set_format('invalid')
|
|
111
|
-
assert.equal(this.logger.format, this.logger.formats.DEFAULT)
|
|
112
|
-
})
|
|
48
|
+
// [input, expected format key]
|
|
49
|
+
const cases = [
|
|
50
|
+
['DEFAULT', 'DEFAULT'],
|
|
51
|
+
['LOGFMT', 'LOGFMT'],
|
|
52
|
+
['JSON', 'JSON'],
|
|
53
|
+
['', 'DEFAULT'], // empty → DEFAULT
|
|
54
|
+
['default', 'DEFAULT'], // case-insensitive → DEFAULT
|
|
55
|
+
['invalid', 'DEFAULT'], // unknown → DEFAULT
|
|
56
|
+
]
|
|
57
|
+
for (const [input, expectedKey] of cases) {
|
|
58
|
+
it(`set_format(${JSON.stringify(input)}) → ${expectedKey}`, () => {
|
|
59
|
+
this.logger.format = ''
|
|
60
|
+
this.logger.set_format(input)
|
|
61
|
+
assert.equal(this.logger.format, this.logger.formats[expectedKey])
|
|
62
|
+
})
|
|
63
|
+
}
|
|
113
64
|
})
|
|
114
65
|
|
|
115
66
|
describe('set_loglevel', () => {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
it('set loglevel to 6', () => {
|
|
132
|
-
this.logger.set_loglevel(6)
|
|
133
|
-
assert.equal(this.logger.loglevel, 6)
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
it('set loglevel to WARN if invalid', () => {
|
|
137
|
-
this.logger.set_loglevel('invalid')
|
|
138
|
-
assert.equal(this.logger.loglevel, this.logger.levels.WARN)
|
|
139
|
-
})
|
|
67
|
+
// [input, expected level key or null for numeric assertion]
|
|
68
|
+
const cases = [
|
|
69
|
+
['LOGINFO', 'LOGINFO'],
|
|
70
|
+
['INFO', 'INFO'],
|
|
71
|
+
['emerg', 'EMERG'], // case-insensitive
|
|
72
|
+
[6, null], // numeric passthrough
|
|
73
|
+
['invalid', 'WARN'], // unknown → WARN
|
|
74
|
+
]
|
|
75
|
+
for (const [input, expectedKey] of cases) {
|
|
76
|
+
it(`set_loglevel(${JSON.stringify(input)}) → ${expectedKey ?? input}`, () => {
|
|
77
|
+
this.logger.set_loglevel(input)
|
|
78
|
+
const expected = expectedKey ? this.logger.levels[expectedKey] : input
|
|
79
|
+
assert.equal(this.logger.loglevel, expected)
|
|
80
|
+
})
|
|
81
|
+
}
|
|
140
82
|
})
|
|
141
83
|
|
|
142
84
|
describe('set_timestamps', () => {
|
|
@@ -219,40 +161,38 @@ describe('logger', () => {
|
|
|
219
161
|
})
|
|
220
162
|
|
|
221
163
|
describe('log_if_level', () => {
|
|
222
|
-
it('
|
|
223
|
-
assert.
|
|
164
|
+
it('is a function', () => {
|
|
165
|
+
assert.equal(typeof this.logger.log_if_level, 'function')
|
|
224
166
|
})
|
|
225
167
|
|
|
226
|
-
it('
|
|
168
|
+
it('returns a logging function', () => {
|
|
227
169
|
this.logger.loglevel = 9
|
|
228
170
|
const f = this.logger.log_if_level('INFO', 'LOGINFO')
|
|
229
|
-
assert.
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
assert.ok(f(false))
|
|
248
|
-
assert.equal(3, this.logger.deferred_logs.length)
|
|
249
|
-
})
|
|
171
|
+
assert.equal(typeof f, 'function')
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
// Each of these runs independently with a fresh deferred_logs
|
|
175
|
+
for (const [label, msg] of [
|
|
176
|
+
['string', 'test info message'],
|
|
177
|
+
['null', null],
|
|
178
|
+
['false', false],
|
|
179
|
+
['0 (falsy number)', 0],
|
|
180
|
+
]) {
|
|
181
|
+
it(`logs ${label} value and appends to deferred_logs`, () => {
|
|
182
|
+
this.logger.loglevel = 9
|
|
183
|
+
this.logger.deferred_logs = []
|
|
184
|
+
const f = this.logger.log_if_level('INFO', 'LOGINFO')
|
|
185
|
+
assert.ok(f(msg))
|
|
186
|
+
assert.equal(this.logger.deferred_logs.length, 1)
|
|
187
|
+
})
|
|
188
|
+
}
|
|
250
189
|
|
|
251
|
-
it('
|
|
190
|
+
it('records correct level in deferred log entry', () => {
|
|
252
191
|
this.logger.loglevel = 9
|
|
192
|
+
this.logger.deferred_logs = []
|
|
253
193
|
const f = this.logger.log_if_level('INFO', 'LOGINFO')
|
|
254
|
-
|
|
255
|
-
assert.equal(
|
|
194
|
+
f('test info message')
|
|
195
|
+
assert.equal(this.logger.deferred_logs[0].level, 'INFO')
|
|
256
196
|
})
|
|
257
197
|
})
|
|
258
198
|
|
|
@@ -26,151 +26,107 @@ const queue_dir = path.resolve(__dirname, '../test-queue')
|
|
|
26
26
|
|
|
27
27
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
28
28
|
|
|
29
|
-
const ensureQueueDir = () =>
|
|
29
|
+
const ensureQueueDir = () => fs.promises.mkdir(queue_dir, { recursive: true })
|
|
30
|
+
|
|
31
|
+
const cleanQueueDir = async () => {
|
|
32
|
+
if (!fs.existsSync(queue_dir)) return
|
|
33
|
+
for (const file of fs.readdirSync(queue_dir)) {
|
|
34
|
+
const full = path.resolve(queue_dir, file)
|
|
35
|
+
if (fs.lstatSync(full).isDirectory()) throw new Error(`unexpected subdirectory: ${full}`)
|
|
36
|
+
fs.unlinkSync(full)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Creates a mock HMailItem, resolving with it or rejecting on error. */
|
|
41
|
+
const mockHMailItem = (ctx, opts = {}) =>
|
|
30
42
|
new Promise((resolve, reject) => {
|
|
31
|
-
|
|
32
|
-
if (exists) return resolve()
|
|
33
|
-
fs.mkdir(queue_dir, (err) => (err ? reject(err) : resolve()))
|
|
34
|
-
})
|
|
43
|
+
util_hmailitem.newMockHMailItem(ctx, reject, opts, resolve)
|
|
35
44
|
})
|
|
36
45
|
|
|
37
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Intercepts `HMailItem.prototype[method]`, calls `assertion(this)` when invoked,
|
|
48
|
+
* then triggers the action under test via `trigger()`.
|
|
49
|
+
*/
|
|
50
|
+
const interceptAndAssert = (method, assertion, trigger) =>
|
|
38
51
|
new Promise((resolve, reject) => {
|
|
39
|
-
|
|
40
|
-
|
|
52
|
+
const orig = HMailItem.prototype[method]
|
|
53
|
+
HMailItem.prototype[method] = function () {
|
|
41
54
|
try {
|
|
42
|
-
|
|
43
|
-
const full = path.resolve(queue_dir, file)
|
|
44
|
-
if (fs.lstatSync(full).isDirectory()) return reject(new Error(`unexpected subdirectory: ${full}`))
|
|
45
|
-
fs.unlinkSync(full)
|
|
46
|
-
}
|
|
55
|
+
assertion(this)
|
|
47
56
|
resolve()
|
|
48
|
-
} catch (
|
|
49
|
-
reject(
|
|
57
|
+
} catch (e) {
|
|
58
|
+
reject(e)
|
|
59
|
+
} finally {
|
|
60
|
+
HMailItem.prototype[method] = orig
|
|
50
61
|
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
/** Creates a mock HMailItem, resolving with it or rejecting on error. */
|
|
55
|
-
const mockHMailItem = (ctx, opts = {}) =>
|
|
56
|
-
new Promise((resolve, reject) => {
|
|
57
|
-
util_hmailitem.newMockHMailItem(ctx, reject, opts, resolve)
|
|
62
|
+
}
|
|
63
|
+
trigger()
|
|
58
64
|
})
|
|
59
65
|
|
|
60
66
|
// ── Tests ─────────────────────────────────────────────────────────────────────
|
|
61
67
|
|
|
68
|
+
// [method, dsn_status, optional setup fn, trigger fn, test name]
|
|
69
|
+
const testCases = [
|
|
70
|
+
{
|
|
71
|
+
name: 'get_mx=DENY triggers bounce with dsn_status 5.1.2',
|
|
72
|
+
method: 'bounce',
|
|
73
|
+
status: '5.1.2',
|
|
74
|
+
setup: (h) => {
|
|
75
|
+
h.domain = h.todo.domain
|
|
76
|
+
},
|
|
77
|
+
trigger: (h) => HMailItem.prototype.get_mx_respond.apply(h, [constants.deny, {}]),
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'get_mx=DENYSOFT triggers temp_fail with dsn_status 4.1.2',
|
|
81
|
+
method: 'temp_fail',
|
|
82
|
+
status: '4.1.2',
|
|
83
|
+
setup: (h) => {
|
|
84
|
+
h.domain = h.todo.domain
|
|
85
|
+
},
|
|
86
|
+
trigger: (h) => HMailItem.prototype.get_mx_respond.apply(h, [constants.denysoft, {}]),
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'get_mx_error({code:NXDOMAIN}) triggers bounce with dsn_status 5.1.2',
|
|
90
|
+
method: 'bounce',
|
|
91
|
+
status: '5.1.2',
|
|
92
|
+
trigger: (h) => HMailItem.prototype.get_mx_error.apply(h, [{ code: dns.NXDOMAIN }]),
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: "get_mx_error({code:'SOME-OTHER-ERR'}) triggers temp_fail with dsn_status 4.1.0",
|
|
96
|
+
method: 'temp_fail',
|
|
97
|
+
status: '4.1.0',
|
|
98
|
+
trigger: (h) => HMailItem.prototype.get_mx_error.apply(h, [{ code: 'SOME-OTHER-ERR' }, {}]),
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'found_mx with empty exchange triggers bounce with dsn_status 5.1.2',
|
|
102
|
+
method: 'bounce',
|
|
103
|
+
status: '5.1.2',
|
|
104
|
+
trigger: (h) => HMailItem.prototype.found_mx.apply(h, [[{ priority: 0, exchange: '' }]]),
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: 'try_deliver with empty mxlist triggers temp_fail with dsn_status 5.1.2',
|
|
108
|
+
method: 'temp_fail',
|
|
109
|
+
status: '5.1.2',
|
|
110
|
+
setup: (h) => {
|
|
111
|
+
h.mxlist = []
|
|
112
|
+
},
|
|
113
|
+
trigger: (h) => HMailItem.prototype.try_deliver.apply(h, []),
|
|
114
|
+
},
|
|
115
|
+
]
|
|
116
|
+
|
|
62
117
|
describe('outbound_bounce_net_errors', () => {
|
|
63
118
|
beforeEach(ensureQueueDir)
|
|
64
119
|
afterEach(cleanQueueDir)
|
|
65
120
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
reject(e)
|
|
76
|
-
} finally {
|
|
77
|
-
HMailItem.prototype.bounce = orig
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
mock_hmail.domain = mock_hmail.todo.domain
|
|
81
|
-
HMailItem.prototype.get_mx_respond.apply(mock_hmail, [constants.deny, {}])
|
|
82
|
-
})
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
it('get_mx=DENYSOFT triggers temp_fail with dsn_status 4.1.2', async () => {
|
|
86
|
-
const mock_hmail = await mockHMailItem(outbound_context)
|
|
87
|
-
await new Promise((resolve, reject) => {
|
|
88
|
-
const orig = HMailItem.prototype.temp_fail
|
|
89
|
-
HMailItem.prototype.temp_fail = function (err, opts) {
|
|
90
|
-
try {
|
|
91
|
-
assert.equal(this.todo.rcpt_to[0].dsn_status, '4.1.2', 'dsn status')
|
|
92
|
-
resolve()
|
|
93
|
-
} catch (e) {
|
|
94
|
-
reject(e)
|
|
95
|
-
} finally {
|
|
96
|
-
HMailItem.prototype.temp_fail = orig
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
mock_hmail.domain = mock_hmail.todo.domain
|
|
100
|
-
HMailItem.prototype.get_mx_respond.apply(mock_hmail, [constants.denysoft, {}])
|
|
101
|
-
})
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('get_mx_error({code:NXDOMAIN}) triggers bounce with dsn_status 5.1.2', async () => {
|
|
105
|
-
const mock_hmail = await mockHMailItem(outbound_context)
|
|
106
|
-
await new Promise((resolve, reject) => {
|
|
107
|
-
const orig = HMailItem.prototype.bounce
|
|
108
|
-
HMailItem.prototype.bounce = function (err, opts) {
|
|
109
|
-
try {
|
|
110
|
-
assert.equal(this.todo.rcpt_to[0].dsn_status, '5.1.2', 'dsn status')
|
|
111
|
-
resolve()
|
|
112
|
-
} catch (e) {
|
|
113
|
-
reject(e)
|
|
114
|
-
} finally {
|
|
115
|
-
HMailItem.prototype.bounce = orig
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
HMailItem.prototype.get_mx_error.apply(mock_hmail, [{ code: dns.NXDOMAIN }])
|
|
119
|
-
})
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
it("get_mx_error({code:'SOME-OTHER-ERR'}) triggers temp_fail with dsn_status 4.1.0", async () => {
|
|
123
|
-
const mock_hmail = await mockHMailItem(outbound_context)
|
|
124
|
-
await new Promise((resolve, reject) => {
|
|
125
|
-
const orig = HMailItem.prototype.temp_fail
|
|
126
|
-
HMailItem.prototype.temp_fail = function (err, opts) {
|
|
127
|
-
try {
|
|
128
|
-
assert.equal(this.todo.rcpt_to[0].dsn_status, '4.1.0', 'dsn status')
|
|
129
|
-
resolve()
|
|
130
|
-
} catch (e) {
|
|
131
|
-
reject(e)
|
|
132
|
-
} finally {
|
|
133
|
-
HMailItem.prototype.temp_fail = orig
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
HMailItem.prototype.get_mx_error.apply(mock_hmail, [{ code: 'SOME-OTHER-ERR' }, {}])
|
|
121
|
+
for (const { name, method, status, setup, trigger } of testCases) {
|
|
122
|
+
it(name, async () => {
|
|
123
|
+
const mock_hmail = await mockHMailItem(outbound_context)
|
|
124
|
+
if (setup) setup(mock_hmail)
|
|
125
|
+
await interceptAndAssert(
|
|
126
|
+
method,
|
|
127
|
+
(h) => assert.equal(h.todo.rcpt_to[0].dsn_status, status, 'dsn_status'),
|
|
128
|
+
() => trigger(mock_hmail),
|
|
129
|
+
)
|
|
137
130
|
})
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
it("found_mx(null, [{priority:0,exchange:''}]) triggers bounce with dsn_status 5.1.2", async () => {
|
|
141
|
-
const mock_hmail = await mockHMailItem(outbound_context)
|
|
142
|
-
await new Promise((resolve, reject) => {
|
|
143
|
-
const orig = HMailItem.prototype.bounce
|
|
144
|
-
HMailItem.prototype.bounce = function (err, opts) {
|
|
145
|
-
try {
|
|
146
|
-
assert.equal(this.todo.rcpt_to[0].dsn_status, '5.1.2', 'dsn status')
|
|
147
|
-
resolve()
|
|
148
|
-
} catch (e) {
|
|
149
|
-
reject(e)
|
|
150
|
-
} finally {
|
|
151
|
-
HMailItem.prototype.bounce = orig
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
HMailItem.prototype.found_mx.apply(mock_hmail, [[{ priority: 0, exchange: '' }]])
|
|
155
|
-
})
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
it('try_deliver with empty mxlist triggers temp_fail with dsn_status 5.1.2', async () => {
|
|
159
|
-
const mock_hmail = await mockHMailItem(outbound_context)
|
|
160
|
-
mock_hmail.mxlist = []
|
|
161
|
-
await new Promise((resolve, reject) => {
|
|
162
|
-
const orig = HMailItem.prototype.temp_fail
|
|
163
|
-
HMailItem.prototype.temp_fail = function (err, opts) {
|
|
164
|
-
try {
|
|
165
|
-
assert.equal(this.todo.rcpt_to[0].dsn_status, '5.1.2', 'dsn status')
|
|
166
|
-
resolve()
|
|
167
|
-
} catch (e) {
|
|
168
|
-
reject(e)
|
|
169
|
-
} finally {
|
|
170
|
-
HMailItem.prototype.temp_fail = orig
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
HMailItem.prototype.try_deliver.apply(mock_hmail, [])
|
|
174
|
-
})
|
|
175
|
-
})
|
|
131
|
+
}
|
|
176
132
|
})
|