queen-mq 0.16.0 → 1.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/client-v2/Queen.js +71 -15
- package/client-v2/README.md +37 -0
- package/client-v2/builders/QueueBuilder.js +10 -4
- package/client-v2/builders/TransactionBuilder.js +14 -2
- package/client-v2/consumer/ConsumerManager.js +64 -12
- package/client-v2/http/HttpClient.js +382 -32
- package/client-v2/streams/runtime/Runner.js +45 -0
- package/client-v2/utils/defaults.js +20 -1
- package/package.json +4 -3
- package/test-v2/ackwindow.js +258 -0
- package/test-v2/auth.js +65 -149
- package/test-v2/consume.js +2 -0
- package/test-v2/http-unit/hostHeader.test.js +411 -0
- package/test-v2/http-unit/retry429.test.js +314 -0
- package/test-v2/load.js +37 -41
- package/test-v2/maintenance.js +2 -2
- package/test-v2/pop.js +2 -0
- package/test-v2/push.js +29 -35
- package/test-v2/run.js +37 -5
- package/test-v2/semantics.js +792 -0
- package/test-v2/stream/_helpers.js +1 -1
- package/test-v2/stream/tumbling.js +10 -6
- package/test-v2/streams-unit/ack.test.js +203 -0
- package/test-v2/streams-unit/e2e.test.js +4 -1
- package/test-v2/transaction.js +2 -0
- package/test-v2/watermark.js +78 -62
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-routed proxy support: selecting a tenant cluster at a queen_proxy
|
|
3
|
+
* deployment (PLAN_QUEEN_PROXY_CLOUD.md §2 -- the proxy resolves the cluster
|
|
4
|
+
* from the Host header's first DNS label, proxy/src/cache.rs
|
|
5
|
+
* slug_from_host).
|
|
6
|
+
*
|
|
7
|
+
* The defect these tests lock down: `fetch()` silently drops a caller-supplied
|
|
8
|
+
* `Host` header (WHATWG forbidden header name), so a JS user pointing at a
|
|
9
|
+
* shared address either got 421 cluster_unknown or -- worse, on a cell with a
|
|
10
|
+
* default cluster -- was routed to somebody ELSE's cluster with no signal.
|
|
11
|
+
* `hostHeader` advertises the cluster host while the socket stays pinned to the
|
|
12
|
+
* configured address, and a `headers: { Host }` is mapped onto it out loud.
|
|
13
|
+
*
|
|
14
|
+
* Same style as retry429.test.js: real node:http servers, no mocking framework.
|
|
15
|
+
* Every assertion here is about what actually arrives on the wire (req.headers.host).
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { describe, it } from 'node:test'
|
|
19
|
+
import assert from 'node:assert/strict'
|
|
20
|
+
import { createServer } from 'node:http'
|
|
21
|
+
|
|
22
|
+
import { Queen } from '../../client-v2/index.js'
|
|
23
|
+
import { HttpClient } from '../../client-v2/http/HttpClient.js'
|
|
24
|
+
import { LoadBalancer } from '../../client-v2/http/LoadBalancer.js'
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Spin up a real HTTP server that records the Host header (and the rest) of
|
|
28
|
+
* every request it receives, then answers with `respond(hit)`.
|
|
29
|
+
*/
|
|
30
|
+
function withHostServer(run, respond = () => ({ status: 200, body: { ok: true } })) {
|
|
31
|
+
const hits = []
|
|
32
|
+
const server = createServer((req, res) => {
|
|
33
|
+
let raw = ''
|
|
34
|
+
req.on('data', chunk => { raw += chunk })
|
|
35
|
+
req.on('end', () => {
|
|
36
|
+
const hit = { method: req.method, url: req.url, host: req.headers.host, headers: req.headers }
|
|
37
|
+
hits.push(hit)
|
|
38
|
+
const descriptor = respond(hit)
|
|
39
|
+
res.writeHead(descriptor.status ?? 200, { 'Content-Type': 'application/json' })
|
|
40
|
+
res.end(JSON.stringify(descriptor.body ?? { ok: true }))
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
server.listen(0, '127.0.0.1', () => {
|
|
46
|
+
const { port } = server.address()
|
|
47
|
+
const teardown = () => new Promise(r => server.close(r))
|
|
48
|
+
Promise.resolve()
|
|
49
|
+
.then(() => run({ url: `http://127.0.0.1:${port}`, port, hits }))
|
|
50
|
+
.then(
|
|
51
|
+
async (value) => { await teardown(); resolve(value) },
|
|
52
|
+
async (err) => { await teardown(); reject(err) }
|
|
53
|
+
)
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Capture console.warn for the duration of `fn` (the trap warning is not
|
|
59
|
+
* routed through the QUEEN_CLIENT_LOG-gated logger on purpose). */
|
|
60
|
+
async function captureWarnings(fn) {
|
|
61
|
+
const original = console.warn
|
|
62
|
+
const warnings = []
|
|
63
|
+
console.warn = (...args) => { warnings.push(args.join(' ')) }
|
|
64
|
+
try {
|
|
65
|
+
return { value: await fn(), warnings }
|
|
66
|
+
} finally {
|
|
67
|
+
console.warn = original
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// The wire: what Host actually goes out.
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
|
|
75
|
+
describe('HttpClient — hostHeader on the wire', () => {
|
|
76
|
+
it('sends the address as Host when no hostHeader is configured (unchanged default)', async () => {
|
|
77
|
+
await withHostServer(async ({ url, port, hits }) => {
|
|
78
|
+
const client = new HttpClient({ baseUrl: url })
|
|
79
|
+
try {
|
|
80
|
+
await client.get('/x')
|
|
81
|
+
assert.equal(hits.length, 1)
|
|
82
|
+
assert.equal(hits[0].host, `127.0.0.1:${port}`)
|
|
83
|
+
} finally {
|
|
84
|
+
await client.destroy()
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('sends the configured hostHeader verbatim while still connecting to baseUrl', async () => {
|
|
90
|
+
await withHostServer(async ({ url, hits }) => {
|
|
91
|
+
const client = new HttpClient({ baseUrl: url, hostHeader: 'acme.eu1.queenmq.cloud' })
|
|
92
|
+
try {
|
|
93
|
+
const result = await client.get('/x')
|
|
94
|
+
assert.deepEqual(result, { ok: true }, 'the request must still reach the configured address')
|
|
95
|
+
assert.equal(hits[0].host, 'acme.eu1.queenmq.cloud')
|
|
96
|
+
} finally {
|
|
97
|
+
await client.destroy()
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('advertises a bare slug (what queen_proxy turns into a cluster) without appending the real port', async () => {
|
|
103
|
+
await withHostServer(async ({ url, hits }) => {
|
|
104
|
+
const client = new HttpClient({ baseUrl: url, hostHeader: 'sdka' })
|
|
105
|
+
try {
|
|
106
|
+
await client.get('/x')
|
|
107
|
+
assert.equal(hits[0].host, 'sdka', 'slug_from_host takes the first label — no :port noise')
|
|
108
|
+
} finally {
|
|
109
|
+
await client.destroy()
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it('keeps an explicit port in the advertised Host, and never resolves that name in DNS', async () => {
|
|
115
|
+
await withHostServer(async ({ url, hits }) => {
|
|
116
|
+
// `acme.invalid:6711` cannot resolve and 6711 is not our listener: the
|
|
117
|
+
// request can only arrive if the socket is pinned to baseUrl.
|
|
118
|
+
const client = new HttpClient({ baseUrl: url, hostHeader: 'acme.invalid:6711' })
|
|
119
|
+
try {
|
|
120
|
+
await client.get('/x')
|
|
121
|
+
assert.equal(hits[0].host, 'acme.invalid:6711')
|
|
122
|
+
} finally {
|
|
123
|
+
await client.destroy()
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('applies to every verb and keeps the path/body intact', async () => {
|
|
129
|
+
await withHostServer(async ({ url, hits }) => {
|
|
130
|
+
const client = new HttpClient({ baseUrl: url, hostHeader: 'acme' })
|
|
131
|
+
try {
|
|
132
|
+
await client.post('/api/v1/push', { items: [1] })
|
|
133
|
+
await client.delete('/api/v1/queues/q1')
|
|
134
|
+
assert.deepEqual(hits.map(h => h.host), ['acme', 'acme'])
|
|
135
|
+
assert.deepEqual(hits.map(h => h.url), ['/api/v1/push', '/api/v1/queues/q1'])
|
|
136
|
+
} finally {
|
|
137
|
+
await client.destroy()
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('shares one pinned agent across concurrent first requests', async () => {
|
|
143
|
+
await withHostServer(async ({ url, hits }) => {
|
|
144
|
+
const client = new HttpClient({ baseUrl: url, hostHeader: 'acme' })
|
|
145
|
+
try {
|
|
146
|
+
// All five race the (async) agent creation; each must still get the
|
|
147
|
+
// advertised Host, and destroy() must be able to release every socket.
|
|
148
|
+
const results = await Promise.all(Array.from({ length: 5 }, () => client.get('/x')))
|
|
149
|
+
assert.equal(results.length, 5)
|
|
150
|
+
assert.equal(hits.length, 5)
|
|
151
|
+
for (const hit of hits) assert.equal(hit.host, 'acme')
|
|
152
|
+
} finally {
|
|
153
|
+
await client.destroy()
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('leaves other custom headers (and the bearer token) untouched', async () => {
|
|
159
|
+
await withHostServer(async ({ url, hits }) => {
|
|
160
|
+
const client = new HttpClient({
|
|
161
|
+
baseUrl: url,
|
|
162
|
+
bearerToken: 'key-a',
|
|
163
|
+
hostHeader: 'acme',
|
|
164
|
+
headers: { 'X-Trace': 'abc' }
|
|
165
|
+
})
|
|
166
|
+
try {
|
|
167
|
+
await client.get('/x')
|
|
168
|
+
assert.equal(hits[0].host, 'acme')
|
|
169
|
+
assert.equal(hits[0].headers['x-trace'], 'abc')
|
|
170
|
+
assert.equal(hits[0].headers['authorization'], 'Bearer key-a')
|
|
171
|
+
} finally {
|
|
172
|
+
await client.destroy()
|
|
173
|
+
}
|
|
174
|
+
})
|
|
175
|
+
})
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
// The trap: `headers: { Host }` must never be silently dropped.
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
describe('HttpClient — headers: { Host } is mapped, never silently ignored', () => {
|
|
183
|
+
it('maps headers.Host onto hostHeader and warns on the console once', async () => {
|
|
184
|
+
await withHostServer(async ({ url, hits }) => {
|
|
185
|
+
const { warnings } = await captureWarnings(async () => {
|
|
186
|
+
// Distinct value per test: the warn-once dedupe is keyed by value.
|
|
187
|
+
const client = new HttpClient({ baseUrl: url, headers: { Host: 'mapped-a.example.test' } })
|
|
188
|
+
try {
|
|
189
|
+
await client.get('/x')
|
|
190
|
+
} finally {
|
|
191
|
+
await client.destroy()
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
assert.equal(hits[0].host, 'mapped-a.example.test', 'the Host the caller asked for must reach the wire')
|
|
195
|
+
assert.equal(warnings.length, 1, 'exactly one warning for the first occurrence')
|
|
196
|
+
assert.match(warnings[0], /forbidden header name/)
|
|
197
|
+
assert.match(warnings[0], /hostHeader/)
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
it('warns only once for a repeated identical misconfiguration', async () => {
|
|
202
|
+
await withHostServer(async ({ url }) => {
|
|
203
|
+
const { warnings } = await captureWarnings(async () => {
|
|
204
|
+
for (let i = 0; i < 3; i++) {
|
|
205
|
+
const client = new HttpClient({ baseUrl: url, headers: { Host: 'mapped-b.example.test' } })
|
|
206
|
+
await client.get('/x')
|
|
207
|
+
await client.destroy()
|
|
208
|
+
}
|
|
209
|
+
})
|
|
210
|
+
assert.equal(warnings.length, 1, 'one warning per distinct offending value, not per client')
|
|
211
|
+
})
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('accepts a lower-case `host` key too (header names are case-insensitive)', async () => {
|
|
215
|
+
await withHostServer(async ({ url, hits }) => {
|
|
216
|
+
await captureWarnings(async () => {
|
|
217
|
+
const client = new HttpClient({ baseUrl: url, headers: { host: 'mapped-c.example.test' } })
|
|
218
|
+
try {
|
|
219
|
+
await client.get('/x')
|
|
220
|
+
} finally {
|
|
221
|
+
await client.destroy()
|
|
222
|
+
}
|
|
223
|
+
})
|
|
224
|
+
assert.equal(hits[0].host, 'mapped-c.example.test')
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
it('lets an explicit hostHeader win over a conflicting headers.Host, and says so', async () => {
|
|
229
|
+
await withHostServer(async ({ url, hits }) => {
|
|
230
|
+
const { warnings } = await captureWarnings(async () => {
|
|
231
|
+
const client = new HttpClient({
|
|
232
|
+
baseUrl: url,
|
|
233
|
+
hostHeader: 'explicit-d.example.test',
|
|
234
|
+
headers: { Host: 'conflicting-d.example.test' }
|
|
235
|
+
})
|
|
236
|
+
try {
|
|
237
|
+
await client.get('/x')
|
|
238
|
+
} finally {
|
|
239
|
+
await client.destroy()
|
|
240
|
+
}
|
|
241
|
+
})
|
|
242
|
+
assert.equal(hits[0].host, 'explicit-d.example.test')
|
|
243
|
+
assert.equal(warnings.length, 1)
|
|
244
|
+
assert.match(warnings[0], /is ignored because hostHeader/)
|
|
245
|
+
})
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
it('does not warn when headers.Host merely repeats hostHeader', async () => {
|
|
249
|
+
await withHostServer(async ({ url, hits }) => {
|
|
250
|
+
const { warnings } = await captureWarnings(async () => {
|
|
251
|
+
const client = new HttpClient({
|
|
252
|
+
baseUrl: url,
|
|
253
|
+
hostHeader: 'same-e.example.test',
|
|
254
|
+
headers: { Host: 'same-e.example.test' }
|
|
255
|
+
})
|
|
256
|
+
try {
|
|
257
|
+
await client.get('/x')
|
|
258
|
+
} finally {
|
|
259
|
+
await client.destroy()
|
|
260
|
+
}
|
|
261
|
+
})
|
|
262
|
+
assert.equal(hits[0].host, 'same-e.example.test')
|
|
263
|
+
assert.equal(warnings.length, 0)
|
|
264
|
+
})
|
|
265
|
+
})
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
// ---------------------------------------------------------------------------
|
|
269
|
+
// Validation + failure modes: a bad or unhonorable Host must fail loudly.
|
|
270
|
+
// ---------------------------------------------------------------------------
|
|
271
|
+
|
|
272
|
+
describe('HttpClient — hostHeader validation', () => {
|
|
273
|
+
it('rejects a URL-shaped hostHeader at construction', () => {
|
|
274
|
+
assert.throws(
|
|
275
|
+
() => new HttpClient({ baseUrl: 'http://127.0.0.1:1', hostHeader: 'http://acme.example.test' }),
|
|
276
|
+
/bare authority/
|
|
277
|
+
)
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
it('rejects a hostHeader with a path or whitespace', () => {
|
|
281
|
+
assert.throws(() => new HttpClient({ baseUrl: 'http://127.0.0.1:1', hostHeader: 'acme/foo' }), /bare authority/)
|
|
282
|
+
assert.throws(() => new HttpClient({ baseUrl: 'http://127.0.0.1:1', hostHeader: 'acme host' }), /bare authority/)
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
it('rejects an empty hostHeader', () => {
|
|
286
|
+
assert.throws(() => new HttpClient({ baseUrl: 'http://127.0.0.1:1', hostHeader: ' ' }), /non-empty string/)
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
it('treats hostHeader: null as "no override" (default behaviour)', async () => {
|
|
290
|
+
await withHostServer(async ({ url, port, hits }) => {
|
|
291
|
+
const client = new HttpClient({ baseUrl: url, hostHeader: null })
|
|
292
|
+
try {
|
|
293
|
+
await client.get('/x')
|
|
294
|
+
assert.equal(hits[0].host, `127.0.0.1:${port}`)
|
|
295
|
+
} finally {
|
|
296
|
+
await client.destroy()
|
|
297
|
+
}
|
|
298
|
+
})
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
it('refuses to send (instead of leaking the address as Host) once the client is destroyed', async () => {
|
|
302
|
+
await withHostServer(async ({ url, hits }) => {
|
|
303
|
+
const client = new HttpClient({ baseUrl: url, hostHeader: 'acme', retryAttempts: 1 })
|
|
304
|
+
await client.get('/x')
|
|
305
|
+
await client.destroy()
|
|
306
|
+
await assert.rejects(client.get('/x'), /cannot be honored|Refusing to send/)
|
|
307
|
+
assert.equal(hits.length, 1, 'the post-destroy request must not reach the server with the wrong Host')
|
|
308
|
+
})
|
|
309
|
+
})
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
// ---------------------------------------------------------------------------
|
|
313
|
+
// Load balancing: one virtual Host, several real backends.
|
|
314
|
+
// ---------------------------------------------------------------------------
|
|
315
|
+
|
|
316
|
+
describe('HttpClient — hostHeader with multiple backends', () => {
|
|
317
|
+
it('advertises the same Host to every backend while dialing each one', async () => {
|
|
318
|
+
await withHostServer(async ({ url: urlA, hits: hitsA }) => {
|
|
319
|
+
await withHostServer(async ({ url: urlB, hits: hitsB }) => {
|
|
320
|
+
const loadBalancer = new LoadBalancer([urlA, urlB], 'round-robin')
|
|
321
|
+
const client = new HttpClient({ loadBalancer, hostHeader: 'acme.eu1.queenmq.cloud' })
|
|
322
|
+
try {
|
|
323
|
+
for (let i = 0; i < 4; i++) await client.get('/x')
|
|
324
|
+
assert.ok(hitsA.length > 0 && hitsB.length > 0, `both backends must be dialed (${hitsA.length}/${hitsB.length})`)
|
|
325
|
+
for (const hit of [...hitsA, ...hitsB]) {
|
|
326
|
+
assert.equal(hit.host, 'acme.eu1.queenmq.cloud')
|
|
327
|
+
}
|
|
328
|
+
} finally {
|
|
329
|
+
await client.destroy()
|
|
330
|
+
}
|
|
331
|
+
})
|
|
332
|
+
})
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
it('keeps failover working: a 500 on one backend still lands on the other with the right Host', async () => {
|
|
336
|
+
await withHostServer(
|
|
337
|
+
async ({ url: urlA, hits: hitsA }) => {
|
|
338
|
+
await withHostServer(async ({ url: urlB, hits: hitsB }) => {
|
|
339
|
+
const loadBalancer = new LoadBalancer([urlA, urlB], 'round-robin')
|
|
340
|
+
const client = new HttpClient({ loadBalancer, hostHeader: 'acme', retryAttempts: 1 })
|
|
341
|
+
try {
|
|
342
|
+
const result = await client.get('/x')
|
|
343
|
+
assert.deepEqual(result, { ok: true })
|
|
344
|
+
assert.ok(hitsA.length > 0, 'the failing backend was tried')
|
|
345
|
+
assert.ok(hitsB.length > 0, 'and the request failed over to the healthy one')
|
|
346
|
+
for (const hit of [...hitsA, ...hitsB]) assert.equal(hit.host, 'acme')
|
|
347
|
+
} finally {
|
|
348
|
+
await client.destroy()
|
|
349
|
+
}
|
|
350
|
+
})
|
|
351
|
+
},
|
|
352
|
+
() => ({ status: 500, body: { error: 'boom' } })
|
|
353
|
+
)
|
|
354
|
+
})
|
|
355
|
+
})
|
|
356
|
+
|
|
357
|
+
// ---------------------------------------------------------------------------
|
|
358
|
+
// Queen public API wiring.
|
|
359
|
+
// ---------------------------------------------------------------------------
|
|
360
|
+
|
|
361
|
+
describe('Queen — hostHeader config plumbing', () => {
|
|
362
|
+
it('push() through a Queen configured with hostHeader carries it on the wire', async () => {
|
|
363
|
+
await withHostServer(
|
|
364
|
+
async ({ url, hits }) => {
|
|
365
|
+
const queen = new Queen({ url, hostHeader: 'sdkb', bearerToken: 'key-b', handleSignals: false })
|
|
366
|
+
try {
|
|
367
|
+
const result = await queen.queue('q1').push({ hello: 'world' })
|
|
368
|
+
assert.equal(result[0].status, 'queued')
|
|
369
|
+
assert.equal(hits[0].host, 'sdkb')
|
|
370
|
+
assert.equal(hits[0].headers['authorization'], 'Bearer key-b')
|
|
371
|
+
} finally {
|
|
372
|
+
await queen.close()
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
() => ({ status: 200, body: [{ status: 'queued', transactionId: 'tx-1' }] })
|
|
376
|
+
)
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
it('a Queen built with headers: { Host } routes to that cluster (Go/Python parity)', async () => {
|
|
380
|
+
await withHostServer(
|
|
381
|
+
async ({ url, hits }) => {
|
|
382
|
+
const { value: result } = await captureWarnings(async () => {
|
|
383
|
+
const queen = new Queen({ url, headers: { Host: 'parity-f.example.test' }, handleSignals: false })
|
|
384
|
+
try {
|
|
385
|
+
return await queen.queue('q1').push({ hello: 'world' })
|
|
386
|
+
} finally {
|
|
387
|
+
await queen.close()
|
|
388
|
+
}
|
|
389
|
+
})
|
|
390
|
+
assert.equal(result[0].status, 'queued')
|
|
391
|
+
assert.equal(hits[0].host, 'parity-f.example.test')
|
|
392
|
+
},
|
|
393
|
+
() => ({ status: 200, body: [{ status: 'queued', transactionId: 'tx-1' }] })
|
|
394
|
+
)
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
it('a Queen without hostHeader is byte-for-byte unchanged (Host = the address)', async () => {
|
|
398
|
+
await withHostServer(
|
|
399
|
+
async ({ url, port, hits }) => {
|
|
400
|
+
const queen = new Queen({ url, handleSignals: false })
|
|
401
|
+
try {
|
|
402
|
+
await queen.queue('q1').push({ hello: 'world' })
|
|
403
|
+
assert.equal(hits[0].host, `127.0.0.1:${port}`)
|
|
404
|
+
} finally {
|
|
405
|
+
await queen.close()
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
() => ({ status: 200, body: [{ status: 'queued', transactionId: 'tx-1' }] })
|
|
409
|
+
)
|
|
410
|
+
})
|
|
411
|
+
})
|