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
package/client-js/test/utils.js
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared test utilities for Queen test suite
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import pg from 'pg';
|
|
6
|
-
|
|
7
|
-
// Test configuration
|
|
8
|
-
export const TEST_CONFIG = {
|
|
9
|
-
baseUrls: ['http://localhost:6632'],
|
|
10
|
-
dbConfig: {
|
|
11
|
-
host: process.env.PG_HOST || 'localhost',
|
|
12
|
-
port: process.env.PG_PORT || 5432,
|
|
13
|
-
database: process.env.PG_DB || 'postgres',
|
|
14
|
-
user: process.env.PG_USER || 'postgres',
|
|
15
|
-
password: process.env.PG_PASSWORD || 'postgres'
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
// Global test state
|
|
20
|
-
export let dbPool;
|
|
21
|
-
export let testResults = [];
|
|
22
|
-
export let currentTest = '';
|
|
23
|
-
|
|
24
|
-
// Initialize database pool
|
|
25
|
-
export async function initDb() {
|
|
26
|
-
dbPool = new pg.Pool(TEST_CONFIG.dbConfig);
|
|
27
|
-
await dbPool.query('SELECT 1');
|
|
28
|
-
return dbPool;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Close database pool
|
|
32
|
-
export async function closeDb() {
|
|
33
|
-
if (dbPool) {
|
|
34
|
-
await dbPool.end();
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Test logging utilities
|
|
39
|
-
export const log = (message, type = 'info') => {
|
|
40
|
-
const timestamp = new Date().toISOString().substring(11, 23);
|
|
41
|
-
const prefix = {
|
|
42
|
-
info: '๐',
|
|
43
|
-
success: 'โ
',
|
|
44
|
-
error: 'โ',
|
|
45
|
-
warning: 'โ ๏ธ',
|
|
46
|
-
test: '๐งช',
|
|
47
|
-
enterprise: '๐ข',
|
|
48
|
-
edge: '๐',
|
|
49
|
-
pattern: '๐ฏ',
|
|
50
|
-
workflow: '๐',
|
|
51
|
-
priority: 'โก'
|
|
52
|
-
}[type] || '๐';
|
|
53
|
-
|
|
54
|
-
console.log(`[${timestamp}] ${prefix} ${message}`);
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
export const startTest = (testName, category = 'test') => {
|
|
58
|
-
currentTest = testName;
|
|
59
|
-
log(`Starting: ${testName}`, category);
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export const passTest = (message = '') => {
|
|
63
|
-
const result = { test: currentTest, status: 'PASS', message };
|
|
64
|
-
testResults.push(result);
|
|
65
|
-
log(`PASS: ${currentTest} ${message}`, 'success');
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
export const failTest = (error) => {
|
|
69
|
-
const result = { test: currentTest, status: 'FAIL', error: error.message };
|
|
70
|
-
testResults.push(result);
|
|
71
|
-
log(`FAIL: ${currentTest} - ${error.message}`, 'error');
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
export const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
75
|
-
|
|
76
|
-
// Database utilities
|
|
77
|
-
export const cleanupTestData = async () => {
|
|
78
|
-
try {
|
|
79
|
-
await dbPool.query(`
|
|
80
|
-
DELETE FROM queen.messages
|
|
81
|
-
WHERE partition_id IN (
|
|
82
|
-
SELECT p.id FROM queen.partitions p
|
|
83
|
-
JOIN queen.queues q ON p.queue_id = q.id
|
|
84
|
-
WHERE q.name LIKE 'test-%' OR q.name LIKE 'edge-%' OR q.name LIKE 'pattern-%' OR q.name LIKE 'workflow-%'
|
|
85
|
-
)
|
|
86
|
-
`);
|
|
87
|
-
|
|
88
|
-
await dbPool.query(`
|
|
89
|
-
DELETE FROM queen.partitions
|
|
90
|
-
WHERE queue_id IN (
|
|
91
|
-
SELECT id FROM queen.queues WHERE name LIKE 'test-%' OR name LIKE 'edge-%' OR name LIKE 'pattern-%' OR name LIKE 'workflow-%'
|
|
92
|
-
)
|
|
93
|
-
`);
|
|
94
|
-
|
|
95
|
-
await dbPool.query(`DELETE FROM queen.queues WHERE name LIKE 'test-%' OR name LIKE 'edge-%' OR name LIKE 'pattern-%' OR name LIKE 'workflow-%'`);
|
|
96
|
-
|
|
97
|
-
await dbPool.query(`
|
|
98
|
-
DELETE FROM queen.retention_history
|
|
99
|
-
WHERE partition_id NOT IN (SELECT id FROM queen.partitions)
|
|
100
|
-
`);
|
|
101
|
-
|
|
102
|
-
log('Test data cleaned up');
|
|
103
|
-
} catch (error) {
|
|
104
|
-
log(`Cleanup error: ${error.message}`, 'warning');
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
export const getMessageCount = async (queueName, partitionName = null) => {
|
|
109
|
-
const query = partitionName
|
|
110
|
-
? `SELECT COUNT(*) as count FROM queen.messages m
|
|
111
|
-
JOIN queen.partitions p ON m.partition_id = p.id
|
|
112
|
-
JOIN queen.queues q ON p.queue_id = q.id
|
|
113
|
-
WHERE q.name = $1 AND p.name = $2`
|
|
114
|
-
: `SELECT COUNT(*) as count FROM queen.messages m
|
|
115
|
-
JOIN queen.partitions p ON m.partition_id = p.id
|
|
116
|
-
JOIN queen.queues q ON p.queue_id = q.id
|
|
117
|
-
WHERE q.name = $1`;
|
|
118
|
-
|
|
119
|
-
const params = partitionName ? [queueName, partitionName] : [queueName];
|
|
120
|
-
const result = await dbPool.query(query, params);
|
|
121
|
-
return parseInt(result.rows[0].count);
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
// Print test results summary
|
|
125
|
-
export function printResults() {
|
|
126
|
-
console.log('\n' + '='.repeat(80));
|
|
127
|
-
console.log('๐ TEST RESULTS SUMMARY');
|
|
128
|
-
console.log('='.repeat(80));
|
|
129
|
-
|
|
130
|
-
const passed = testResults.filter(r => r.status === 'PASS').length;
|
|
131
|
-
const failed = testResults.filter(r => r.status === 'FAIL').length;
|
|
132
|
-
const total = testResults.length;
|
|
133
|
-
|
|
134
|
-
console.log(`\n๐ Overall Results: ${passed}/${total} tests passed`);
|
|
135
|
-
|
|
136
|
-
if (failed > 0) {
|
|
137
|
-
console.log('\nโ Failed Tests:');
|
|
138
|
-
testResults
|
|
139
|
-
.filter(r => r.status === 'FAIL')
|
|
140
|
-
.forEach(r => {
|
|
141
|
-
console.log(` โข ${r.test}: ${r.error}`);
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (passed > 0) {
|
|
146
|
-
console.log('\nโ
Passed Tests:');
|
|
147
|
-
testResults
|
|
148
|
-
.filter(r => r.status === 'PASS')
|
|
149
|
-
.forEach(r => {
|
|
150
|
-
console.log(` โข ${r.test}${r.message ? ': ' + r.message : ''}`);
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
console.log('\n' + '='.repeat(80));
|
|
155
|
-
|
|
156
|
-
if (failed === 0) {
|
|
157
|
-
console.log('๐ ALL TESTS PASSED! Queen Message Queue System is working correctly.');
|
|
158
|
-
console.log(' All features are operational.');
|
|
159
|
-
} else {
|
|
160
|
-
console.log(`โ ๏ธ ${failed} test(s) failed. Please review the failures above.`);
|
|
161
|
-
process.exit(1);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Reset test results (useful for running tests in batches)
|
|
166
|
-
export function resetResults() {
|
|
167
|
-
testResults = [];
|
|
168
|
-
}
|
|
169
|
-
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test to validate windowBuffer functionality
|
|
3
|
-
* Ensures windowBuffer works correctly for all access modes:
|
|
4
|
-
* - Direct partition access
|
|
5
|
-
* - Queue-level access
|
|
6
|
-
* - Namespace/task filtered access
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { Queen } from '../client/index.js';
|
|
10
|
-
import { sleep } from './utils.js';
|
|
11
|
-
|
|
12
|
-
const client = new Queen({
|
|
13
|
-
baseUrl: 'http://localhost:6632'
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
async function testWindowBuffer() {
|
|
17
|
-
console.log('\n๐งช Testing windowBuffer functionality...\n');
|
|
18
|
-
|
|
19
|
-
const timestamp = Date.now();
|
|
20
|
-
const queueName = 'test-window-buffer-' + timestamp;
|
|
21
|
-
const partition = 'test-partition';
|
|
22
|
-
|
|
23
|
-
// Delete queue if it exists (cleanup from previous runs)
|
|
24
|
-
try {
|
|
25
|
-
await client.queueDelete(queueName);
|
|
26
|
-
console.log(`0. Deleted existing queue "${queueName}" (cleanup)`);
|
|
27
|
-
} catch (err) {
|
|
28
|
-
// Queue doesn't exist, that's fine
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Configure queue with 10 second window buffer
|
|
32
|
-
console.log(`1. Creating queue "${queueName}" with windowBuffer: 10 seconds`);
|
|
33
|
-
await client.queue(queueName, {
|
|
34
|
-
windowBuffer: 10
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
// Push first message
|
|
38
|
-
console.log('2. Pushing first message at', new Date().toISOString());
|
|
39
|
-
await client.push(`${queueName}/${partition}`, { message: 'Message 1' });
|
|
40
|
-
|
|
41
|
-
// Wait 2 seconds and push second message
|
|
42
|
-
await sleep(2000);
|
|
43
|
-
console.log('3. Pushing second message at', new Date().toISOString());
|
|
44
|
-
await client.push(`${queueName}/${partition}`, { message: 'Message 2' });
|
|
45
|
-
|
|
46
|
-
// Try to pop immediately (should get NOTHING due to windowBuffer)
|
|
47
|
-
console.log('\n4. Trying to pop immediately (should get NOTHING due to 10s windowBuffer)...');
|
|
48
|
-
|
|
49
|
-
// Test CASE 1: Direct partition access
|
|
50
|
-
console.log('\n TEST CASE 1: Direct partition access');
|
|
51
|
-
let case1Result = 0;
|
|
52
|
-
for await (const msg of client.take(`${queueName}/${partition}`, { limit: 10, batch: 10 })) {
|
|
53
|
-
case1Result++;
|
|
54
|
-
await client.ack(msg, true);
|
|
55
|
-
}
|
|
56
|
-
const case1Status = case1Result === 0 ? 'โ
' : 'โ';
|
|
57
|
-
console.log(` ${case1Status} DIRECT ACCESS: Got ${case1Result} messages (expected 0)`);
|
|
58
|
-
|
|
59
|
-
// Test CASE 2: Queue-level access
|
|
60
|
-
console.log('\n TEST CASE 2: Queue-level access');
|
|
61
|
-
let case2Result = 0;
|
|
62
|
-
for await (const msg of client.take(queueName, { limit: 10, batch: 10 })) {
|
|
63
|
-
case2Result++;
|
|
64
|
-
await client.ack(msg, true);
|
|
65
|
-
}
|
|
66
|
-
const case2Status = case2Result === 0 ? 'โ
' : 'โ';
|
|
67
|
-
console.log(` ${case2Status} QUEUE ACCESS: Got ${case2Result} messages (expected 0)`);
|
|
68
|
-
|
|
69
|
-
// Test CASE 3: Namespace/task filtered access (should work correctly)
|
|
70
|
-
console.log('\n TEST CASE 3: Namespace/task filtered access');
|
|
71
|
-
|
|
72
|
-
// Use unique namespace/task per test run to avoid collisions
|
|
73
|
-
const testNamespace = `test-ns-${timestamp}`;
|
|
74
|
-
const testTask = `test-task-${timestamp}`;
|
|
75
|
-
const filteredQueue = `${queueName}-filtered`;
|
|
76
|
-
|
|
77
|
-
// Delete queue if it exists
|
|
78
|
-
try {
|
|
79
|
-
await client.queueDelete(filteredQueue);
|
|
80
|
-
} catch (err) {
|
|
81
|
-
// Queue doesn't exist, that's fine
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
await client.queue(filteredQueue, {
|
|
85
|
-
windowBuffer: 10
|
|
86
|
-
}, testNamespace, testTask);
|
|
87
|
-
|
|
88
|
-
await client.push(`${filteredQueue}/${partition}`, { message: 'Filtered message' });
|
|
89
|
-
|
|
90
|
-
let case3Result = 0;
|
|
91
|
-
for await (const msg of client.take(`namespace:${testNamespace}/task:${testTask}`, { limit: 10, batch: 10 })) {
|
|
92
|
-
case3Result++;
|
|
93
|
-
await client.ack(msg, true);
|
|
94
|
-
}
|
|
95
|
-
const case3Status = case3Result === 0 ? 'โ
' : 'โ';
|
|
96
|
-
console.log(` ${case3Status} FILTERED ACCESS: Got ${case3Result} messages (expected 0)`);
|
|
97
|
-
|
|
98
|
-
console.log('\n๐ SUMMARY:');
|
|
99
|
-
const allPassed = case1Result === 0 && case2Result === 0 && case3Result === 0;
|
|
100
|
-
|
|
101
|
-
if (allPassed) {
|
|
102
|
-
console.log(' โ
All tests PASSED! windowBuffer is working correctly in all access modes.');
|
|
103
|
-
} else {
|
|
104
|
-
console.log(' Issues found:');
|
|
105
|
-
if (case1Result !== 0) console.log(' โ Direct partition access: windowBuffer NOT enforced');
|
|
106
|
-
if (case2Result !== 0) console.log(' โ Queue-level access: windowBuffer NOT enforced');
|
|
107
|
-
if (case3Result !== 0) console.log(' โ Namespace/task filtered access: windowBuffer NOT enforced');
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
console.log('\n' + (allPassed ? 'โ
Test complete. All fixed!' : 'โ Test complete. Issues remain.') + '\n');
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
testWindowBuffer().catch(console.error);
|
|
114
|
-
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Centralized logging utility for Queen message queue
|
|
3
|
-
* Logs all state-changing operations with consistent formatting
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Log a message with timestamp and process ID
|
|
8
|
-
* @param {...any} args - Arguments to log (objects will be JSON stringified)
|
|
9
|
-
*/
|
|
10
|
-
export function log(...args) {
|
|
11
|
-
args = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : arg);
|
|
12
|
-
const msg = args.join(' ');
|
|
13
|
-
console.log(new Date(), `| ${process.pid} | #> ${msg}`);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Log operation types for consistency
|
|
18
|
-
*/
|
|
19
|
-
export const LogTypes = {
|
|
20
|
-
// Message operations
|
|
21
|
-
PUSH: 'PUSH',
|
|
22
|
-
POP: 'POP',
|
|
23
|
-
POP_FILTERED: 'POP_FILTERED',
|
|
24
|
-
ACK: 'ACK',
|
|
25
|
-
ACK_BATCH: 'ACK_BATCH',
|
|
26
|
-
|
|
27
|
-
// Message state changes
|
|
28
|
-
DELETE: 'DELETE',
|
|
29
|
-
RETRY: 'RETRY',
|
|
30
|
-
RETRY_SCHEDULED: 'RETRY_SCHEDULED',
|
|
31
|
-
MOVED_TO_DLQ: 'MOVED_TO_DLQ',
|
|
32
|
-
MANUAL_DLQ: 'MANUAL_DLQ',
|
|
33
|
-
|
|
34
|
-
// Queue operations
|
|
35
|
-
CLEAR_QUEUE: 'CLEAR_QUEUE',
|
|
36
|
-
CONFIGURE_QUEUE: 'CONFIGURE_QUEUE',
|
|
37
|
-
|
|
38
|
-
// System operations
|
|
39
|
-
EVICTION: 'EVICTION',
|
|
40
|
-
RETENTION: 'RETENTION',
|
|
41
|
-
LEASE_EXPIRED: 'LEASE_EXPIRED'
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export default log;
|