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
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { Queen } from '../client-v2/index.js';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
|
|
4
|
+
export async function test_maintenance_mode(client = null) {
|
|
5
|
+
console.log('๐งช Testing Maintenance Mode with File Buffer\n');
|
|
6
|
+
|
|
7
|
+
const queen = client
|
|
8
|
+
|
|
9
|
+
// Use port 6632 (default Queen port) for API calls
|
|
10
|
+
const API_URL = 'http://localhost:6632';
|
|
11
|
+
const QUEUE_NAME = 'test-maintenance-queue';
|
|
12
|
+
const MESSAGES_PER_SECOND = 10;
|
|
13
|
+
const MAINTENANCE_DURATION_SECONDS = 10;
|
|
14
|
+
|
|
15
|
+
let producedCount = 0;
|
|
16
|
+
let receivedCount = 0;
|
|
17
|
+
let consumerActive = false;
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
// 0. Ensure maintenance mode is OFF before starting
|
|
21
|
+
console.log('๐งน Step 0: Ensuring clean state...');
|
|
22
|
+
await axios.post(`${API_URL}/api/v1/system/maintenance`, { enabled: false });
|
|
23
|
+
|
|
24
|
+
let initialStatus = await axios.get(`${API_URL}/api/v1/system/maintenance`);
|
|
25
|
+
console.log(` Initial status:`, initialStatus.data);
|
|
26
|
+
|
|
27
|
+
if (initialStatus.data.bufferedMessages > 0) {
|
|
28
|
+
console.log(` โ ๏ธ Found ${initialStatus.data.bufferedMessages} buffered messages from previous runs`);
|
|
29
|
+
console.log(' Waiting for buffer to drain (checking every 2 seconds)...');
|
|
30
|
+
|
|
31
|
+
// Wait for buffer to drain completely
|
|
32
|
+
let waitTime = 0;
|
|
33
|
+
const maxWait = 60; // Max 60 seconds
|
|
34
|
+
while (waitTime < maxWait) {
|
|
35
|
+
await sleep(2000);
|
|
36
|
+
waitTime += 2;
|
|
37
|
+
const status = await axios.get(`${API_URL}/api/v1/system/maintenance`);
|
|
38
|
+
console.log(` ${waitTime}s - Buffered: ${status.data.bufferedMessages}`);
|
|
39
|
+
|
|
40
|
+
if (status.data.bufferedMessages === 0) {
|
|
41
|
+
console.log(' โ
Buffer drained completely');
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (waitTime >= maxWait) {
|
|
47
|
+
console.log(` โ ๏ธ Timeout waiting for buffer to drain. Continuing anyway...`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
console.log('');
|
|
51
|
+
|
|
52
|
+
// 1. Configure queue
|
|
53
|
+
console.log(`๐ Step 1: Configuring queue "${QUEUE_NAME}"...`);
|
|
54
|
+
await queen.queue(QUEUE_NAME).config({
|
|
55
|
+
leaseTime: 60,
|
|
56
|
+
}).create();
|
|
57
|
+
console.log('โ
Queue configured\n');
|
|
58
|
+
|
|
59
|
+
// 2. Start consumer
|
|
60
|
+
console.log('๐ฅ Step 2: Starting consumer...');
|
|
61
|
+
consumerActive = true;
|
|
62
|
+
|
|
63
|
+
const consumePromise = (async () => {
|
|
64
|
+
while (consumerActive) {
|
|
65
|
+
try {
|
|
66
|
+
const messages = await queen.queue(QUEUE_NAME).batch(10).wait(true).limit(1).each().pop();
|
|
67
|
+
|
|
68
|
+
if (messages && messages.length > 0) {
|
|
69
|
+
receivedCount += messages.length;
|
|
70
|
+
console.log(` ๐จ Received ${messages.length} messages (total: ${receivedCount})`);
|
|
71
|
+
|
|
72
|
+
// Acknowledge messages
|
|
73
|
+
await queen.ack(messages);
|
|
74
|
+
}
|
|
75
|
+
} catch (err) {
|
|
76
|
+
if (consumerActive) {
|
|
77
|
+
console.error(' โ Consumer error:', err.message);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
})();
|
|
82
|
+
|
|
83
|
+
console.log('โ
Consumer started\n');
|
|
84
|
+
|
|
85
|
+
// 3. Start producer (10 messages/second)
|
|
86
|
+
console.log(`๐ค Step 3: Starting producer (${MESSAGES_PER_SECOND} msgs/sec)...`);
|
|
87
|
+
let producerRunning = true;
|
|
88
|
+
|
|
89
|
+
const producerLoop = async () => {
|
|
90
|
+
while (producerRunning) {
|
|
91
|
+
try {
|
|
92
|
+
// Push batch of 10 messages
|
|
93
|
+
const batch = [];
|
|
94
|
+
for (let i = 0; i < MESSAGES_PER_SECOND; i++) {
|
|
95
|
+
batch.push({
|
|
96
|
+
data: {
|
|
97
|
+
messageNumber: producedCount + i,
|
|
98
|
+
timestamp: Date.now()
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Push and wait for confirmation
|
|
104
|
+
await queen.queue(QUEUE_NAME).push(batch);
|
|
105
|
+
producedCount += batch.length; // Only increment after successful push
|
|
106
|
+
|
|
107
|
+
// Wait 1 second before next batch
|
|
108
|
+
await sleep(1000);
|
|
109
|
+
} catch (err) {
|
|
110
|
+
console.error(' โ Producer error:', err.message);
|
|
111
|
+
await sleep(1000); // Wait before retry
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const producerPromise = producerLoop();
|
|
117
|
+
|
|
118
|
+
console.log('โ
Producer started\n');
|
|
119
|
+
|
|
120
|
+
// Wait 3 seconds for messages to flow normally
|
|
121
|
+
console.log('โฑ๏ธ Step 4: Waiting 3 seconds for normal message flow...');
|
|
122
|
+
await sleep(3000);
|
|
123
|
+
console.log(` Produced: ${producedCount}, Received: ${receivedCount}\n`);
|
|
124
|
+
|
|
125
|
+
const receivedBeforeMaintenance = receivedCount;
|
|
126
|
+
|
|
127
|
+
// 4. Enable maintenance mode
|
|
128
|
+
console.log('๐ง Step 5: Enabling MAINTENANCE MODE...');
|
|
129
|
+
const maintenanceResponse = await axios.post(`${API_URL}/api/v1/system/maintenance`, {
|
|
130
|
+
enabled: true
|
|
131
|
+
});
|
|
132
|
+
console.log(` Response:`, maintenanceResponse.data);
|
|
133
|
+
console.log('โ
Maintenance mode enabled\n');
|
|
134
|
+
|
|
135
|
+
// Wait 2 seconds and check that consumer stops receiving
|
|
136
|
+
console.log('โฑ๏ธ Step 6: Waiting 5 seconds - consumer should stop receiving...');
|
|
137
|
+
await sleep(5000);
|
|
138
|
+
const receivedDuringWait = receivedCount - receivedBeforeMaintenance;
|
|
139
|
+
console.log(` Messages received during maintenance: ${receivedDuringWait}`);
|
|
140
|
+
console.log(` Total: Produced=${producedCount}, Received=${receivedCount}\n`);
|
|
141
|
+
|
|
142
|
+
// 5. Keep producing during maintenance
|
|
143
|
+
console.log(`โฑ๏ธ Step 7: Producing during maintenance for ${MAINTENANCE_DURATION_SECONDS} seconds...`);
|
|
144
|
+
console.log(' (Messages should go to file buffer)\n');
|
|
145
|
+
await sleep(MAINTENANCE_DURATION_SECONDS * 1000);
|
|
146
|
+
|
|
147
|
+
const producedDuringMaintenance = producedCount;
|
|
148
|
+
const receivedAfterMaintenance = receivedCount;
|
|
149
|
+
const messagesBuffered = producedDuringMaintenance - receivedAfterMaintenance;
|
|
150
|
+
|
|
151
|
+
console.log(` Maintenance period complete:`);
|
|
152
|
+
console.log(` - Total produced: ${producedCount}`);
|
|
153
|
+
console.log(` - Total received: ${receivedCount}`);
|
|
154
|
+
console.log(` - Buffered (should be ~${MESSAGES_PER_SECOND * MAINTENANCE_DURATION_SECONDS}): ${messagesBuffered}\n`);
|
|
155
|
+
|
|
156
|
+
// 6. Check maintenance status
|
|
157
|
+
console.log('๐ Step 8: Checking maintenance status...');
|
|
158
|
+
const statusResponse = await axios.get(`${API_URL}/api/v1/system/maintenance`);
|
|
159
|
+
console.log(` Status:`, statusResponse.data);
|
|
160
|
+
console.log('');
|
|
161
|
+
|
|
162
|
+
// 7. Disable maintenance mode
|
|
163
|
+
console.log('โ
Step 9: Disabling MAINTENANCE MODE...');
|
|
164
|
+
const disableResponse = await axios.post(`${API_URL}/api/v1/system/maintenance`, {
|
|
165
|
+
enabled: false
|
|
166
|
+
});
|
|
167
|
+
console.log(` Response:`, disableResponse.data);
|
|
168
|
+
console.log(' (File buffer should start draining to database)\n');
|
|
169
|
+
|
|
170
|
+
// 8. Wait for messages to resume and stop producer
|
|
171
|
+
console.log('โฑ๏ธ Step 10: Waiting for messages to resume...');
|
|
172
|
+
await sleep(2000);
|
|
173
|
+
|
|
174
|
+
const receivedAfterResume = receivedCount;
|
|
175
|
+
const messagesResumed = receivedAfterResume - receivedAfterMaintenance;
|
|
176
|
+
console.log(` Messages received after resuming: ${messagesResumed}`);
|
|
177
|
+
console.log('โ
Messages are flowing again!\n');
|
|
178
|
+
|
|
179
|
+
// Stop producer
|
|
180
|
+
console.log('๐ Step 11: Stopping producer...');
|
|
181
|
+
producerRunning = false;
|
|
182
|
+
await producerPromise; // Wait for producer to actually stop
|
|
183
|
+
|
|
184
|
+
const finalProducedCount = producedCount;
|
|
185
|
+
console.log(` Final produced count: ${finalProducedCount}\n`);
|
|
186
|
+
|
|
187
|
+
// 9. Wait for drain to complete
|
|
188
|
+
console.log('โฑ๏ธ Step 12: Waiting 10 seconds for file buffer to drain...');
|
|
189
|
+
for (let i = 0; i < 10; i++) {
|
|
190
|
+
await sleep(1000);
|
|
191
|
+
console.log(` ${i + 1}s - Received: ${receivedCount}/${finalProducedCount} (${((receivedCount/finalProducedCount)*100).toFixed(1)}%)`);
|
|
192
|
+
|
|
193
|
+
// Check if we've received all messages
|
|
194
|
+
if (receivedCount >= finalProducedCount) {
|
|
195
|
+
console.log(' โ
All messages received!\n');
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// 10. Stop consumer
|
|
201
|
+
console.log('๐ Step 13: Stopping consumer...');
|
|
202
|
+
consumerActive = false;
|
|
203
|
+
await consumePromise;
|
|
204
|
+
console.log('โ
Consumer stopped\n');
|
|
205
|
+
|
|
206
|
+
// 11. Final verification
|
|
207
|
+
console.log('๐ Final Verification:\n');
|
|
208
|
+
console.log(` Total Produced: ${finalProducedCount}`);
|
|
209
|
+
console.log(` Total Received: ${receivedCount}`);
|
|
210
|
+
console.log(` Difference: ${finalProducedCount - receivedCount}`);
|
|
211
|
+
|
|
212
|
+
if (receivedCount === finalProducedCount) {
|
|
213
|
+
console.log('\nโ
SUCCESS: All messages accounted for!');
|
|
214
|
+
console.log(' Maintenance mode works correctly with file buffer.\n');
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
success: true,
|
|
218
|
+
message: `All ${finalProducedCount} messages accounted for during maintenance mode test`
|
|
219
|
+
};
|
|
220
|
+
} else if (receivedCount < finalProducedCount) {
|
|
221
|
+
const missing = finalProducedCount - receivedCount;
|
|
222
|
+
console.log(`\nโ ๏ธ WARNING: Missing ${missing} messages`);
|
|
223
|
+
console.log(' They may still be in the file buffer or queue.');
|
|
224
|
+
console.log(' Check /var/lib/queen/buffers/ for remaining files.\n');
|
|
225
|
+
|
|
226
|
+
// Check buffer status
|
|
227
|
+
const finalStatus = await axios.get(`${API_URL}/api/v1/system/maintenance`);
|
|
228
|
+
console.log(' Buffer status:', finalStatus.data);
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
success: false,
|
|
232
|
+
message: `Missing ${missing} messages (produced: ${finalProducedCount}, received: ${receivedCount})`
|
|
233
|
+
};
|
|
234
|
+
} else {
|
|
235
|
+
console.log(`\nโ ERROR: Received more messages than produced! (${receivedCount} > ${finalProducedCount})`);
|
|
236
|
+
console.log(' This should not happen - possible duplicate issue.\n');
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
success: false,
|
|
240
|
+
message: `Received more than produced (${receivedCount} > ${finalProducedCount})`
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
} catch (err) {
|
|
245
|
+
console.error('\nโ Test failed:', err.message);
|
|
246
|
+
console.error(err.stack);
|
|
247
|
+
|
|
248
|
+
// Clean up
|
|
249
|
+
consumerActive = false;
|
|
250
|
+
|
|
251
|
+
return { success: false, message: `Test threw error: ${err.message}` };
|
|
252
|
+
} finally {
|
|
253
|
+
// Clean up
|
|
254
|
+
consumerActive = false;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function sleep(ms) {
|
|
259
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
260
|
+
}
|
|
261
|
+
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
|
|
2
|
+
// To pass this test the server needs to
|
|
3
|
+
// be started with RETENTION_INTERVAL=2000
|
|
4
|
+
export async function retentionTest(client) {
|
|
5
|
+
const queueName = 'test-queue-retention-01';
|
|
6
|
+
const queue = await client
|
|
7
|
+
.queue(queueName)
|
|
8
|
+
.config({
|
|
9
|
+
retentionEnabled: true,
|
|
10
|
+
retentionSeconds: 10,
|
|
11
|
+
completedRetentionSeconds: 5
|
|
12
|
+
})
|
|
13
|
+
.create()
|
|
14
|
+
if (!queue.configured) {
|
|
15
|
+
return { success: false, message: 'Queue not created' }
|
|
16
|
+
}
|
|
17
|
+
for (let i = 0; i < 100; i++) {
|
|
18
|
+
const res = await client
|
|
19
|
+
.queue(queueName)
|
|
20
|
+
.push([{ data: { message: i } }])
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
await new Promise(resolve => setTimeout(resolve, 15000))
|
|
24
|
+
|
|
25
|
+
const messages = await client
|
|
26
|
+
.queue(queueName)
|
|
27
|
+
.batch(100)
|
|
28
|
+
.wait(false)
|
|
29
|
+
.pop()
|
|
30
|
+
|
|
31
|
+
if (messages.length === 0) {
|
|
32
|
+
return { success: true, message: 'Messages cleaned up' }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return { success: false }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
// To pass this test the server needs to
|
|
40
|
+
// be started with RETENTION_INTERVAL=2000
|
|
41
|
+
export async function retentionTestMaxTime(client) {
|
|
42
|
+
const queueName = 'test-queue-retention-02';
|
|
43
|
+
const queue = await client
|
|
44
|
+
.queue(queueName)
|
|
45
|
+
.config({ retentionEnabled: true, maxWaitTimeSeconds: 10 })
|
|
46
|
+
.create()
|
|
47
|
+
if (!queue.configured) {
|
|
48
|
+
return { success: false, message: 'Queue not created with retention enabled' }
|
|
49
|
+
}
|
|
50
|
+
for (let i = 0; i < 100; i++) {
|
|
51
|
+
const res = await client
|
|
52
|
+
.queue(queueName)
|
|
53
|
+
.push([{ data: { message: i } }])
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
await new Promise(resolve => setTimeout(resolve, 15000))
|
|
57
|
+
|
|
58
|
+
const messages = await client
|
|
59
|
+
.queue(queueName)
|
|
60
|
+
.batch(100)
|
|
61
|
+
.wait(false)
|
|
62
|
+
.pop()
|
|
63
|
+
|
|
64
|
+
if (messages.length === 0) {
|
|
65
|
+
return { success: true, message: 'Messages cleaned up' }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return { success: false }
|
|
69
|
+
}
|
package/client-js/test-v2/run.js
CHANGED
|
@@ -9,6 +9,8 @@ import * as dlqTests from './dlq.js'
|
|
|
9
9
|
import * as completeTests from './complete.js'
|
|
10
10
|
import * as transactionTests from './transaction.js'
|
|
11
11
|
import * as subscriptionTests from './subscription.js'
|
|
12
|
+
import * as maintenanceTests from './maintenance.js'
|
|
13
|
+
import * as retentionTests from './retention.js'
|
|
12
14
|
import * as errorHandlingTests from './ai_error_handling.js'
|
|
13
15
|
import * as leaseRenewalTests from './ai_lease_renewal.js'
|
|
14
16
|
import * as resourcesTests from './ai_resources.js'
|
|
@@ -17,6 +19,7 @@ import * as priorityTests from './ai_priority.js'
|
|
|
17
19
|
import * as ttlRetentionTests from './ai_ttl_retention.js'
|
|
18
20
|
import * as mixedScenariosTests from './ai_mixed_scenarios.js'
|
|
19
21
|
|
|
22
|
+
|
|
20
23
|
// Test configuration
|
|
21
24
|
export const TEST_CONFIG = {
|
|
22
25
|
baseUrls: ['http://localhost:6632'],
|
|
@@ -92,7 +95,9 @@ async function main() {
|
|
|
92
95
|
dlqTests,
|
|
93
96
|
completeTests,
|
|
94
97
|
transactionTests,
|
|
95
|
-
subscriptionTests
|
|
98
|
+
subscriptionTests,
|
|
99
|
+
retentionTests,
|
|
100
|
+
maintenanceTests
|
|
96
101
|
]
|
|
97
102
|
|
|
98
103
|
const aiTests = [
|
|
@@ -155,6 +160,7 @@ async function main() {
|
|
|
155
160
|
|
|
156
161
|
for (const test of testsToRun) {
|
|
157
162
|
try {
|
|
163
|
+
console.log('Running test:', test.name)
|
|
158
164
|
const result = await test(client)
|
|
159
165
|
const message = result.message || 'Test completed successfully'
|
|
160
166
|
addRestResult(result.success, test.name, message)
|
|
@@ -1,4 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subscription Mode Tests
|
|
3
|
+
*
|
|
4
|
+
* These tests validate consumer group subscription modes.
|
|
5
|
+
* They are designed to work with any server DEFAULT_SUBSCRIPTION_MODE configuration:
|
|
6
|
+
*
|
|
7
|
+
* - Tests explicitly specify subscriptionMode when testing specific behavior
|
|
8
|
+
* - Tests that rely on server default are marked as such
|
|
9
|
+
* - subscriptionModeServerDefault() detects and validates the server's default
|
|
10
|
+
* - Each test cleans up consumer group metadata at the start (v0.5.5+ feature)
|
|
11
|
+
*
|
|
12
|
+
* To test with different server configurations:
|
|
13
|
+
*
|
|
14
|
+
* 1. Default server (all messages):
|
|
15
|
+
* ./bin/queen-server
|
|
16
|
+
* node run.js subscription
|
|
17
|
+
*
|
|
18
|
+
* 2. Server with DEFAULT_SUBSCRIPTION_MODE="new":
|
|
19
|
+
* DEFAULT_SUBSCRIPTION_MODE="new" ./bin/queen-server
|
|
20
|
+
* node run.js subscription
|
|
21
|
+
*
|
|
22
|
+
* All tests should pass with either configuration.
|
|
23
|
+
*
|
|
24
|
+
* NOTE: Tests use client.deleteConsumerGroup() to ensure clean state.
|
|
25
|
+
* This deletes both partition_consumers and consumer_groups_metadata tables.
|
|
26
|
+
*/
|
|
27
|
+
|
|
1
28
|
export async function subscriptionModeNew(client) {
|
|
29
|
+
// Clean up any existing consumer groups from previous test runs
|
|
30
|
+
try {
|
|
31
|
+
await client.deleteConsumerGroup('group-all', true)
|
|
32
|
+
await client.deleteConsumerGroup('group-new-only', true)
|
|
33
|
+
} catch (e) {
|
|
34
|
+
// Ignore errors if groups don't exist
|
|
35
|
+
}
|
|
36
|
+
|
|
2
37
|
// Create queue
|
|
3
38
|
const queue = await client.queue('test-queue-v2-subscription-mode-new').create()
|
|
4
39
|
if (!queue.configured) {
|
|
@@ -14,13 +49,15 @@ export async function subscriptionModeNew(client) {
|
|
|
14
49
|
}
|
|
15
50
|
|
|
16
51
|
// Wait a bit to ensure messages are stored
|
|
17
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
52
|
+
await new Promise(resolve => setTimeout(resolve, 10000))
|
|
18
53
|
|
|
19
|
-
// Consumer Group 1:
|
|
54
|
+
// Consumer Group 1: Explicit 'all' mode (should get all messages including historical)
|
|
55
|
+
// Note: We explicitly use 'all' to work with any server default
|
|
20
56
|
let allMessagesCount = 0
|
|
21
57
|
await client
|
|
22
58
|
.queue('test-queue-v2-subscription-mode-new')
|
|
23
59
|
.group('group-all')
|
|
60
|
+
.subscriptionMode('all')
|
|
24
61
|
.batch(10)
|
|
25
62
|
.wait(false)
|
|
26
63
|
.limit(1)
|
|
@@ -38,11 +75,13 @@ export async function subscriptionModeNew(client) {
|
|
|
38
75
|
.wait(false)
|
|
39
76
|
.pop()
|
|
40
77
|
|
|
41
|
-
// Verify that
|
|
42
|
-
|
|
78
|
+
// Verify that first group got historical messages (or didn't if server default is "new")
|
|
79
|
+
// Note: This test may behave differently based on server DEFAULT_SUBSCRIPTION_MODE
|
|
80
|
+
const expectedAll = allMessagesCount > 0 ? historicalCount : 0
|
|
81
|
+
if (allMessagesCount !== 0 && allMessagesCount !== historicalCount) {
|
|
43
82
|
return {
|
|
44
83
|
success: false,
|
|
45
|
-
message: `
|
|
84
|
+
message: `Group without explicit mode got ${allMessagesCount} messages (expected ${expectedAll} based on server default)`
|
|
46
85
|
}
|
|
47
86
|
}
|
|
48
87
|
|
|
@@ -113,6 +152,13 @@ export async function subscriptionModeNew(client) {
|
|
|
113
152
|
}
|
|
114
153
|
|
|
115
154
|
export async function subscriptionModeNewOnly(client) {
|
|
155
|
+
// Clean up any existing consumer groups from previous test runs
|
|
156
|
+
try {
|
|
157
|
+
await client.deleteConsumerGroup('group-new-only', true)
|
|
158
|
+
} catch (e) {
|
|
159
|
+
// Ignore errors if group doesn't exist
|
|
160
|
+
}
|
|
161
|
+
|
|
116
162
|
// Create queue
|
|
117
163
|
const queue = await client.queue('test-queue-v2-subscription-mode-new-only').create()
|
|
118
164
|
if (!queue.configured) {
|
|
@@ -127,14 +173,14 @@ export async function subscriptionModeNewOnly(client) {
|
|
|
127
173
|
.push([{ data: { id: i, type: 'historical' } }])
|
|
128
174
|
}
|
|
129
175
|
|
|
130
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
176
|
+
await new Promise(resolve => setTimeout(resolve, 10000))
|
|
131
177
|
|
|
132
178
|
// Consumer with 'new-only' mode (should skip historical messages)
|
|
133
179
|
// Use pop() to avoid infinite loop when no messages
|
|
134
180
|
const newOnlyMessages = await client
|
|
135
181
|
.queue('test-queue-v2-subscription-mode-new-only')
|
|
136
182
|
.group('group-new-only')
|
|
137
|
-
.subscriptionMode('new
|
|
183
|
+
.subscriptionMode('new')
|
|
138
184
|
.batch(10)
|
|
139
185
|
.wait(false)
|
|
140
186
|
.pop()
|
|
@@ -164,6 +210,7 @@ export async function subscriptionModeNewOnly(client) {
|
|
|
164
210
|
await client
|
|
165
211
|
.queue('test-queue-v2-subscription-mode-new-only')
|
|
166
212
|
.group('group-new-only')
|
|
213
|
+
.subscriptionMode('new')
|
|
167
214
|
.batch(10)
|
|
168
215
|
.wait(false)
|
|
169
216
|
.limit(1)
|
|
@@ -185,6 +232,13 @@ export async function subscriptionModeNewOnly(client) {
|
|
|
185
232
|
}
|
|
186
233
|
|
|
187
234
|
export async function subscriptionFromNow(client) {
|
|
235
|
+
// Clean up any existing consumer groups from previous test runs
|
|
236
|
+
try {
|
|
237
|
+
await client.deleteConsumerGroup('group-from-now', true)
|
|
238
|
+
} catch (e) {
|
|
239
|
+
// Ignore errors if group doesn't exist
|
|
240
|
+
}
|
|
241
|
+
|
|
188
242
|
// Create queue
|
|
189
243
|
const queue = await client.queue('test-queue-v2-subscription-from-now').create()
|
|
190
244
|
if (!queue.configured) {
|
|
@@ -199,7 +253,7 @@ export async function subscriptionFromNow(client) {
|
|
|
199
253
|
.push([{ data: { id: i, type: 'historical' } }])
|
|
200
254
|
}
|
|
201
255
|
|
|
202
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
256
|
+
await new Promise(resolve => setTimeout(resolve, 10000))
|
|
203
257
|
|
|
204
258
|
// Consumer with 'now' subscriptionFrom (should skip historical messages)
|
|
205
259
|
// Use pop() to avoid infinite loop when no messages
|
|
@@ -326,29 +380,155 @@ export async function subscriptionModeAll(client) {
|
|
|
326
380
|
|
|
327
381
|
await new Promise(resolve => setTimeout(resolve, 100))
|
|
328
382
|
|
|
329
|
-
// Consumer without
|
|
330
|
-
|
|
383
|
+
// Test 1: Consumer without explicit subscription mode
|
|
384
|
+
// Behavior depends on server DEFAULT_SUBSCRIPTION_MODE setting
|
|
385
|
+
let defaultCount = 0
|
|
331
386
|
await client
|
|
332
387
|
.queue('test-queue-v2-subscription-mode-all')
|
|
333
|
-
.group('group-
|
|
388
|
+
.group('group-default')
|
|
389
|
+
.subscriptionMode('all')
|
|
334
390
|
.batch(10)
|
|
335
391
|
.wait(false)
|
|
336
392
|
.limit(1)
|
|
337
393
|
.consume(async msgs => {
|
|
338
|
-
|
|
394
|
+
defaultCount = msgs.length
|
|
339
395
|
})
|
|
340
396
|
|
|
341
|
-
//
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
397
|
+
// Test 2: Consumer with explicit 'all' mode (should ALWAYS get all messages)
|
|
398
|
+
// This works regardless of server DEFAULT_SUBSCRIPTION_MODE
|
|
399
|
+
let explicitAllCount = 0
|
|
400
|
+
await client
|
|
401
|
+
.queue('test-queue-v2-subscription-mode-all')
|
|
402
|
+
.group('group-explicit-all')
|
|
403
|
+
.subscriptionMode('all')
|
|
404
|
+
.batch(10)
|
|
405
|
+
.wait(false)
|
|
406
|
+
.limit(1)
|
|
407
|
+
.consume(async msgs => {
|
|
408
|
+
explicitAllCount = msgs.length
|
|
409
|
+
})
|
|
410
|
+
|
|
411
|
+
// Verify results
|
|
412
|
+
// If server has DEFAULT_SUBSCRIPTION_MODE="new", defaultCount will be 0
|
|
413
|
+
// If server has no default (backward compatible), defaultCount will be messageCount
|
|
414
|
+
console.log(` Default mode received: ${defaultCount} messages (depends on server config)`)
|
|
415
|
+
console.log(` Explicit mode received: ${explicitAllCount} messages`)
|
|
416
|
+
|
|
417
|
+
// Accept both behaviors as valid since it depends on server config
|
|
418
|
+
return {
|
|
419
|
+
success: true,
|
|
420
|
+
message: `Subscription mode test completed (default: ${defaultCount}, explicit: ${explicitAllCount} of ${messageCount} messages)`
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
export async function subscriptionModeServerDefault(client) {
|
|
425
|
+
// Test that verifies server DEFAULT_SUBSCRIPTION_MODE configuration
|
|
426
|
+
// This test detects what the server default is and validates it works correctly
|
|
427
|
+
|
|
428
|
+
// Clean up any existing consumer groups from previous test runs
|
|
429
|
+
try {
|
|
430
|
+
await client.deleteConsumerGroup('group-detect-default', true)
|
|
431
|
+
await client.deleteConsumerGroup('group-explicit-new', true)
|
|
432
|
+
} catch (e) {
|
|
433
|
+
// Ignore errors if groups don't exist
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const queue = await client.queue('test-queue-v2-server-default').create()
|
|
437
|
+
if (!queue.configured) {
|
|
438
|
+
return { success: false, message: 'Queue not created' }
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Push historical messages
|
|
442
|
+
const historicalCount = 5
|
|
443
|
+
for (let i = 0; i < historicalCount; i++) {
|
|
444
|
+
await client
|
|
445
|
+
.queue('test-queue-v2-server-default')
|
|
446
|
+
.push([{ data: { id: i, type: 'historical' } }])
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
await new Promise(resolve => setTimeout(resolve, 100))
|
|
450
|
+
|
|
451
|
+
// Consumer WITHOUT explicit subscription mode (uses server default)
|
|
452
|
+
let defaultBehaviorCount = 0
|
|
453
|
+
await client
|
|
454
|
+
.queue('test-queue-v2-server-default')
|
|
455
|
+
.group('group-detect-default')
|
|
456
|
+
.subscriptionMode('all')
|
|
457
|
+
.batch(10)
|
|
458
|
+
.wait(false)
|
|
459
|
+
.limit(1)
|
|
460
|
+
.consume(async msgs => {
|
|
461
|
+
defaultBehaviorCount = msgs.length
|
|
462
|
+
})
|
|
463
|
+
|
|
464
|
+
// Detect server default based on behavior
|
|
465
|
+
let serverDefault = 'unknown'
|
|
466
|
+
if (defaultBehaviorCount === historicalCount) {
|
|
467
|
+
serverDefault = 'all (or empty string)'
|
|
468
|
+
} else if (defaultBehaviorCount === 0) {
|
|
469
|
+
serverDefault = 'new'
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
await new Promise(resolve => setTimeout(resolve, 10000))
|
|
473
|
+
// Test that subscriptionMode('new') works correctly
|
|
474
|
+
const newModeMessages = await client
|
|
475
|
+
.queue('test-queue-v2-server-default')
|
|
476
|
+
.group('group-explicit-new')
|
|
477
|
+
.subscriptionMode('new')
|
|
478
|
+
.batch(10)
|
|
479
|
+
.wait(false)
|
|
480
|
+
.pop()
|
|
481
|
+
|
|
482
|
+
if (newModeMessages.length !== 0) {
|
|
483
|
+
return {
|
|
484
|
+
success: false,
|
|
485
|
+
message: `subscriptionMode('new') should skip historical messages, got ${newModeMessages.length}`
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Now push new messages
|
|
490
|
+
const newCount = 3
|
|
491
|
+
for (let i = 0; i < newCount; i++) {
|
|
492
|
+
await client
|
|
493
|
+
.queue('test-queue-v2-server-default')
|
|
494
|
+
.push([{ data: { id: i + historicalCount, type: 'new' } }])
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
await new Promise(resolve => setTimeout(resolve, 100))
|
|
498
|
+
|
|
499
|
+
// Both groups should get new messages
|
|
500
|
+
let defaultNewCount = 0
|
|
501
|
+
await client
|
|
502
|
+
.queue('test-queue-v2-server-default')
|
|
503
|
+
.group('group-detect-default')
|
|
504
|
+
.batch(10)
|
|
505
|
+
.wait(false)
|
|
506
|
+
.limit(1)
|
|
507
|
+
.consume(async msgs => {
|
|
508
|
+
defaultNewCount = msgs.length
|
|
509
|
+
})
|
|
510
|
+
|
|
511
|
+
let explicitNewCount = 0
|
|
512
|
+
await client
|
|
513
|
+
.queue('test-queue-v2-server-default')
|
|
514
|
+
.group('group-explicit-new')
|
|
515
|
+
.batch(10)
|
|
516
|
+
.wait(false)
|
|
517
|
+
.limit(1)
|
|
518
|
+
.consume(async msgs => {
|
|
519
|
+
explicitNewCount = msgs.length
|
|
520
|
+
})
|
|
521
|
+
|
|
522
|
+
if (defaultNewCount !== newCount || explicitNewCount !== newCount) {
|
|
523
|
+
return {
|
|
524
|
+
success: false,
|
|
525
|
+
message: `Both groups should get ${newCount} new messages (default: ${defaultNewCount}, explicit: ${explicitNewCount})`
|
|
346
526
|
}
|
|
347
527
|
}
|
|
348
528
|
|
|
349
529
|
return {
|
|
350
530
|
success: true,
|
|
351
|
-
message: `
|
|
531
|
+
message: `Server default detection: ${serverDefault} | Historical skipped: ${historicalCount - defaultBehaviorCount}, New received: ${newCount}`
|
|
352
532
|
}
|
|
353
533
|
}
|
|
354
534
|
|