core-services-sdk 1.3.56 → 1.3.59
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/.claude/settings.local.json +22 -0
- package/.prettierrc.json +0 -1
- package/package.json +4 -2
- package/src/http/helpers/create-request-logger.js +38 -0
- package/src/http/index.js +1 -0
- package/src/mongodb/index.js +1 -0
- package/src/postgresql/index.js +1 -0
- package/src/postgresql/paginate.js +61 -0
- package/src/postgresql/start-stop-postgres-docker.js +1 -1
- package/src/rabbit-mq/index.js +1 -0
- package/src/rabbit-mq/rabbit-mq.js +102 -21
- package/src/rabbit-mq/start-stop-rabbitmq.js +152 -0
- package/src/util/context.js +64 -32
- package/tests/http/helpers/create-request-logger.test.js +113 -0
- package/tests/postgresql/paginate.integration.test.js +165 -0
- package/tests/rabbit-mq/rabbit-mq.test.js +121 -51
- package/tests/resources/docker-mongo-test.js +3 -5
- package/types/http/helpers/create-request-logger.d.ts +4 -0
- package/types/http/index.d.ts +1 -0
- package/types/mongodb/index.d.ts +1 -0
- package/types/postgresql/index.d.ts +1 -0
- package/types/postgresql/paginate.d.ts +26 -0
- package/types/rabbit-mq/index.d.ts +1 -0
- package/types/rabbit-mq/rabbit-mq.d.ts +1 -1
- package/types/rabbit-mq/start-stop-rabbitmq.d.ts +74 -0
- package/types/util/context.d.ts +93 -24
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import { createRequestLogger } from '../../../src/http/helpers/create-request-logger.js'
|
|
5
|
+
import { Context } from '../../../src/util/context.js'
|
|
6
|
+
//@ts-ignore
|
|
7
|
+
describe('createRequestLogger', () => {
|
|
8
|
+
let baseLogger
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
baseLogger = {
|
|
12
|
+
child: vi.fn(),
|
|
13
|
+
}
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('creates a child logger with context and request data', () => {
|
|
17
|
+
const request = {
|
|
18
|
+
method: 'GET',
|
|
19
|
+
url: '/test',
|
|
20
|
+
raw: { url: '/raw-test' },
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const childLogger = {}
|
|
24
|
+
baseLogger.child.mockReturnValue(childLogger)
|
|
25
|
+
|
|
26
|
+
Context.run(
|
|
27
|
+
{
|
|
28
|
+
correlationId: 'corr-123',
|
|
29
|
+
ip: '127.0.0.1',
|
|
30
|
+
userAgent: 'vitest-agent',
|
|
31
|
+
},
|
|
32
|
+
() => {
|
|
33
|
+
const result = createRequestLogger(request, baseLogger)
|
|
34
|
+
|
|
35
|
+
expect(baseLogger.child).toHaveBeenCalledOnce()
|
|
36
|
+
expect(baseLogger.child).toHaveBeenCalledWith({
|
|
37
|
+
ip: '127.0.0.1',
|
|
38
|
+
userAgent: 'vitest-agent',
|
|
39
|
+
correlationId: 'corr-123',
|
|
40
|
+
op: 'GET /test',
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
expect(result).toBe(childLogger)
|
|
44
|
+
},
|
|
45
|
+
)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('falls back to routeOptions.url when request.url is missing', () => {
|
|
49
|
+
const request = {
|
|
50
|
+
method: 'POST',
|
|
51
|
+
routeOptions: { url: '/route-url' },
|
|
52
|
+
raw: { url: '/raw-url' },
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
Context.run(
|
|
56
|
+
{
|
|
57
|
+
correlationId: 'corr-456',
|
|
58
|
+
ip: '10.0.0.1',
|
|
59
|
+
userAgent: 'agent-x',
|
|
60
|
+
},
|
|
61
|
+
() => {
|
|
62
|
+
createRequestLogger(request, baseLogger)
|
|
63
|
+
|
|
64
|
+
expect(baseLogger.child).toHaveBeenCalledWith(
|
|
65
|
+
expect.objectContaining({
|
|
66
|
+
op: 'POST /route-url',
|
|
67
|
+
}),
|
|
68
|
+
)
|
|
69
|
+
},
|
|
70
|
+
)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('falls back to request.raw.url when neither url nor routeOptions.url exist', () => {
|
|
74
|
+
const request = {
|
|
75
|
+
method: 'PUT',
|
|
76
|
+
raw: { url: '/raw-only' },
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
Context.run(
|
|
80
|
+
{
|
|
81
|
+
correlationId: 'corr-789',
|
|
82
|
+
ip: '192.168.1.1',
|
|
83
|
+
userAgent: 'agent-y',
|
|
84
|
+
},
|
|
85
|
+
() => {
|
|
86
|
+
createRequestLogger(request, baseLogger)
|
|
87
|
+
|
|
88
|
+
expect(baseLogger.child).toHaveBeenCalledWith(
|
|
89
|
+
expect.objectContaining({
|
|
90
|
+
op: 'PUT /raw-only',
|
|
91
|
+
}),
|
|
92
|
+
)
|
|
93
|
+
},
|
|
94
|
+
)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('handles missing context gracefully when no Context is active', () => {
|
|
98
|
+
const request = {
|
|
99
|
+
method: 'DELETE',
|
|
100
|
+
url: '/delete',
|
|
101
|
+
raw: { url: '/delete' },
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
createRequestLogger(request, baseLogger)
|
|
105
|
+
|
|
106
|
+
expect(baseLogger.child).toHaveBeenCalledWith({
|
|
107
|
+
ip: undefined,
|
|
108
|
+
userAgent: undefined,
|
|
109
|
+
correlationId: undefined,
|
|
110
|
+
op: 'DELETE /delete',
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
})
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { describe, it, beforeAll, afterAll, beforeEach, expect } from 'vitest'
|
|
2
|
+
import knex from 'knex'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
stopPostgres,
|
|
6
|
+
startPostgres,
|
|
7
|
+
buildPostgresUri,
|
|
8
|
+
} from '../../src/postgresql/start-stop-postgres-docker.js'
|
|
9
|
+
|
|
10
|
+
import { sqlPaginate } from '../../src/postgresql/paginate.js'
|
|
11
|
+
|
|
12
|
+
const PG_OPTIONS = {
|
|
13
|
+
port: 5443,
|
|
14
|
+
containerName: 'postgres-paginate-test',
|
|
15
|
+
user: 'testuser',
|
|
16
|
+
pass: 'testpass',
|
|
17
|
+
db: 'testdb',
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const DATABASE_URI = buildPostgresUri(PG_OPTIONS)
|
|
21
|
+
|
|
22
|
+
let db
|
|
23
|
+
|
|
24
|
+
beforeAll(async () => {
|
|
25
|
+
startPostgres(PG_OPTIONS)
|
|
26
|
+
|
|
27
|
+
db = knex({
|
|
28
|
+
client: 'pg',
|
|
29
|
+
connection: DATABASE_URI,
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
await db.schema.createTable('tenants', (table) => {
|
|
33
|
+
table.uuid('id').primary()
|
|
34
|
+
table.string('name').notNullable()
|
|
35
|
+
table.string('type').notNullable()
|
|
36
|
+
table.timestamp('created_at').notNullable()
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
afterAll(async () => {
|
|
41
|
+
if (db) {
|
|
42
|
+
await db.destroy()
|
|
43
|
+
}
|
|
44
|
+
stopPostgres(PG_OPTIONS.containerName)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
beforeEach(async () => {
|
|
48
|
+
await db('tenants').truncate()
|
|
49
|
+
|
|
50
|
+
await db('tenants').insert([
|
|
51
|
+
{
|
|
52
|
+
id: '00000000-0000-0000-0000-000000000001',
|
|
53
|
+
name: 'Tenant A',
|
|
54
|
+
type: 'business',
|
|
55
|
+
created_at: new Date('2024-01-01'),
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: '00000000-0000-0000-0000-000000000002',
|
|
59
|
+
name: 'Tenant B',
|
|
60
|
+
type: 'business',
|
|
61
|
+
created_at: new Date('2024-01-02'),
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: '00000000-0000-0000-0000-000000000003',
|
|
65
|
+
name: 'Tenant C',
|
|
66
|
+
type: 'cpa',
|
|
67
|
+
created_at: new Date('2024-01-03'),
|
|
68
|
+
},
|
|
69
|
+
])
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
describe('paginate integration', () => {
|
|
73
|
+
it('returns first page without ordering guarantees', async () => {
|
|
74
|
+
const result = await sqlPaginate({
|
|
75
|
+
db,
|
|
76
|
+
tableName: 'tenants',
|
|
77
|
+
page: 1,
|
|
78
|
+
limit: 2,
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
expect(result.totalCount).toBe(3)
|
|
82
|
+
expect(result.totalPages).toBe(2)
|
|
83
|
+
expect(result.currentPage).toBe(1)
|
|
84
|
+
expect(result.hasPrevious).toBe(false)
|
|
85
|
+
expect(result.hasNext).toBe(true)
|
|
86
|
+
|
|
87
|
+
expect(result.list).toHaveLength(2)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('returns second page without ordering guarantees', async () => {
|
|
91
|
+
const result = await sqlPaginate({
|
|
92
|
+
db,
|
|
93
|
+
tableName: 'tenants',
|
|
94
|
+
page: 2,
|
|
95
|
+
limit: 2,
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
expect(result.totalCount).toBe(3)
|
|
99
|
+
expect(result.totalPages).toBe(2)
|
|
100
|
+
expect(result.currentPage).toBe(2)
|
|
101
|
+
expect(result.hasPrevious).toBe(true)
|
|
102
|
+
expect(result.hasNext).toBe(false)
|
|
103
|
+
|
|
104
|
+
expect(result.list).toHaveLength(1)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('applies filters correctly without ordering guarantees', async () => {
|
|
108
|
+
const result = await sqlPaginate({
|
|
109
|
+
db,
|
|
110
|
+
tableName: 'tenants',
|
|
111
|
+
filter: { type: 'business' },
|
|
112
|
+
limit: 10,
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
expect(result.totalCount).toBe(2)
|
|
116
|
+
|
|
117
|
+
const names = result.list.map((t) => t.name).sort()
|
|
118
|
+
expect(names).toEqual(['Tenant A', 'Tenant B'])
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('supports custom ordering', async () => {
|
|
122
|
+
const result = await sqlPaginate({
|
|
123
|
+
db,
|
|
124
|
+
tableName: 'tenants',
|
|
125
|
+
orderBy: {
|
|
126
|
+
column: 'created_at',
|
|
127
|
+
direction: 'asc',
|
|
128
|
+
},
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
expect(result.list.map((t) => t.name)).toEqual([
|
|
132
|
+
'Tenant A',
|
|
133
|
+
'Tenant B',
|
|
134
|
+
'Tenant C',
|
|
135
|
+
])
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it('supports row mapping', async () => {
|
|
139
|
+
const result = await sqlPaginate({
|
|
140
|
+
db,
|
|
141
|
+
tableName: 'tenants',
|
|
142
|
+
mapRow: (row) => ({
|
|
143
|
+
...row,
|
|
144
|
+
mapped: true,
|
|
145
|
+
}),
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
expect(result.list.length).toBeGreaterThan(0)
|
|
149
|
+
expect(result.list[0].mapped).toBe(true)
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it('returns empty list when no records match filter', async () => {
|
|
153
|
+
const result = await sqlPaginate({
|
|
154
|
+
db,
|
|
155
|
+
tableName: 'tenants',
|
|
156
|
+
filter: { type: 'non-existing' },
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
expect(result.totalCount).toBe(0)
|
|
160
|
+
expect(result.totalPages).toBe(0)
|
|
161
|
+
expect(result.list).toEqual([])
|
|
162
|
+
expect(result.hasNext).toBe(false)
|
|
163
|
+
expect(result.hasPrevious).toBe(false)
|
|
164
|
+
})
|
|
165
|
+
})
|
|
@@ -1,69 +1,139 @@
|
|
|
1
|
-
import
|
|
1
|
+
import pino from 'pino'
|
|
2
|
+
import { describe, it, beforeAll, afterAll, expect } from 'vitest'
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
stopRabbit,
|
|
6
|
+
startRabbit,
|
|
7
|
+
buildRabbitUri,
|
|
8
|
+
} from '../../src/rabbit-mq/start-stop-rabbitmq.js'
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
import { initializeQueue } from '../../src/rabbit-mq/index.js'
|
|
6
11
|
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
},
|
|
14
|
-
}
|
|
12
|
+
const RABBIT_CONTAINER = 'rabbit-test'
|
|
13
|
+
const AMQP_PORT = 5679
|
|
14
|
+
const UI_PORT = 15679
|
|
15
|
+
const USER = 'test'
|
|
16
|
+
const PASS = 'test'
|
|
17
|
+
const QUEUE = 'integration-test-queue'
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
const log = pino({
|
|
20
|
+
level: 'silent',
|
|
21
|
+
})
|
|
22
|
+
// @ts-ignore
|
|
23
|
+
async function waitForRabbitConnection({ uri, log, timeoutMs = 10000 }) {
|
|
24
|
+
const start = Date.now()
|
|
20
25
|
|
|
21
|
-
|
|
22
|
-
|
|
26
|
+
while (Date.now() - start < timeoutMs) {
|
|
27
|
+
try {
|
|
28
|
+
const rabbit = await initializeQueue({ host: uri, log })
|
|
29
|
+
return rabbit
|
|
30
|
+
} catch {
|
|
31
|
+
await sleep(300)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
throw new Error('RabbitMQ AMQP endpoint did not become ready in time')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe('RabbitMQ integration', () => {
|
|
39
|
+
// @ts-ignore
|
|
40
|
+
let rabbit
|
|
41
|
+
// @ts-ignore
|
|
42
|
+
let unsubscribe
|
|
43
|
+
// @ts-ignore
|
|
44
|
+
let receivedMessages
|
|
23
45
|
|
|
24
46
|
beforeAll(async () => {
|
|
25
|
-
|
|
26
|
-
|
|
47
|
+
startRabbit({
|
|
48
|
+
containerName: RABBIT_CONTAINER,
|
|
49
|
+
amqpPort: AMQP_PORT,
|
|
50
|
+
uiPort: UI_PORT,
|
|
51
|
+
user: USER,
|
|
52
|
+
pass: PASS,
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
const uri = buildRabbitUri({
|
|
56
|
+
user: USER,
|
|
57
|
+
pass: PASS,
|
|
58
|
+
port: AMQP_PORT,
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
rabbit = await waitForRabbitConnection({
|
|
62
|
+
uri,
|
|
63
|
+
log,
|
|
64
|
+
})
|
|
65
|
+
}, 60_000)
|
|
27
66
|
|
|
28
|
-
|
|
29
|
-
|
|
67
|
+
afterAll(async () => {
|
|
68
|
+
try {
|
|
69
|
+
// @ts-ignore
|
|
70
|
+
if (unsubscribe) {
|
|
71
|
+
await unsubscribe()
|
|
72
|
+
}
|
|
73
|
+
// @ts-ignore
|
|
74
|
+
if (rabbit) {
|
|
75
|
+
await rabbit.close()
|
|
76
|
+
}
|
|
77
|
+
} finally {
|
|
78
|
+
stopRabbit(RABBIT_CONTAINER)
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('should publish, consume, unsubscribe, and stop consuming', async () => {
|
|
83
|
+
receivedMessages = []
|
|
84
|
+
|
|
85
|
+
// @ts-ignore
|
|
86
|
+
unsubscribe = await rabbit.subscribe({
|
|
87
|
+
queue: QUEUE,
|
|
88
|
+
// @ts-ignore
|
|
30
89
|
onReceive: async (data) => {
|
|
31
|
-
|
|
90
|
+
receivedMessages.push(data)
|
|
32
91
|
},
|
|
33
92
|
})
|
|
34
|
-
})
|
|
35
93
|
|
|
36
|
-
|
|
37
|
-
await
|
|
38
|
-
await
|
|
39
|
-
expect(received).toEqual(testMessage)
|
|
40
|
-
})
|
|
41
|
-
})
|
|
94
|
+
// @ts-ignore
|
|
95
|
+
await rabbit.publish(QUEUE, { step: 1 })
|
|
96
|
+
await waitFor(() => receivedMessages.length === 1)
|
|
42
97
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const env = {
|
|
46
|
-
RABBIT_PORT: 5672,
|
|
47
|
-
RABBIT_HOST: '0.0.0.0',
|
|
48
|
-
RABBIT_USERNAME: 'botq',
|
|
49
|
-
RABBIT_PASSWORD: 'botq',
|
|
50
|
-
_RABBIT_HOST: '0.0.0.0',
|
|
51
|
-
RABBIT_PROTOCOL: 'amqp',
|
|
52
|
-
}
|
|
98
|
+
// @ts-ignore
|
|
99
|
+
expect(receivedMessages).toEqual([{ step: 1 }])
|
|
53
100
|
|
|
54
|
-
|
|
55
|
-
expect(uri).toBe('amqp://botq:botq@0.0.0.0:5672')
|
|
56
|
-
})
|
|
101
|
+
await unsubscribe()
|
|
57
102
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
RABBIT_USERNAME: 'botq',
|
|
63
|
-
RABBIT_PASSWORD: 'botq',
|
|
64
|
-
}
|
|
103
|
+
// @ts-ignore
|
|
104
|
+
await rabbit.publish(QUEUE, { step: 2 })
|
|
105
|
+
|
|
106
|
+
await sleep(1000)
|
|
65
107
|
|
|
66
|
-
|
|
67
|
-
expect(
|
|
108
|
+
// @ts-ignore
|
|
109
|
+
expect(receivedMessages).toEqual([{ step: 1 }])
|
|
68
110
|
})
|
|
69
111
|
})
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Waits until a condition becomes true or times out.
|
|
115
|
+
*
|
|
116
|
+
* @param {() => boolean} predicate
|
|
117
|
+
* @param {number} timeoutMs
|
|
118
|
+
*/
|
|
119
|
+
async function waitFor(predicate, timeoutMs = 5000) {
|
|
120
|
+
const start = Date.now()
|
|
121
|
+
|
|
122
|
+
while (Date.now() - start < timeoutMs) {
|
|
123
|
+
if (predicate()) {
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
await sleep(50)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
throw new Error('Condition not met within timeout')
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Sleeps for the given number of milliseconds.
|
|
134
|
+
*
|
|
135
|
+
* @param {number} ms
|
|
136
|
+
*/
|
|
137
|
+
function sleep(ms) {
|
|
138
|
+
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
139
|
+
}
|
|
@@ -80,15 +80,13 @@ function isConnected(port) {
|
|
|
80
80
|
*/
|
|
81
81
|
function waitForMongo(port) {
|
|
82
82
|
console.log(`[MongoTest] Waiting for MongoDB to be ready...`)
|
|
83
|
-
const maxRetries =
|
|
83
|
+
const maxRetries = 60
|
|
84
84
|
let retries = 0
|
|
85
85
|
let connected = false
|
|
86
86
|
|
|
87
87
|
while (!connected && retries < maxRetries) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
retries++
|
|
91
|
-
} catch {
|
|
88
|
+
connected = isConnected(port)
|
|
89
|
+
if (!connected) {
|
|
92
90
|
retries++
|
|
93
91
|
execSync(`sleep 1`)
|
|
94
92
|
}
|
package/types/http/index.d.ts
CHANGED
package/types/mongodb/index.d.ts
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function sqlPaginate({
|
|
2
|
+
db,
|
|
3
|
+
mapRow,
|
|
4
|
+
orderBy,
|
|
5
|
+
page,
|
|
6
|
+
limit,
|
|
7
|
+
tableName,
|
|
8
|
+
filter,
|
|
9
|
+
}?: {
|
|
10
|
+
db: any
|
|
11
|
+
tableName: string
|
|
12
|
+
page?: number
|
|
13
|
+
limit?: number
|
|
14
|
+
filter?: any
|
|
15
|
+
orderBy?: {
|
|
16
|
+
column: string
|
|
17
|
+
direction?: 'asc' | 'desc'
|
|
18
|
+
}
|
|
19
|
+
}): Promise<{
|
|
20
|
+
list: any[]
|
|
21
|
+
totalCount: number
|
|
22
|
+
totalPages: number
|
|
23
|
+
currentPage: number
|
|
24
|
+
hasNext: boolean
|
|
25
|
+
hasPrevious: boolean
|
|
26
|
+
}>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Starts a RabbitMQ Docker container for testing purposes.
|
|
3
|
+
*
|
|
4
|
+
* If a container with the same name already exists, it will be removed first.
|
|
5
|
+
* The container is started with the RabbitMQ Management plugin enabled and
|
|
6
|
+
* waits until the health check reports the container as healthy.
|
|
7
|
+
*
|
|
8
|
+
* @param {Object} options
|
|
9
|
+
* @param {string} options.containerName
|
|
10
|
+
* Docker container name.
|
|
11
|
+
*
|
|
12
|
+
* @param {number} options.amqpPort
|
|
13
|
+
* Host port mapped to RabbitMQ AMQP port (5672).
|
|
14
|
+
*
|
|
15
|
+
* @param {number} options.uiPort
|
|
16
|
+
* Host port mapped to RabbitMQ Management UI port (15672).
|
|
17
|
+
*
|
|
18
|
+
* @param {string} options.user
|
|
19
|
+
* Default RabbitMQ username.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} options.pass
|
|
22
|
+
* Default RabbitMQ password.
|
|
23
|
+
*
|
|
24
|
+
* @returns {void}
|
|
25
|
+
*
|
|
26
|
+
* @throws {Error}
|
|
27
|
+
* Throws if the RabbitMQ container fails to become healthy within the timeout.
|
|
28
|
+
*/
|
|
29
|
+
export function startRabbit({
|
|
30
|
+
containerName,
|
|
31
|
+
...rest
|
|
32
|
+
}: {
|
|
33
|
+
containerName: string
|
|
34
|
+
amqpPort: number
|
|
35
|
+
uiPort: number
|
|
36
|
+
user: string
|
|
37
|
+
pass: string
|
|
38
|
+
}): void
|
|
39
|
+
/**
|
|
40
|
+
* Stops and removes a RabbitMQ Docker container.
|
|
41
|
+
*
|
|
42
|
+
* This function is safe to call even if the container does not exist.
|
|
43
|
+
*
|
|
44
|
+
* @param {string} [containerName='rabbit-test']
|
|
45
|
+
* Docker container name.
|
|
46
|
+
*
|
|
47
|
+
* @returns {void}
|
|
48
|
+
*/
|
|
49
|
+
export function stopRabbit(containerName?: string): void
|
|
50
|
+
/**
|
|
51
|
+
* Builds a RabbitMQ AMQP connection URI for local testing.
|
|
52
|
+
*
|
|
53
|
+
* @param {Object} options
|
|
54
|
+
* @param {string} options.user
|
|
55
|
+
* RabbitMQ username.
|
|
56
|
+
*
|
|
57
|
+
* @param {string} options.pass
|
|
58
|
+
* RabbitMQ password.
|
|
59
|
+
*
|
|
60
|
+
* @param {number} options.port
|
|
61
|
+
* Host port mapped to RabbitMQ AMQP port.
|
|
62
|
+
*
|
|
63
|
+
* @returns {string}
|
|
64
|
+
* RabbitMQ AMQP connection URI.
|
|
65
|
+
*/
|
|
66
|
+
export function buildRabbitUri({
|
|
67
|
+
user,
|
|
68
|
+
pass,
|
|
69
|
+
port,
|
|
70
|
+
}: {
|
|
71
|
+
user: string
|
|
72
|
+
pass: string
|
|
73
|
+
port: number
|
|
74
|
+
}): string
|