queen-mq 0.2.22 → 0.3.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/README.md +162 -136
- package/client-js/benchmark/consumer.js +1 -1
- package/client-js/client/client.js +24 -3
- package/client-js/client-v2/LOGGING.md +240 -0
- package/client-js/client-v2/Queen.js +389 -0
- package/client-js/client-v2/README.md +1883 -0
- package/client-js/client-v2/buffer/BufferManager.js +215 -0
- package/client-js/client-v2/buffer/MessageBuffer.js +132 -0
- package/client-js/client-v2/builders/QueueBuilder.js +724 -0
- package/client-js/client-v2/builders/TransactionBuilder.js +110 -0
- package/client-js/client-v2/consumer/ConsumerManager.js +390 -0
- package/client-js/client-v2/http/HttpClient.js +215 -0
- package/client-js/client-v2/http/LoadBalancer.js +50 -0
- package/client-js/client-v2/index.js +7 -0
- package/client-js/client-v2/utils/defaults.js +54 -0
- package/client-js/client-v2/utils/logger.js +54 -0
- package/client-js/client-v2/utils/validation.js +31 -0
- package/client-js/test-v2/AI_TEST_SUMMARY.md +226 -0
- package/client-js/test-v2/GETTING_STARTED.md +154 -0
- package/client-js/test-v2/ai_buffering.js +194 -0
- package/client-js/test-v2/ai_error_handling.js +223 -0
- package/client-js/test-v2/ai_lease_renewal.js +206 -0
- package/client-js/test-v2/ai_mixed_scenarios.js +278 -0
- package/client-js/test-v2/ai_priority.js +169 -0
- package/client-js/test-v2/ai_resources.js +217 -0
- package/client-js/test-v2/ai_ttl_retention.js +170 -0
- package/client-js/test-v2/complete.js +59 -0
- package/client-js/test-v2/consume.js +655 -0
- package/client-js/test-v2/dlq.js +82 -0
- package/client-js/test-v2/load.js +177 -0
- package/client-js/test-v2/pop.js +114 -0
- package/client-js/test-v2/push.js +333 -0
- package/client-js/test-v2/queue.js +39 -0
- package/client-js/test-v2/run.js +187 -0
- package/client-js/test-v2/subscription.js +354 -0
- package/client-js/test-v2/transaction.js +443 -0
- package/package.json +1 -1
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export async function testDLQ(client) {
|
|
2
|
+
const queueName = 'test-queue-v2-dlq'
|
|
3
|
+
|
|
4
|
+
// 1. Create queue with low retry limit
|
|
5
|
+
const queue = await client
|
|
6
|
+
.queue(queueName)
|
|
7
|
+
.config({
|
|
8
|
+
retryLimit: 1, // Only 1 retry before DLQ
|
|
9
|
+
dlqAfterMaxRetries: true // Enable DLQ after max retries
|
|
10
|
+
})
|
|
11
|
+
.create()
|
|
12
|
+
|
|
13
|
+
if (!queue.configured) {
|
|
14
|
+
return { success: false, message: 'Queue not created' }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// 2. Push a test message
|
|
18
|
+
const testMessage = { message: 'Test DLQ message', timestamp: Date.now() }
|
|
19
|
+
await client
|
|
20
|
+
.queue(queueName)
|
|
21
|
+
.push([{ data: testMessage }])
|
|
22
|
+
|
|
23
|
+
// 3. Consume and fail the message (will trigger retry then DLQ)
|
|
24
|
+
await client
|
|
25
|
+
.queue(queueName)
|
|
26
|
+
.batch(1)
|
|
27
|
+
.wait(false)
|
|
28
|
+
.limit(1) // Process up to 2 messages (original + retry)
|
|
29
|
+
.each()
|
|
30
|
+
.consume(async msg => {
|
|
31
|
+
// Always fail to trigger DLQ
|
|
32
|
+
throw new Error('Test error - triggering DLQ')
|
|
33
|
+
})
|
|
34
|
+
.onError(async (msg, error) => {
|
|
35
|
+
// Ack as failed to trigger DLQ logic
|
|
36
|
+
await client.ack(msg, false, { error: error.message })
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// 4. Wait a moment for DLQ processing
|
|
40
|
+
await new Promise(resolve => setTimeout(resolve, 500))
|
|
41
|
+
// 5. Check DLQ for the message
|
|
42
|
+
const dlqResult = await client
|
|
43
|
+
.queue(queueName)
|
|
44
|
+
.dlq()
|
|
45
|
+
.limit(10)
|
|
46
|
+
.get()
|
|
47
|
+
|
|
48
|
+
console.log('DLQ result: ', JSON.stringify(dlqResult, null, 2))
|
|
49
|
+
|
|
50
|
+
if (!dlqResult || !dlqResult.messages) {
|
|
51
|
+
return { success: false, message: 'Failed to query DLQ' }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (dlqResult.messages.length === 0) {
|
|
55
|
+
return {
|
|
56
|
+
success: false,
|
|
57
|
+
message: `No messages in DLQ. Expected at least 1 message. Total: ${dlqResult.total}`
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 6. Verify the message content
|
|
62
|
+
const dlqMessage = dlqResult.messages[0]
|
|
63
|
+
|
|
64
|
+
if (!dlqMessage.data || dlqMessage.data.message !== testMessage.message) {
|
|
65
|
+
return {
|
|
66
|
+
success: false,
|
|
67
|
+
message: 'DLQ message data does not match expected content'
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!dlqMessage.errorMessage || !dlqMessage.errorMessage.includes('Test error')) {
|
|
72
|
+
return {
|
|
73
|
+
success: false,
|
|
74
|
+
message: 'DLQ message missing or incorrect error message'
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
success: true,
|
|
80
|
+
message: `DLQ test completed successfully. Found ${dlqResult.total} message(s) in DLQ`
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
export async function testLoad(client) {
|
|
2
|
+
const queue = await client
|
|
3
|
+
.queue('test-queue-v2-load')
|
|
4
|
+
.create()
|
|
5
|
+
if (!queue.configured) {
|
|
6
|
+
return { success: false, message: 'Queue not created' }
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const messagesToPush = 100000;
|
|
10
|
+
|
|
11
|
+
let messages = [];
|
|
12
|
+
for (let i = 0; i < messagesToPush; i++) {
|
|
13
|
+
messages.push({ data: { id: i } })
|
|
14
|
+
if (messages.length >= 10000) {
|
|
15
|
+
await client.queue('test-queue-v2-load').push(messages)
|
|
16
|
+
messages = [];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
await client.queue('test-queue-v2-load').push(messages)
|
|
20
|
+
|
|
21
|
+
let uniqueIds = new Set();
|
|
22
|
+
let lastId = null;
|
|
23
|
+
|
|
24
|
+
await client
|
|
25
|
+
.queue('test-queue-v2-load')
|
|
26
|
+
.concurrency(10)
|
|
27
|
+
.batch(10000)
|
|
28
|
+
.wait(false)
|
|
29
|
+
.limit(1)
|
|
30
|
+
.consume(async msgs => {
|
|
31
|
+
console.log(`Messages: ${msgs.length}`)
|
|
32
|
+
for (const msg of msgs) {
|
|
33
|
+
if (lastId === null) {
|
|
34
|
+
lastId = msg.data.id
|
|
35
|
+
} else {
|
|
36
|
+
if (msg.data.id !== lastId + 1) {
|
|
37
|
+
throw new Error('Message ordering violation')
|
|
38
|
+
}
|
|
39
|
+
lastId = msg.data.id
|
|
40
|
+
}
|
|
41
|
+
uniqueIds.add(msg.data.id)
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
const uniqueIdsCount = uniqueIds.size;
|
|
46
|
+
console.log(`Unique IDs count: ${uniqueIdsCount}`)
|
|
47
|
+
|
|
48
|
+
return { success: uniqueIdsCount === messagesToPush, message: 'Load test completed successfully' }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function testLoadPartition(client) {
|
|
52
|
+
const queue = await client
|
|
53
|
+
.queue('test-queue-v2-load-partition')
|
|
54
|
+
.create()
|
|
55
|
+
if (!queue.configured) {
|
|
56
|
+
return { success: false, message: 'Queue not created' }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const messagesToPush = 100000;
|
|
60
|
+
|
|
61
|
+
let messages = [];
|
|
62
|
+
let k = 0
|
|
63
|
+
for (let i = 0; i < messagesToPush; i++) {
|
|
64
|
+
messages.push({ data: { id: i } })
|
|
65
|
+
if (messages.length >= 10000) {
|
|
66
|
+
await client
|
|
67
|
+
.queue('test-queue-v2-load-partition')
|
|
68
|
+
.partition(k.toString())
|
|
69
|
+
.push(messages)
|
|
70
|
+
messages = [];
|
|
71
|
+
k++;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
await client.queue('test-queue-v2-load-partition').push(messages)
|
|
75
|
+
|
|
76
|
+
let uniqueIds = new Set();
|
|
77
|
+
let lastId = null;
|
|
78
|
+
|
|
79
|
+
for (let i = 0; i < 10; i++) {
|
|
80
|
+
await client
|
|
81
|
+
.queue('test-queue-v2-load-partition')
|
|
82
|
+
.partition(i.toString())
|
|
83
|
+
.batch(10000)
|
|
84
|
+
.wait(false)
|
|
85
|
+
.limit(1)
|
|
86
|
+
.consume(async msgs => {
|
|
87
|
+
for (const msg of msgs) {
|
|
88
|
+
if (lastId === null) {
|
|
89
|
+
lastId = msg.data.id
|
|
90
|
+
} else {
|
|
91
|
+
if (msg.data.id !== lastId + 1) {
|
|
92
|
+
throw new Error('Message ordering violation')
|
|
93
|
+
}
|
|
94
|
+
lastId = msg.data.id
|
|
95
|
+
}
|
|
96
|
+
uniqueIds.add(msg.data.id)
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const uniqueIdsCount = uniqueIds.size;
|
|
102
|
+
|
|
103
|
+
return { success: uniqueIdsCount === messagesToPush, message: 'Load test completed successfully' }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function testLoadConsumerGroup(client) {
|
|
107
|
+
const queue = await client
|
|
108
|
+
.queue('test-queue-v2-load-consumer-group')
|
|
109
|
+
.create()
|
|
110
|
+
if (!queue.configured) {
|
|
111
|
+
return { success: false, message: 'Queue not created' }
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const messagesToPush = 100000;
|
|
115
|
+
|
|
116
|
+
let messages = [];
|
|
117
|
+
for (let i = 0; i < messagesToPush; i++) {
|
|
118
|
+
messages.push({ data: { id: i } })
|
|
119
|
+
if (messages.length >= 10000) {
|
|
120
|
+
await client.queue('test-queue-v2-load-consumer-group').push(messages)
|
|
121
|
+
messages = [];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
await client.queue('test-queue-v2-load-consumer-group').push(messages)
|
|
125
|
+
|
|
126
|
+
let uniqueIdsA = new Set();
|
|
127
|
+
let lastIdA = null;
|
|
128
|
+
|
|
129
|
+
await client
|
|
130
|
+
.queue('test-queue-v2-load-consumer-group')
|
|
131
|
+
.group('test-consumer-group-a')
|
|
132
|
+
.concurrency(10)
|
|
133
|
+
.batch(10000)
|
|
134
|
+
.wait(false)
|
|
135
|
+
.limit(1)
|
|
136
|
+
.consume(async msgs => {
|
|
137
|
+
for (const msg of msgs) {
|
|
138
|
+
if (lastIdA === null) {
|
|
139
|
+
lastIdA = msg.data.id
|
|
140
|
+
} else {
|
|
141
|
+
if (msg.data.id !== lastIdA + 1) {
|
|
142
|
+
throw new Error('Message ordering violation')
|
|
143
|
+
}
|
|
144
|
+
lastIdA = msg.data.id
|
|
145
|
+
}
|
|
146
|
+
uniqueIdsA.add(msg.data.id)
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
let uniqueIdsB = new Set();
|
|
151
|
+
let lastIdB = null;
|
|
152
|
+
|
|
153
|
+
await client
|
|
154
|
+
.queue('test-queue-v2-load-consumer-group')
|
|
155
|
+
.group('test-consumer-group-b')
|
|
156
|
+
.concurrency(10)
|
|
157
|
+
.batch(10000)
|
|
158
|
+
.wait(false)
|
|
159
|
+
.limit(1)
|
|
160
|
+
.consume(async msgs => {
|
|
161
|
+
for (const msg of msgs) {
|
|
162
|
+
if (lastIdB === null) {
|
|
163
|
+
lastIdB = msg.data.id
|
|
164
|
+
} else {
|
|
165
|
+
if (msg.data.id !== lastIdB + 1) {
|
|
166
|
+
throw new Error('Message ordering violation')
|
|
167
|
+
}
|
|
168
|
+
lastIdB = msg.data.id
|
|
169
|
+
}
|
|
170
|
+
uniqueIdsB.add(msg.data.id)
|
|
171
|
+
}
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
const uniqueIdsCountA = uniqueIdsA.size;
|
|
175
|
+
const uniqueIdsCountB = uniqueIdsB.size;
|
|
176
|
+
return { success: uniqueIdsCountA === messagesToPush && uniqueIdsCountB === messagesToPush, message: 'Load test completed successfully' }
|
|
177
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
export async function popEmptyQueue(client) {
|
|
2
|
+
const queue = await client.queue('test-queue-v2-pop-empty').create()
|
|
3
|
+
if (!queue.configured) {
|
|
4
|
+
return { success: false, message: 'Queue not created' }
|
|
5
|
+
}
|
|
6
|
+
const res = await client
|
|
7
|
+
.queue('test-queue-v2-pop-empty')
|
|
8
|
+
.batch(1) // Fetch 1 message
|
|
9
|
+
.wait(false)
|
|
10
|
+
.pop()
|
|
11
|
+
return { success: res.length === 0 }
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function popNonEmptyQueue(client) {
|
|
15
|
+
const queue = await client.queue('test-queue-v2-pop-non-empty').create()
|
|
16
|
+
if (!queue.configured) {
|
|
17
|
+
return { success: false, message: 'Queue not created' }
|
|
18
|
+
}
|
|
19
|
+
await client
|
|
20
|
+
.queue('test-queue-v2-pop-non-empty')
|
|
21
|
+
.push([{ data: { message: 'Hello, world!' } }])
|
|
22
|
+
|
|
23
|
+
const res = await client
|
|
24
|
+
.queue('test-queue-v2-pop-non-empty')
|
|
25
|
+
.batch(1)
|
|
26
|
+
.wait(false)
|
|
27
|
+
.pop()
|
|
28
|
+
return { success: res.length === 1 }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function popWithWait(client) {
|
|
32
|
+
const queue = await client.queue('test-queue-v2-pop-with-wait').create()
|
|
33
|
+
if (!queue.configured) {
|
|
34
|
+
return { success: false, message: 'Queue not created' }
|
|
35
|
+
}
|
|
36
|
+
setTimeout(async () => {
|
|
37
|
+
await client
|
|
38
|
+
.queue('test-queue-v2-pop-with-wait')
|
|
39
|
+
.push([{ data: { message: 'Hello, world!' } }])
|
|
40
|
+
}, 2000);
|
|
41
|
+
|
|
42
|
+
const res = await client
|
|
43
|
+
.queue('test-queue-v2-pop-with-wait')
|
|
44
|
+
.batch(1)
|
|
45
|
+
.wait(true)
|
|
46
|
+
.pop()
|
|
47
|
+
|
|
48
|
+
return { success: res.length === 1 }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function popWithAck(client) {
|
|
52
|
+
const queue = await client.queue('test-queue-v2-pop-with-ack').create()
|
|
53
|
+
if (!queue.configured) {
|
|
54
|
+
return { success: false, message: 'Queue not created' }
|
|
55
|
+
}
|
|
56
|
+
await client
|
|
57
|
+
.queue('test-queue-v2-pop-with-ack')
|
|
58
|
+
.push([{ data: { message: 'Hello, world!' } }])
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
const res = await client
|
|
62
|
+
.queue('test-queue-v2-pop-with-ack')
|
|
63
|
+
.batch(1)
|
|
64
|
+
.wait(false)
|
|
65
|
+
.pop()
|
|
66
|
+
|
|
67
|
+
const resAck = await client.ack(res[0])
|
|
68
|
+
|
|
69
|
+
if (!resAck.success) {
|
|
70
|
+
return { success: false, message: `Ack failed: ${resAck.error}` }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return { success: true, message: 'Message popped and acked successfully' }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function popWithAckReconsume(client) {
|
|
77
|
+
const queue = await client
|
|
78
|
+
.queue('test-queue-v2-pop-with-ack-reconsume')
|
|
79
|
+
.config({
|
|
80
|
+
leaseTime: 1, // 1 second lease
|
|
81
|
+
})
|
|
82
|
+
.create()
|
|
83
|
+
if (!queue.configured) {
|
|
84
|
+
return { success: false, message: 'Queue not created' }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
await client
|
|
88
|
+
.queue('test-queue-v2-pop-with-ack-reconsume')
|
|
89
|
+
.push([{ data: { message: 'Hello, world!' } }])
|
|
90
|
+
|
|
91
|
+
const res = await client
|
|
92
|
+
.queue('test-queue-v2-pop-with-ack-reconsume')
|
|
93
|
+
.batch(1)
|
|
94
|
+
.wait(false)
|
|
95
|
+
.pop()
|
|
96
|
+
|
|
97
|
+
if (res.length !== 1) {
|
|
98
|
+
return { success: false, message: `Expected 1 message, got ${res.length}` }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
await new Promise(r => setTimeout(r, 2000))
|
|
102
|
+
|
|
103
|
+
const res2 = await client
|
|
104
|
+
.queue('test-queue-v2-pop-with-ack-reconsume')
|
|
105
|
+
.batch(1)
|
|
106
|
+
.wait(true)
|
|
107
|
+
.pop()
|
|
108
|
+
|
|
109
|
+
if (res2.length === 1) {
|
|
110
|
+
return { success: true, message: 'Message consumed and reconsumed after lease expiry' }
|
|
111
|
+
}
|
|
112
|
+
return { success: false, message: `Expected 1 message, got ${res2.length}` }
|
|
113
|
+
}
|
|
114
|
+
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
|
|
3
|
+
export async function pushMessage(client) {
|
|
4
|
+
const queue = await client.queue('test-queue-v2').create()
|
|
5
|
+
if (!queue.configured) {
|
|
6
|
+
return { success: false, message: 'Queue not created' }
|
|
7
|
+
}
|
|
8
|
+
const res = await client
|
|
9
|
+
.queue('test-queue-v2')
|
|
10
|
+
.push([{ data: { message: 'Hello, world!' } }])
|
|
11
|
+
|
|
12
|
+
return { success: res[0].status === 'queued' }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function pushDuplicateMessage(client) {
|
|
16
|
+
const queue = await client.queue('test-queue-v2').create()
|
|
17
|
+
if (!queue.configured) {
|
|
18
|
+
return { success: false, message: 'Queue not created' }
|
|
19
|
+
}
|
|
20
|
+
const res1 = await client
|
|
21
|
+
.queue('test-queue-v2')
|
|
22
|
+
.push([{ transactionId: 'test-transaction-id', data: { message: 'Hello, world!' } }])
|
|
23
|
+
|
|
24
|
+
const res2 = await client
|
|
25
|
+
.queue('test-queue-v2')
|
|
26
|
+
.push([{ transactionId: 'test-transaction-id', data: { message: 'Hello, world!' } }])
|
|
27
|
+
|
|
28
|
+
return { success: res1[0].status === 'queued' && res2[0].status === 'duplicate' }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function pushDuplicateMessageOnSpecificPartition(client) {
|
|
32
|
+
const queue = await client.queue('test-queue-partition-duplicate').create()
|
|
33
|
+
if (!queue.configured) {
|
|
34
|
+
return { success: false, message: 'Queue not created' }
|
|
35
|
+
}
|
|
36
|
+
const res1 = await client
|
|
37
|
+
.queue('test-queue-partition-duplicate')
|
|
38
|
+
.partition('0')
|
|
39
|
+
.push([{ transactionId: 'test-transaction-id', data: { message: 'Hello, world!' } }])
|
|
40
|
+
|
|
41
|
+
const res2 = await client
|
|
42
|
+
.queue('test-queue-partition-duplicate')
|
|
43
|
+
.partition('0')
|
|
44
|
+
.push([{ transactionId: 'test-transaction-id', data: { message: 'Hello, world!' } }])
|
|
45
|
+
|
|
46
|
+
return { success: res1[0].status === 'queued' && res2[0].status === 'duplicate' }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function pushDuplicateMessageOnDifferentPartition(client) {
|
|
50
|
+
const queue = await client.queue('test-queue-partition-duplicate-different').create()
|
|
51
|
+
if (!queue.configured) {
|
|
52
|
+
return { success: false, message: 'Queue not created' }
|
|
53
|
+
}
|
|
54
|
+
const res1 = await client
|
|
55
|
+
.queue('test-queue-partition-duplicate-different')
|
|
56
|
+
.partition('0')
|
|
57
|
+
.push([{ transactionId: 'test-transaction-id', data: { message: 'Hello, world!' } }])
|
|
58
|
+
|
|
59
|
+
const res2 = await client
|
|
60
|
+
.queue('test-queue-partition-duplicate-different')
|
|
61
|
+
.partition('1')
|
|
62
|
+
.push([{ transactionId: 'test-transaction-id', data: { message: 'Hello, world!' } }])
|
|
63
|
+
const success = res1[0].status === 'queued' && res2[0].status === 'queued'
|
|
64
|
+
|
|
65
|
+
return { success: success }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function pushMessageWithTransactionId(client) {
|
|
69
|
+
const queue = await client.queue('test-queue-transaction-id').create()
|
|
70
|
+
if (!queue.configured) {
|
|
71
|
+
return { success: false, message: 'Queue not created' }
|
|
72
|
+
}
|
|
73
|
+
const res = await client
|
|
74
|
+
.queue('test-queue-transaction-id')
|
|
75
|
+
.push([{ transactionId: 'test-transaction-id', data: { message: 'Hello, world!' } }])
|
|
76
|
+
|
|
77
|
+
if (res[0].status !== 'queued' && res[0].transactionId !== 'test-transaction-id') {
|
|
78
|
+
return { success: false, message: 'Transaction ID mismatch' }
|
|
79
|
+
}
|
|
80
|
+
return { success: true }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function pushBufferedMessage(client) {
|
|
84
|
+
const queue = await client.queue('test-queue-buffered').create()
|
|
85
|
+
if (!queue.configured) {
|
|
86
|
+
return { success: false, message: 'Queue not created' }
|
|
87
|
+
}
|
|
88
|
+
const res = await client
|
|
89
|
+
.queue('test-queue-buffered')
|
|
90
|
+
.buffer({ messageCount: 10, timeMillis: 1000 })
|
|
91
|
+
.push([{ data: { message: 'Hello, world!' } }])
|
|
92
|
+
|
|
93
|
+
const pop = await client
|
|
94
|
+
.queue('test-queue-buffered')
|
|
95
|
+
.batch(1)
|
|
96
|
+
.wait(false)
|
|
97
|
+
.pop()
|
|
98
|
+
|
|
99
|
+
if (pop.length !== 0) {
|
|
100
|
+
return { success: false, message: 'Message not buffered' }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
104
|
+
|
|
105
|
+
const pop2 = await client
|
|
106
|
+
.queue('test-queue-buffered')
|
|
107
|
+
.batch(1)
|
|
108
|
+
.wait(false)
|
|
109
|
+
.pop()
|
|
110
|
+
|
|
111
|
+
if (pop2.length === 0) {
|
|
112
|
+
return { success: false, message: 'Message not popped' }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return { success: true }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export async function pushMaxQueueSize(client) {
|
|
119
|
+
const queue = await client.queue('test-queue-max-size').config({ maxSize: 10 }).create()
|
|
120
|
+
if (!queue.configured) {
|
|
121
|
+
return { success: false, message: 'Queue not created' }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Necessary to create a partition consumer to trigger the capacity check
|
|
125
|
+
const popToCreateParititonConsumer = await client
|
|
126
|
+
.queue('test-queue-max-size')
|
|
127
|
+
.batch(1)
|
|
128
|
+
.wait(false)
|
|
129
|
+
.pop()
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
for (let i = 0; i < 20; i++) {
|
|
133
|
+
const result = await client
|
|
134
|
+
.queue('test-queue-max-size')
|
|
135
|
+
.push([{ data: { message: i } }])
|
|
136
|
+
}
|
|
137
|
+
} catch (error) {
|
|
138
|
+
if (!error.message.includes(' Queue "test-queue-max-size" would exceed max capacity (10).')) {
|
|
139
|
+
return { success: true, message: 'Correct error message' }
|
|
140
|
+
} else {
|
|
141
|
+
return { success: false, message: 'Incorrect error message' }
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return { success: false }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export async function pushDelayedMessage(client) {
|
|
148
|
+
const queue = await client.queue('test-queue-delayed')
|
|
149
|
+
.config({ delayedProcessing: 2 })
|
|
150
|
+
.create()
|
|
151
|
+
if (!queue.configured) {
|
|
152
|
+
return { success: false, message: 'Queue not created' }
|
|
153
|
+
}
|
|
154
|
+
const res = await client
|
|
155
|
+
.queue('test-queue-delayed')
|
|
156
|
+
.push([{ transactionId: 'test-transaction-dealyed-id', data: { message: 'Hello, world!', aaa: '1' } }])
|
|
157
|
+
|
|
158
|
+
const pop = await client
|
|
159
|
+
.queue('test-queue-delayed')
|
|
160
|
+
.batch(1)
|
|
161
|
+
.wait(false)
|
|
162
|
+
.pop()
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
if (pop.length !== 0) {
|
|
167
|
+
return { success: false, message: 'Message not delayed' }
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
await new Promise(r => setTimeout(r, 2500));
|
|
171
|
+
|
|
172
|
+
const pop2 = await client
|
|
173
|
+
.queue('test-queue-delayed')
|
|
174
|
+
.batch(1)
|
|
175
|
+
.wait(true)
|
|
176
|
+
.pop()
|
|
177
|
+
|
|
178
|
+
if (pop2.length === 1) {
|
|
179
|
+
return { success: true, message: 'Message delayed' }
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return { success: false, message: 'Message not delayed' }
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export async function pushWindowBuffer(client) {
|
|
186
|
+
const queue = await client.queue('test-queue-window-buffer')
|
|
187
|
+
.config({ windowBuffer: 2 })
|
|
188
|
+
.create()
|
|
189
|
+
if (!queue.configured) {
|
|
190
|
+
return { success: false, message: 'Queue not created' }
|
|
191
|
+
}
|
|
192
|
+
const res = await client
|
|
193
|
+
.queue('test-queue-window-buffer')
|
|
194
|
+
.push(
|
|
195
|
+
[
|
|
196
|
+
{ data: { message: 'Hello, world 1!' } },
|
|
197
|
+
{ data: { message: 'Hello, world 2!' } },
|
|
198
|
+
{ data: { message: 'Hello, world 3!' } }
|
|
199
|
+
])
|
|
200
|
+
|
|
201
|
+
const pop = await client
|
|
202
|
+
.queue('test-queue-window-buffer')
|
|
203
|
+
.batch(1)
|
|
204
|
+
.wait(false)
|
|
205
|
+
.pop()
|
|
206
|
+
|
|
207
|
+
if (pop.length !== 0) {
|
|
208
|
+
return { success: false, message: 'Message not delayed' }
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
await new Promise(r => setTimeout(r, 2500));
|
|
212
|
+
|
|
213
|
+
const pop2 = await client
|
|
214
|
+
.queue('test-queue-window-buffer')
|
|
215
|
+
.batch(4)
|
|
216
|
+
.wait(false)
|
|
217
|
+
.pop()
|
|
218
|
+
if (pop2.length === 3) {
|
|
219
|
+
return { success: true, message: 'Window buffer works correctly' }
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return { success: false, message: 'Window buffer does not work correctly' }
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export async function pushLargePayload(client) {
|
|
226
|
+
const queue = await client.queue('test-queue-large-payload').create()
|
|
227
|
+
if (!queue.configured) {
|
|
228
|
+
return { success: false, message: 'Queue not created' }
|
|
229
|
+
}
|
|
230
|
+
// Create a large payload (1MB of data)
|
|
231
|
+
const largeArray = new Array(10000).fill({
|
|
232
|
+
id: crypto.randomBytes(16).toString('hex'),
|
|
233
|
+
data: 'x'.repeat(100),
|
|
234
|
+
nested: {
|
|
235
|
+
field1: 'value1',
|
|
236
|
+
field2: 'value2',
|
|
237
|
+
field3: 'value3'
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
const largePayload = {
|
|
242
|
+
array: largeArray,
|
|
243
|
+
metadata: {
|
|
244
|
+
size: JSON.stringify(largeArray).length,
|
|
245
|
+
timestamp: Date.now()
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const res = await client
|
|
250
|
+
.queue('test-queue-large-payload')
|
|
251
|
+
.push([{ data: largePayload }])
|
|
252
|
+
|
|
253
|
+
const received = await client
|
|
254
|
+
.queue('test-queue-large-payload')
|
|
255
|
+
.batch(1)
|
|
256
|
+
.wait(false)
|
|
257
|
+
.pop()
|
|
258
|
+
|
|
259
|
+
if (!received || !received[0].data || !received[0].data.array || received[0].data.array.length !== 10000) {
|
|
260
|
+
return { success: false, message: 'Large payload corrupted' }
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return { success: true }
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export async function pushNullPayload(client) {
|
|
267
|
+
const queue = await client.queue('test-queue-null-payload').create()
|
|
268
|
+
if (!queue.configured) {
|
|
269
|
+
return { success: false, message: 'Queue not created' }
|
|
270
|
+
}
|
|
271
|
+
const res = await client
|
|
272
|
+
.queue('test-queue-null-payload')
|
|
273
|
+
.push([{ data: null }])
|
|
274
|
+
|
|
275
|
+
const received = await client
|
|
276
|
+
.queue('test-queue-null-payload')
|
|
277
|
+
.batch(1)
|
|
278
|
+
.wait(false)
|
|
279
|
+
.pop()
|
|
280
|
+
|
|
281
|
+
if (!received || received[0].data !== null) {
|
|
282
|
+
return { success: false, message: 'Null data corrupted' }
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return { success: true }
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export async function pushEmptyPayload(client) {
|
|
289
|
+
const queue = await client.queue('test-queue-empty-payload').create()
|
|
290
|
+
if (!queue.configured) {
|
|
291
|
+
return { success: false, message: 'Queue not created' }
|
|
292
|
+
}
|
|
293
|
+
const res = await client
|
|
294
|
+
.queue('test-queue-empty-payload')
|
|
295
|
+
.push([{ data: {} }])
|
|
296
|
+
|
|
297
|
+
const received = await client
|
|
298
|
+
.queue('test-queue-empty-payload')
|
|
299
|
+
.batch(1)
|
|
300
|
+
.wait(false)
|
|
301
|
+
.pop()
|
|
302
|
+
|
|
303
|
+
if (!received || Object.keys(received[0].data).length !== 0) {
|
|
304
|
+
return { success: false, message: 'Empty object payload corrupted' }
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return { success: true }
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export async function pushEncryptedPayload(client) {
|
|
311
|
+
const queue = await client.queue('test-queue-encrypted-payload')
|
|
312
|
+
.config({ encryptionEnabled: true })
|
|
313
|
+
.create()
|
|
314
|
+
if (!queue.configured) {
|
|
315
|
+
return { success: false, message: 'Queue not created' }
|
|
316
|
+
}
|
|
317
|
+
const res = await client
|
|
318
|
+
.queue('test-queue-encrypted-payload')
|
|
319
|
+
.push([{ data: { message: 'Hello, world!' } }])
|
|
320
|
+
|
|
321
|
+
const received = await client
|
|
322
|
+
.queue('test-queue-encrypted-payload')
|
|
323
|
+
.batch(1)
|
|
324
|
+
.wait(false)
|
|
325
|
+
.pop()
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
if (!received || received[0].data.message !== 'Hello, world!') {
|
|
329
|
+
return { success: false, message: 'Encrypted payload corrupted' }
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return { success: true }
|
|
333
|
+
}
|