Haraka 3.1.2 → 3.1.4

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.
Files changed (66) hide show
  1. package/.prettierignore +2 -0
  2. package/CONTRIBUTORS.md +24 -2
  3. package/Changes.md +48 -0
  4. package/Plugins.md +81 -64
  5. package/README.md +1 -1
  6. package/bin/haraka +9 -7
  7. package/config/connection.ini +10 -0
  8. package/config/smtp.ini +0 -9
  9. package/connection.js +15 -19
  10. package/docs/CoreConfig.md +2 -3
  11. package/docs/Plugins.md +1 -1
  12. package/docs/plugins/aliases.md +0 -2
  13. package/docs/plugins/queue/qmail-queue.md +0 -1
  14. package/docs/tutorials/Migrating_from_v1_to_v2.md +1 -1
  15. package/logger.js +2 -2
  16. package/outbound/client_pool.js +1 -1
  17. package/outbound/hmail.js +76 -83
  18. package/outbound/index.js +36 -34
  19. package/outbound/queue.js +231 -176
  20. package/package.json +29 -31
  21. package/plugins/prevent_credential_leaks.js +2 -2
  22. package/plugins/process_title.js +1 -1
  23. package/plugins/queue/smtp_forward.js +1 -1
  24. package/plugins/status.js +8 -5
  25. package/plugins/tls.js +1 -1
  26. package/plugins.js +19 -14
  27. package/rfc1869.js +10 -10
  28. package/run_tests +20 -2
  29. package/server.js +15 -10
  30. package/smtp_client.js +2 -9
  31. package/test/config/tls/haraka.local.pem +47 -47
  32. package/test/connection.js +286 -147
  33. package/test/fixtures/line_socket.js +1 -0
  34. package/test/fixtures/util_hmailitem.js +1 -1
  35. package/test/outbound/bounce_net_errors.js +176 -0
  36. package/test/outbound/bounce_rfc3464.js +303 -0
  37. package/test/outbound/hmail.js +140 -104
  38. package/test/outbound/index.js +61 -101
  39. package/test/outbound/qfile.js +25 -25
  40. package/test/outbound/queue.js +233 -0
  41. package/test/plugins/queue/smtp_forward.js +1 -1
  42. package/test/plugins/record_envelope_addresses.js +93 -0
  43. package/test/plugins/tls.js +2 -2
  44. package/test/plugins/xclient.js +137 -0
  45. package/test/rfc1869.js +43 -0
  46. package/test/smtp_client.js +6 -6
  47. package/test/transaction.js +486 -201
  48. package/tls_socket.js +3 -3
  49. package/transaction.js +33 -10
  50. package/config/me +0 -1
  51. package/config/rabbitmq.ini +0 -10
  52. package/config/rabbitmq_amqplib.ini +0 -19
  53. package/config/tls_cert.pem +0 -23
  54. package/config/tls_key.pem +0 -28
  55. package/docs/plugins/queue/rabbitmq.md +0 -34
  56. package/docs/plugins/queue/rabbitmq_amqplib.md +0 -51
  57. package/plugins/queue/rabbitmq.js +0 -141
  58. package/plugins/queue/rabbitmq_amqplib.js +0 -96
  59. package/test/config/tls/ec.pem +0 -23
  60. package/test/config/tls/mismatched.pem +0 -49
  61. package/test/outbound_bounce_net_errors.js +0 -157
  62. package/test/outbound_bounce_rfc3464.js +0 -366
  63. package/test/queue/multibyte +0 -0
  64. package/test/queue/plain +0 -0
  65. package/test/test-queue/delete-me +0 -0
  66. package/test/tls_socket.js +0 -273
@@ -1,155 +1,191 @@
1
+ 'use strict'
2
+
3
+ const { describe, it, before, beforeEach, afterEach } = require('node:test')
1
4
  const assert = require('node:assert')
5
+ const { EventEmitter } = require('node:events')
2
6
  const fs = require('node:fs')
3
7
  const path = require('node:path')
4
8
 
