@zdavison/matador 2.0.9 → 2.0.11

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 (259) hide show
  1. package/dist/checkpoint/context.d.ts +59 -0
  2. package/dist/checkpoint/context.d.ts.map +1 -0
  3. package/dist/checkpoint/context.js +140 -0
  4. package/dist/checkpoint/context.test.d.ts +2 -0
  5. package/dist/checkpoint/context.test.d.ts.map +1 -0
  6. package/dist/checkpoint/context.test.js +424 -0
  7. package/dist/checkpoint/index.d.ts +7 -0
  8. package/dist/checkpoint/index.d.ts.map +1 -0
  9. package/dist/checkpoint/index.js +6 -0
  10. package/dist/checkpoint/stores/memory.d.ts +29 -0
  11. package/dist/checkpoint/stores/memory.d.ts.map +1 -0
  12. package/dist/checkpoint/stores/memory.js +39 -0
  13. package/{src/checkpoint/stores/noop.ts → dist/checkpoint/stores/noop.d.ts} +5 -13
  14. package/dist/checkpoint/stores/noop.d.ts.map +1 -0
  15. package/dist/checkpoint/stores/noop.js +18 -0
  16. package/dist/checkpoint/stores/stores.test.d.ts +2 -0
  17. package/dist/checkpoint/stores/stores.test.d.ts.map +1 -0
  18. package/dist/checkpoint/stores/stores.test.js +146 -0
  19. package/dist/checkpoint/types.d.ts +119 -0
  20. package/dist/checkpoint/types.d.ts.map +1 -0
  21. package/dist/checkpoint/types.js +1 -0
  22. package/dist/codec/codec.d.ts +29 -0
  23. package/dist/codec/codec.d.ts.map +1 -0
  24. package/dist/codec/codec.js +15 -0
  25. package/dist/codec/header-aware-codec.d.ts +36 -0
  26. package/dist/codec/header-aware-codec.d.ts.map +1 -0
  27. package/dist/codec/header-aware-codec.js +1 -0
  28. package/{src/codec/index.ts → dist/codec/index.d.ts} +2 -7
  29. package/dist/codec/index.d.ts.map +1 -0
  30. package/dist/codec/index.js +3 -0
  31. package/dist/codec/json-codec.d.ts +13 -0
  32. package/dist/codec/json-codec.d.ts.map +1 -0
  33. package/dist/codec/json-codec.js +64 -0
  34. package/dist/codec/rabbitmq-codec.d.ts +28 -0
  35. package/dist/codec/rabbitmq-codec.d.ts.map +1 -0
  36. package/dist/codec/rabbitmq-codec.js +242 -0
  37. package/dist/codec/rabbitmq-codec.test.d.ts +2 -0
  38. package/dist/codec/rabbitmq-codec.test.d.ts.map +1 -0
  39. package/dist/codec/rabbitmq-codec.test.js +433 -0
  40. package/dist/core/fanout.d.ts +59 -0
  41. package/dist/core/fanout.d.ts.map +1 -0
  42. package/dist/core/fanout.js +121 -0
  43. package/dist/core/fanout.test.d.ts +2 -0
  44. package/dist/core/fanout.test.d.ts.map +1 -0
  45. package/dist/core/fanout.test.js +1054 -0
  46. package/{src/core/index.ts → dist/core/index.d.ts} +2 -7
  47. package/dist/core/index.d.ts.map +1 -0
  48. package/dist/core/index.js +3 -0
  49. package/dist/core/matador.d.ts +133 -0
  50. package/dist/core/matador.d.ts.map +1 -0
  51. package/dist/core/matador.js +228 -0
  52. package/dist/core/matador.test.d.ts +2 -0
  53. package/dist/core/matador.test.d.ts.map +1 -0
  54. package/dist/core/matador.test.js +446 -0
  55. package/dist/core/shutdown.d.ts +78 -0
  56. package/dist/core/shutdown.d.ts.map +1 -0
  57. package/dist/core/shutdown.js +111 -0
  58. package/dist/core/shutdown.test.d.ts +2 -0
  59. package/dist/core/shutdown.test.d.ts.map +1 -0
  60. package/dist/core/shutdown.test.js +524 -0
  61. package/dist/errors/checkpoint-errors.d.ts +30 -0
  62. package/dist/errors/checkpoint-errors.d.ts.map +1 -0
  63. package/dist/errors/checkpoint-errors.js +49 -0
  64. package/dist/errors/has-description.d.ts +18 -0
  65. package/dist/errors/has-description.d.ts.map +1 -0
  66. package/dist/errors/has-description.js +9 -0
  67. package/dist/errors/index.d.ts +6 -0
  68. package/dist/errors/index.d.ts.map +1 -0
  69. package/dist/errors/index.js +22 -0
  70. package/dist/errors/matador-errors.d.ts +183 -0
  71. package/dist/errors/matador-errors.d.ts.map +1 -0
  72. package/dist/errors/matador-errors.js +376 -0
  73. package/dist/errors/retry-errors.d.ts +102 -0
  74. package/dist/errors/retry-errors.d.ts.map +1 -0
  75. package/dist/errors/retry-errors.js +155 -0
  76. package/dist/errors/retry-errors.test.d.ts +2 -0
  77. package/dist/errors/retry-errors.test.d.ts.map +1 -0
  78. package/dist/errors/retry-errors.test.js +136 -0
  79. package/dist/hooks/index.d.ts +4 -0
  80. package/dist/hooks/index.d.ts.map +1 -0
  81. package/dist/hooks/index.js +2 -0
  82. package/dist/hooks/safe-hooks.d.ts +32 -0
  83. package/dist/hooks/safe-hooks.d.ts.map +1 -0
  84. package/dist/hooks/safe-hooks.js +120 -0
  85. package/dist/hooks/types.d.ts +172 -0
  86. package/dist/hooks/types.d.ts.map +1 -0
  87. package/dist/hooks/types.js +9 -0
  88. package/dist/index.cjs +3228 -0
  89. package/dist/index.cjs.map +1 -0
  90. package/dist/index.d.cts +23 -0
  91. package/dist/index.d.ts +23 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +31 -0
  94. package/dist/index.js.map +1 -0
  95. package/{src/pipeline/index.ts → dist/pipeline/index.d.ts} +1 -0
  96. package/dist/pipeline/index.d.ts.map +1 -0
  97. package/dist/pipeline/index.js +1 -0
  98. package/dist/pipeline/pipeline.d.ts +63 -0
  99. package/dist/pipeline/pipeline.d.ts.map +1 -0
  100. package/dist/pipeline/pipeline.js +273 -0
  101. package/dist/pipeline/pipeline.test.d.ts +2 -0
  102. package/dist/pipeline/pipeline.test.d.ts.map +1 -0
  103. package/dist/pipeline/pipeline.test.js +1112 -0
  104. package/{src/retry/index.ts → dist/retry/index.d.ts} +1 -1
  105. package/dist/retry/index.d.ts.map +1 -0
  106. package/dist/retry/index.js +1 -0
  107. package/dist/retry/policy.d.ts +43 -0
  108. package/dist/retry/policy.d.ts.map +1 -0
  109. package/dist/retry/policy.js +1 -0
  110. package/dist/retry/standard-policy.d.ts +44 -0
  111. package/dist/retry/standard-policy.d.ts.map +1 -0
  112. package/dist/retry/standard-policy.js +102 -0
  113. package/dist/retry/standard-policy.test.d.ts +2 -0
  114. package/dist/retry/standard-policy.test.d.ts.map +1 -0
  115. package/dist/retry/standard-policy.test.js +196 -0
  116. package/dist/schema/index.d.ts +4 -0
  117. package/dist/schema/index.d.ts.map +1 -0
  118. package/dist/schema/index.js +2 -0
  119. package/dist/schema/registry.d.ts +63 -0
  120. package/dist/schema/registry.d.ts.map +1 -0
  121. package/dist/schema/registry.js +174 -0
  122. package/dist/schema/registry.test.d.ts +2 -0
  123. package/dist/schema/registry.test.d.ts.map +1 -0
  124. package/dist/schema/registry.test.js +278 -0
  125. package/{src/schema/types.ts → dist/schema/types.d.ts} +29 -88
  126. package/dist/schema/types.d.ts.map +1 -0
  127. package/dist/schema/types.js +74 -0
  128. package/dist/schema/types.test.d.ts +2 -0
  129. package/dist/schema/types.test.d.ts.map +1 -0
  130. package/dist/schema/types.test.js +242 -0
  131. package/dist/topology/builder.d.ts +80 -0
  132. package/dist/topology/builder.d.ts.map +1 -0
  133. package/dist/topology/builder.js +165 -0
  134. package/dist/topology/builder.test.d.ts +2 -0
  135. package/dist/topology/builder.test.d.ts.map +1 -0
  136. package/dist/topology/builder.test.js +360 -0
  137. package/dist/topology/index.d.ts +5 -0
  138. package/dist/topology/index.d.ts.map +1 -0
  139. package/dist/topology/index.js +2 -0
  140. package/dist/topology/types.d.ts +128 -0
  141. package/dist/topology/types.d.ts.map +1 -0
  142. package/dist/topology/types.js +28 -0
  143. package/dist/transport/capabilities.d.ts +66 -0
  144. package/dist/transport/capabilities.d.ts.map +1 -0
  145. package/dist/transport/capabilities.js +18 -0
  146. package/dist/transport/connection-manager.d.ts +95 -0
  147. package/dist/transport/connection-manager.d.ts.map +1 -0
  148. package/dist/transport/connection-manager.js +144 -0
  149. package/dist/transport/index.d.ts +11 -0
  150. package/dist/transport/index.d.ts.map +1 -0
  151. package/dist/transport/index.js +5 -0
  152. package/dist/transport/local/local-transport.d.ts +62 -0
  153. package/dist/transport/local/local-transport.d.ts.map +1 -0
  154. package/dist/transport/local/local-transport.js +241 -0
  155. package/dist/transport/local/local-transport.test.d.ts +2 -0
  156. package/dist/transport/local/local-transport.test.d.ts.map +1 -0
  157. package/dist/transport/local/local-transport.test.js +192 -0
  158. package/dist/transport/multi/multi-transport.d.ts +94 -0
  159. package/dist/transport/multi/multi-transport.d.ts.map +1 -0
  160. package/dist/transport/multi/multi-transport.js +184 -0
  161. package/dist/transport/multi/multi-transport.test.d.ts +2 -0
  162. package/dist/transport/multi/multi-transport.test.d.ts.map +1 -0
  163. package/dist/transport/multi/multi-transport.test.js +236 -0
  164. package/dist/transport/rabbitmq/rabbitmq-transport.d.ts +83 -0
  165. package/dist/transport/rabbitmq/rabbitmq-transport.d.ts.map +1 -0
  166. package/dist/transport/rabbitmq/rabbitmq-transport.js +541 -0
  167. package/dist/transport/rabbitmq/rabbitmq-transport.test.d.ts +2 -0
  168. package/dist/transport/rabbitmq/rabbitmq-transport.test.d.ts.map +1 -0
  169. package/dist/transport/rabbitmq/rabbitmq-transport.test.js +100 -0
  170. package/dist/transport/transport.d.ts +152 -0
  171. package/dist/transport/transport.d.ts.map +1 -0
  172. package/dist/transport/transport.js +1 -0
  173. package/{src/types/common.ts → dist/types/common.d.ts} +8 -20
  174. package/dist/types/common.d.ts.map +1 -0
  175. package/dist/types/common.js +12 -0
  176. package/dist/types/dispatcher.d.ts +14 -0
  177. package/dist/types/dispatcher.d.ts.map +1 -0
  178. package/dist/types/dispatcher.js +1 -0
  179. package/dist/types/envelope.d.ts +131 -0
  180. package/dist/types/envelope.d.ts.map +1 -0
  181. package/dist/types/envelope.js +99 -0
  182. package/dist/types/event.d.ts +92 -0
  183. package/dist/types/event.d.ts.map +1 -0
  184. package/dist/types/event.js +26 -0
  185. package/dist/types/event.test.d.ts +2 -0
  186. package/dist/types/event.test.d.ts.map +1 -0
  187. package/dist/types/event.test.js +130 -0
  188. package/dist/types/index.d.ts +11 -0
  189. package/dist/types/index.d.ts.map +1 -0
  190. package/dist/types/index.js +4 -0
  191. package/dist/types/subscriber.d.ts +227 -0
  192. package/dist/types/subscriber.d.ts.map +1 -0
  193. package/dist/types/subscriber.js +97 -0
  194. package/package.json +6 -2
  195. package/examples/config.ts +0 -126
  196. package/examples/event.ts +0 -26
  197. package/examples/order-event.json +0 -19
  198. package/src/checkpoint/context.test.ts +0 -510
  199. package/src/checkpoint/context.ts +0 -213
  200. package/src/checkpoint/index.ts +0 -30
  201. package/src/checkpoint/stores/memory.ts +0 -47
  202. package/src/checkpoint/stores/stores.test.ts +0 -177
  203. package/src/checkpoint/types.ts +0 -147
  204. package/src/codec/codec.ts +0 -42
  205. package/src/codec/header-aware-codec.ts +0 -41
  206. package/src/codec/json-codec.ts +0 -69
  207. package/src/codec/rabbitmq-codec.test.ts +0 -516
  208. package/src/codec/rabbitmq-codec.ts +0 -336
  209. package/src/core/fanout.test.ts +0 -1350
  210. package/src/core/fanout.ts +0 -184
  211. package/src/core/matador.test.ts +0 -575
  212. package/src/core/matador.ts +0 -357
  213. package/src/core/shutdown.test.ts +0 -853
  214. package/src/core/shutdown.ts +0 -165
  215. package/src/errors/checkpoint-errors.ts +0 -62
  216. package/src/errors/has-description.ts +0 -25
  217. package/src/errors/index.ts +0 -58
  218. package/src/errors/matador-errors.ts +0 -477
  219. package/src/errors/retry-errors.test.ts +0 -175
  220. package/src/errors/retry-errors.ts +0 -188
  221. package/src/hooks/index.ts +0 -14
  222. package/src/hooks/safe-hooks.ts +0 -200
  223. package/src/hooks/types.ts +0 -226
  224. package/src/index.ts +0 -242
  225. package/src/pipeline/pipeline.test.ts +0 -1377
  226. package/src/pipeline/pipeline.ts +0 -393
  227. package/src/retry/policy.ts +0 -46
  228. package/src/retry/standard-policy.test.ts +0 -290
  229. package/src/retry/standard-policy.ts +0 -156
  230. package/src/schema/index.ts +0 -16
  231. package/src/schema/registry.test.ts +0 -339
  232. package/src/schema/registry.ts +0 -229
  233. package/src/schema/types.test.ts +0 -280
  234. package/src/topology/builder.test.ts +0 -451
  235. package/src/topology/builder.ts +0 -238
  236. package/src/topology/index.ts +0 -19
  237. package/src/topology/types.ts +0 -183
  238. package/src/transport/capabilities.ts +0 -88
  239. package/src/transport/connection-manager.ts +0 -218
  240. package/src/transport/index.ts +0 -42
  241. package/src/transport/local/local-transport.test.ts +0 -262
  242. package/src/transport/local/local-transport.ts +0 -330
  243. package/src/transport/multi/multi-transport.test.ts +0 -320
  244. package/src/transport/multi/multi-transport.ts +0 -294
  245. package/src/transport/rabbitmq/rabbitmq-transport.test.ts +0 -120
  246. package/src/transport/rabbitmq/rabbitmq-transport.ts +0 -782
  247. package/src/transport/transport.ts +0 -200
  248. package/src/types/dispatcher.ts +0 -18
  249. package/src/types/envelope.ts +0 -244
  250. package/src/types/event.test.ts +0 -157
  251. package/src/types/event.ts +0 -112
  252. package/src/types/index.ts +0 -62
  253. package/src/types/subscriber.ts +0 -333
  254. package/test/e2e/multi-transport.e2e.test.ts +0 -237
  255. package/test/e2e/rabbitmq-transport.e2e.test.ts +0 -618
  256. package/test/e2e/transport-compliance.e2e.test.ts +0 -506
  257. package/test/integration/matador.integration.test.ts +0 -634
  258. package/tsconfig.json +0 -29
  259. package/tsup.config.ts +0 -13
