@amqp-contract/testing 0.20.0 โ 0.22.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/extension.d.mts +4 -4
- package/dist/extension.d.mts.map +1 -1
- package/dist/extension.mjs +110 -5
- package/dist/extension.mjs.map +1 -1
- package/dist/global-setup.mjs +1 -2
- package/dist/global-setup.mjs.map +1 -1
- package/dist/index.mjs +1 -2
- package/docs/extension.md +2 -2
- package/docs/global-setup.md +1 -1
- package/package.json +15 -15
package/dist/extension.d.mts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import amqpLib from "amqplib";
|
|
2
|
-
import * as vitest from "vitest";
|
|
1
|
+
import amqpLib, { Options } from "amqplib";
|
|
2
|
+
import * as _$vitest from "vitest";
|
|
3
3
|
|
|
4
4
|
//#region src/extension.d.ts
|
|
5
|
-
declare const it: vitest.TestAPI<{
|
|
5
|
+
declare const it: _$vitest.TestAPI<{
|
|
6
6
|
vhost: string;
|
|
7
7
|
amqpConnectionUrl: string;
|
|
8
8
|
amqpConnection: amqpLib.ChannelModel;
|
|
9
9
|
amqpChannel: amqpLib.Channel;
|
|
10
|
-
publishMessage: (exchange: string, routingKey: string, content: unknown) => void;
|
|
10
|
+
publishMessage: (exchange: string, routingKey: string, content: unknown, options?: Options.Publish) => void;
|
|
11
11
|
initConsumer: (exchange: string, routingKey: string) => Promise<(options?: {
|
|
12
12
|
nbEvents?: number;
|
|
13
13
|
timeout?: number;
|
package/dist/extension.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.d.mts","names":[],"sources":["../src/extension.ts"],"mappings":";;;;cAea,EAAA,
|
|
1
|
+
{"version":3,"file":"extension.d.mts","names":[],"sources":["../src/extension.ts"],"mappings":";;;;cAea,EAAA,EAcC,QAAA,CAdC,OAAA;;;;;qCAMK,UAAA,UACE,OAAA,WACF,OAAA,GACN,OAAA,CAAQ,OAAA;mCAGF,UAAA,aAEb,OAAA,EACF,OAAA;IAAY,QAAA;IAAmB,OAAA;EAAA,MAAuB,OAAA,CAAQ,OAAA,CAAQ,cAAA;AAAA"}
|
package/dist/extension.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import amqpLib from "amqplib";
|
|
2
|
-
import { inject, it as it$1, vi } from "vitest";
|
|
3
2
|
import { randomUUID } from "node:crypto";
|
|
4
|
-
|
|
3
|
+
import { inject, it as it$1, vi } from "vitest";
|
|
5
4
|
//#region src/extension.ts
|
|
6
5
|
/**
|
|
7
6
|
* Vitest extension module for AMQP testing utilities
|
|
@@ -14,6 +13,19 @@ import { randomUUID } from "node:crypto";
|
|
|
14
13
|
* @packageDocumentation
|
|
15
14
|
*/
|
|
16
15
|
const it = it$1.extend({
|
|
16
|
+
/**
|
|
17
|
+
* Test fixture that provides an isolated RabbitMQ virtual host (vhost) for the test.
|
|
18
|
+
*
|
|
19
|
+
* Creates a new vhost with a random UUID name for test isolation. The vhost is automatically
|
|
20
|
+
* created before the test runs using the RabbitMQ Management API.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* it('should use isolated vhost', async ({ vhost }) => {
|
|
25
|
+
* console.log(`Test running in vhost: ${vhost}`);
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
17
29
|
vhost: async ({}, use) => {
|
|
18
30
|
const vhost = await createVhost();
|
|
19
31
|
try {
|
|
@@ -22,25 +34,118 @@ const it = it$1.extend({
|
|
|
22
34
|
await deleteVhost(vhost);
|
|
23
35
|
}
|
|
24
36
|
},
|
|
37
|
+
/**
|
|
38
|
+
* Test fixture that provides the AMQP connection URL for the test container.
|
|
39
|
+
*
|
|
40
|
+
* Constructs a connection URL using the test container's IP and port, along with
|
|
41
|
+
* the isolated vhost. The URL follows the format: `amqp://guest:guest@host:port/vhost`.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* it('should connect with URL', async ({ amqpConnectionUrl }) => {
|
|
46
|
+
* console.log(`Connecting to: ${amqpConnectionUrl}`);
|
|
47
|
+
* });
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
25
50
|
amqpConnectionUrl: async ({ vhost }, use) => {
|
|
26
51
|
await use(`amqp://guest:guest@${inject("__TESTCONTAINERS_RABBITMQ_IP__")}:${inject("__TESTCONTAINERS_RABBITMQ_PORT_5672__")}/${vhost}`);
|
|
27
52
|
},
|
|
53
|
+
/**
|
|
54
|
+
* Test fixture that provides an active AMQP connection to RabbitMQ.
|
|
55
|
+
*
|
|
56
|
+
* Establishes a connection using the provided connection URL and automatically closes
|
|
57
|
+
* it after the test completes. This fixture is useful for tests that need direct
|
|
58
|
+
* access to the connection object (e.g., to create multiple channels).
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* it('should use connection', async ({ amqpConnection }) => {
|
|
63
|
+
* const channel = await amqpConnection.createChannel();
|
|
64
|
+
* // ... use channel
|
|
65
|
+
* });
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
28
68
|
amqpConnection: async ({ amqpConnectionUrl }, use) => {
|
|
29
69
|
const connection = await amqpLib.connect(amqpConnectionUrl);
|
|
30
70
|
await use(connection);
|
|
31
71
|
await connection.close();
|
|
32
72
|
},
|
|
73
|
+
/**
|
|
74
|
+
* Test fixture that provides an AMQP channel for interacting with RabbitMQ.
|
|
75
|
+
*
|
|
76
|
+
* Creates a channel from the active connection and automatically closes it after
|
|
77
|
+
* the test completes. The channel is used for declaring exchanges, queues, bindings,
|
|
78
|
+
* and publishing/consuming messages.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* it('should use channel', async ({ amqpChannel }) => {
|
|
83
|
+
* await amqpChannel.assertExchange('test-exchange', 'topic');
|
|
84
|
+
* await amqpChannel.assertQueue('test-queue');
|
|
85
|
+
* });
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
33
88
|
amqpChannel: async ({ amqpConnection }, use) => {
|
|
34
89
|
const channel = await amqpConnection.createChannel();
|
|
35
90
|
await use(channel);
|
|
36
91
|
await channel.close();
|
|
37
92
|
},
|
|
93
|
+
/**
|
|
94
|
+
* Test fixture for publishing messages to an AMQP exchange.
|
|
95
|
+
*
|
|
96
|
+
* Provides a helper function to publish messages directly to an exchange during tests.
|
|
97
|
+
* The message content is automatically serialized to JSON and converted to a Buffer.
|
|
98
|
+
*
|
|
99
|
+
* @param exchange - The name of the exchange to publish to
|
|
100
|
+
* @param routingKey - The routing key for message routing
|
|
101
|
+
* @param content - The message payload (will be JSON serialized)
|
|
102
|
+
* @throws Error if the message cannot be published (e.g., write buffer is full)
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* it('should publish message', async ({ publishMessage }) => {
|
|
107
|
+
* publishMessage('my-exchange', 'routing.key', { data: 'test' });
|
|
108
|
+
* });
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
38
111
|
publishMessage: async ({ amqpChannel }, use) => {
|
|
39
|
-
function publishMessage(exchange, routingKey, content) {
|
|
40
|
-
if (!amqpChannel.publish(exchange, routingKey, Buffer.from(JSON.stringify(content)))) throw new Error(`Failed to publish message to exchange "${exchange}" with routing key "${routingKey}"`);
|
|
112
|
+
function publishMessage(exchange, routingKey, content, options) {
|
|
113
|
+
if (!amqpChannel.publish(exchange, routingKey, Buffer.from(JSON.stringify(content)), options)) throw new Error(`Failed to publish message to exchange "${exchange}" with routing key "${routingKey}"`);
|
|
41
114
|
}
|
|
42
115
|
await use(publishMessage);
|
|
43
116
|
},
|
|
117
|
+
/**
|
|
118
|
+
* Test fixture for initializing a message consumer on an AMQP queue.
|
|
119
|
+
*
|
|
120
|
+
* Creates a temporary queue, binds it to the specified exchange with the given routing key,
|
|
121
|
+
* and returns a function to collect messages from that queue. The queue is automatically
|
|
122
|
+
* created with a random UUID name to avoid conflicts between tests.
|
|
123
|
+
*
|
|
124
|
+
* The returned function uses `vi.waitFor()` with a configurable timeout to wait for messages.
|
|
125
|
+
* If the expected number of messages is not received within the timeout period, the Promise
|
|
126
|
+
* will reject with a timeout error, preventing tests from hanging indefinitely.
|
|
127
|
+
*
|
|
128
|
+
* @param exchange - The name of the exchange to bind the queue to
|
|
129
|
+
* @param routingKey - The routing key pattern for message filtering
|
|
130
|
+
* @returns A function that accepts optional configuration ({ nbEvents?, timeout? }) and returns a Promise that resolves to an array of ConsumeMessage objects
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```typescript
|
|
134
|
+
* it('should consume messages', async ({ initConsumer, publishMessage }) => {
|
|
135
|
+
* const waitForMessages = await initConsumer('my-exchange', 'routing.key');
|
|
136
|
+
* publishMessage('my-exchange', 'routing.key', { data: 'test' });
|
|
137
|
+
* // With defaults (1 message, 5000ms timeout)
|
|
138
|
+
* const messages = await waitForMessages();
|
|
139
|
+
* expect(messages).toHaveLength(1);
|
|
140
|
+
*
|
|
141
|
+
* // With custom options
|
|
142
|
+
* publishMessage('my-exchange', 'routing.key', { data: 'test2' });
|
|
143
|
+
* publishMessage('my-exchange', 'routing.key', { data: 'test3' });
|
|
144
|
+
* const messages2 = await waitForMessages({ nbEvents: 2, timeout: 10000 });
|
|
145
|
+
* expect(messages2).toHaveLength(2);
|
|
146
|
+
* });
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
44
149
|
initConsumer: async ({ amqpChannel }, use) => {
|
|
45
150
|
const consumerTags = [];
|
|
46
151
|
async function initConsumer(exchange, routingKey) {
|
|
@@ -101,7 +206,7 @@ async function deleteVhost(vhost) {
|
|
|
101
206
|
throw new Error(errorMessage, { cause: vhostResponse });
|
|
102
207
|
}
|
|
103
208
|
}
|
|
104
|
-
|
|
105
209
|
//#endregion
|
|
106
210
|
export { it };
|
|
211
|
+
|
|
107
212
|
//# sourceMappingURL=extension.mjs.map
|
package/dist/extension.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.mjs","names":["vitestIt"],"sources":["../src/extension.ts"],"sourcesContent":["/**\n * Vitest extension module for AMQP testing utilities\n *\n * This module provides a Vitest test extension that adds AMQP-specific fixtures\n * to your tests. Each test gets an isolated virtual host (vhost) with pre-configured\n * connections, channels, and helper functions for publishing and consuming messages.\n *\n * @module extension\n * @packageDocumentation\n */\n\nimport amqpLib, { type Channel, type ChannelModel } from \"amqplib\";\nimport { inject, vi, it as vitestIt } from \"vitest\";\nimport { randomUUID } from \"node:crypto\";\n\nexport const it = vitestIt.extend<{\n vhost: string;\n amqpConnectionUrl: string;\n amqpConnection: ChannelModel;\n amqpChannel: Channel;\n publishMessage: (exchange: string, routingKey: string, content: unknown) => void;\n initConsumer: (\n exchange: string,\n routingKey: string,\n ) => Promise<\n (options?: { nbEvents?: number; timeout?: number }) => Promise<amqpLib.ConsumeMessage[]>\n >;\n}>({\n /**\n * Test fixture that provides an isolated RabbitMQ virtual host (vhost) for the test.\n *\n * Creates a new vhost with a random UUID name for test isolation. The vhost is automatically\n * created before the test runs using the RabbitMQ Management API.\n *\n * @example\n * ```typescript\n * it('should use isolated vhost', async ({ vhost }) => {\n * console.log(`Test running in vhost: ${vhost}`);\n * });\n * ```\n */\n // oxlint-disable-next-line no-empty-pattern\n vhost: async ({}, use) => {\n const vhost = await createVhost();\n try {\n await use(vhost);\n } finally {\n await deleteVhost(vhost);\n }\n },\n /**\n * Test fixture that provides the AMQP connection URL for the test container.\n *\n * Constructs a connection URL using the test container's IP and port, along with\n * the isolated vhost. The URL follows the format: `amqp://guest:guest@host:port/vhost`.\n *\n * @example\n * ```typescript\n * it('should connect with URL', async ({ amqpConnectionUrl }) => {\n * console.log(`Connecting to: ${amqpConnectionUrl}`);\n * });\n * ```\n */\n amqpConnectionUrl: async ({ vhost }, use) => {\n const url = `amqp://guest:guest@${inject(\"__TESTCONTAINERS_RABBITMQ_IP__\")}:${inject(\"__TESTCONTAINERS_RABBITMQ_PORT_5672__\")}/${vhost}`;\n await use(url);\n },\n /**\n * Test fixture that provides an active AMQP connection to RabbitMQ.\n *\n * Establishes a connection using the provided connection URL and automatically closes\n * it after the test completes. This fixture is useful for tests that need direct\n * access to the connection object (e.g., to create multiple channels).\n *\n * @example\n * ```typescript\n * it('should use connection', async ({ amqpConnection }) => {\n * const channel = await amqpConnection.createChannel();\n * // ... use channel\n * });\n * ```\n */\n amqpConnection: async ({ amqpConnectionUrl }, use) => {\n const connection = await amqpLib.connect(amqpConnectionUrl);\n await use(connection);\n await connection.close();\n },\n /**\n * Test fixture that provides an AMQP channel for interacting with RabbitMQ.\n *\n * Creates a channel from the active connection and automatically closes it after\n * the test completes. The channel is used for declaring exchanges, queues, bindings,\n * and publishing/consuming messages.\n *\n * @example\n * ```typescript\n * it('should use channel', async ({ amqpChannel }) => {\n * await amqpChannel.assertExchange('test-exchange', 'topic');\n * await amqpChannel.assertQueue('test-queue');\n * });\n * ```\n */\n amqpChannel: async ({ amqpConnection }, use) => {\n const channel = await amqpConnection.createChannel();\n await use(channel);\n await channel.close();\n },\n /**\n * Test fixture for publishing messages to an AMQP exchange.\n *\n * Provides a helper function to publish messages directly to an exchange during tests.\n * The message content is automatically serialized to JSON and converted to a Buffer.\n *\n * @param exchange - The name of the exchange to publish to\n * @param routingKey - The routing key for message routing\n * @param content - The message payload (will be JSON serialized)\n * @throws Error if the message cannot be published (e.g., write buffer is full)\n *\n * @example\n * ```typescript\n * it('should publish message', async ({ publishMessage }) => {\n * publishMessage('my-exchange', 'routing.key', { data: 'test' });\n * });\n * ```\n */\n publishMessage: async ({ amqpChannel }, use) => {\n function publishMessage(exchange: string, routingKey: string, content: unknown): void {\n const success = amqpChannel.publish(\n exchange,\n routingKey,\n Buffer.from(JSON.stringify(content)),\n );\n if (!success) {\n throw new Error(\n `Failed to publish message to exchange \"${exchange}\" with routing key \"${routingKey}\"`,\n );\n }\n }\n await use(publishMessage);\n },\n /**\n * Test fixture for initializing a message consumer on an AMQP queue.\n *\n * Creates a temporary queue, binds it to the specified exchange with the given routing key,\n * and returns a function to collect messages from that queue. The queue is automatically\n * created with a random UUID name to avoid conflicts between tests.\n *\n * The returned function uses `vi.waitFor()` with a configurable timeout to wait for messages.\n * If the expected number of messages is not received within the timeout period, the Promise\n * will reject with a timeout error, preventing tests from hanging indefinitely.\n *\n * @param exchange - The name of the exchange to bind the queue to\n * @param routingKey - The routing key pattern for message filtering\n * @returns A function that accepts optional configuration ({ nbEvents?, timeout? }) and returns a Promise that resolves to an array of ConsumeMessage objects\n *\n * @example\n * ```typescript\n * it('should consume messages', async ({ initConsumer, publishMessage }) => {\n * const waitForMessages = await initConsumer('my-exchange', 'routing.key');\n * publishMessage('my-exchange', 'routing.key', { data: 'test' });\n * // With defaults (1 message, 5000ms timeout)\n * const messages = await waitForMessages();\n * expect(messages).toHaveLength(1);\n *\n * // With custom options\n * publishMessage('my-exchange', 'routing.key', { data: 'test2' });\n * publishMessage('my-exchange', 'routing.key', { data: 'test3' });\n * const messages2 = await waitForMessages({ nbEvents: 2, timeout: 10000 });\n * expect(messages2).toHaveLength(2);\n * });\n * ```\n */\n initConsumer: async ({ amqpChannel }, use) => {\n const consumerTags: string[] = [];\n\n async function initConsumer(\n exchange: string,\n routingKey: string,\n ): Promise<\n (options?: { nbEvents?: number; timeout?: number }) => Promise<amqpLib.ConsumeMessage[]>\n > {\n const queue = randomUUID();\n\n await amqpChannel.assertQueue(queue);\n await amqpChannel.bindQueue(queue, exchange, routingKey);\n\n const messages: amqpLib.ConsumeMessage[] = [];\n const consumer = await amqpChannel.consume(\n queue,\n (msg) => {\n if (msg) {\n messages.push(msg);\n }\n },\n { noAck: true },\n );\n\n consumerTags.push(consumer.consumerTag);\n\n return async (options = {}) => {\n const { nbEvents = 1, timeout = 5000 } = options;\n await vi.waitFor(\n () => {\n if (messages.length < nbEvents) {\n throw new Error(\n `Expected ${nbEvents} message(s) but only received ${messages.length}`,\n );\n }\n },\n { timeout },\n );\n return messages.splice(0, nbEvents);\n };\n }\n\n try {\n await use(initConsumer);\n } finally {\n // Cancel all consumers before fixture cleanup (which deletes the vhost)\n await Promise.all(\n consumerTags.map(async (consumerTag) => {\n try {\n await amqpChannel.cancel(consumerTag);\n } catch (error) {\n // Swallow cancellation errors during cleanup\n // eslint-disable-next-line no-console\n console.error(\"Failed to cancel AMQP consumer during fixture cleanup:\", error);\n }\n }),\n );\n }\n },\n});\n\nasync function createVhost() {\n const namespace = randomUUID();\n\n const username = inject(\"__TESTCONTAINERS_RABBITMQ_USERNAME__\");\n const password = inject(\"__TESTCONTAINERS_RABBITMQ_PASSWORD__\");\n\n const vhostResponse = await fetch(\n `http://${inject(\"__TESTCONTAINERS_RABBITMQ_IP__\")}:${inject(\"__TESTCONTAINERS_RABBITMQ_PORT_15672__\")}/api/vhosts/${encodeURIComponent(namespace)}`,\n {\n method: \"PUT\",\n headers: {\n Authorization: `Basic ${btoa(`${username}:${password}`)}`,\n },\n },\n );\n\n if (vhostResponse.status !== 201) {\n const responseBody = await vhostResponse.text().catch(() => \"\");\n const errorMessage = responseBody\n ? `Failed to create vhost '${namespace}': ${vhostResponse.status} - ${responseBody}`\n : `Failed to create vhost '${namespace}': ${vhostResponse.status}`;\n throw new Error(errorMessage, {\n cause: vhostResponse,\n });\n }\n\n return namespace;\n}\n\nasync function deleteVhost(vhost: string) {\n const username = inject(\"__TESTCONTAINERS_RABBITMQ_USERNAME__\");\n const password = inject(\"__TESTCONTAINERS_RABBITMQ_PASSWORD__\");\n\n const vhostResponse = await fetch(\n `http://${inject(\"__TESTCONTAINERS_RABBITMQ_IP__\")}:${inject(\"__TESTCONTAINERS_RABBITMQ_PORT_15672__\")}/api/vhosts/${encodeURIComponent(vhost)}`,\n {\n method: \"DELETE\",\n headers: {\n Authorization: `Basic ${btoa(`${username}:${password}`)}`,\n },\n },\n );\n\n // 204 = successfully deleted, 404 = already deleted or doesn't exist\n if (vhostResponse.status !== 204 && vhostResponse.status !== 404) {\n const responseBody = await vhostResponse.text().catch(() => \"\");\n const errorMessage = responseBody\n ? `Failed to delete vhost '${vhost}': ${vhostResponse.status} - ${responseBody}`\n : `Failed to delete vhost '${vhost}': ${vhostResponse.status}`;\n throw new Error(errorMessage, {\n cause: vhostResponse,\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"extension.mjs","names":["vitestIt"],"sources":["../src/extension.ts"],"sourcesContent":["/**\n * Vitest extension module for AMQP testing utilities\n *\n * This module provides a Vitest test extension that adds AMQP-specific fixtures\n * to your tests. Each test gets an isolated virtual host (vhost) with pre-configured\n * connections, channels, and helper functions for publishing and consuming messages.\n *\n * @module extension\n * @packageDocumentation\n */\n\nimport amqpLib, { Options, type Channel, type ChannelModel } from \"amqplib\";\nimport { randomUUID } from \"node:crypto\";\nimport { inject, vi, it as vitestIt } from \"vitest\";\n\nexport const it = vitestIt.extend<{\n vhost: string;\n amqpConnectionUrl: string;\n amqpConnection: ChannelModel;\n amqpChannel: Channel;\n publishMessage: (\n exchange: string,\n routingKey: string,\n content: unknown,\n options?: Options.Publish,\n ) => void;\n initConsumer: (\n exchange: string,\n routingKey: string,\n ) => Promise<\n (options?: { nbEvents?: number; timeout?: number }) => Promise<amqpLib.ConsumeMessage[]>\n >;\n}>({\n /**\n * Test fixture that provides an isolated RabbitMQ virtual host (vhost) for the test.\n *\n * Creates a new vhost with a random UUID name for test isolation. The vhost is automatically\n * created before the test runs using the RabbitMQ Management API.\n *\n * @example\n * ```typescript\n * it('should use isolated vhost', async ({ vhost }) => {\n * console.log(`Test running in vhost: ${vhost}`);\n * });\n * ```\n */\n // oxlint-disable-next-line no-empty-pattern\n vhost: async ({}, use) => {\n const vhost = await createVhost();\n try {\n await use(vhost);\n } finally {\n await deleteVhost(vhost);\n }\n },\n /**\n * Test fixture that provides the AMQP connection URL for the test container.\n *\n * Constructs a connection URL using the test container's IP and port, along with\n * the isolated vhost. The URL follows the format: `amqp://guest:guest@host:port/vhost`.\n *\n * @example\n * ```typescript\n * it('should connect with URL', async ({ amqpConnectionUrl }) => {\n * console.log(`Connecting to: ${amqpConnectionUrl}`);\n * });\n * ```\n */\n amqpConnectionUrl: async ({ vhost }, use) => {\n const url = `amqp://guest:guest@${inject(\"__TESTCONTAINERS_RABBITMQ_IP__\")}:${inject(\"__TESTCONTAINERS_RABBITMQ_PORT_5672__\")}/${vhost}`;\n await use(url);\n },\n /**\n * Test fixture that provides an active AMQP connection to RabbitMQ.\n *\n * Establishes a connection using the provided connection URL and automatically closes\n * it after the test completes. This fixture is useful for tests that need direct\n * access to the connection object (e.g., to create multiple channels).\n *\n * @example\n * ```typescript\n * it('should use connection', async ({ amqpConnection }) => {\n * const channel = await amqpConnection.createChannel();\n * // ... use channel\n * });\n * ```\n */\n amqpConnection: async ({ amqpConnectionUrl }, use) => {\n const connection = await amqpLib.connect(amqpConnectionUrl);\n await use(connection);\n await connection.close();\n },\n /**\n * Test fixture that provides an AMQP channel for interacting with RabbitMQ.\n *\n * Creates a channel from the active connection and automatically closes it after\n * the test completes. The channel is used for declaring exchanges, queues, bindings,\n * and publishing/consuming messages.\n *\n * @example\n * ```typescript\n * it('should use channel', async ({ amqpChannel }) => {\n * await amqpChannel.assertExchange('test-exchange', 'topic');\n * await amqpChannel.assertQueue('test-queue');\n * });\n * ```\n */\n amqpChannel: async ({ amqpConnection }, use) => {\n const channel = await amqpConnection.createChannel();\n await use(channel);\n await channel.close();\n },\n /**\n * Test fixture for publishing messages to an AMQP exchange.\n *\n * Provides a helper function to publish messages directly to an exchange during tests.\n * The message content is automatically serialized to JSON and converted to a Buffer.\n *\n * @param exchange - The name of the exchange to publish to\n * @param routingKey - The routing key for message routing\n * @param content - The message payload (will be JSON serialized)\n * @throws Error if the message cannot be published (e.g., write buffer is full)\n *\n * @example\n * ```typescript\n * it('should publish message', async ({ publishMessage }) => {\n * publishMessage('my-exchange', 'routing.key', { data: 'test' });\n * });\n * ```\n */\n publishMessage: async ({ amqpChannel }, use) => {\n function publishMessage(\n exchange: string,\n routingKey: string,\n content: unknown,\n options?: Options.Publish,\n ): void {\n const success = amqpChannel.publish(\n exchange,\n routingKey,\n Buffer.from(JSON.stringify(content)),\n options,\n );\n if (!success) {\n throw new Error(\n `Failed to publish message to exchange \"${exchange}\" with routing key \"${routingKey}\"`,\n );\n }\n }\n await use(publishMessage);\n },\n /**\n * Test fixture for initializing a message consumer on an AMQP queue.\n *\n * Creates a temporary queue, binds it to the specified exchange with the given routing key,\n * and returns a function to collect messages from that queue. The queue is automatically\n * created with a random UUID name to avoid conflicts between tests.\n *\n * The returned function uses `vi.waitFor()` with a configurable timeout to wait for messages.\n * If the expected number of messages is not received within the timeout period, the Promise\n * will reject with a timeout error, preventing tests from hanging indefinitely.\n *\n * @param exchange - The name of the exchange to bind the queue to\n * @param routingKey - The routing key pattern for message filtering\n * @returns A function that accepts optional configuration ({ nbEvents?, timeout? }) and returns a Promise that resolves to an array of ConsumeMessage objects\n *\n * @example\n * ```typescript\n * it('should consume messages', async ({ initConsumer, publishMessage }) => {\n * const waitForMessages = await initConsumer('my-exchange', 'routing.key');\n * publishMessage('my-exchange', 'routing.key', { data: 'test' });\n * // With defaults (1 message, 5000ms timeout)\n * const messages = await waitForMessages();\n * expect(messages).toHaveLength(1);\n *\n * // With custom options\n * publishMessage('my-exchange', 'routing.key', { data: 'test2' });\n * publishMessage('my-exchange', 'routing.key', { data: 'test3' });\n * const messages2 = await waitForMessages({ nbEvents: 2, timeout: 10000 });\n * expect(messages2).toHaveLength(2);\n * });\n * ```\n */\n initConsumer: async ({ amqpChannel }, use) => {\n const consumerTags: string[] = [];\n\n async function initConsumer(\n exchange: string,\n routingKey: string,\n ): Promise<\n (options?: { nbEvents?: number; timeout?: number }) => Promise<amqpLib.ConsumeMessage[]>\n > {\n const queue = randomUUID();\n\n await amqpChannel.assertQueue(queue);\n await amqpChannel.bindQueue(queue, exchange, routingKey);\n\n const messages: amqpLib.ConsumeMessage[] = [];\n const consumer = await amqpChannel.consume(\n queue,\n (msg) => {\n if (msg) {\n messages.push(msg);\n }\n },\n { noAck: true },\n );\n\n consumerTags.push(consumer.consumerTag);\n\n return async (options = {}) => {\n const { nbEvents = 1, timeout = 5000 } = options;\n await vi.waitFor(\n () => {\n if (messages.length < nbEvents) {\n throw new Error(\n `Expected ${nbEvents} message(s) but only received ${messages.length}`,\n );\n }\n },\n { timeout },\n );\n return messages.splice(0, nbEvents);\n };\n }\n\n try {\n await use(initConsumer);\n } finally {\n // Cancel all consumers before fixture cleanup (which deletes the vhost)\n await Promise.all(\n consumerTags.map(async (consumerTag) => {\n try {\n await amqpChannel.cancel(consumerTag);\n } catch (error) {\n // Swallow cancellation errors during cleanup\n // eslint-disable-next-line no-console\n console.error(\"Failed to cancel AMQP consumer during fixture cleanup:\", error);\n }\n }),\n );\n }\n },\n});\n\nasync function createVhost() {\n const namespace = randomUUID();\n\n const username = inject(\"__TESTCONTAINERS_RABBITMQ_USERNAME__\");\n const password = inject(\"__TESTCONTAINERS_RABBITMQ_PASSWORD__\");\n\n const vhostResponse = await fetch(\n `http://${inject(\"__TESTCONTAINERS_RABBITMQ_IP__\")}:${inject(\"__TESTCONTAINERS_RABBITMQ_PORT_15672__\")}/api/vhosts/${encodeURIComponent(namespace)}`,\n {\n method: \"PUT\",\n headers: {\n Authorization: `Basic ${btoa(`${username}:${password}`)}`,\n },\n },\n );\n\n if (vhostResponse.status !== 201) {\n const responseBody = await vhostResponse.text().catch(() => \"\");\n const errorMessage = responseBody\n ? `Failed to create vhost '${namespace}': ${vhostResponse.status} - ${responseBody}`\n : `Failed to create vhost '${namespace}': ${vhostResponse.status}`;\n throw new Error(errorMessage, {\n cause: vhostResponse,\n });\n }\n\n return namespace;\n}\n\nasync function deleteVhost(vhost: string) {\n const username = inject(\"__TESTCONTAINERS_RABBITMQ_USERNAME__\");\n const password = inject(\"__TESTCONTAINERS_RABBITMQ_PASSWORD__\");\n\n const vhostResponse = await fetch(\n `http://${inject(\"__TESTCONTAINERS_RABBITMQ_IP__\")}:${inject(\"__TESTCONTAINERS_RABBITMQ_PORT_15672__\")}/api/vhosts/${encodeURIComponent(vhost)}`,\n {\n method: \"DELETE\",\n headers: {\n Authorization: `Basic ${btoa(`${username}:${password}`)}`,\n },\n },\n );\n\n // 204 = successfully deleted, 404 = already deleted or doesn't exist\n if (vhostResponse.status !== 204 && vhostResponse.status !== 404) {\n const responseBody = await vhostResponse.text().catch(() => \"\");\n const errorMessage = responseBody\n ? `Failed to delete vhost '${vhost}': ${vhostResponse.status} - ${responseBody}`\n : `Failed to delete vhost '${vhost}': ${vhostResponse.status}`;\n throw new Error(errorMessage, {\n cause: vhostResponse,\n });\n }\n}\n"],"mappings":"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"}
|
package/dist/global-setup.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { GenericContainer, Wait } from "testcontainers";
|
|
2
|
-
|
|
3
2
|
//#region src/global-setup.ts
|
|
4
3
|
/**
|
|
5
4
|
* Global setup module for starting RabbitMQ test containers
|
|
@@ -69,7 +68,7 @@ async function setup({ provide }) {
|
|
|
69
68
|
console.log("โ
RabbitMQ container stopped");
|
|
70
69
|
};
|
|
71
70
|
}
|
|
72
|
-
|
|
73
71
|
//#endregion
|
|
74
72
|
export { setup as default };
|
|
73
|
+
|
|
75
74
|
//# sourceMappingURL=global-setup.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"global-setup.mjs","names":[],"sources":["../src/global-setup.ts"],"sourcesContent":["/**\n * Global setup module for starting RabbitMQ test containers\n *\n * This module provides a Vitest globalSetup function that automatically starts\n * a RabbitMQ container with the management plugin before tests run, and stops\n * it after all tests complete.\n *\n * The RabbitMQ image can be configured via the `RABBITMQ_IMAGE` environment variable.\n * By default, it uses the public Docker Hub image (`rabbitmq:4.2.1-management-alpine`).\n *\n * @module global-setup\n * @packageDocumentation\n */\n\nimport { GenericContainer, Wait } from \"testcontainers\";\nimport type { TestProject } from \"vitest/node\";\n\n/**\n * Default RabbitMQ Docker image to use for testing.\n * Can be overridden via RABBITMQ_IMAGE environment variable.\n */\nconst DEFAULT_RABBITMQ_IMAGE = \"rabbitmq:4.2.1-management-alpine\";\n\ndeclare module \"vitest\" {\n // eslint-disable-next-line @typescript-eslint/consistent-type-definitions -- Module augmentation requires interface for declaration merging\n export interface ProvidedContext {\n __TESTCONTAINERS_RABBITMQ_IP__: string;\n __TESTCONTAINERS_RABBITMQ_PORT_5672__: number;\n __TESTCONTAINERS_RABBITMQ_PORT_15672__: number;\n __TESTCONTAINERS_RABBITMQ_USERNAME__: string;\n __TESTCONTAINERS_RABBITMQ_PASSWORD__: string;\n }\n}\n\n/**\n * Setup function for Vitest globalSetup\n *\n * Starts a RabbitMQ container before all tests and provides connection details\n * to tests via Vitest's provide API. The container is automatically stopped\n * and cleaned up after all tests complete.\n *\n * This function should be configured in your `vitest.config.ts`:\n *\n * @example\n * ```typescript\n * import { defineConfig } from \"vitest/config\";\n *\n * export default defineConfig({\n * test: {\n * globalSetup: [\"@amqp-contract/testing/global-setup\"],\n * },\n * });\n * ```\n *\n * @param provide - Function to provide context values to tests\n * @returns Cleanup function that stops the RabbitMQ container\n */\nexport default async function setup({ provide }: TestProject) {\n const rabbitmqImage = process.env[\"RABBITMQ_IMAGE\"] ?? DEFAULT_RABBITMQ_IMAGE;\n\n console.log(\"๐ณ Starting RabbitMQ test environment...\");\n console.log(`๐ฆ Using RabbitMQ image: ${rabbitmqImage}`);\n\n // Start RabbitMQ container with management plugin\n const rabbitmqContainer = await new GenericContainer(rabbitmqImage)\n .withExposedPorts(5672, 15672)\n .withEnvironment({\n RABBITMQ_DEFAULT_USER: \"guest\",\n RABBITMQ_DEFAULT_PASS: \"guest\",\n })\n .withWaitStrategy(\n Wait.forAll([Wait.forLogMessage(/Server startup complete/), Wait.forListeningPorts()]),\n )\n .start();\n\n console.log(\"โ
RabbitMQ container started\");\n\n const __TESTCONTAINERS_RABBITMQ_IP__ = rabbitmqContainer.getHost();\n const __TESTCONTAINERS_RABBITMQ_PORT_5672__ = rabbitmqContainer.getMappedPort(5672);\n const __TESTCONTAINERS_RABBITMQ_PORT_15672__ = rabbitmqContainer.getMappedPort(15672);\n const __TESTCONTAINERS_RABBITMQ_USERNAME__ = \"guest\";\n const __TESTCONTAINERS_RABBITMQ_PASSWORD__ = \"guest\";\n\n // Provide context values with type assertions to work around TypeScript limitations\n provide(\"__TESTCONTAINERS_RABBITMQ_IP__\", __TESTCONTAINERS_RABBITMQ_IP__);\n provide(\"__TESTCONTAINERS_RABBITMQ_PORT_5672__\", __TESTCONTAINERS_RABBITMQ_PORT_5672__);\n provide(\"__TESTCONTAINERS_RABBITMQ_PORT_15672__\", __TESTCONTAINERS_RABBITMQ_PORT_15672__);\n provide(\"__TESTCONTAINERS_RABBITMQ_USERNAME__\", __TESTCONTAINERS_RABBITMQ_USERNAME__);\n provide(\"__TESTCONTAINERS_RABBITMQ_PASSWORD__\", __TESTCONTAINERS_RABBITMQ_PASSWORD__);\n\n console.log(\n `๐ RabbitMQ test environment is ready at ${__TESTCONTAINERS_RABBITMQ_IP__}:${__TESTCONTAINERS_RABBITMQ_PORT_5672__}`,\n );\n console.log(\n `๐ RabbitMQ management console available at http://${__TESTCONTAINERS_RABBITMQ_IP__}:${__TESTCONTAINERS_RABBITMQ_PORT_15672__}`,\n );\n\n // Return cleanup function that stops the container\n return async () => {\n console.log(\"๐งน Stopping RabbitMQ test environment...\");\n await rabbitmqContainer.stop();\n console.log(\"โ
RabbitMQ container stopped\");\n };\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"global-setup.mjs","names":[],"sources":["../src/global-setup.ts"],"sourcesContent":["/**\n * Global setup module for starting RabbitMQ test containers\n *\n * This module provides a Vitest globalSetup function that automatically starts\n * a RabbitMQ container with the management plugin before tests run, and stops\n * it after all tests complete.\n *\n * The RabbitMQ image can be configured via the `RABBITMQ_IMAGE` environment variable.\n * By default, it uses the public Docker Hub image (`rabbitmq:4.2.1-management-alpine`).\n *\n * @module global-setup\n * @packageDocumentation\n */\n\nimport { GenericContainer, Wait } from \"testcontainers\";\nimport type { TestProject } from \"vitest/node\";\n\n/**\n * Default RabbitMQ Docker image to use for testing.\n * Can be overridden via RABBITMQ_IMAGE environment variable.\n */\nconst DEFAULT_RABBITMQ_IMAGE = \"rabbitmq:4.2.1-management-alpine\";\n\ndeclare module \"vitest\" {\n // eslint-disable-next-line @typescript-eslint/consistent-type-definitions -- Module augmentation requires interface for declaration merging\n export interface ProvidedContext {\n __TESTCONTAINERS_RABBITMQ_IP__: string;\n __TESTCONTAINERS_RABBITMQ_PORT_5672__: number;\n __TESTCONTAINERS_RABBITMQ_PORT_15672__: number;\n __TESTCONTAINERS_RABBITMQ_USERNAME__: string;\n __TESTCONTAINERS_RABBITMQ_PASSWORD__: string;\n }\n}\n\n/**\n * Setup function for Vitest globalSetup\n *\n * Starts a RabbitMQ container before all tests and provides connection details\n * to tests via Vitest's provide API. The container is automatically stopped\n * and cleaned up after all tests complete.\n *\n * This function should be configured in your `vitest.config.ts`:\n *\n * @example\n * ```typescript\n * import { defineConfig } from \"vitest/config\";\n *\n * export default defineConfig({\n * test: {\n * globalSetup: [\"@amqp-contract/testing/global-setup\"],\n * },\n * });\n * ```\n *\n * @param provide - Function to provide context values to tests\n * @returns Cleanup function that stops the RabbitMQ container\n */\nexport default async function setup({ provide }: TestProject) {\n const rabbitmqImage = process.env[\"RABBITMQ_IMAGE\"] ?? DEFAULT_RABBITMQ_IMAGE;\n\n console.log(\"๐ณ Starting RabbitMQ test environment...\");\n console.log(`๐ฆ Using RabbitMQ image: ${rabbitmqImage}`);\n\n // Start RabbitMQ container with management plugin\n const rabbitmqContainer = await new GenericContainer(rabbitmqImage)\n .withExposedPorts(5672, 15672)\n .withEnvironment({\n RABBITMQ_DEFAULT_USER: \"guest\",\n RABBITMQ_DEFAULT_PASS: \"guest\",\n })\n .withWaitStrategy(\n Wait.forAll([Wait.forLogMessage(/Server startup complete/), Wait.forListeningPorts()]),\n )\n .start();\n\n console.log(\"โ
RabbitMQ container started\");\n\n const __TESTCONTAINERS_RABBITMQ_IP__ = rabbitmqContainer.getHost();\n const __TESTCONTAINERS_RABBITMQ_PORT_5672__ = rabbitmqContainer.getMappedPort(5672);\n const __TESTCONTAINERS_RABBITMQ_PORT_15672__ = rabbitmqContainer.getMappedPort(15672);\n const __TESTCONTAINERS_RABBITMQ_USERNAME__ = \"guest\";\n const __TESTCONTAINERS_RABBITMQ_PASSWORD__ = \"guest\";\n\n // Provide context values with type assertions to work around TypeScript limitations\n provide(\"__TESTCONTAINERS_RABBITMQ_IP__\", __TESTCONTAINERS_RABBITMQ_IP__);\n provide(\"__TESTCONTAINERS_RABBITMQ_PORT_5672__\", __TESTCONTAINERS_RABBITMQ_PORT_5672__);\n provide(\"__TESTCONTAINERS_RABBITMQ_PORT_15672__\", __TESTCONTAINERS_RABBITMQ_PORT_15672__);\n provide(\"__TESTCONTAINERS_RABBITMQ_USERNAME__\", __TESTCONTAINERS_RABBITMQ_USERNAME__);\n provide(\"__TESTCONTAINERS_RABBITMQ_PASSWORD__\", __TESTCONTAINERS_RABBITMQ_PASSWORD__);\n\n console.log(\n `๐ RabbitMQ test environment is ready at ${__TESTCONTAINERS_RABBITMQ_IP__}:${__TESTCONTAINERS_RABBITMQ_PORT_5672__}`,\n );\n console.log(\n `๐ RabbitMQ management console available at http://${__TESTCONTAINERS_RABBITMQ_IP__}:${__TESTCONTAINERS_RABBITMQ_PORT_15672__}`,\n );\n\n // Return cleanup function that stops the container\n return async () => {\n console.log(\"๐งน Stopping RabbitMQ test environment...\");\n await rabbitmqContainer.stop();\n console.log(\"โ
RabbitMQ container stopped\");\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAqBA,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;AAoC/B,eAA8B,MAAM,EAAE,WAAwB;CAC5D,MAAM,gBAAgB,QAAQ,IAAI,qBAAqB;AAEvD,SAAQ,IAAI,2CAA2C;AACvD,SAAQ,IAAI,4BAA4B,gBAAgB;CAGxD,MAAM,oBAAoB,MAAM,IAAI,iBAAiB,cAAc,CAChE,iBAAiB,MAAM,MAAM,CAC7B,gBAAgB;EACf,uBAAuB;EACvB,uBAAuB;EACxB,CAAC,CACD,iBACC,KAAK,OAAO,CAAC,KAAK,cAAc,0BAA0B,EAAE,KAAK,mBAAmB,CAAC,CAAC,CACvF,CACA,OAAO;AAEV,SAAQ,IAAI,+BAA+B;CAE3C,MAAM,iCAAiC,kBAAkB,SAAS;CAClE,MAAM,wCAAwC,kBAAkB,cAAc,KAAK;CACnF,MAAM,yCAAyC,kBAAkB,cAAc,MAAM;CACrF,MAAM,uCAAuC;CAC7C,MAAM,uCAAuC;AAG7C,SAAQ,kCAAkC,+BAA+B;AACzE,SAAQ,yCAAyC,sCAAsC;AACvF,SAAQ,0CAA0C,uCAAuC;AACzF,SAAQ,wCAAwC,qCAAqC;AACrF,SAAQ,wCAAwC,qCAAqC;AAErF,SAAQ,IACN,4CAA4C,+BAA+B,GAAG,wCAC/E;AACD,SAAQ,IACN,sDAAsD,+BAA+B,GAAG,yCACzF;AAGD,QAAO,YAAY;AACjB,UAAQ,IAAI,2CAA2C;AACvD,QAAM,kBAAkB,MAAM;AAC9B,UAAQ,IAAI,+BAA+B"}
|
package/dist/index.mjs
CHANGED
package/docs/extension.md
CHANGED
|
@@ -22,9 +22,9 @@ const it: TestAPI<{
|
|
|
22
22
|
amqpConnection: ChannelModel;
|
|
23
23
|
amqpConnectionUrl: string;
|
|
24
24
|
initConsumer: (exchange, routingKey) => Promise<(options?) => Promise<ConsumeMessage[]>>;
|
|
25
|
-
publishMessage: (exchange, routingKey, content) => void;
|
|
25
|
+
publishMessage: (exchange, routingKey, content, options?) => void;
|
|
26
26
|
vhost: string;
|
|
27
27
|
}>;
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
Defined in: [extension.ts:16](https://github.com/btravers/amqp-contract/blob/
|
|
30
|
+
Defined in: [extension.ts:16](https://github.com/btravers/amqp-contract/blob/da9b95c747db8d5af9183ca6fe2a6e0af558d1fa/packages/testing/src/extension.ts#L16)
|
package/docs/global-setup.md
CHANGED
|
@@ -23,7 +23,7 @@ By default, it uses the public Docker Hub image (`rabbitmq:4.2.1-management-alpi
|
|
|
23
23
|
function default(provide): Promise<() => Promise<void>>;
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
Defined in: [global-setup.ts:58](https://github.com/btravers/amqp-contract/blob/
|
|
26
|
+
Defined in: [global-setup.ts:58](https://github.com/btravers/amqp-contract/blob/da9b95c747db8d5af9183ca6fe2a6e0af558d1fa/packages/testing/src/global-setup.ts#L58)
|
|
27
27
|
|
|
28
28
|
Setup function for Vitest globalSetup
|
|
29
29
|
|
package/package.json
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amqp-contract/testing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "Testing utilities for AMQP contracts with testcontainers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"amqp",
|
|
7
7
|
"contract",
|
|
8
|
+
"nodejs",
|
|
8
9
|
"rabbitmq",
|
|
9
|
-
"typescript",
|
|
10
|
-
"testing",
|
|
11
10
|
"testcontainers",
|
|
12
|
-
"
|
|
11
|
+
"testing",
|
|
13
12
|
"type-safe",
|
|
13
|
+
"typescript",
|
|
14
14
|
"vitest"
|
|
15
15
|
],
|
|
16
16
|
"license": "MIT",
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"docs"
|
|
20
|
+
],
|
|
17
21
|
"type": "module",
|
|
18
22
|
"exports": {
|
|
19
23
|
".": {
|
|
@@ -29,21 +33,17 @@
|
|
|
29
33
|
"import": "./dist/extension.mjs"
|
|
30
34
|
}
|
|
31
35
|
},
|
|
32
|
-
"files": [
|
|
33
|
-
"dist",
|
|
34
|
-
"docs"
|
|
35
|
-
],
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"amqplib": "0.
|
|
38
|
-
"testcontainers": "11.
|
|
37
|
+
"amqplib": "1.0.3",
|
|
38
|
+
"testcontainers": "11.14.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/amqplib": "0.10.8",
|
|
42
|
-
"tsdown": "0.
|
|
43
|
-
"typedoc": "0.28.
|
|
44
|
-
"typedoc-plugin-markdown": "4.
|
|
45
|
-
"typescript": "
|
|
46
|
-
"vitest": "4.
|
|
42
|
+
"tsdown": "0.21.10",
|
|
43
|
+
"typedoc": "0.28.19",
|
|
44
|
+
"typedoc-plugin-markdown": "4.11.0",
|
|
45
|
+
"typescript": "6.0.3",
|
|
46
|
+
"vitest": "4.1.5",
|
|
47
47
|
"@amqp-contract/tsconfig": "0.1.0",
|
|
48
48
|
"@amqp-contract/typedoc": "0.1.0"
|
|
49
49
|
},
|