queen-mq 0.3.1 ā 0.6.3
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 +309 -167
- package/client-js/client-v2/Queen.js +66 -0
- package/client-js/client-v2/README.md +67 -10
- package/client-js/client-v2/builders/QueueBuilder.js +7 -1
- package/client-js/client-v2/builders/TransactionBuilder.js +20 -4
- package/client-js/client-v2/stream/StreamBuilder.js +72 -0
- package/client-js/client-v2/stream/StreamConsumer.js +156 -0
- package/client-js/client-v2/stream/Window.js +140 -0
- package/client-js/test-v2/GETTING_STARTED.md +27 -0
- package/client-js/test-v2/MAINTENANCE_TEST.md +148 -0
- package/client-js/test-v2/README_SUBSCRIPTION_TESTS.md +201 -0
- package/client-js/test-v2/consume.js +11 -0
- package/client-js/test-v2/dlq.js +1 -1
- package/client-js/test-v2/load.js +2 -0
- package/client-js/test-v2/maintenance.js +261 -0
- package/client-js/test-v2/retention.js +69 -0
- package/client-js/test-v2/run.js +7 -1
- package/client-js/test-v2/subscription.js +198 -18
- package/client-js/test-v2/transaction.js +66 -4
- package/package.json +2 -2
- package/client-js/client/client.js +0 -1536
- package/client-js/client/index.js +0 -4
- package/client-js/client/utils/http.js +0 -173
- package/client-js/client/utils/loadBalancer.js +0 -152
- package/client-js/client/utils/retry.js +0 -41
- package/client-js/services/encryptionService.js +0 -82
- package/client-js/services/evictionService.js +0 -160
- package/client-js/services/retentionService.js +0 -162
- package/client-js/services/startupSync.js +0 -35
- package/client-js/test/README.md +0 -224
- package/client-js/test/advanced-client-tests.js +0 -761
- package/client-js/test/advanced-pattern-tests.js +0 -1137
- package/client-js/test/bus-mode-tests.js +0 -361
- package/client-js/test/core-tests.js +0 -457
- package/client-js/test/edge-case-tests.js +0 -562
- package/client-js/test/enterprise-tests.js +0 -637
- package/client-js/test/human.js +0 -162
- package/client-js/test/partition-locking-tests.js +0 -545
- package/client-js/test/partition-transaction-tests.js +0 -482
- package/client-js/test/qos0-tests.js +0 -334
- package/client-js/test/test-new.js +0 -370
- package/client-js/test/utils.js +0 -169
- package/client-js/test/window-buffer-test.js +0 -114
- package/client-js/utils/logger.js +0 -44
- package/client-js/utils/uuid.js +0 -5
|
@@ -1,334 +0,0 @@
|
|
|
1
|
-
import { startTest, passTest, failTest } from './utils.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Test server-side buffering
|
|
5
|
-
* Uses file buffer for fast writes, server flushes every 100ms (configurable via FILE_BUFFER_FLUSH_MS)
|
|
6
|
-
*/
|
|
7
|
-
export async function testQoS0Buffering(client) {
|
|
8
|
-
startTest('Server-side buffering', 'qos0');
|
|
9
|
-
|
|
10
|
-
try {
|
|
11
|
-
const queue = 'test-qos0-buffer-' + Date.now();
|
|
12
|
-
await client.queue(queue);
|
|
13
|
-
|
|
14
|
-
// Push 10 events with server-side buffering
|
|
15
|
-
const start = Date.now();
|
|
16
|
-
for (let i = 0; i < 10; i++) {
|
|
17
|
-
await client.push(queue, { index: i }, { buffer: true });
|
|
18
|
-
}
|
|
19
|
-
const pushTime = Date.now() - start;
|
|
20
|
-
|
|
21
|
-
// Should return quickly (buffered to file, server-side batching)
|
|
22
|
-
// With file buffering, push should be fast (< 500ms for 10 messages)
|
|
23
|
-
if (pushTime >= 500) {
|
|
24
|
-
throw new Error(`Push should be fast (file buffered), took ${pushTime}ms`);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Wait for server to flush buffer (FILE_BUFFER_FLUSH_MS default is 100ms)
|
|
28
|
-
// Add extra margin for processing
|
|
29
|
-
await new Promise(resolve => setTimeout(resolve, 300));
|
|
30
|
-
|
|
31
|
-
// Consume all events
|
|
32
|
-
const messages = [];
|
|
33
|
-
for await (const msg of client.take(queue, { batch: 20 })) {
|
|
34
|
-
messages.push(msg);
|
|
35
|
-
await client.ack(msg);
|
|
36
|
-
if (messages.length >= 10) break;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (messages.length !== 10) {
|
|
40
|
-
throw new Error(`Should receive all 10 events, got ${messages.length}`);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Verify all events received
|
|
44
|
-
const indices = messages.map(m => m.data.index).sort((a, b) => a - b);
|
|
45
|
-
for (let i = 0; i < 10; i++) {
|
|
46
|
-
if (!indices.includes(i)) {
|
|
47
|
-
throw new Error(`Should have event with index ${i}`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
passTest('Server-side buffering works correctly');
|
|
52
|
-
} catch (error) {
|
|
53
|
-
failTest(error);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Test auto-acknowledgment
|
|
59
|
-
*/
|
|
60
|
-
export async function testAutoAck(client) {
|
|
61
|
-
startTest('Auto-acknowledgment', 'qos0');
|
|
62
|
-
|
|
63
|
-
try {
|
|
64
|
-
const queue = 'test-autoack-' + Date.now();
|
|
65
|
-
await client.queue(queue);
|
|
66
|
-
|
|
67
|
-
// Push message
|
|
68
|
-
await client.push(queue, { test: 'autoack-data' });
|
|
69
|
-
|
|
70
|
-
// Take with auto-ack
|
|
71
|
-
let msg;
|
|
72
|
-
for await (const m of client.take(queue, { autoAck: true })) {
|
|
73
|
-
msg = m;
|
|
74
|
-
break;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (msg === undefined) {
|
|
78
|
-
throw new Error('Should receive message');
|
|
79
|
-
}
|
|
80
|
-
if (msg.data.test !== 'autoack-data') {
|
|
81
|
-
throw new Error('Should have correct data');
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Message should NOT be available again (already auto-acked)
|
|
85
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
86
|
-
|
|
87
|
-
let count = 0;
|
|
88
|
-
for await (const m of client.take(queue, { wait: false, batch: 10 })) {
|
|
89
|
-
count++;
|
|
90
|
-
await client.ack(m);
|
|
91
|
-
break;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (count !== 0) {
|
|
95
|
-
throw new Error('Message should not be available again (auto-acked)');
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
passTest('Auto-ack works correctly');
|
|
99
|
-
} catch (error) {
|
|
100
|
-
failTest(error);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Test consumer group with auto-ack
|
|
106
|
-
*/
|
|
107
|
-
export async function testConsumerGroupAutoAck(client) {
|
|
108
|
-
startTest('Consumer group with auto-ack', 'qos0');
|
|
109
|
-
|
|
110
|
-
try {
|
|
111
|
-
const queue = 'test-cg-autoack-' + Date.now();
|
|
112
|
-
await client.queue(queue);
|
|
113
|
-
|
|
114
|
-
// Push message
|
|
115
|
-
await client.push(queue, { value: 42, type: 'test' });
|
|
116
|
-
|
|
117
|
-
// Wait a bit for message to be available
|
|
118
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
119
|
-
|
|
120
|
-
// Two consumer groups consume same message
|
|
121
|
-
let msgA, msgB;
|
|
122
|
-
|
|
123
|
-
for await (const msg of client.take(`${queue}@groupA`, { autoAck: true })) {
|
|
124
|
-
msgA = msg;
|
|
125
|
-
break;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
for await (const msg of client.take(`${queue}@groupB`, { autoAck: true })) {
|
|
129
|
-
msgB = msg;
|
|
130
|
-
break;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (msgA === undefined) {
|
|
134
|
-
throw new Error('Group A should receive message');
|
|
135
|
-
}
|
|
136
|
-
if (msgB === undefined) {
|
|
137
|
-
throw new Error('Group B should receive message');
|
|
138
|
-
}
|
|
139
|
-
if (msgA.data.value !== 42) {
|
|
140
|
-
throw new Error('Group A should have correct data');
|
|
141
|
-
}
|
|
142
|
-
if (msgB.data.value !== 42) {
|
|
143
|
-
throw new Error('Group B should have correct data');
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Both groups should have auto-acked - message not available again
|
|
147
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
148
|
-
|
|
149
|
-
let countA = 0, countB = 0;
|
|
150
|
-
|
|
151
|
-
for await (const msg of client.take(`${queue}@groupA`, { wait: false })) {
|
|
152
|
-
countA++;
|
|
153
|
-
await client.ack(msg, true, { group: 'groupA' });
|
|
154
|
-
break;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
for await (const msg of client.take(`${queue}@groupB`, { wait: false })) {
|
|
158
|
-
countB++;
|
|
159
|
-
await client.ack(msg, true, { group: 'groupB' });
|
|
160
|
-
break;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (countA !== 0) {
|
|
164
|
-
throw new Error('Group A should have auto-acked');
|
|
165
|
-
}
|
|
166
|
-
if (countB !== 0) {
|
|
167
|
-
throw new Error('Group B should have auto-acked');
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
passTest('Consumer group auto-ack works correctly');
|
|
171
|
-
} catch (error) {
|
|
172
|
-
failTest(error);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Test normal push preserves FIFO ordering
|
|
178
|
-
*/
|
|
179
|
-
export async function testFIFOOrdering(client) {
|
|
180
|
-
startTest('Normal push preserves FIFO', 'qos0');
|
|
181
|
-
|
|
182
|
-
try {
|
|
183
|
-
const queue = 'test-fifo-' + Date.now();
|
|
184
|
-
await client.queue(queue);
|
|
185
|
-
|
|
186
|
-
// Push without buffering (normal mode)
|
|
187
|
-
for (let i = 0; i < 20; i++) {
|
|
188
|
-
await client.push(queue, { order: i });
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Consume and verify order
|
|
192
|
-
const messages = [];
|
|
193
|
-
for await (const msg of client.take(queue, { batch: 20 })) {
|
|
194
|
-
messages.push(msg);
|
|
195
|
-
await client.ack(msg);
|
|
196
|
-
if (messages.length >= 20) break;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (messages.length !== 20) {
|
|
200
|
-
throw new Error(`Should have 20 messages, got ${messages.length}`);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Verify FIFO ordering
|
|
204
|
-
for (let i = 0; i < 20; i++) {
|
|
205
|
-
if (messages[i].data.order !== i) {
|
|
206
|
-
throw new Error(`Message ${i} out of order: got ${messages[i].data.order}`);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
passTest('FIFO ordering preserved for normal push');
|
|
211
|
-
} catch (error) {
|
|
212
|
-
failTest(error);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Test mixed buffered and non-buffered operations
|
|
218
|
-
*/
|
|
219
|
-
export async function testMixedOperations(client) {
|
|
220
|
-
startTest('Mixed buffered and normal operations', 'qos0');
|
|
221
|
-
|
|
222
|
-
try {
|
|
223
|
-
const queue = 'test-mixed-' + Date.now();
|
|
224
|
-
await client.queue(queue);
|
|
225
|
-
|
|
226
|
-
// Push with buffering (file buffered)
|
|
227
|
-
await client.push(queue, { type: 'buffered', id: 1 }, { buffer: true });
|
|
228
|
-
|
|
229
|
-
// Push normal (direct DB)
|
|
230
|
-
await client.push(queue, { type: 'normal', id: 2 });
|
|
231
|
-
|
|
232
|
-
// Wait for buffer flush (100ms + margin)
|
|
233
|
-
await new Promise(resolve => setTimeout(resolve, 200));
|
|
234
|
-
|
|
235
|
-
// Should have both messages
|
|
236
|
-
const messages = [];
|
|
237
|
-
for await (const msg of client.take(queue, { batch: 10 })) {
|
|
238
|
-
messages.push(msg);
|
|
239
|
-
await client.ack(msg);
|
|
240
|
-
if (messages.length >= 2) break;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (messages.length !== 2) {
|
|
244
|
-
throw new Error(`Should have 2 messages, got ${messages.length}`);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const types = messages.map(m => m.data.type);
|
|
248
|
-
if (!types.includes('buffered')) {
|
|
249
|
-
throw new Error('Should have buffered message');
|
|
250
|
-
}
|
|
251
|
-
if (!types.includes('normal')) {
|
|
252
|
-
throw new Error('Should have normal message');
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
passTest('Mixed buffered and normal operations work correctly');
|
|
256
|
-
} catch (error) {
|
|
257
|
-
failTest(error);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Test buffered events are stored individually
|
|
263
|
-
* Note: Buffering is a server-side optimization for DB writes,
|
|
264
|
-
* not for combining events into a single message
|
|
265
|
-
*/
|
|
266
|
-
export async function testBatchedPayload(client) {
|
|
267
|
-
startTest('Buffered events stored individually', 'qos0');
|
|
268
|
-
|
|
269
|
-
try {
|
|
270
|
-
const queue = 'test-batch-payload-' + Date.now();
|
|
271
|
-
await client.queue(queue);
|
|
272
|
-
|
|
273
|
-
// Push multiple events with buffering
|
|
274
|
-
for (let i = 0; i < 5; i++) {
|
|
275
|
-
await client.push(queue, { index: i, timestamp: Date.now() }, { buffer: true });
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Wait for server flush (100ms + margin)
|
|
279
|
-
await new Promise(resolve => setTimeout(resolve, 200));
|
|
280
|
-
|
|
281
|
-
// Consume all messages - each event should be a separate message
|
|
282
|
-
const messages = [];
|
|
283
|
-
for await (const msg of client.take(queue, { batch: 10 })) {
|
|
284
|
-
messages.push(msg);
|
|
285
|
-
await client.ack(msg);
|
|
286
|
-
if (messages.length >= 5) break;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
if (messages.length !== 5) {
|
|
290
|
-
throw new Error(`Should receive 5 separate messages, got ${messages.length}`);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Verify each message has individual payload (not batched into array)
|
|
294
|
-
for (const msg of messages) {
|
|
295
|
-
if (typeof msg.data !== 'object' || Array.isArray(msg.data)) {
|
|
296
|
-
throw new Error(`Each message should have object payload, got ${typeof msg.data}`);
|
|
297
|
-
}
|
|
298
|
-
if (typeof msg.data.index !== 'number') {
|
|
299
|
-
throw new Error('Each message should have index property');
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// Verify all indices are present
|
|
304
|
-
const indices = messages.map(m => m.data.index).sort((a, b) => a - b);
|
|
305
|
-
for (let i = 0; i < 5; i++) {
|
|
306
|
-
if (!indices.includes(i)) {
|
|
307
|
-
throw new Error(`Should have message with index ${i}`);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
passTest('Buffered events stored and retrieved individually');
|
|
312
|
-
} catch (error) {
|
|
313
|
-
failTest(error);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Run all QoS 0 tests
|
|
319
|
-
*/
|
|
320
|
-
export async function runQoS0Tests(client) {
|
|
321
|
-
console.log('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
322
|
-
console.log('ā Queen QoS 0 Test Suite ā');
|
|
323
|
-
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
324
|
-
|
|
325
|
-
await testQoS0Buffering(client);
|
|
326
|
-
await testAutoAck(client);
|
|
327
|
-
await testConsumerGroupAutoAck(client);
|
|
328
|
-
await testFIFOOrdering(client);
|
|
329
|
-
await testMixedOperations(client);
|
|
330
|
-
await testBatchedPayload(client);
|
|
331
|
-
|
|
332
|
-
console.log('\nā
All QoS 0 tests passed!\n');
|
|
333
|
-
}
|
|
334
|
-
|
|
@@ -1,370 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Complete Test Suite for Queen Message Queue System
|
|
5
|
-
* Using the new minimalist Queen client interface
|
|
6
|
-
*
|
|
7
|
-
* Test Categories:
|
|
8
|
-
* 1. Core Features
|
|
9
|
-
* 2. Partition Locking
|
|
10
|
-
* 3. Enterprise Features (coming soon)
|
|
11
|
-
* 4. Bus Mode Features (coming soon)
|
|
12
|
-
* 5. Edge Cases (coming soon)
|
|
13
|
-
* 6. Advanced Scenarios & Patterns (coming soon)
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { Queen } from '../client/client.js';
|
|
17
|
-
import {
|
|
18
|
-
TEST_CONFIG,
|
|
19
|
-
initDb,
|
|
20
|
-
closeDb,
|
|
21
|
-
cleanupTestData,
|
|
22
|
-
printResults,
|
|
23
|
-
log,
|
|
24
|
-
resetResults
|
|
25
|
-
} from './utils.js';
|
|
26
|
-
|
|
27
|
-
// Import human tests
|
|
28
|
-
import {
|
|
29
|
-
testCreateQueue,
|
|
30
|
-
workerQueueTest
|
|
31
|
-
} from './human.js';
|
|
32
|
-
|
|
33
|
-
// Import test modules
|
|
34
|
-
import {
|
|
35
|
-
testQueueCreationPolicy,
|
|
36
|
-
testSingleMessagePush,
|
|
37
|
-
testBatchMessagePush,
|
|
38
|
-
testQueueConfiguration,
|
|
39
|
-
testTakeAndAcknowledgment,
|
|
40
|
-
testDelayedProcessing,
|
|
41
|
-
testPartitionFIFOOrdering,
|
|
42
|
-
testWindowBuffer
|
|
43
|
-
} from './core-tests.js';
|
|
44
|
-
|
|
45
|
-
import {
|
|
46
|
-
testPartitionLocking,
|
|
47
|
-
testBusPartitionLocking,
|
|
48
|
-
testSpecificPartitionLocking,
|
|
49
|
-
testNamespaceTaskFiltering,
|
|
50
|
-
testNamespaceTaskBusMode
|
|
51
|
-
} from './partition-locking-tests.js';
|
|
52
|
-
|
|
53
|
-
import {
|
|
54
|
-
testMessageEncryption,
|
|
55
|
-
testRetentionPendingMessages,
|
|
56
|
-
testRetentionCompletedMessages,
|
|
57
|
-
testPartitionRetention,
|
|
58
|
-
testConsumerEncryption,
|
|
59
|
-
testMessageEviction,
|
|
60
|
-
testCombinedEnterpriseFeatures,
|
|
61
|
-
testEnterpriseErrorHandling
|
|
62
|
-
} from './enterprise-tests.js';
|
|
63
|
-
|
|
64
|
-
import {
|
|
65
|
-
testBusConsumerGroups,
|
|
66
|
-
testMixedMode,
|
|
67
|
-
testConsumerGroupSubscriptionModes,
|
|
68
|
-
testConsumerGroupIsolation
|
|
69
|
-
} from './bus-mode-tests.js';
|
|
70
|
-
|
|
71
|
-
import {
|
|
72
|
-
testEmptyAndNullPayloads,
|
|
73
|
-
testVeryLargePayloads,
|
|
74
|
-
testConcurrentPushOperations,
|
|
75
|
-
testConcurrentTakeOperations,
|
|
76
|
-
testManyConsumerGroups,
|
|
77
|
-
testRetryLimitExhaustion,
|
|
78
|
-
testLeaseExpiration,
|
|
79
|
-
testSQLInjectionPrevention,
|
|
80
|
-
testXSSPrevention
|
|
81
|
-
} from './edge-case-tests.js';
|
|
82
|
-
|
|
83
|
-
import {
|
|
84
|
-
testDuplicateTransactionIdsAcrossPartitions,
|
|
85
|
-
testBatchAckWithDuplicateTransactionIds,
|
|
86
|
-
testDLQWithDuplicateTransactionIds,
|
|
87
|
-
testAnalyticsAPIWithDuplicateTransactionIds
|
|
88
|
-
} from './partition-transaction-tests.js';
|
|
89
|
-
|
|
90
|
-
import {
|
|
91
|
-
testMultiStagePipeline,
|
|
92
|
-
testFanOutFanIn,
|
|
93
|
-
testComplexPriorityScenarios,
|
|
94
|
-
testDynamicPriorityAdjustment,
|
|
95
|
-
testSagaPattern,
|
|
96
|
-
testRateLimiting,
|
|
97
|
-
testTimeBatchProcessing,
|
|
98
|
-
testEventSourcing,
|
|
99
|
-
testQueueMetrics,
|
|
100
|
-
testMultipleConsumersSinglePartitionOrdering,
|
|
101
|
-
testDeadLetterQueuePattern,
|
|
102
|
-
testCircuitBreaker,
|
|
103
|
-
testMessageDeduplication
|
|
104
|
-
} from './advanced-pattern-tests.js';
|
|
105
|
-
|
|
106
|
-
import {
|
|
107
|
-
testPipelineBasic,
|
|
108
|
-
testPipelineErrorHandling,
|
|
109
|
-
testPipelineAutoRenewal,
|
|
110
|
-
testPipelineParallel,
|
|
111
|
-
testPipelineConcurrencyRaceCondition,
|
|
112
|
-
testPipelineContinuous,
|
|
113
|
-
testTransactionAPI,
|
|
114
|
-
testPipelineAtomic,
|
|
115
|
-
testManualLeaseRenewal,
|
|
116
|
-
testLeaseValidation,
|
|
117
|
-
testComplexPipelineWorkflow,
|
|
118
|
-
runAdvancedClientTests
|
|
119
|
-
} from './advanced-client-tests.js';
|
|
120
|
-
|
|
121
|
-
import {
|
|
122
|
-
testQoS0Buffering,
|
|
123
|
-
testAutoAck,
|
|
124
|
-
testConsumerGroupAutoAck,
|
|
125
|
-
testFIFOOrdering,
|
|
126
|
-
testMixedOperations,
|
|
127
|
-
testBatchedPayload
|
|
128
|
-
} from './qos0-tests.js';
|
|
129
|
-
|
|
130
|
-
// Helper to run a test with error handling and delay
|
|
131
|
-
async function runTest(testFn) {
|
|
132
|
-
try {
|
|
133
|
-
await testFn();
|
|
134
|
-
} catch (error) {
|
|
135
|
-
log(`Unexpected error in test: ${error.message}`, 'error');
|
|
136
|
-
}
|
|
137
|
-
await new Promise(resolve => setTimeout(resolve, 100)); // Small delay between tests
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Parse command-line arguments
|
|
141
|
-
const args = process.argv.slice(2);
|
|
142
|
-
const testFilter = args[0]; // e.g., 'core', 'bus', 'edge', 'enterprise', 'partition', 'advanced'
|
|
143
|
-
|
|
144
|
-
// Show help if requested
|
|
145
|
-
if (testFilter === 'help' || testFilter === '--help' || testFilter === '-h') {
|
|
146
|
-
console.log('Usage: node src/test/test-new.js [category]');
|
|
147
|
-
console.log('\nAvailable test categories:');
|
|
148
|
-
console.log(' core - Core features (queue creation, push, take, ack, etc.)');
|
|
149
|
-
console.log(' partition - Partition locking tests');
|
|
150
|
-
console.log(' enterprise - Enterprise features (encryption, retention, eviction)');
|
|
151
|
-
console.log(' bus - Bus mode features (consumer groups, mixed mode)');
|
|
152
|
-
console.log(' edge - Edge cases (null payloads, large payloads, SQL injection, etc.)');
|
|
153
|
-
console.log(' qos0 - QoS 0 features (buffering, auto-ack, FIFO ordering)');
|
|
154
|
-
console.log(' advanced - Advanced patterns (pipeline, fan-out, DLQ, circuit breaker)');
|
|
155
|
-
console.log('\nExamples:');
|
|
156
|
-
console.log(' node src/test/test-new.js # Run all tests');
|
|
157
|
-
console.log(' node src/test/test-new.js core # Run only core tests');
|
|
158
|
-
console.log(' node src/test/test-new.js bus # Run only bus mode tests');
|
|
159
|
-
console.log(' node src/test/test-new.js edge # Run only edge case tests');
|
|
160
|
-
console.log(' node src/test/test-new.js qos0 # Run only QoS 0 tests');
|
|
161
|
-
process.exit(0);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
async function runTests() {
|
|
165
|
-
console.log('š Starting Queen Message Queue Test Suite');
|
|
166
|
-
console.log(' Using the new minimalist Queen client interface\n');
|
|
167
|
-
if (testFilter) {
|
|
168
|
-
console.log(` šÆ Running only: ${testFilter} tests\n`);
|
|
169
|
-
}
|
|
170
|
-
console.log('='.repeat(80));
|
|
171
|
-
|
|
172
|
-
let client;
|
|
173
|
-
|
|
174
|
-
try {
|
|
175
|
-
// Initialize database connection
|
|
176
|
-
await initDb();
|
|
177
|
-
log('Database connection established');
|
|
178
|
-
|
|
179
|
-
// Initialize client
|
|
180
|
-
client = new Queen({
|
|
181
|
-
baseUrls: TEST_CONFIG.baseUrls,
|
|
182
|
-
timeout: 30000,
|
|
183
|
-
retryAttempts: 3
|
|
184
|
-
});
|
|
185
|
-
log('Queen client initialized');
|
|
186
|
-
|
|
187
|
-
// Check for encryption key
|
|
188
|
-
if (process.env.QUEEN_ENCRYPTION_KEY) {
|
|
189
|
-
log('Encryption key detected - encryption tests will run', 'info');
|
|
190
|
-
} else {
|
|
191
|
-
log('No encryption key found - encryption tests will be skipped', 'warning');
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Clean up any existing test data
|
|
195
|
-
await cleanupTestData();
|
|
196
|
-
|
|
197
|
-
// ============================================
|
|
198
|
-
// HUMAN TESTS
|
|
199
|
-
// ============================================
|
|
200
|
-
if (!testFilter || testFilter === 'human') {
|
|
201
|
-
console.log('\nš¤ HUMAN TESTS');
|
|
202
|
-
console.log('-'.repeat(40));
|
|
203
|
-
await runTest(() => testCreateQueue(client));
|
|
204
|
-
await runTest(() => workerQueueTest(client));
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// ============================================
|
|
208
|
-
// CORE FEATURE TESTS
|
|
209
|
-
// ============================================
|
|
210
|
-
if (!testFilter || testFilter === 'core') {
|
|
211
|
-
console.log('\nš¦ CORE FEATURES');
|
|
212
|
-
console.log('-'.repeat(40));
|
|
213
|
-
|
|
214
|
-
await runTest(() => testQueueCreationPolicy(client));
|
|
215
|
-
await runTest(() => testSingleMessagePush(client));
|
|
216
|
-
await runTest(() => testBatchMessagePush(client));
|
|
217
|
-
await runTest(() => testQueueConfiguration(client));
|
|
218
|
-
await runTest(() => testTakeAndAcknowledgment(client));
|
|
219
|
-
await runTest(() => testDelayedProcessing(client));
|
|
220
|
-
await runTest(() => testPartitionFIFOOrdering(client));
|
|
221
|
-
await runTest(() => testWindowBuffer(client));
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// ============================================
|
|
225
|
-
// PARTITION LOCKING TESTS
|
|
226
|
-
// ============================================
|
|
227
|
-
if (!testFilter || testFilter === 'partition' || testFilter === 'locking') {
|
|
228
|
-
console.log('\nš PARTITION LOCKING');
|
|
229
|
-
console.log('-'.repeat(40));
|
|
230
|
-
|
|
231
|
-
await runTest(() => testPartitionLocking(client));
|
|
232
|
-
await runTest(() => testBusPartitionLocking(client));
|
|
233
|
-
await runTest(() => testSpecificPartitionLocking(client));
|
|
234
|
-
await runTest(() => testNamespaceTaskFiltering(client));
|
|
235
|
-
await runTest(() => testNamespaceTaskBusMode(client));
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// ============================================
|
|
239
|
-
// ENTERPRISE FEATURE TESTS
|
|
240
|
-
// ============================================
|
|
241
|
-
if (!testFilter || testFilter === 'enterprise') {
|
|
242
|
-
console.log('\nš¢ ENTERPRISE FEATURES');
|
|
243
|
-
console.log('-'.repeat(40));
|
|
244
|
-
|
|
245
|
-
await runTest(() => testMessageEncryption(client));
|
|
246
|
-
await runTest(() => testRetentionPendingMessages(client));
|
|
247
|
-
await runTest(() => testRetentionCompletedMessages(client));
|
|
248
|
-
await runTest(() => testPartitionRetention(client));
|
|
249
|
-
await runTest(() => testConsumerEncryption(client));
|
|
250
|
-
await runTest(() => testMessageEviction(client));
|
|
251
|
-
await runTest(() => testCombinedEnterpriseFeatures(client));
|
|
252
|
-
await runTest(() => testEnterpriseErrorHandling(client));
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// ============================================
|
|
256
|
-
// BUS MODE FEATURE TESTS
|
|
257
|
-
// ============================================
|
|
258
|
-
if (!testFilter || testFilter === 'bus') {
|
|
259
|
-
console.log('\nš BUS MODE FEATURES');
|
|
260
|
-
console.log('-'.repeat(40));
|
|
261
|
-
|
|
262
|
-
await runTest(() => testBusConsumerGroups(client));
|
|
263
|
-
await runTest(() => testMixedMode(client));
|
|
264
|
-
await runTest(() => testConsumerGroupSubscriptionModes(client));
|
|
265
|
-
await runTest(() => testConsumerGroupIsolation(client));
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// ============================================
|
|
269
|
-
// EDGE CASE TESTS
|
|
270
|
-
// ============================================
|
|
271
|
-
if (!testFilter || testFilter === 'edge') {
|
|
272
|
-
console.log('\nš EDGE CASES');
|
|
273
|
-
console.log('-'.repeat(40));
|
|
274
|
-
|
|
275
|
-
await runTest(() => testEmptyAndNullPayloads(client));
|
|
276
|
-
await runTest(() => testVeryLargePayloads(client));
|
|
277
|
-
await runTest(() => testConcurrentPushOperations(client));
|
|
278
|
-
await runTest(() => testConcurrentTakeOperations(client));
|
|
279
|
-
await runTest(() => testManyConsumerGroups(client));
|
|
280
|
-
await runTest(() => testRetryLimitExhaustion(client));
|
|
281
|
-
await runTest(() => testLeaseExpiration(client));
|
|
282
|
-
await runTest(() => testSQLInjectionPrevention(client));
|
|
283
|
-
await runTest(() => testXSSPrevention(client));
|
|
284
|
-
|
|
285
|
-
// Partition-scoped transaction_id tests
|
|
286
|
-
await runTest(() => testDuplicateTransactionIdsAcrossPartitions(client));
|
|
287
|
-
await runTest(() => testBatchAckWithDuplicateTransactionIds(client));
|
|
288
|
-
await runTest(() => testDLQWithDuplicateTransactionIds(client));
|
|
289
|
-
await runTest(() => testAnalyticsAPIWithDuplicateTransactionIds(client));
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// ============================================
|
|
293
|
-
// QOS 0 FEATURE TESTS
|
|
294
|
-
// ============================================
|
|
295
|
-
if (!testFilter || testFilter === 'qos0') {
|
|
296
|
-
console.log('\nā” QOS 0 FEATURES');
|
|
297
|
-
console.log('-'.repeat(40));
|
|
298
|
-
|
|
299
|
-
await runTest(() => testQoS0Buffering(client));
|
|
300
|
-
await runTest(() => testAutoAck(client));
|
|
301
|
-
await runTest(() => testConsumerGroupAutoAck(client));
|
|
302
|
-
await runTest(() => testFIFOOrdering(client));
|
|
303
|
-
await runTest(() => testMixedOperations(client));
|
|
304
|
-
await runTest(() => testBatchedPayload(client));
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// ============================================
|
|
308
|
-
// ADVANCED CLIENT FEATURE TESTS
|
|
309
|
-
// ============================================
|
|
310
|
-
if (!testFilter || testFilter === 'advanced' || testFilter === 'client') {
|
|
311
|
-
console.log('\nš ADVANCED CLIENT FEATURES');
|
|
312
|
-
console.log('-'.repeat(40));
|
|
313
|
-
|
|
314
|
-
await runTest(() => testPipelineBasic(client));
|
|
315
|
-
await runTest(() => testPipelineErrorHandling(client));
|
|
316
|
-
await runTest(() => testPipelineAutoRenewal(client));
|
|
317
|
-
await runTest(() => testPipelineParallel(client));
|
|
318
|
-
await runTest(() => testPipelineConcurrencyRaceCondition(client));
|
|
319
|
-
await runTest(() => testPipelineContinuous(client));
|
|
320
|
-
await runTest(() => testTransactionAPI(client));
|
|
321
|
-
await runTest(() => testPipelineAtomic(client));
|
|
322
|
-
await runTest(() => testManualLeaseRenewal(client));
|
|
323
|
-
await runTest(() => testLeaseValidation(client));
|
|
324
|
-
await runTest(() => testComplexPipelineWorkflow(client));
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// ============================================
|
|
328
|
-
// ADVANCED SCENARIO TESTS
|
|
329
|
-
// ============================================
|
|
330
|
-
if (!testFilter || testFilter === 'advanced' || testFilter === 'pattern') {
|
|
331
|
-
console.log('\nšÆ ADVANCED PATTERNS');
|
|
332
|
-
console.log('-'.repeat(40));
|
|
333
|
-
|
|
334
|
-
await runTest(() => testMultiStagePipeline(client));
|
|
335
|
-
await runTest(() => testFanOutFanIn(client));
|
|
336
|
-
await runTest(() => testComplexPriorityScenarios(client));
|
|
337
|
-
await runTest(() => testDynamicPriorityAdjustment(client));
|
|
338
|
-
await runTest(() => testDeadLetterQueuePattern(client));
|
|
339
|
-
await runTest(() => testSagaPattern(client));
|
|
340
|
-
await runTest(() => testRateLimiting(client));
|
|
341
|
-
await runTest(() => testMessageDeduplication(client));
|
|
342
|
-
await runTest(() => testTimeBatchProcessing(client));
|
|
343
|
-
await runTest(() => testEventSourcing(client));
|
|
344
|
-
await runTest(() => testQueueMetrics(client));
|
|
345
|
-
await runTest(() => testCircuitBreaker(client));
|
|
346
|
-
await runTest(() => testMultipleConsumersSinglePartitionOrdering(client));
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
} catch (error) {
|
|
350
|
-
log(`Test suite setup failed: ${error.message}`, 'error');
|
|
351
|
-
console.error(error);
|
|
352
|
-
} finally {
|
|
353
|
-
// Cleanup
|
|
354
|
-
if (client) {
|
|
355
|
-
await client.close();
|
|
356
|
-
}
|
|
357
|
-
await cleanupTestData();
|
|
358
|
-
await closeDb();
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// Print results
|
|
362
|
-
printResults();
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
// Run the test suite
|
|
366
|
-
runTests().catch(error => {
|
|
367
|
-
console.error('š„ Test suite crashed:', error);
|
|
368
|
-
process.exit(1);
|
|
369
|
-
});
|
|
370
|
-
|