@zdavison/matador 4.0.3 → 4.2.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/dist/core/matador.d.ts.map +1 -1
- package/dist/core/matador.js +4 -1
- package/dist/index.cjs +156 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/pipeline/pipeline.d.ts +30 -2
- package/dist/pipeline/pipeline.d.ts.map +1 -1
- package/dist/pipeline/pipeline.js +65 -9
- package/dist/retry/index.d.ts +1 -1
- package/dist/retry/index.d.ts.map +1 -1
- package/dist/retry/policy.d.ts +27 -0
- package/dist/retry/policy.d.ts.map +1 -1
- package/dist/retry/standard-policy.d.ts +33 -2
- package/dist/retry/standard-policy.d.ts.map +1 -1
- package/dist/retry/standard-policy.js +73 -9
- package/dist/topology/types.d.ts +14 -0
- package/dist/topology/types.d.ts.map +1 -1
- package/dist/transport/rabbitmq/rabbitmq-transport.d.ts.map +1 -1
- package/dist/transport/rabbitmq/rabbitmq-transport.js +3 -0
- package/package.json +1 -1
- package/dist/checkpoint/context.test.d.ts +0 -2
- package/dist/checkpoint/context.test.d.ts.map +0 -1
- package/dist/checkpoint/context.test.js +0 -428
- package/dist/checkpoint/stores/stores.test.d.ts +0 -2
- package/dist/checkpoint/stores/stores.test.d.ts.map +0 -1
- package/dist/checkpoint/stores/stores.test.js +0 -146
- package/dist/codec/rabbitmq-codec.test.d.ts +0 -2
- package/dist/codec/rabbitmq-codec.test.d.ts.map +0 -1
- package/dist/codec/rabbitmq-codec.test.js +0 -428
- package/dist/core/fanout.test.d.ts +0 -2
- package/dist/core/fanout.test.d.ts.map +0 -1
- package/dist/core/fanout.test.js +0 -1970
- package/dist/core/matador.test.d.ts +0 -2
- package/dist/core/matador.test.d.ts.map +0 -1
- package/dist/core/matador.test.js +0 -813
- package/dist/core/shutdown.test.d.ts +0 -2
- package/dist/core/shutdown.test.d.ts.map +0 -1
- package/dist/core/shutdown.test.js +0 -599
- package/dist/errors/retry-errors.test.d.ts +0 -2
- package/dist/errors/retry-errors.test.d.ts.map +0 -1
- package/dist/errors/retry-errors.test.js +0 -136
- package/dist/pipeline/pipeline.test.d.ts +0 -2
- package/dist/pipeline/pipeline.test.d.ts.map +0 -1
- package/dist/pipeline/pipeline.test.js +0 -1124
- package/dist/retry/standard-policy.test.d.ts +0 -2
- package/dist/retry/standard-policy.test.d.ts.map +0 -1
- package/dist/retry/standard-policy.test.js +0 -196
- package/dist/schema/registry.test.d.ts +0 -2
- package/dist/schema/registry.test.d.ts.map +0 -1
- package/dist/schema/registry.test.js +0 -278
- package/dist/schema/types.test.d.ts +0 -2
- package/dist/schema/types.test.d.ts.map +0 -1
- package/dist/schema/types.test.js +0 -246
- package/dist/topology/builder.test.d.ts +0 -2
- package/dist/topology/builder.test.d.ts.map +0 -1
- package/dist/topology/builder.test.js +0 -547
- package/dist/transport/local/local-transport.test.d.ts +0 -2
- package/dist/transport/local/local-transport.test.d.ts.map +0 -1
- package/dist/transport/local/local-transport.test.js +0 -232
- package/dist/transport/multi/multi-transport.test.d.ts +0 -2
- package/dist/transport/multi/multi-transport.test.d.ts.map +0 -1
- package/dist/transport/multi/multi-transport.test.js +0 -322
- package/dist/transport/rabbitmq/rabbitmq-transport-reconnection.test.d.ts +0 -2
- package/dist/transport/rabbitmq/rabbitmq-transport-reconnection.test.d.ts.map +0 -1
- package/dist/transport/rabbitmq/rabbitmq-transport-reconnection.test.js +0 -149
- package/dist/transport/rabbitmq/rabbitmq-transport.test.d.ts +0 -2
- package/dist/transport/rabbitmq/rabbitmq-transport.test.d.ts.map +0 -1
- package/dist/transport/rabbitmq/rabbitmq-transport.test.js +0 -230
- package/dist/types/event.test.d.ts +0 -2
- package/dist/types/event.test.d.ts.map +0 -1
- package/dist/types/event.test.js +0 -130
|
@@ -1,547 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'bun:test';
|
|
2
|
-
import { UnknownQueueReferenceError } from '../errors/matador-errors.js';
|
|
3
|
-
import { TopologyBuilder, TopologyValidationError } from './builder.js';
|
|
4
|
-
import { findQueueDefinition, getDeadLetterQueueName, getQualifiedQueueName, getRetryQueueName, resolveQueueName, resolveTargetQueueName, } from './types.js';
|
|
5
|
-
describe('TopologyBuilder', () => {
|
|
6
|
-
describe('withNamespace', () => {
|
|
7
|
-
it('should set the namespace', () => {
|
|
8
|
-
const topology = TopologyBuilder.create()
|
|
9
|
-
.withNamespace('myapp')
|
|
10
|
-
.addQueue('events')
|
|
11
|
-
.build();
|
|
12
|
-
expect(topology.namespace).toBe('myapp');
|
|
13
|
-
});
|
|
14
|
-
it('should reject empty namespace', () => {
|
|
15
|
-
const builder = TopologyBuilder.create()
|
|
16
|
-
.withNamespace('')
|
|
17
|
-
.addQueue('events');
|
|
18
|
-
expect(() => builder.build()).toThrow(TopologyValidationError);
|
|
19
|
-
});
|
|
20
|
-
it('should reject namespace starting with number', () => {
|
|
21
|
-
const builder = TopologyBuilder.create()
|
|
22
|
-
.withNamespace('123app')
|
|
23
|
-
.addQueue('events');
|
|
24
|
-
expect(() => builder.build()).toThrow(TopologyValidationError);
|
|
25
|
-
});
|
|
26
|
-
it('should allow hyphens and underscores in namespace', () => {
|
|
27
|
-
const topology = TopologyBuilder.create()
|
|
28
|
-
.withNamespace('my-app_v2')
|
|
29
|
-
.addQueue('events')
|
|
30
|
-
.build();
|
|
31
|
-
expect(topology.namespace).toBe('my-app_v2');
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
describe('addQueue', () => {
|
|
35
|
-
it('should add a queue with defaults', () => {
|
|
36
|
-
const topology = TopologyBuilder.create()
|
|
37
|
-
.withNamespace('test')
|
|
38
|
-
.addQueue('events')
|
|
39
|
-
.build();
|
|
40
|
-
expect(topology.queues).toHaveLength(1);
|
|
41
|
-
expect(topology.queues[0]?.name).toBe('events');
|
|
42
|
-
});
|
|
43
|
-
it('should add queue with options', () => {
|
|
44
|
-
const topology = TopologyBuilder.create()
|
|
45
|
-
.withNamespace('test')
|
|
46
|
-
.addQueue('events', {
|
|
47
|
-
concurrency: 5,
|
|
48
|
-
consumerTimeout: 30000,
|
|
49
|
-
priorities: true,
|
|
50
|
-
})
|
|
51
|
-
.build();
|
|
52
|
-
expect(topology.queues[0]?.concurrency).toBe(5);
|
|
53
|
-
expect(topology.queues[0]?.consumerTimeout).toBe(30000);
|
|
54
|
-
expect(topology.queues[0]?.priorities).toBe(true);
|
|
55
|
-
});
|
|
56
|
-
it('should add multiple queues', () => {
|
|
57
|
-
const topology = TopologyBuilder.create()
|
|
58
|
-
.withNamespace('test')
|
|
59
|
-
.addQueue('events')
|
|
60
|
-
.addQueue('notifications')
|
|
61
|
-
.addQueue('analytics')
|
|
62
|
-
.build();
|
|
63
|
-
expect(topology.queues).toHaveLength(3);
|
|
64
|
-
});
|
|
65
|
-
it('should reject duplicate queue names', () => {
|
|
66
|
-
const builder = TopologyBuilder.create()
|
|
67
|
-
.withNamespace('test')
|
|
68
|
-
.addQueue('events')
|
|
69
|
-
.addQueue('events');
|
|
70
|
-
expect(() => builder.build()).toThrow('Duplicate queue name');
|
|
71
|
-
});
|
|
72
|
-
it('should reject empty queue name', () => {
|
|
73
|
-
const builder = TopologyBuilder.create()
|
|
74
|
-
.withNamespace('test')
|
|
75
|
-
.addQueue('');
|
|
76
|
-
expect(() => builder.build()).toThrow('Queue name cannot be empty');
|
|
77
|
-
});
|
|
78
|
-
it('should reject queue names starting with number', () => {
|
|
79
|
-
const builder = TopologyBuilder.create()
|
|
80
|
-
.withNamespace('test')
|
|
81
|
-
.addQueue('123queue');
|
|
82
|
-
expect(() => builder.build()).toThrow('must start with a letter');
|
|
83
|
-
});
|
|
84
|
-
it('should reject invalid concurrency', () => {
|
|
85
|
-
const builder = TopologyBuilder.create()
|
|
86
|
-
.withNamespace('test')
|
|
87
|
-
.addQueue('events', { concurrency: 0 });
|
|
88
|
-
expect(() => builder.build()).toThrow('concurrency must be at least 1');
|
|
89
|
-
});
|
|
90
|
-
it('should reject negative consumer timeout', () => {
|
|
91
|
-
const builder = TopologyBuilder.create()
|
|
92
|
-
.withNamespace('test')
|
|
93
|
-
.addQueue('events', { consumerTimeout: -1 });
|
|
94
|
-
expect(() => builder.build()).toThrow('consumer timeout must be non-negative');
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
describe('withDeadLetter', () => {
|
|
98
|
-
it('should configure dead letter settings', () => {
|
|
99
|
-
const topology = TopologyBuilder.create()
|
|
100
|
-
.withNamespace('test')
|
|
101
|
-
.addQueue('events')
|
|
102
|
-
.withDeadLetter({
|
|
103
|
-
unhandled: { enabled: false },
|
|
104
|
-
undeliverable: { enabled: true },
|
|
105
|
-
})
|
|
106
|
-
.build();
|
|
107
|
-
expect(topology.deadLetter.unhandled.enabled).toBe(false);
|
|
108
|
-
expect(topology.deadLetter.undeliverable.enabled).toBe(true);
|
|
109
|
-
});
|
|
110
|
-
it('should merge with defaults', () => {
|
|
111
|
-
const topology = TopologyBuilder.create()
|
|
112
|
-
.withNamespace('test')
|
|
113
|
-
.addQueue('events')
|
|
114
|
-
.withDeadLetter({ unhandled: { enabled: false } })
|
|
115
|
-
.build();
|
|
116
|
-
// undeliverable should keep default
|
|
117
|
-
expect(topology.deadLetter.unhandled.enabled).toBe(false);
|
|
118
|
-
expect(topology.deadLetter.undeliverable.enabled).toBe(true);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
describe('withRetry', () => {
|
|
122
|
-
it('should configure retry settings', () => {
|
|
123
|
-
const topology = TopologyBuilder.create()
|
|
124
|
-
.withNamespace('test')
|
|
125
|
-
.addQueue('events')
|
|
126
|
-
.withRetry({
|
|
127
|
-
enabled: true,
|
|
128
|
-
defaultDelayMs: 5000,
|
|
129
|
-
maxDelayMs: 600000,
|
|
130
|
-
})
|
|
131
|
-
.build();
|
|
132
|
-
expect(topology.retry.enabled).toBe(true);
|
|
133
|
-
expect(topology.retry.defaultDelayMs).toBe(5000);
|
|
134
|
-
expect(topology.retry.maxDelayMs).toBe(600000);
|
|
135
|
-
});
|
|
136
|
-
it('should reject negative default delay', () => {
|
|
137
|
-
const builder = TopologyBuilder.create()
|
|
138
|
-
.withNamespace('test')
|
|
139
|
-
.addQueue('events')
|
|
140
|
-
.withRetry({ defaultDelayMs: -1 });
|
|
141
|
-
expect(() => builder.build()).toThrow('delay must be non-negative');
|
|
142
|
-
});
|
|
143
|
-
it('should reject max delay less than default', () => {
|
|
144
|
-
const builder = TopologyBuilder.create()
|
|
145
|
-
.withNamespace('test')
|
|
146
|
-
.addQueue('events')
|
|
147
|
-
.withRetry({
|
|
148
|
-
defaultDelayMs: 10000,
|
|
149
|
-
maxDelayMs: 5000,
|
|
150
|
-
});
|
|
151
|
-
expect(() => builder.build()).toThrow('Max retry delay must be greater than or equal to default');
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
describe('withoutRetry', () => {
|
|
155
|
-
it('should disable retry', () => {
|
|
156
|
-
const topology = TopologyBuilder.create()
|
|
157
|
-
.withNamespace('test')
|
|
158
|
-
.addQueue('events')
|
|
159
|
-
.withoutRetry()
|
|
160
|
-
.build();
|
|
161
|
-
expect(topology.retry.enabled).toBe(false);
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
describe('withoutDeadLetter', () => {
|
|
165
|
-
it('should disable dead letter queues', () => {
|
|
166
|
-
const topology = TopologyBuilder.create()
|
|
167
|
-
.withNamespace('test')
|
|
168
|
-
.addQueue('events')
|
|
169
|
-
.withoutDeadLetter()
|
|
170
|
-
.build();
|
|
171
|
-
expect(topology.deadLetter.unhandled.enabled).toBe(false);
|
|
172
|
-
expect(topology.deadLetter.undeliverable.enabled).toBe(false);
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
describe('validate', () => {
|
|
176
|
-
it('should return issues without throwing', () => {
|
|
177
|
-
const builder = TopologyBuilder.create();
|
|
178
|
-
const issues = builder.validate();
|
|
179
|
-
expect(issues.length).toBeGreaterThan(0);
|
|
180
|
-
expect(issues).toContain('Namespace is required');
|
|
181
|
-
expect(issues).toContain('At least one queue is required');
|
|
182
|
-
});
|
|
183
|
-
it('should return empty array for valid topology', () => {
|
|
184
|
-
const builder = TopologyBuilder.create()
|
|
185
|
-
.withNamespace('test')
|
|
186
|
-
.addQueue('events');
|
|
187
|
-
const issues = builder.validate();
|
|
188
|
-
expect(issues).toHaveLength(0);
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
describe('build', () => {
|
|
192
|
-
it('should throw TopologyValidationError with issues', () => {
|
|
193
|
-
const builder = TopologyBuilder.create();
|
|
194
|
-
try {
|
|
195
|
-
builder.build();
|
|
196
|
-
expect(true).toBe(false); // Should not reach here
|
|
197
|
-
}
|
|
198
|
-
catch (error) {
|
|
199
|
-
expect(error).toBeInstanceOf(TopologyValidationError);
|
|
200
|
-
expect(error.issues).toContain('Namespace is required');
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
it('should return immutable topology', () => {
|
|
204
|
-
const topology = TopologyBuilder.create()
|
|
205
|
-
.withNamespace('test')
|
|
206
|
-
.addQueue('events')
|
|
207
|
-
.build();
|
|
208
|
-
// Verify structure
|
|
209
|
-
expect(topology.namespace).toBe('test');
|
|
210
|
-
expect(topology.queues).toHaveLength(1);
|
|
211
|
-
expect(topology.deadLetter.unhandled.enabled).toBe(true);
|
|
212
|
-
expect(topology.retry.enabled).toBe(true);
|
|
213
|
-
});
|
|
214
|
-
});
|
|
215
|
-
describe('exact queue option', () => {
|
|
216
|
-
it('should mark queue as exact (external)', () => {
|
|
217
|
-
const topology = TopologyBuilder.create()
|
|
218
|
-
.withNamespace('test')
|
|
219
|
-
.addQueue('external-queue', { exact: true })
|
|
220
|
-
.build();
|
|
221
|
-
expect(topology.queues[0]?.exact).toBe(true);
|
|
222
|
-
});
|
|
223
|
-
it('should allow dots in queue name when exact: true', () => {
|
|
224
|
-
const topology = TopologyBuilder.create()
|
|
225
|
-
.withNamespace('test')
|
|
226
|
-
.addQueue('matador.shared.id-platform', { exact: true })
|
|
227
|
-
.build();
|
|
228
|
-
expect(topology.queues[0]?.name).toBe('matador.shared.id-platform');
|
|
229
|
-
expect(topology.queues[0]?.exact).toBe(true);
|
|
230
|
-
});
|
|
231
|
-
it('should reject dots in queue name when exact: false', () => {
|
|
232
|
-
const builder = TopologyBuilder.create()
|
|
233
|
-
.withNamespace('test')
|
|
234
|
-
.addQueue('invalid.queue.name');
|
|
235
|
-
expect(() => builder.build()).toThrow('must start with a letter');
|
|
236
|
-
});
|
|
237
|
-
it('should allow transport-specific RabbitMQ options with exact queue', () => {
|
|
238
|
-
const topology = TopologyBuilder.create()
|
|
239
|
-
.withNamespace('test')
|
|
240
|
-
.addQueue('matador.shared.id-platform', {
|
|
241
|
-
exact: true,
|
|
242
|
-
transport: {
|
|
243
|
-
rabbitmq: {
|
|
244
|
-
options: {
|
|
245
|
-
durable: true,
|
|
246
|
-
deadLetterExchange: 'matador.shared.dlx-undeliverable',
|
|
247
|
-
arguments: {
|
|
248
|
-
'x-queue-type': 'quorum',
|
|
249
|
-
},
|
|
250
|
-
},
|
|
251
|
-
},
|
|
252
|
-
},
|
|
253
|
-
})
|
|
254
|
-
.build();
|
|
255
|
-
expect(topology.queues[0]?.name).toBe('matador.shared.id-platform');
|
|
256
|
-
expect(topology.queues[0]?.exact).toBe(true);
|
|
257
|
-
expect(topology.queues[0]?.transport?.rabbitmq?.options?.durable).toBe(true);
|
|
258
|
-
expect(topology.queues[0]?.transport?.rabbitmq?.options?.deadLetterExchange).toBe('matador.shared.dlx-undeliverable');
|
|
259
|
-
expect(topology.queues[0]?.transport?.rabbitmq?.options?.arguments?.['x-queue-type']).toBe('quorum');
|
|
260
|
-
});
|
|
261
|
-
});
|
|
262
|
-
describe('addQueue with QueueDefinition object', () => {
|
|
263
|
-
it('should accept a QueueDefinition object', () => {
|
|
264
|
-
const queueDef = {
|
|
265
|
-
name: 'events',
|
|
266
|
-
concurrency: 5,
|
|
267
|
-
consumerTimeout: 30000,
|
|
268
|
-
};
|
|
269
|
-
const topology = TopologyBuilder.create()
|
|
270
|
-
.withNamespace('test')
|
|
271
|
-
.addQueue(queueDef)
|
|
272
|
-
.build();
|
|
273
|
-
expect(topology.queues).toHaveLength(1);
|
|
274
|
-
expect(topology.queues[0]?.name).toBe('events');
|
|
275
|
-
expect(topology.queues[0]?.concurrency).toBe(5);
|
|
276
|
-
expect(topology.queues[0]?.consumerTimeout).toBe(30000);
|
|
277
|
-
});
|
|
278
|
-
it('should allow reusing queue definitions across builders', () => {
|
|
279
|
-
const sharedQueue = {
|
|
280
|
-
name: 'shared-queue',
|
|
281
|
-
concurrency: 10,
|
|
282
|
-
exact: true,
|
|
283
|
-
};
|
|
284
|
-
const topology1 = TopologyBuilder.create()
|
|
285
|
-
.withNamespace('app1')
|
|
286
|
-
.addQueue(sharedQueue)
|
|
287
|
-
.build();
|
|
288
|
-
const topology2 = TopologyBuilder.create()
|
|
289
|
-
.withNamespace('app2')
|
|
290
|
-
.addQueue(sharedQueue)
|
|
291
|
-
.build();
|
|
292
|
-
expect(topology1.queues[0]).toEqual(sharedQueue);
|
|
293
|
-
expect(topology2.queues[0]).toEqual(sharedQueue);
|
|
294
|
-
});
|
|
295
|
-
it('should work with queue() alias', () => {
|
|
296
|
-
const queueDef = {
|
|
297
|
-
name: 'analytics',
|
|
298
|
-
priorities: true,
|
|
299
|
-
};
|
|
300
|
-
const topology = TopologyBuilder.create()
|
|
301
|
-
.withNamespace('test')
|
|
302
|
-
.queue(queueDef)
|
|
303
|
-
.build();
|
|
304
|
-
expect(topology.queues[0]?.name).toBe('analytics');
|
|
305
|
-
expect(topology.queues[0]?.priorities).toBe(true);
|
|
306
|
-
});
|
|
307
|
-
it('should mix QueueDefinition objects with name+options style', () => {
|
|
308
|
-
const reusableQueue = {
|
|
309
|
-
name: 'shared',
|
|
310
|
-
concurrency: 3,
|
|
311
|
-
};
|
|
312
|
-
const topology = TopologyBuilder.create()
|
|
313
|
-
.withNamespace('test')
|
|
314
|
-
.addQueue(reusableQueue)
|
|
315
|
-
.addQueue('local', { concurrency: 1 })
|
|
316
|
-
.build();
|
|
317
|
-
expect(topology.queues).toHaveLength(2);
|
|
318
|
-
expect(topology.queues[0]?.name).toBe('shared');
|
|
319
|
-
expect(topology.queues[1]?.name).toBe('local');
|
|
320
|
-
});
|
|
321
|
-
it('should validate QueueDefinition objects the same way', () => {
|
|
322
|
-
const invalidQueue = {
|
|
323
|
-
name: '123invalid',
|
|
324
|
-
concurrency: 5,
|
|
325
|
-
};
|
|
326
|
-
const builder = TopologyBuilder.create()
|
|
327
|
-
.withNamespace('test')
|
|
328
|
-
.addQueue(invalidQueue);
|
|
329
|
-
expect(() => builder.build()).toThrow('must start with a letter');
|
|
330
|
-
});
|
|
331
|
-
});
|
|
332
|
-
});
|
|
333
|
-
describe('resolveQueueName', () => {
|
|
334
|
-
it('should return namespace.name for regular queues', () => {
|
|
335
|
-
const queueDef = { name: 'events' };
|
|
336
|
-
expect(resolveQueueName('myapp', queueDef)).toBe('myapp.events');
|
|
337
|
-
});
|
|
338
|
-
it('should return name as-is when exact: true', () => {
|
|
339
|
-
const queueDef = { name: 'matador.shared.id-platform', exact: true };
|
|
340
|
-
expect(resolveQueueName('myapp', queueDef)).toBe('matador.shared.id-platform');
|
|
341
|
-
});
|
|
342
|
-
it('should return namespace.name when exact: false', () => {
|
|
343
|
-
const queueDef = { name: 'events', exact: false };
|
|
344
|
-
expect(resolveQueueName('myapp', queueDef)).toBe('myapp.events');
|
|
345
|
-
});
|
|
346
|
-
it('should work with full QueueDefinition including transport options', () => {
|
|
347
|
-
const queueDef = {
|
|
348
|
-
name: 'matador.shared.id-platform',
|
|
349
|
-
exact: true,
|
|
350
|
-
transport: {
|
|
351
|
-
rabbitmq: {
|
|
352
|
-
options: {
|
|
353
|
-
durable: true,
|
|
354
|
-
deadLetterExchange: 'matador.shared.dlx-undeliverable',
|
|
355
|
-
},
|
|
356
|
-
},
|
|
357
|
-
},
|
|
358
|
-
};
|
|
359
|
-
expect(resolveQueueName('myapp', queueDef)).toBe('matador.shared.id-platform');
|
|
360
|
-
});
|
|
361
|
-
});
|
|
362
|
-
describe('TopologyNaming', () => {
|
|
363
|
-
const v1Naming = {
|
|
364
|
-
queue: (ns, q) => `matador.${ns}.${q}`,
|
|
365
|
-
mainExchange: (ns) => `matador.${ns}`,
|
|
366
|
-
dlxExchange: (ns) => `matador.${ns}.dlx-undeliverable`,
|
|
367
|
-
dlxExchangeType: 'topic',
|
|
368
|
-
delayedExchange: (ns) => `matador.${ns}.delayed`,
|
|
369
|
-
};
|
|
370
|
-
describe('builder validation', () => {
|
|
371
|
-
it('should accept a valid naming configuration', () => {
|
|
372
|
-
const topology = TopologyBuilder.create()
|
|
373
|
-
.withNamespace('myapp')
|
|
374
|
-
.withNaming(v1Naming)
|
|
375
|
-
.addQueue('events')
|
|
376
|
-
.build();
|
|
377
|
-
expect(topology.naming?.queue?.('myapp', 'events')).toBe('matador.myapp.events');
|
|
378
|
-
expect(topology.naming?.mainExchange?.('myapp')).toBe('matador.myapp');
|
|
379
|
-
});
|
|
380
|
-
it('should accept partial naming overrides', () => {
|
|
381
|
-
const topology = TopologyBuilder.create()
|
|
382
|
-
.withNamespace('myapp')
|
|
383
|
-
.withNaming({ queue: (ns, q) => `legacy.matador.${ns}.${q}` })
|
|
384
|
-
.addQueue('events')
|
|
385
|
-
.build();
|
|
386
|
-
expect(topology.naming?.queue?.('myapp', 'events')).toBe('legacy.matador.myapp.events');
|
|
387
|
-
});
|
|
388
|
-
it('should reject a non-function naming override', () => {
|
|
389
|
-
expect(() => TopologyBuilder.create()
|
|
390
|
-
.withNamespace('myapp')
|
|
391
|
-
.withNaming({ queue: 'matador.' })
|
|
392
|
-
.addQueue('events')
|
|
393
|
-
.build()).toThrow(TopologyValidationError);
|
|
394
|
-
});
|
|
395
|
-
it('should reject an invalid dlxExchangeType', () => {
|
|
396
|
-
expect(() => TopologyBuilder.create()
|
|
397
|
-
.withNamespace('myapp')
|
|
398
|
-
.withNaming({
|
|
399
|
-
dlxExchangeType: 'fanout',
|
|
400
|
-
})
|
|
401
|
-
.addQueue('events')
|
|
402
|
-
.build()).toThrow(TopologyValidationError);
|
|
403
|
-
});
|
|
404
|
-
});
|
|
405
|
-
describe('name resolution with naming overrides', () => {
|
|
406
|
-
const topology = TopologyBuilder.create()
|
|
407
|
-
.withNamespace('myapp')
|
|
408
|
-
.withNaming({ queue: (ns, q) => `matador.${ns}.${q}` })
|
|
409
|
-
.addQueue('events')
|
|
410
|
-
.addQueue({
|
|
411
|
-
name: 'matador.shared.id-platform',
|
|
412
|
-
exact: true,
|
|
413
|
-
})
|
|
414
|
-
.build();
|
|
415
|
-
it('should build qualified queue names via the strategy', () => {
|
|
416
|
-
expect(getQualifiedQueueName('myapp', 'events', topology.naming)).toBe('matador.myapp.events');
|
|
417
|
-
});
|
|
418
|
-
it('should build resolved queue definitions via the strategy', () => {
|
|
419
|
-
const queueDef = { name: 'events' };
|
|
420
|
-
expect(resolveQueueName('myapp', queueDef, topology.naming)).toBe('matador.myapp.events');
|
|
421
|
-
});
|
|
422
|
-
it('should not transform exact queue definitions', () => {
|
|
423
|
-
const queueDef = { name: 'matador.shared.id-platform', exact: true };
|
|
424
|
-
expect(resolveQueueName('myapp', queueDef, topology.naming)).toBe('matador.shared.id-platform');
|
|
425
|
-
});
|
|
426
|
-
it('should resolve a target queue by exact name as-is', () => {
|
|
427
|
-
expect(resolveTargetQueueName(topology, 'matador.shared.id-platform')).toBe('matador.shared.id-platform');
|
|
428
|
-
});
|
|
429
|
-
it('should build non-exact target queues via the strategy', () => {
|
|
430
|
-
expect(resolveTargetQueueName(topology, 'events')).toBe('matador.myapp.events');
|
|
431
|
-
});
|
|
432
|
-
it('should derive retry and DLQ names from the strategy name', () => {
|
|
433
|
-
expect(getRetryQueueName('myapp', 'events', topology.naming)).toBe('matador.myapp.events.retry');
|
|
434
|
-
expect(getDeadLetterQueueName('myapp', 'events', 'unhandled', topology.naming)).toBe('matador.myapp.events.unhandled');
|
|
435
|
-
});
|
|
436
|
-
it('should find queue definitions by name', () => {
|
|
437
|
-
expect(findQueueDefinition(topology, 'events')?.name).toBe('events');
|
|
438
|
-
expect(findQueueDefinition(topology, 'matador.shared.id-platform')?.name).toBe('matador.shared.id-platform');
|
|
439
|
-
expect(findQueueDefinition(topology, 'missing')).toBeUndefined();
|
|
440
|
-
});
|
|
441
|
-
it('should throw for a queue reference not declared in the topology', () => {
|
|
442
|
-
// A targetQueue/consumeFrom name that was never added to the topology
|
|
443
|
-
// must fail loudly rather than be silently namespace-qualified and
|
|
444
|
-
// routed to a queue nobody consumes.
|
|
445
|
-
expect(() => resolveTargetQueueName(topology, 'ext-service.payments')).toThrow(UnknownQueueReferenceError);
|
|
446
|
-
});
|
|
447
|
-
});
|
|
448
|
-
});
|
|
449
|
-
describe('resource name prefix', () => {
|
|
450
|
-
describe('default', () => {
|
|
451
|
-
it('prefixes resource names with "matador" by default', () => {
|
|
452
|
-
const topology = TopologyBuilder.create()
|
|
453
|
-
.withNamespace('myapp')
|
|
454
|
-
.addQueue('events')
|
|
455
|
-
.build();
|
|
456
|
-
expect(topology.prefix).toBe('matador');
|
|
457
|
-
expect(resolveTargetQueueName(topology, 'events')).toBe('matador.myapp.events');
|
|
458
|
-
});
|
|
459
|
-
it('inherits the default prefix into retry and DLQ names', () => {
|
|
460
|
-
expect(getRetryQueueName('myapp', 'events', undefined, 'matador')).toBe('matador.myapp.events.retry');
|
|
461
|
-
expect(getDeadLetterQueueName('myapp', 'events', 'unhandled', undefined, 'matador')).toBe('matador.myapp.events.unhandled');
|
|
462
|
-
});
|
|
463
|
-
});
|
|
464
|
-
describe('withGlobalPrefix', () => {
|
|
465
|
-
it('uses a custom prefix string', () => {
|
|
466
|
-
const topology = TopologyBuilder.create()
|
|
467
|
-
.withNamespace('myapp')
|
|
468
|
-
.withGlobalPrefix('acme')
|
|
469
|
-
.addQueue('events')
|
|
470
|
-
.build();
|
|
471
|
-
expect(topology.prefix).toBe('acme');
|
|
472
|
-
expect(resolveTargetQueueName(topology, 'events')).toBe('acme.myapp.events');
|
|
473
|
-
});
|
|
474
|
-
it('disables prefixing when set to null', () => {
|
|
475
|
-
const topology = TopologyBuilder.create()
|
|
476
|
-
.withNamespace('myapp')
|
|
477
|
-
.withGlobalPrefix(null)
|
|
478
|
-
.addQueue('events')
|
|
479
|
-
.build();
|
|
480
|
-
expect(topology.prefix).toBeNull();
|
|
481
|
-
expect(resolveTargetQueueName(topology, 'events')).toBe('myapp.events');
|
|
482
|
-
});
|
|
483
|
-
it('disables prefixing when set to undefined', () => {
|
|
484
|
-
const topology = TopologyBuilder.create()
|
|
485
|
-
.withNamespace('myapp')
|
|
486
|
-
.withGlobalPrefix(undefined)
|
|
487
|
-
.addQueue('events')
|
|
488
|
-
.build();
|
|
489
|
-
expect(topology.prefix).toBeUndefined();
|
|
490
|
-
expect(resolveTargetQueueName(topology, 'events')).toBe('myapp.events');
|
|
491
|
-
});
|
|
492
|
-
});
|
|
493
|
-
describe('helper-level prefixing', () => {
|
|
494
|
-
it('applies a non-null prefix to the default qualified name', () => {
|
|
495
|
-
expect(getQualifiedQueueName('myapp', 'events', undefined, 'acme')).toBe('acme.myapp.events');
|
|
496
|
-
});
|
|
497
|
-
it('omits the prefix when null or undefined', () => {
|
|
498
|
-
expect(getQualifiedQueueName('myapp', 'events', undefined, null)).toBe('myapp.events');
|
|
499
|
-
expect(getQualifiedQueueName('myapp', 'events', undefined, undefined)).toBe('myapp.events');
|
|
500
|
-
});
|
|
501
|
-
});
|
|
502
|
-
describe('composition with withNaming', () => {
|
|
503
|
-
it('does not prefix names produced by a withNaming override', () => {
|
|
504
|
-
const topology = TopologyBuilder.create()
|
|
505
|
-
.withNamespace('myapp')
|
|
506
|
-
.withGlobalPrefix('matador')
|
|
507
|
-
.withNaming({ queue: (_ns, q) => `legacy-${q}` })
|
|
508
|
-
.addQueue('events')
|
|
509
|
-
.build();
|
|
510
|
-
// The override fully owns the name; the prefix is not prepended.
|
|
511
|
-
expect(resolveTargetQueueName(topology, 'events')).toBe('legacy-events');
|
|
512
|
-
});
|
|
513
|
-
});
|
|
514
|
-
describe('validation', () => {
|
|
515
|
-
it('rejects an empty-string prefix', () => {
|
|
516
|
-
expect(() => TopologyBuilder.create()
|
|
517
|
-
.withNamespace('myapp')
|
|
518
|
-
.withGlobalPrefix('')
|
|
519
|
-
.addQueue('events')
|
|
520
|
-
.build()).toThrow(TopologyValidationError);
|
|
521
|
-
});
|
|
522
|
-
it('rejects a prefix that is not a valid identifier', () => {
|
|
523
|
-
expect(() => TopologyBuilder.create()
|
|
524
|
-
.withNamespace('myapp')
|
|
525
|
-
.withGlobalPrefix('1bad')
|
|
526
|
-
.addQueue('events')
|
|
527
|
-
.build()).toThrow(TopologyValidationError);
|
|
528
|
-
expect(() => TopologyBuilder.create()
|
|
529
|
-
.withNamespace('myapp')
|
|
530
|
-
.withGlobalPrefix('has space')
|
|
531
|
-
.addQueue('events')
|
|
532
|
-
.build()).toThrow(TopologyValidationError);
|
|
533
|
-
});
|
|
534
|
-
it('accepts null (no prefix) and valid identifier prefixes', () => {
|
|
535
|
-
expect(() => TopologyBuilder.create()
|
|
536
|
-
.withNamespace('myapp')
|
|
537
|
-
.withGlobalPrefix(null)
|
|
538
|
-
.addQueue('events')
|
|
539
|
-
.build()).not.toThrow();
|
|
540
|
-
expect(() => TopologyBuilder.create()
|
|
541
|
-
.withNamespace('myapp')
|
|
542
|
-
.withGlobalPrefix('acme_co-1')
|
|
543
|
-
.addQueue('events')
|
|
544
|
-
.build()).not.toThrow();
|
|
545
|
-
});
|
|
546
|
-
});
|
|
547
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"local-transport.test.d.ts","sourceRoot":"","sources":["../../../src/transport/local/local-transport.test.ts"],"names":[],"mappings":""}
|