5
- const Hmail = require('../../outbound/hmail')
6
- const outbound = require('../../outbound/index')
9
+ // Load outbound/index FIRST to avoid the circular-dependency boot-order issue.
10
+ const outbound = require('../../outbound')
11
+ const Hmail = outbound.HMailItem
12
+ const client_pool = require('../../outbound/client_pool')
13
+
14
+ // ── Helpers ───────────────────────────────────────────────────────────────────
15
+
16
+ const onEvent = (emitter, event) => new Promise((resolve) => emitter.once(event, resolve))
17
+
18
+ // ── Tests ─────────────────────────────────────────────────────────────────────
7
19
 
8
20
  describe('outbound/hmail', () => {
9
- beforeEach((done) => {
10
- this.hmail = new Hmail(
21
+ let hmail
22
+
23
+ beforeEach(() => {
24
+ hmail = new Hmail(
11
25
  '1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka',
12
26
  'test/queue/1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka',
13
27
  {},
14
28
  )
15
- done()
16
29
  })
17
30
 
18
- it('sort_mx', (done) => {
19
- const sorted = this.hmail.sort_mx([
31
+ describe('socket error/timeout handler robustness (#3388)', () => {
32
+ const mx = { using_lmtp: false, port: 25, exchange: 'mx.example.com', bind: null, bind_helo: 'test' }
33
+ let origRelease
34
+
35
+ function makeSocket() {
36
+ const s = new EventEmitter()
37
+ s.name = 'mock'
38
+ s.writable = true
39
+ s.write = () => {}
40
+ s.destroy = () => {}
41
+ return s
42
+ }
43
+
44
+ beforeEach(() => {
45
+ origRelease = client_pool.release_client
46
+ client_pool.release_client = () => {}
47
+ hmail.todo = { rcpt_to: [] }
48
+ hmail.try_deliver = () => {}
49
+ hmail.logerror = () => {}
50
+ })
51
+
52
+ afterEach(() => {
53
+ client_pool.release_client = origRelease
54
+ })
55
+
56
+ it('error then timeout does not throw ERR_UNHANDLED_ERROR', () => {
57
+ const socket = makeSocket()
58
+ hmail.try_deliver_host_on_socket(mx, '1.2.3.4', 25, socket)
59
+ socket.emit('error', new Error('connection refused'))
60
+ assert.doesNotThrow(() => socket.emit('timeout'), 'timeout after error must not crash')
61
+ })
62
+
63
+ it('timeout then error does not throw ERR_UNHANDLED_ERROR', () => {
64
+ const socket = makeSocket()
65
+ hmail.try_deliver_host_on_socket(mx, '1.2.3.4', 25, socket)
66
+ socket.emit('timeout')
67
+ assert.doesNotThrow(
68
+ () => socket.emit('error', new Error('late error')),
69
+ 'error after timeout must not crash',
70
+ )
71
+ })
72
+
73
+ it('multiple timeouts do not throw ERR_UNHANDLED_ERROR', () => {
74
+ const socket = makeSocket()
75
+ hmail.try_deliver_host_on_socket(mx, '1.2.3.4', 25, socket)
76
+ socket.emit('timeout')
77
+ assert.doesNotThrow(() => socket.emit('timeout'), 'second timeout must not crash')
78
+ })
79
+ })
80
+
81
+ it('sort_mx orders by priority ascending', () => {
82
+ const sorted = hmail.sort_mx([
20
83
  { exchange: 'mx2.example.com', priority: 5 },
21
84
  { exchange: 'mx1.example.com', priority: 6 },
22
85
  ])
23
86
  assert.equal(sorted[0].exchange, 'mx2.example.com')
24
- done()
25
87
  })
26
- it('sort_mx, shuffled', (done) => {
27
- const sorted = this.hmail.sort_mx([
88
+
89
+ it('sort_mx shuffles equal-priority entries', () => {
90
+ const sorted = hmail.sort_mx([
28
91
  { exchange: 'mx2.example.com', priority: 5 },
29
92
  { exchange: 'mx1.example.com', priority: 6 },
30
93
  { exchange: 'mx3.example.com', priority: 6 },
31
94
  ])
32
95
  assert.equal(sorted[0].exchange, 'mx2.example.com')
33
- assert.ok(sorted[1].exchange == 'mx3.example.com' || sorted[1].exchange == 'mx1.example.com')
34
- done()
96
+ assert.ok(['mx1.example.com', 'mx3.example.com'].includes(sorted[1].exchange))
35
97
  })
36
- it('force_tls', (done) => {
37
- this.hmail.todo = { domain: 'miss.example.com' }
38
- this.hmail.obtls.cfg = {
39
- force_tls_hosts: ['1.2.3.4', 'hit.example.com'],
40
- }
41
- assert.equal(this.hmail.get_force_tls({ exchange: '1.2.3.4' }), true)
42
- assert.equal(this.hmail.get_force_tls({ exchange: '1.2.3.5' }), false)
43
- this.hmail.todo = { domain: 'hit.example.com' }
44
- assert.equal(this.hmail.get_force_tls({ exchange: '1.2.3.5' }), true)
45
- done()
98
+
99
+ it('get_force_tls matches by IP and domain', () => {
100
+ hmail.todo = { domain: 'miss.example.com' }
101
+ hmail.obtls.cfg = { force_tls_hosts: ['1.2.3.4', 'hit.example.com'] }
102
+ assert.equal(hmail.get_force_tls({ exchange: '1.2.3.4' }), true)
103
+ assert.equal(hmail.get_force_tls({ exchange: '1.2.3.5' }), false)
104
+ hmail.todo = { domain: 'hit.example.com' }
105
+ assert.equal(hmail.get_force_tls({ exchange: '1.2.3.5' }), true)
46
106
  })
47
107
  })
48
108
 
49
- describe('outbound/hmail.HMailItem', () => {
50
- it('normal queue file', (done) => {
51
- this.hmail = new Hmail(
109
+ const TOOLONG_FIXTURE = 'test/queue/1509000000000_1509000000000_0_99999_ToLong_1_haraka'
110
+
111
+ const makeToolongFixture = () => {
112
+ const buf = Buffer.alloc(50)
113
+ buf.writeUInt32BE(9999, 0) // declares 9999 bytes but file has only 46 after the header
114
+ buf.write('{"domain":"example.com"', 4)
115
+ fs.writeFileSync(TOOLONG_FIXTURE, buf)
116
+ }
117
+
118
+ describe('outbound/hmail.HMailItem — queue file loading', () => {
119
+ before(makeToolongFixture)
120
+
121
+ it('loads a valid queue file', async () => {
122
+ const h = new Hmail(
52
123
  '1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka',
53
124
  'test/queue/1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka',
54
125
  {},
55
126
  )
56
- this.hmail.on('ready', () => {
57
- // console.log(this.hmail);
58
- assert.ok(this.hmail)
59
- done()
60
- })
61
- this.hmail.on('error', (err) => {
62
- console.log(err)
63
- assert.equal(err, undefined)
64
- done()
65
- })
127
+ await onEvent(h, 'ready')
128
+ assert.ok(h)
66
129
  })
67
- it('normal TODO w/multibyte chars loads w/o error', (done) => {
68
- this.hmail = new Hmail('1507509981169_1507509981169_0_61403_e0Y0Ym_1_qfile', 'test/fixtures/todo_qfile.txt', {})
69
- this.hmail.on('ready', () => {
70
- // console.log(this.hmail);
71
- assert.ok(this.hmail)
72
- done()
73
- })
74
- this.hmail.on('error', (err) => {
75
- console.log(err)
76
- assert.equal(err, undefined)
77
- done()
78
- })
130
+
131
+ it('loads a TODO with multibyte chars without error', async () => {
132
+ const h = new Hmail('1507509981169_1507509981169_0_61403_e0Y0Ym_1_qfile', 'test/fixtures/todo_qfile.txt', {})
133
+ await onEvent(h, 'ready')
134
+ assert.ok(h)
79
135
  })
80
- it('too short TODO length declared', (done) => {
81
- this.hmail = new Hmail(
136
+
137
+ it('emits error on too-short declared TODO length', async () => {
138
+ const h = new Hmail(
82
139
  '1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka',
83
140
  'test/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka',
84
141
  {},
85
142
  )
86
- this.hmail.on('ready', () => {
87
- // console.log(this.hmail);
88
- assert.ok(this.hmail)
89
- done()
90
- })
91
- this.hmail.on('error', (err) => {
92
- console.log(err)
93
- assert.ok(err)
94
- done()
143
+ const err = await new Promise((resolve) => {
144
+ h.once('ready', () => resolve(null))
145
+ h.once('error', resolve)
95
146
  })
147
+ assert.ok(err, 'expected an error for truncated TODO')
96
148
  })
97
- it('too long TODO length declared', (done) => {
98
- this.hmail = new Hmail(
99
- '1508269674999_1508269674999_0_34002_socVUF_1_haraka',
100
- 'test/queue/1508269674999_1508269674999_0_34002_socVUF_1_haraka',
101
- {},
102
- )
103
- this.hmail.on('ready', () => {
104
- // console.log(this.hmail);
105
- assert.ok(this.hmail)
106
- done()
107
- })
108
- this.hmail.on('error', (err) => {
109
- console.log(err)
110
- assert.ok(err)
111
- done()
149
+
150
+ it('emits error on too-long declared TODO length', async () => {
151
+ // Recreate fixture in case a prior run renamed it to the error queue
152
+ makeToolongFixture()
153
+ const h = new Hmail('1509000000000_1509000000000_0_99999_ToLong_1_haraka', TOOLONG_FIXTURE, {})
154
+ const err = await new Promise((resolve) => {
155
+ h.once('ready', () => resolve(null))
156
+ h.once('error', resolve)
112
157
  })
158
+ assert.ok(err, 'expected an error for oversized TODO')
113
159
  })
114
- it('zero-length file load skip w/o crash', (done) => {
115
- this.hmail = new Hmail('1507509981169_1507509981169_0_61403_e0Y0Ym_2_zero', 'test/queue/zero-length', {})
116
- this.hmail.on('ready', () => {
117
- assert.ok(this.hmail)
118
- done()
119
- })
120
- this.hmail.on('error', (err) => {
121
- console.error(err)
122
- assert.ok(err)
123
- done()
160
+
161
+ it('skips zero-length file without crash', async () => {
162
+ const h = new Hmail('1507509981169_1507509981169_0_61403_e0Y0Ym_2_zero', 'test/queue/zero-length', {})
163
+ await new Promise((resolve) => {
164
+ h.once('ready', resolve)
165
+ h.once('error', resolve)
124
166
  })
167
+ assert.ok(h)
125
168
  })
126
- it('lifecycle, reads and writes a haraka queue file', (done) => {
127
- this.hmail = new Hmail('1507509981169_1507509981169_0_61403_e0Y0Ym_2_qfile', 'test/fixtures/todo_qfile.txt', {})
128
169
 
129
- this.hmail.on('error', (err) => {
130
- // console.log(err);
131
- assert.equals(err, undefined)
132
- done()
133
- })
170
+ it('lifecycle: reads and writes a queue file', async () => {
171
+ const h = new Hmail('1507509981169_1507509981169_0_61403_e0Y0Ym_2_qfile', 'test/fixtures/todo_qfile.txt', {})
134
172
 
135
- this.hmail.on('ready', () => {
136
- const tmpfile = path.resolve('test', 'test-queue', 'delete-me')
137
- const ws = new fs.createWriteStream(tmpfile)
173
+ await onEvent(h, 'ready')
138
174
 
139
- outbound.build_todo(this.hmail.todo, ws, () => {
140
- // console.log('returned from build_todo, piping')
141
- // console.log(this.hmail.todo)
142
- // assert.equals(this.hmail.todo.message_stream.headers.length, 22);
175
+ const tmpfile = path.resolve('test', 'test-queue', 'delete-me')
176
+ await fs.promises.mkdir(path.dirname(tmpfile), { recursive: true })
177
+ const ws = new fs.WriteStream(tmpfile)
143
178
 
144
- const ds = this.hmail.data_stream()
179
+ await new Promise((resolve, reject) => {
180
+ outbound.build_todo(h.todo, ws, () => {
181
+ const ds = h.data_stream()
145
182
  ds.pipe(ws)
146
-
147
- ws.on('close', () => {
148
- // console.log(this.hmail.todo)
149
- assert.equal(fs.statSync(tmpfile).size, 4204)
150
- done()
151
- })
183
+ ws.on('close', resolve)
184
+ ws.on('error', reject)
152
185
  })
153
186
  })
187
+
188
+ assert.equal(fs.statSync(tmpfile).size, 4204)
189
+ fs.unlinkSync(tmpfile)
154
190
  })
155
191
  })
@@ -1,8 +1,9 @@
1
1
  'use strict'
2
2
 
3
+ const { describe, it, beforeEach, afterEach } = require('node:test')
3
4
  const assert = require('node:assert')
4
- const fs = require('fs')
5
- const path = require('path')
5
+ const fs = require('node:fs')
6
+ const path = require('node:path')
6
7
 
7
8
  const constants = require('haraka-constants')
8
9
  const logger = require('../../logger')
@@ -22,56 +23,51 @@ describe('outbound', () => {
22
23
  let contents = lines.join(ending)
23
24
  let result = ''
24
25
 
25
- // Set data_lines to lines in contents
26
26
  let match
27
27
  const re = /^([^\n]*\n?)/
28
28
  while ((match = re.exec(contents))) {
29
29
  let line = match[1]
30
- line = line.replace(/\r?\n?$/, '\r\n') // assure \r\n ending
30
+ line = line.replace(/\r?\n?$/, '\r\n')
31
31
  result += line
32
- contents = contents.substr(match[1].length)
33
- if (contents.length === 0) {
34
- break
35
- }
32
+ contents = contents.substring(match[1].length)
33
+ if (contents.length === 0) break
36
34
  }
37
35
 
38
36
  assert.deepEqual(lines.join('\r\n'), result)
39
37
  }
40
38
  })
41
39
 
42
- it('log_methods added', () => {
40
+ it('log_methods added to HMailItem prototype', () => {
43
41
  const levels = ['DATA', 'PROTOCOL', 'DEBUG', 'INFO', 'NOTICE', 'WARN', 'ERROR', 'CRIT', 'ALERT', 'EMERG']
44
-
45
- const HMailItem = require('../../outbound/hmail')
46
-
42
+ // Load via outbound/index to avoid circular-dep boot-order issue
43
+ const HMailItem = require('../../outbound').HMailItem
47
44
  for (const level of levels) {
48
- assert.ok(HMailItem.prototype[`log${level.toLowerCase()}`], `Log method for level: ${level}`)
45
+ assert.ok(HMailItem.prototype[`log${level.toLowerCase()}`], `log method for ${level}`)
49
46
  }
50
47
  })
51
48
 
52
49
  it('set_temp_fail_intervals coverage', () => {
53
50
  const config = require('../../outbound/config')
54
- // Test default configuration
55
51
  assert.deepEqual(
56
52
  config.cfg.temp_fail_intervals,
57
53
  [64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072],
58
54
  )
59
- // Test a simple configuration
55
+
60
56
  config.cfg.temp_fail_intervals = '10s, 1m*2'
61
57
  config.set_temp_fail_intervals()
62
58
  assert.deepEqual(config.cfg.temp_fail_intervals, [10, 60, 60])
63
- // Test a complex configuration
59
+
64
60
  config.cfg.temp_fail_intervals = '30s, 1m, 5m, 9m, 15m*3, 30m*2, 1h*3, 2h*3, 1d'
65
61
  config.set_temp_fail_intervals()
66
62
  assert.deepEqual(
67
63
  config.cfg.temp_fail_intervals,
68
64
  [30, 60, 300, 540, 900, 900, 900, 1800, 1800, 3600, 3600, 3600, 7200, 7200, 7200, 86400],
69
65
  )
70
- // Test the "none" configuration
66
+
71
67
  config.cfg.temp_fail_intervals = 'none'
72
68
  config.set_temp_fail_intervals()
73
69
  assert.deepEqual(config.cfg.temp_fail_intervals, [])
74
- // Test bad config (should revert to default)
70
+
75
71
  config.cfg.temp_fail_intervals = '60 min'
76
72
  config.set_temp_fail_intervals()
77
73
  assert.deepEqual(
@@ -81,29 +77,26 @@ describe('outbound', () => {
81
77
  })
82
78
 
83
79
  describe('get_tls_options', () => {
84
- beforeEach((done) => {
80
+ let outbound, obtls
81
+
82
+ beforeEach(async () => {
85
83
  process.env.HARAKA_TEST_DIR = path.resolve('test')
86
- this.outbound = require('../../outbound')
87
- this.obtls = require('../../outbound/tls')
84
+ outbound = require('../../outbound')
85
+ obtls = require('../../outbound/tls')
88
86
  const tls_socket = require('../../tls_socket')
89
87
 
90
- // reset config to load from tests directory
91
88
  const testDir = path.resolve('test')
92
- this.outbound.config = this.outbound.config.module_config(testDir)
93
- this.obtls.test_config(tls_socket.config.module_config(testDir), this.outbound.config)
94
- this.obtls.init(done)
89
+ outbound.config = outbound.config.module_config(testDir)
90
+ obtls.test_config(tls_socket.config.module_config(testDir), outbound.config)
91
+ await new Promise((resolve) => obtls.init(resolve))
95
92
  })
96
93
 
97
- afterEach((done) => {
94
+ afterEach(() => {
98
95
  delete process.env.HARAKA_TEST_DIR
99
- done()
100
96
  })
101
97
 
102
98
  it('gets TLS properties from tls.ini.outbound', () => {
103
- const tls_config = this.obtls.get_tls_options({
104
- exchange: 'mail.example.com',
105
- })
106
-
99
+ const tls_config = obtls.get_tls_options({ exchange: 'mail.example.com' })
107
100
  assert.deepEqual(tls_config, {
108
101
  servername: 'mail.example.com',
109
102
  key: fs.readFileSync(path.resolve('test', 'config', 'outbound_tls_key.pem')),
@@ -122,116 +115,83 @@ describe('outbound', () => {
122
115
  })
123
116
 
124
117
  describe('build_todo', () => {
125
- beforeEach((done) => {
126
- this.outbound = require('../../outbound')
118
+ let outbound
119
+
120
+ beforeEach(() => {
121
+ outbound = require('../../outbound')
127
122
  try {
128
123
  fs.unlinkSync('test/queue/multibyte')
129
124
  fs.unlinkSync('test/queue/plain')
130
125
  } catch (ignore) {}
131
- done()
132
126
  })
133
127
 
134
- it('saves a file', () => {
128
+ it('saves a plain queue file', () => {
135
129
  const todo = JSON.parse(
136
130
  '{"queue_time":1507509981169,"domain":"redacteed.com","rcpt_to":[{"original":"<postmaster@redacteed.com>","original_host":"redacteed.com","host":"redacteed.com","user":"postmaster"}],"mail_from":{"original":"<matt@tnpi.net>","original_host":"tnpi.net","host":"tnpi.net","user":"matt"},"notes":{"authentication_results":["spf=pass smtp.mailfrom=tnpi.net"],"spf_mail_result":"Pass","spf_mail_record":"v=spf1 a mx include:mx.theartfarm.com ?include:forwards._spf.tnpi.net include:lists._spf.tnpi.net -all","attachment_count":0,"attachments":[{"ctype":"application/pdf","filename":"FileWithoutAccent Chars.pdf","extension":".pdf","md5":"6c1d5f5c047cff3f6320b1210970bdf6"}],"attachment_ctypes":["application/pdf","multipart/mixed","text/plain","application/pdf"],"attachment_files":["FileWithoutaccent Chars.pdf"],"attachment_archive_files":[]},"uuid":"1D5483B0-3E00-4280-A961-3AFD2017B4FC.1"}',
137
131
  )
138
132
  const fd = fs.openSync('test/queue/plain', 'w')
139
- const ws = new fs.createWriteStream('test/queue/plain', {
140
- fd,
141
- flags: constants.WRITE_EXCL,
142
- })
143
- ws.on('close', () => {
144
- // console.log(arguments);
145
- assert.ok(1)
146
- })
147
- ws.on('error', (e) => {
148
- console.error(e)
149
- })
150
- this.outbound.build_todo(todo, ws, () => {
133
+ const ws = new fs.createWriteStream('test/queue/plain', { fd, flags: constants.WRITE_EXCL })
134
+ ws.on('error', (e) => console.error(e))
135
+ outbound.build_todo(todo, ws, () => {
151
136
  ws.write(Buffer.from('This is the message body'))
152
- fs.fsync(fd, () => {
153
- ws.close()
154
- })
137
+ fs.fsync(fd, () => ws.close())
155
138
  })
139
+ assert.ok(true)
156
140
  })
157
141
 
158
- it('saves a file with multibyte chars', () => {
142
+ it('saves a queue file with multibyte chars', () => {
159
143
  const todo = JSON.parse(
160
- '{"queue_time":1507509981169,"domain":"redacteed.com","rcpt_to":[{"original":"<postmaster@redacteed.com>","original_host":"redacteed.com","host":"redacteed.com","user":"postmaster"}],"mail_from":{"original":"<matt@tnpi.net>","original_host":"tnpi.net","host":"tnpi.net","user":"matt"},"notes":{"authentication_results":["spf=pass smtp.mailfrom=tnpi.net"],"spf_mail_result":"Pass","spf_mail_record":"v=spf1 a mx include:mx.theartfarm.com ?include:forwards._spf.tnpi.net include:lists._spf.tnpi.net -all","attachment_count":0,"attachments":[{"ctype":"application/pdf","filename":"FileWîthÁccent Chars.pdf","extension":".pdf","md5":"6c1d5f5c047cff3f6320b1210970bdf6"}],"attachment_ctypes":["application/pdf","multipart/mixed","text/plain","application/pdf"],"attachment_files":["FileWîthÁccent Chars.pdf"],"attachment_archive_files":[]},"uuid":"1D5483B0-3E00-4280-A961-3AFD2017B4FC.1"}',
144
+ '{"queue_time":1507509981169,"domain":"redacteed.com","rcpt_to":[{"original":"<postmaster@redacteed.com>","original_host":"redacteed.com","host":"redacteed.com","user":"postmaster"}],"mail_from":{"original":"<matt@tnpi.net>","original_host":"tnpi.net","host":"tnpi.net","user":"matt"},"notes":{"authentication_results":["spf=pass smtp.mailfrom=tnpi.net"],"spf_mail_result":"Pass","spf_mail_record":"v=spf1 a mx include:mx.theartfarm.com ?include:forwards._spf.tnpi.net include:lists._spf.tnpi.net -all","attachment_count":0,"attachments":[{"ctype":"application/pdf","filename":"FileW\\u00eeth\\u00c1ccent Chars.pdf","extension":".pdf","md5":"6c1d5f5c047cff3f6320b1210970bdf6"}],"attachment_ctypes":["application/pdf","multipart/mixed","text/plain","application/pdf"],"attachment_files":["FileW\\u00eeth\\u00c1ccent Chars.pdf"],"attachment_archive_files":[]},"uuid":"1D5483B0-3E00-4280-A961-3AFD2017B4FC.1"}',
161
145
  )
162
146
  const fd = fs.openSync('test/queue/multibyte', 'w')
163
- const ws = new fs.WriteStream('test/queue/multibyte', {
164
- fd,
165
- flags: constants.WRITE_EXCL,
166
- })
167
- ws.on('close', () => {
168
- assert.ok(1)
169
- })
170
- ws.on('error', (e) => {
171
- console.error(e)
172
- })
173
- this.outbound.build_todo(todo, ws, () => {
147
+ const ws = new fs.WriteStream('test/queue/multibyte', { fd, flags: constants.WRITE_EXCL })
148
+ ws.on('error', (e) => console.error(e))
149
+ outbound.build_todo(todo, ws, () => {
174
150
  ws.write(Buffer.from('This is the message body'))
175
- fs.fsync(fd, () => {
176
- ws.close()
177
- })
151
+ fs.fsync(fd, () => ws.close())
178
152
  })
153
+ assert.ok(true)
179
154
  })
180
155
  })
181
156
 
182
157
  describe('timer_queue', () => {
183
- beforeEach((done) => {
158
+ let outbound, ob_timer_queue
159
+
160
+ beforeEach(() => {
184
161
  process.env.HARAKA_TEST_DIR = path.resolve('test')
185
- this.outbound = require('../../outbound')
162
+ outbound = require('../../outbound')
186
163
  const TimerQueue = require('../../outbound/timer_queue')
187
- this.ob_timer_queue = new TimerQueue(500)
188
- done()
164
+ ob_timer_queue = new TimerQueue(500)
189
165
  })
190
166
 
191
- afterEach((done) => {
167
+ afterEach(() => {
192
168
  delete process.env.HARAKA_TEST_DIR
193
- this.ob_timer_queue.shutdown()
194
- done()
169
+ ob_timer_queue.shutdown()
195
170
  })
196
171
 
197
172
  it('has initial length of 0', () => {
198
- assert.equal(this.ob_timer_queue.length(), 0)
173
+ assert.equal(ob_timer_queue.length(), 0)
199
174
  })
200
175
 
201
176
  it('can add items', () => {
202
- this.ob_timer_queue.add('1', 1000)
203
- this.ob_timer_queue.add('2', 2000)
204
-
205
- assert.equal(this.ob_timer_queue.length(), 2)
177
+ ob_timer_queue.add('1', 1000)
178
+ ob_timer_queue.add('2', 2000)
179
+ assert.equal(ob_timer_queue.length(), 2)
206
180
  })
207
181
 
208
182
  it('can drain items', () => {
209
- this.ob_timer_queue.add('1', 1000)
210
- this.ob_timer_queue.add('2', 2000)
211
-
212
- let tq_length = this.ob_timer_queue.length()
213
-
214
- assert.equal(tq_length, 2)
215
-
216
- this.ob_timer_queue.drain()
217
- tq_length = this.ob_timer_queue.length()
218
-
219
- assert.equal(tq_length, 0)
183
+ ob_timer_queue.add('1', 1000)
184
+ ob_timer_queue.add('2', 2000)
185
+ ob_timer_queue.drain()
186
+ assert.equal(ob_timer_queue.length(), 0)
220
187
  })
221
188
 
222
189
  it('can discard items by id', () => {
223
- this.ob_timer_queue.add('1', 1000)
224
- this.ob_timer_queue.add('2', 2000)
225
-
226
- let tq_length = this.ob_timer_queue.length()
227
-
228
- assert.equal(tq_length, 2)
229
-
230
- this.ob_timer_queue.discard('2')
231
- tq_length = this.ob_timer_queue.length()
232
-
233
- assert.equal(tq_length, 1)
234
- assert.equal(this.ob_timer_queue.queue[0].id, '1')
190
+ ob_timer_queue.add('1', 1000)
191
+ ob_timer_queue.add('2', 2000)
192
+ ob_timer_queue.discard('2')
193
+ assert.equal(ob_timer_queue.length(), 1)
194
+ assert.equal(ob_timer_queue.queue[0].id, '1')
235
195
  })
236
196
  })
237
197
  })