@@ -1,506 +0,0 @@
1
- import {
2
- afterAll,
3
- afterEach,
4
- beforeAll,
5
- beforeEach,
6
- describe,
7
- expect,
8
- it,
9
- } from 'bun:test';
10
- import {
11
- RabbitMQContainer,
12
- type StartedRabbitMQContainer,
13
- } from '@testcontainers/rabbitmq';
14
- import type { Topology } from '../../src/topology/types.js';
15
- import type { Subscription, Transport } from '../../src/transport/index.js';
16
- import { LocalTransport } from '../../src/transport/local/local-transport.js';
17
- import { RabbitMQTransport } from '../../src/transport/rabbitmq/rabbitmq-transport.js';
18
- import { createEnvelope } from '../../src/types/index.js';
19
-
20
- /**
21
- * Creates a test topology for transport compliance tests.
22
- */
23
- export function createTestTopology(namespace = 'test'): Topology {
24
- return {
25
- namespace,
26
- queues: [{ name: 'events' }, { name: 'notifications' }],
27
- deadLetter: {
28
- unhandled: { enabled: true },
29
- undeliverable: { enabled: true },
30
- },
31
- retry: {
32
- enabled: true,
33
- defaultDelayMs: 1000,
34
- maxDelayMs: 30000,
35
- },
36
- };
37
- }
38
-
39
- /**
40
- * Creates a test envelope.
41
- */
42
- export function createTestEnvelope(overrides?: {
43
- id?: string;
44
- eventKey?: string;
45
- }) {
46
- return createEnvelope({
47
- id: overrides?.id,
48
- eventKey: overrides?.eventKey ?? 'test.event',
49
- targetSubscriber: 'test-subscriber',
50
- data: { test: 'data', timestamp: Date.now() },
51
- importance: 'should-investigate',
52
- });
53
- }
54
-
55
- /**
56
- * Waits for a condition to be true, with timeout.
57
- */
58
- async function waitFor(
59
- condition: () => boolean,
60
- timeoutMs: number,
61
- intervalMs = 50,
62
- ): Promise<void> {
63
- const startTime = Date.now();
64
- while (!condition()) {
65
- if (Date.now() - startTime > timeoutMs) {
66
- throw new Error(`Timeout waiting for condition after ${timeoutMs}ms`);
67
- }
68
- await new Promise((resolve) => setTimeout(resolve, intervalMs));
69
- }
70
- }
71
-
72
- // Skip E2E tests (RabbitMQ) if flag is set
73
- const SKIP_E2E = process.env.SKIP_E2E_TESTS === 'true';
74
-
75
- // RabbitMQ container for E2E tests
76
- let rabbitContainer: StartedRabbitMQContainer | null = null;
77
- let rabbitConnectionUrl: string | null = null;
78
-
79
- // Start RabbitMQ container before all tests (if not skipping E2E)
80
- beforeAll(async () => {
81
- if (!SKIP_E2E) {
82
- rabbitContainer = await new RabbitMQContainer('rabbitmq:3.13-management')
83
- .withExposedPorts(5672, 15672)
84
- .start();
85
- rabbitConnectionUrl = rabbitContainer.getAmqpUrl();
86
- console.log(`RabbitMQ container started at ${rabbitConnectionUrl}`);
87
- }
88
- }, 120_000);
89
-
90
- afterAll(async () => {
91
- if (rabbitContainer) {
92
- await rabbitContainer.stop();
93
- }
94
- });
95
-
96
- // Transport factories for test.each
97
- type TransportFactory = {
98
- name: string;
99
- create: () => Transport;
100
- cleanup?: (transport: Transport) => void;
101
- skip?: boolean;
102
- };
103
-
104
- const transportFactories: TransportFactory[] = [
105
- {
106
- name: 'LocalTransport',
107
- create: () => new LocalTransport(),
108
- cleanup: (transport) => (transport as LocalTransport).clear(),
109
- skip: false,
110
- },
111
- {
112
- name: 'RabbitMQTransport',
113
- create: () => {
114
- if (!rabbitConnectionUrl) {
115
- throw new Error('RabbitMQ container not started');
116
- }
117
- return new RabbitMQTransport({
118
- url: rabbitConnectionUrl,
119
- connectionName: 'matador-compliance-test',
120
- quorumQueues: false, // Use classic queues for faster tests
121
- defaultPrefetch: 10,
122
- });
123
- },
124
- cleanup: undefined,
125
- skip: SKIP_E2E,
126
- },
127
- ];
128
-
129
- describe.each(transportFactories)(
130
- '$name Transport Compliance',
131
- ({ name, create, cleanup, skip }) => {
132
- // Use unique namespace per transport to avoid conflicts
133
- const getNamespace = () => `${name.toLowerCase()}-${Date.now()}`;
134
-
135
- describe.skipIf(skip ?? false)(`${name} tests`, () => {
136
- let transport: Transport;
137
- let subscriptions: Subscription[] = [];
138
-
139
- beforeEach(async () => {
140
- transport = create();
141
- subscriptions = [];
142
- });
143
-
144
- afterEach(async () => {
145
- // Clean up subscriptions
146
- for (const sub of subscriptions) {
147
- if (sub.isActive) {
148
- await sub.unsubscribe();
149
- }
150
- }
151
- subscriptions = [];
152
-
153
- // Disconnect transport
154
- if (transport.isConnected()) {
155
- await transport.disconnect();
156
- }
157
-
158
- // Additional cleanup
159
- if (cleanup) {
160
- cleanup(transport);
161
- }
162
- });
163
-
164
- describe('connection lifecycle', () => {
165
- it('should start disconnected', () => {
166
- expect(transport.isConnected()).toBe(false);
167
- });
168
-
169
- it('should connect successfully', async () => {
170
- await transport.connect();
171
- expect(transport.isConnected()).toBe(true);
172
- });
173
-
174
- it('should disconnect successfully', async () => {
175
- await transport.connect();
176
- await transport.disconnect();
177
- expect(transport.isConnected()).toBe(false);
178
- });
179
-
180
- it('should handle multiple connect calls idempotently', async () => {
181
- await transport.connect();
182
- await transport.connect();
183
- expect(transport.isConnected()).toBe(true);
184
- });
185
-
186
- it('should handle multiple disconnect calls idempotently', async () => {
187
- await transport.connect();
188
- await transport.disconnect();
189
- await transport.disconnect();
190
- expect(transport.isConnected()).toBe(false);
191
- });
192
- });
193
-
194
- describe('capabilities', () => {
195
- it('should have a name', () => {
196
- expect(transport.name).toBeTruthy();
197
- expect(typeof transport.name).toBe('string');
198
- });
199
-
200
- it('should report capabilities', () => {
201
- expect(transport.capabilities).toBeTruthy();
202
- expect(Array.isArray(transport.capabilities.deliveryModes)).toBe(
203
- true,
204
- );
205
- expect(transport.capabilities.deliveryModes.length).toBeGreaterThan(
206
- 0,
207
- );
208
- });
209
-
210
- it('should report at least one delivery mode', () => {
211
- const { deliveryModes } = transport.capabilities;
212
- const validModes = ['at-least-once', 'at-most-once'];
213
- expect(deliveryModes.some((m) => validModes.includes(m))).toBe(true);
214
- });
215
- });
216
-
217
- describe('topology', () => {
218
- it('should apply basic topology without error', async () => {
219
- await transport.connect();
220
- const topology = createTestTopology(getNamespace());
221
-
222
- await expect(
223
- transport.applyTopology(topology),
224
- ).resolves.toBeUndefined();
225
- });
226
-
227
- it('should throw when applying topology while disconnected', async () => {
228
- const topology = createTestTopology(getNamespace());
229
-
230
- await expect(transport.applyTopology(topology)).rejects.toThrow();
231
- });
232
- });
233
-
234
- describe('send and receive', () => {
235
- let namespace: string;
236
- let queueName: string;
237
-
238
- beforeEach(async () => {
239
- namespace = getNamespace();
240
- queueName = `${namespace}.events`;
241
- await transport.connect();
242
- await transport.applyTopology(createTestTopology(namespace));
243
- });
244
-
245
- it('should send a message without error', async () => {
246
- const envelope = createTestEnvelope();
247
-
248
- await expect(transport.send(queueName, envelope)).resolves.toBe(
249
- transport.name,
250
- );
251
- });
252
-
253
- it('should receive sent messages via subscription', async () => {
254
- const envelope = createTestEnvelope();
255
- const received: unknown[] = [];
256
-
257
- const subscription = await transport.subscribe(
258
- queueName,
259
- async (env, receipt) => {
260
- received.push(env);
261
- await transport.complete(receipt);
262
- },
263
- );
264
- subscriptions.push(subscription);
265
-
266
- await transport.send(queueName, envelope);
267
-
268
- // Wait for message delivery
269
- await waitFor(() => received.length > 0, 5000);
270
-
271
- expect(received.length).toBe(1);
272
- expect((received[0] as { id: string }).id).toBe(envelope.id);
273
- });
274
-
275
- it('should preserve message payload', async () => {
276
- const envelope = createTestEnvelope();
277
- const received: unknown[] = [];
278
-
279
- const subscription = await transport.subscribe(
280
- queueName,
281
- async (env, receipt) => {
282
- received.push(env);
283
- await transport.complete(receipt);
284
- },
285
- );
286
- subscriptions.push(subscription);
287
-
288
- await transport.send(queueName, envelope);
289
-
290
- await waitFor(() => received.length > 0, 5000);
291
-
292
- const receivedEnvelope = received[0] as typeof envelope;
293
- expect(receivedEnvelope.data).toEqual(envelope.data);
294
- expect(receivedEnvelope.docket.eventKey).toBe(
295
- envelope.docket.eventKey,
296
- );
297
- expect(receivedEnvelope.docket.targetSubscriber).toBe(
298
- envelope.docket.targetSubscriber,
299
- );
300
- });
301
-
302
- it('should throw when sending while disconnected', async () => {
303
- await transport.disconnect();
304
- const envelope = createTestEnvelope();
305
-
306
- await expect(transport.send(queueName, envelope)).rejects.toThrow();
307
- });
308
- });
309
-
310
- describe('subscriptions', () => {
311
- let namespace: string;
312
- let queueName: string;
313
-
314
- beforeEach(async () => {
315
- namespace = getNamespace();
316
- queueName = `${namespace}.events`;
317
- await transport.connect();
318
- await transport.applyTopology(createTestTopology(namespace));
319
- });
320
-
321
- it('should create an active subscription', async () => {
322
- const subscription = await transport.subscribe(
323
- queueName,
324
- async () => {},
325
- );
326
- subscriptions.push(subscription);
327
-
328
- expect(subscription.isActive).toBe(true);
329
- });
330
-
331
- it('should deactivate subscription after unsubscribe', async () => {
332
- const subscription = await transport.subscribe(
333
- queueName,
334
- async () => {},
335
- );
336
- subscriptions.push(subscription);
337
-
338
- await subscription.unsubscribe();
339
-
340
- expect(subscription.isActive).toBe(false);
341
- });
342
-
343
- it('should stop receiving messages after unsubscribe', async () => {
344
- const received: unknown[] = [];
345
-
346
- const subscription = await transport.subscribe(
347
- queueName,
348
- async (env, receipt) => {
349
- received.push(env);
350
- await transport.complete(receipt);
351
- },
352
- );
353
-
354
- await subscription.unsubscribe();
355
-
356
- // Send message after unsubscribe
357
- await transport.send(queueName, createTestEnvelope());
358
-
359
- // Wait a bit to ensure no message is received
360
- await new Promise((resolve) => setTimeout(resolve, 500));
361
-
362
- expect(received.length).toBe(0);
363
- });
364
-
365
- it('should throw when subscribing while disconnected', async () => {
366
- await transport.disconnect();
367
-
368
- await expect(
369
- transport.subscribe(queueName, async () => {}),
370
- ).rejects.toThrow();
371
- });
372
- });
373
-
374
- describe('message completion', () => {
375
- let namespace: string;
376
- let queueName: string;
377
-
378
- beforeEach(async () => {
379
- namespace = getNamespace();
380
- queueName = `${namespace}.events`;
381
- await transport.connect();
382
- await transport.applyTopology(createTestTopology(namespace));
383
- });
384
-
385
- it('should complete messages successfully', async () => {
386
- const envelope = createTestEnvelope();
387
- let completed = false;
388
-
389
- const subscription = await transport.subscribe(
390
- queueName,
391
- async (_env, receipt) => {
392
- await transport.complete(receipt);
393
- completed = true;
394
- },
395
- );
396
- subscriptions.push(subscription);
397
-
398
- await transport.send(queueName, envelope);
399
-
400
- await waitFor(() => completed, 5000);
401
-
402
- expect(completed).toBe(true);
403
- });
404
- });
405
-
406
- describe('dead letter queue', () => {
407
- let namespace: string;
408
- let queueName: string;
409
-
410
- beforeEach(async () => {
411
- namespace = getNamespace();
412
- queueName = `${namespace}.events`;
413
- await transport.connect();
414
- await transport.applyTopology(createTestTopology(namespace));
415
- });
416
-
417
- it('should send messages to dead letter queue', async () => {
418
- // Only test if transport supports sendToDeadLetter
419
- if (!transport.sendToDeadLetter) {
420
- return;
421
- }
422
-
423
- const envelope = createTestEnvelope();
424
- let dlqReceived = false;
425
-
426
- // Subscribe to DLQ
427
- const dlqSub = await transport.subscribe(
428
- `${queueName}.undeliverable`,
429
- async (_env, receipt) => {
430
- dlqReceived = true;
431
- await transport.complete(receipt);
432
- },
433
- );
434
- subscriptions.push(dlqSub);
435
-
436
- // Subscribe to main queue and send to DLQ
437
- const mainSub = await transport.subscribe(
438
- queueName,
439
- async (_env, receipt) => {
440
- await transport.sendToDeadLetter!(
441
- receipt,
442
- 'undeliverable',
443
- envelope,
444
- 'Test error',
445
- );
446
- },
447
- );
448
- subscriptions.push(mainSub);
449
-
450
- await transport.send(queueName, envelope);
451
-
452
- await waitFor(() => dlqReceived, 5000);
453
-
454
- expect(dlqReceived).toBe(true);
455
- });
456
- });
457
-
458
- describe('message isolation', () => {
459
- let namespace: string;
460
-
461
- beforeEach(async () => {
462
- namespace = getNamespace();
463
- await transport.connect();
464
- await transport.applyTopology(createTestTopology(namespace));
465
- });
466
-
467
- it('should isolate messages between queues', async () => {
468
- const queue1 = `${namespace}.events`;
469
- const queue2 = `${namespace}.notifications`;
470
-
471
- const receivedQueue1: unknown[] = [];
472
- const receivedQueue2: unknown[] = [];
473
-
474
- const sub1 = await transport.subscribe(
475
- queue1,
476
- async (env, receipt) => {
477
- receivedQueue1.push(env);
478
- await transport.complete(receipt);
479
- },
480
- );
481
- subscriptions.push(sub1);
482
-
483
- const sub2 = await transport.subscribe(
484
- queue2,
485
- async (env, receipt) => {
486
- receivedQueue2.push(env);
487
- await transport.complete(receipt);
488
- },
489
- );
490
- subscriptions.push(sub2);
491
-
492
- // Send to queue1 only
493
- await transport.send(
494
- queue1,
495
- createTestEnvelope({ eventKey: 'queue1.event' }),
496
- );
497
-
498
- await waitFor(() => receivedQueue1.length > 0, 5000);
499
-
500
- expect(receivedQueue1.length).toBe(1);
501
- expect(receivedQueue2.length).toBe(0);
502
- });
503
- });
504
- });
505
- },
506
- );