pp-command-bus 1.4.0 → 2.0.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 +402 -1113
- package/dist/command-bus/command-bus.spec.js +144 -370
- package/dist/command-bus/command-bus.spec.js.map +1 -1
- package/dist/command-bus/command.d.ts +23 -5
- package/dist/command-bus/command.js +20 -34
- package/dist/command-bus/command.js.map +1 -1
- package/dist/command-bus/config/command-bus-config.d.ts +75 -21
- package/dist/command-bus/config/command-bus-config.js +99 -58
- package/dist/command-bus/config/command-bus-config.js.map +1 -1
- package/dist/command-bus/config/command-bus-config.spec.js +174 -100
- package/dist/command-bus/config/command-bus-config.spec.js.map +1 -1
- package/dist/command-bus/index.d.ts +39 -52
- package/dist/command-bus/index.js +133 -126
- package/dist/command-bus/index.js.map +1 -1
- package/dist/command-bus/logging/command-logger.d.ts +2 -0
- package/dist/command-bus/logging/command-logger.js +7 -0
- package/dist/command-bus/logging/command-logger.js.map +1 -1
- package/dist/command-bus/logging/command-logger.spec.js +49 -14
- package/dist/command-bus/logging/command-logger.spec.js.map +1 -1
- package/dist/command-bus/serialization/index.d.ts +6 -0
- package/dist/command-bus/serialization/index.js +9 -0
- package/dist/command-bus/serialization/index.js.map +1 -0
- package/dist/command-bus/serialization/msgpack-serializer.d.ts +26 -0
- package/dist/command-bus/serialization/msgpack-serializer.js +70 -0
- package/dist/command-bus/serialization/msgpack-serializer.js.map +1 -0
- package/dist/command-bus/serialization/msgpack-serializer.spec.js +223 -0
- package/dist/command-bus/serialization/msgpack-serializer.spec.js.map +1 -0
- package/dist/command-bus/serialization/serializer.interface.d.ts +21 -0
- package/dist/command-bus/serialization/serializer.interface.js +3 -0
- package/dist/command-bus/serialization/serializer.interface.js.map +1 -0
- package/dist/command-bus/transport/consumer-loop.d.ts +45 -0
- package/dist/command-bus/transport/consumer-loop.js +90 -0
- package/dist/command-bus/transport/consumer-loop.js.map +1 -0
- package/dist/command-bus/transport/consumer-loop.spec.js +216 -0
- package/dist/command-bus/transport/consumer-loop.spec.js.map +1 -0
- package/dist/command-bus/transport/index.d.ts +21 -0
- package/dist/command-bus/transport/index.js +23 -0
- package/dist/command-bus/transport/index.js.map +1 -0
- package/dist/command-bus/transport/message-processor.d.ts +59 -0
- package/dist/command-bus/transport/message-processor.js +111 -0
- package/dist/command-bus/transport/message-processor.js.map +1 -0
- package/dist/command-bus/transport/message-processor.spec.js +185 -0
- package/dist/command-bus/transport/message-processor.spec.js.map +1 -0
- package/dist/command-bus/transport/pending-recovery.d.ts +54 -0
- package/dist/command-bus/transport/pending-recovery.js +139 -0
- package/dist/command-bus/transport/pending-recovery.js.map +1 -0
- package/dist/command-bus/transport/pending-recovery.spec.js +176 -0
- package/dist/command-bus/transport/pending-recovery.spec.js.map +1 -0
- package/dist/command-bus/transport/redis-codec.d.ts +24 -0
- package/dist/command-bus/transport/redis-codec.js +33 -0
- package/dist/command-bus/transport/redis-codec.js.map +1 -0
- package/dist/command-bus/transport/redis-codec.spec.js +53 -0
- package/dist/command-bus/transport/redis-codec.spec.js.map +1 -0
- package/dist/command-bus/transport/redis-streams-transport.d.ts +91 -0
- package/dist/command-bus/transport/redis-streams-transport.js +134 -0
- package/dist/command-bus/transport/redis-streams-transport.js.map +1 -0
- package/dist/command-bus/transport/redis-streams-transport.spec.js +420 -0
- package/dist/command-bus/transport/redis-streams-transport.spec.js.map +1 -0
- package/dist/command-bus/transport/rpc-handler.d.ts +39 -0
- package/dist/command-bus/transport/rpc-handler.js +87 -0
- package/dist/command-bus/transport/rpc-handler.js.map +1 -0
- package/dist/command-bus/transport/rpc-handler.spec.js +157 -0
- package/dist/command-bus/transport/rpc-handler.spec.js.map +1 -0
- package/dist/command-bus/transport/stream-consumer.d.ts +89 -0
- package/dist/command-bus/transport/stream-consumer.js +181 -0
- package/dist/command-bus/transport/stream-consumer.js.map +1 -0
- package/dist/command-bus/transport/stream-consumer.spec.js +284 -0
- package/dist/command-bus/transport/stream-consumer.spec.js.map +1 -0
- package/dist/command-bus/transport/stream-producer.d.ts +23 -0
- package/dist/command-bus/transport/stream-producer.js +70 -0
- package/dist/command-bus/transport/stream-producer.js.map +1 -0
- package/dist/command-bus/transport/stream-producer.spec.js +125 -0
- package/dist/command-bus/transport/stream-producer.spec.js.map +1 -0
- package/dist/command-bus/transport/transport.interface.d.ts +87 -0
- package/dist/command-bus/transport/transport.interface.js +3 -0
- package/dist/command-bus/transport/transport.interface.js.map +1 -0
- package/dist/command-bus/types/index.d.ts +9 -80
- package/dist/examples/rpc-throughput.demo.js +24 -22
- package/dist/examples/rpc-throughput.demo.js.map +1 -1
- package/dist/examples/rpc.demo.js +47 -53
- package/dist/examples/rpc.demo.js.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/pp-command-bus-2.0.0.tgz +0 -0
- package/dist/shared/redis/connection-pool.d.ts +54 -0
- package/dist/shared/redis/connection-pool.js +117 -0
- package/dist/shared/redis/connection-pool.js.map +1 -0
- package/dist/shared/redis/connection-pool.spec.js +114 -0
- package/dist/shared/redis/connection-pool.spec.js.map +1 -0
- package/dist/shared/redis/index.d.ts +5 -3
- package/dist/shared/redis/index.js +6 -4
- package/dist/shared/redis/index.js.map +1 -1
- package/dist/shared/redis/rpc-connection-pool.d.ts +61 -0
- package/dist/shared/redis/rpc-connection-pool.js +154 -0
- package/dist/shared/redis/rpc-connection-pool.js.map +1 -0
- package/dist/shared/redis/rpc-connection-pool.spec.d.ts +1 -0
- package/dist/shared/redis/rpc-connection-pool.spec.js +173 -0
- package/dist/shared/redis/rpc-connection-pool.spec.js.map +1 -0
- package/dist/shared/types.d.ts +0 -4
- package/dist/shared/utils/error-utils.d.ts +8 -0
- package/dist/shared/utils/error-utils.js +14 -0
- package/dist/shared/utils/error-utils.js.map +1 -0
- package/package.json +12 -12
- package/dist/command-bus/config/auto-config-optimizer.d.ts +0 -35
- package/dist/command-bus/config/auto-config-optimizer.js +0 -52
- package/dist/command-bus/config/auto-config-optimizer.js.map +0 -1
- package/dist/command-bus/config/auto-config-optimizer.spec.js +0 -42
- package/dist/command-bus/config/auto-config-optimizer.spec.js.map +0 -1
- package/dist/command-bus/job/index.d.ts +0 -6
- package/dist/command-bus/job/index.js +0 -15
- package/dist/command-bus/job/index.js.map +0 -1
- package/dist/command-bus/job/job-options-builder.d.ts +0 -21
- package/dist/command-bus/job/job-options-builder.js +0 -58
- package/dist/command-bus/job/job-options-builder.js.map +0 -1
- package/dist/command-bus/job/job-options-builder.spec.js +0 -156
- package/dist/command-bus/job/job-options-builder.spec.js.map +0 -1
- package/dist/command-bus/job/job-processor.d.ts +0 -39
- package/dist/command-bus/job/job-processor.js +0 -203
- package/dist/command-bus/job/job-processor.js.map +0 -1
- package/dist/command-bus/job/job-processor.spec.js +0 -437
- package/dist/command-bus/job/job-processor.spec.js.map +0 -1
- package/dist/command-bus/queue/index.d.ts +0 -5
- package/dist/command-bus/queue/index.js +0 -13
- package/dist/command-bus/queue/index.js.map +0 -1
- package/dist/command-bus/queue/queue-manager.d.ts +0 -56
- package/dist/command-bus/queue/queue-manager.js +0 -163
- package/dist/command-bus/queue/queue-manager.js.map +0 -1
- package/dist/command-bus/queue/queue-manager.spec.js +0 -371
- package/dist/command-bus/queue/queue-manager.spec.js.map +0 -1
- package/dist/command-bus/rpc/index.d.ts +0 -11
- package/dist/command-bus/rpc/index.js +0 -19
- package/dist/command-bus/rpc/index.js.map +0 -1
- package/dist/command-bus/rpc/payload-compression.service.d.ts +0 -51
- package/dist/command-bus/rpc/payload-compression.service.js +0 -218
- package/dist/command-bus/rpc/payload-compression.service.js.map +0 -1
- package/dist/command-bus/rpc/payload-compression.service.spec.js +0 -379
- package/dist/command-bus/rpc/payload-compression.service.spec.js.map +0 -1
- package/dist/command-bus/rpc/rpc-coordinator.d.ts +0 -96
- package/dist/command-bus/rpc/rpc-coordinator.js +0 -500
- package/dist/command-bus/rpc/rpc-coordinator.js.map +0 -1
- package/dist/command-bus/rpc/rpc-coordinator.spec.js +0 -622
- package/dist/command-bus/rpc/rpc-coordinator.spec.js.map +0 -1
- package/dist/command-bus/rpc/rpc-job-cancellation.service.d.ts +0 -82
- package/dist/command-bus/rpc/rpc-job-cancellation.service.js +0 -180
- package/dist/command-bus/rpc/rpc-job-cancellation.service.js.map +0 -1
- package/dist/command-bus/rpc/rpc-job-cancellation.service.spec.js +0 -286
- package/dist/command-bus/rpc/rpc-job-cancellation.service.spec.js.map +0 -1
- package/dist/command-bus/worker/index.d.ts +0 -10
- package/dist/command-bus/worker/index.js +0 -19
- package/dist/command-bus/worker/index.js.map +0 -1
- package/dist/command-bus/worker/worker-benchmark.d.ts +0 -71
- package/dist/command-bus/worker/worker-benchmark.js +0 -203
- package/dist/command-bus/worker/worker-benchmark.js.map +0 -1
- package/dist/command-bus/worker/worker-benchmark.spec.js +0 -310
- package/dist/command-bus/worker/worker-benchmark.spec.js.map +0 -1
- package/dist/command-bus/worker/worker-metrics-collector.d.ts +0 -98
- package/dist/command-bus/worker/worker-metrics-collector.js +0 -242
- package/dist/command-bus/worker/worker-metrics-collector.js.map +0 -1
- package/dist/command-bus/worker/worker-orchestrator.d.ts +0 -70
- package/dist/command-bus/worker/worker-orchestrator.js +0 -339
- package/dist/command-bus/worker/worker-orchestrator.js.map +0 -1
- package/dist/command-bus/worker/worker-orchestrator.spec.js +0 -712
- package/dist/command-bus/worker/worker-orchestrator.spec.js.map +0 -1
- package/dist/examples/auto-config.demo.d.ts +0 -9
- package/dist/examples/auto-config.demo.js +0 -106
- package/dist/examples/auto-config.demo.js.map +0 -1
- package/dist/examples/rpc-compression.demo.d.ts +0 -5
- package/dist/examples/rpc-compression.demo.js +0 -363
- package/dist/examples/rpc-compression.demo.js.map +0 -1
- package/dist/examples/rpc-resilience.demo.d.ts +0 -11
- package/dist/examples/rpc-resilience.demo.js +0 -235
- package/dist/examples/rpc-resilience.demo.js.map +0 -1
- package/dist/pp-command-bus-1.4.0.tgz +0 -0
- package/dist/shared/config/base-config.d.ts +0 -54
- package/dist/shared/config/base-config.js +0 -114
- package/dist/shared/config/base-config.js.map +0 -1
- package/dist/shared/config/base-config.spec.js +0 -204
- package/dist/shared/config/base-config.spec.js.map +0 -1
- package/dist/shared/config/index.d.ts +0 -1
- package/dist/shared/config/index.js +0 -9
- package/dist/shared/config/index.js.map +0 -1
- package/dist/shared/redis/redis-connection-factory.d.ts +0 -66
- package/dist/shared/redis/redis-connection-factory.js +0 -113
- package/dist/shared/redis/redis-connection-factory.js.map +0 -1
- /package/dist/command-bus/{config/auto-config-optimizer.spec.d.ts → serialization/msgpack-serializer.spec.d.ts} +0 -0
- /package/dist/command-bus/{job/job-options-builder.spec.d.ts → transport/consumer-loop.spec.d.ts} +0 -0
- /package/dist/command-bus/{job/job-processor.spec.d.ts → transport/message-processor.spec.d.ts} +0 -0
- /package/dist/command-bus/{queue/queue-manager.spec.d.ts → transport/pending-recovery.spec.d.ts} +0 -0
- /package/dist/command-bus/{rpc/payload-compression.service.spec.d.ts → transport/redis-codec.spec.d.ts} +0 -0
- /package/dist/command-bus/{rpc/rpc-coordinator.spec.d.ts → transport/redis-streams-transport.spec.d.ts} +0 -0
- /package/dist/command-bus/{rpc/rpc-job-cancellation.service.spec.d.ts → transport/rpc-handler.spec.d.ts} +0 -0
- /package/dist/command-bus/{worker/worker-benchmark.spec.d.ts → transport/stream-consumer.spec.d.ts} +0 -0
- /package/dist/command-bus/{worker/worker-orchestrator.spec.d.ts → transport/stream-producer.spec.d.ts} +0 -0
- /package/dist/shared/{config/base-config.spec.d.ts → redis/connection-pool.spec.d.ts} +0 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const consumer_loop_1 = require("./consumer-loop");
|
|
13
|
+
/**
|
|
14
|
+
* Mock helpers
|
|
15
|
+
*/
|
|
16
|
+
function createMockLogger() {
|
|
17
|
+
return {
|
|
18
|
+
log: jest.fn(),
|
|
19
|
+
error: jest.fn(),
|
|
20
|
+
warn: jest.fn(),
|
|
21
|
+
debug: jest.fn(),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Tworzy mock Redis connection z disconnect() który przerywa blocking operations
|
|
26
|
+
*/
|
|
27
|
+
function createMockConnection() {
|
|
28
|
+
const pendingRejects = [];
|
|
29
|
+
return {
|
|
30
|
+
xreadgroup: jest.fn().mockImplementation(() => new Promise((_resolve, reject) => {
|
|
31
|
+
pendingRejects.push(reject);
|
|
32
|
+
})),
|
|
33
|
+
disconnect: jest.fn().mockImplementation(() => {
|
|
34
|
+
const disconnectError = new Error('Connection is closed.');
|
|
35
|
+
while (pendingRejects.length > 0) {
|
|
36
|
+
const reject = pendingRejects.shift();
|
|
37
|
+
if (reject)
|
|
38
|
+
reject(disconnectError);
|
|
39
|
+
}
|
|
40
|
+
}),
|
|
41
|
+
_pendingRejects: pendingRejects,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
describe('ConsumerLoop', () => {
|
|
45
|
+
let loop;
|
|
46
|
+
let logger;
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
jest.clearAllMocks();
|
|
49
|
+
logger = createMockLogger();
|
|
50
|
+
loop = new consumer_loop_1.ConsumerLoop({
|
|
51
|
+
consumerId: 'test-consumer',
|
|
52
|
+
batchSize: 10,
|
|
53
|
+
concurrency: 5,
|
|
54
|
+
logger,
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
afterEach(() => {
|
|
58
|
+
loop.running = false;
|
|
59
|
+
});
|
|
60
|
+
describe('run()', () => {
|
|
61
|
+
it('powinien wywołać XREADGROUP z prawidłowymi parametrami', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
62
|
+
const conn = createMockConnection();
|
|
63
|
+
const onMessage = jest.fn().mockResolvedValue(undefined);
|
|
64
|
+
// XREADGROUP zwraca jedną wiadomość, potem blocking
|
|
65
|
+
conn.xreadgroup.mockResolvedValueOnce([['cmd:Test', [['msg-1', ['data', 'payload']]]]]);
|
|
66
|
+
const runPromise = loop.run(conn, 'cmd:Test', 'workers', onMessage);
|
|
67
|
+
// Poczekaj na przetworzenie pierwszej wiadomości
|
|
68
|
+
yield new Promise((r) => setTimeout(r, 10));
|
|
69
|
+
// Zatrzymaj loop
|
|
70
|
+
loop.running = false;
|
|
71
|
+
conn.disconnect();
|
|
72
|
+
yield runPromise;
|
|
73
|
+
expect(conn.xreadgroup).toHaveBeenCalledWith('GROUP', 'workers', 'test-consumer', 'COUNT', expect.any(Number), 'BLOCK', 5000, 'STREAMS', 'cmd:Test', '>');
|
|
74
|
+
}));
|
|
75
|
+
it('powinien wywołać onMessage dla każdej wiadomości', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
76
|
+
const conn = createMockConnection();
|
|
77
|
+
const onMessage = jest.fn().mockResolvedValue(undefined);
|
|
78
|
+
conn.xreadgroup.mockResolvedValueOnce([
|
|
79
|
+
[
|
|
80
|
+
'cmd:Test',
|
|
81
|
+
[
|
|
82
|
+
['msg-1', ['data', 'payload1']],
|
|
83
|
+
['msg-2', ['data', 'payload2']],
|
|
84
|
+
],
|
|
85
|
+
],
|
|
86
|
+
]);
|
|
87
|
+
const runPromise = loop.run(conn, 'cmd:Test', 'workers', onMessage);
|
|
88
|
+
yield new Promise((r) => setTimeout(r, 10));
|
|
89
|
+
loop.running = false;
|
|
90
|
+
conn.disconnect();
|
|
91
|
+
yield runPromise;
|
|
92
|
+
expect(onMessage).toHaveBeenCalledTimes(2);
|
|
93
|
+
expect(onMessage).toHaveBeenCalledWith('msg-1', ['data', 'payload1']);
|
|
94
|
+
expect(onMessage).toHaveBeenCalledWith('msg-2', ['data', 'payload2']);
|
|
95
|
+
}));
|
|
96
|
+
it('powinien zakończyć gracefully gdy running = false', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
97
|
+
const conn = createMockConnection();
|
|
98
|
+
const onMessage = jest.fn().mockResolvedValue(undefined);
|
|
99
|
+
loop.running = false;
|
|
100
|
+
const runPromise = loop.run(conn, 'cmd:Test', 'workers', onMessage);
|
|
101
|
+
yield runPromise;
|
|
102
|
+
expect(onMessage).not.toHaveBeenCalled();
|
|
103
|
+
}));
|
|
104
|
+
it('powinien czekać na aktywne zadania przy shutdown', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
105
|
+
const conn = createMockConnection();
|
|
106
|
+
let handlerResolve;
|
|
107
|
+
const handlerPromise = new Promise((resolve) => {
|
|
108
|
+
handlerResolve = resolve;
|
|
109
|
+
});
|
|
110
|
+
const onMessage = jest.fn().mockReturnValueOnce(handlerPromise);
|
|
111
|
+
conn.xreadgroup.mockResolvedValueOnce([['cmd:Test', [['msg-1', ['data', 'payload']]]]]);
|
|
112
|
+
const runPromise = loop.run(conn, 'cmd:Test', 'workers', onMessage);
|
|
113
|
+
// Poczekaj na start przetwarzania
|
|
114
|
+
yield new Promise((r) => setTimeout(r, 10));
|
|
115
|
+
// Zatrzymaj loop (ale handler jeszcze pracuje)
|
|
116
|
+
loop.running = false;
|
|
117
|
+
conn.disconnect();
|
|
118
|
+
// Handler jeszcze nie zakończony — run() powinno czekać
|
|
119
|
+
const raceResult = yield Promise.race([
|
|
120
|
+
runPromise.then(() => 'finished'),
|
|
121
|
+
new Promise((r) => setTimeout(r, 50)).then(() => 'timeout'),
|
|
122
|
+
]);
|
|
123
|
+
expect(raceResult).toBe('timeout');
|
|
124
|
+
// Zakończ handler
|
|
125
|
+
handlerResolve();
|
|
126
|
+
yield runPromise;
|
|
127
|
+
}));
|
|
128
|
+
it('powinien zalogować błąd i kontynuować po błędzie XREADGROUP', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
129
|
+
jest.useFakeTimers();
|
|
130
|
+
const conn = createMockConnection();
|
|
131
|
+
const onMessage = jest.fn().mockResolvedValue(undefined);
|
|
132
|
+
// Pierwszy call — error, drugi — null (timeout), trzeci — zatrzymujemy
|
|
133
|
+
conn.xreadgroup.mockRejectedValueOnce(new Error('Redis error')).mockResolvedValueOnce(null);
|
|
134
|
+
const runPromise = loop.run(conn, 'cmd:Test', 'workers', onMessage);
|
|
135
|
+
// Advance timer past backoff
|
|
136
|
+
yield jest.advanceTimersByTimeAsync(2000);
|
|
137
|
+
// Zatrzymaj loop
|
|
138
|
+
loop.running = false;
|
|
139
|
+
conn.disconnect();
|
|
140
|
+
yield jest.advanceTimersByTimeAsync(1000);
|
|
141
|
+
yield runPromise;
|
|
142
|
+
expect(logger.error).toHaveBeenCalledWith('Błąd w consumer loop', expect.objectContaining({
|
|
143
|
+
error: 'Redis error',
|
|
144
|
+
consecutiveErrors: 1,
|
|
145
|
+
}));
|
|
146
|
+
jest.useRealTimers();
|
|
147
|
+
}));
|
|
148
|
+
it('powinien stosować exponential backoff przy kolejnych błędach', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
149
|
+
jest.useFakeTimers();
|
|
150
|
+
const conn = createMockConnection();
|
|
151
|
+
const onMessage = jest.fn().mockResolvedValue(undefined);
|
|
152
|
+
// 3 kolejne błędy
|
|
153
|
+
conn.xreadgroup
|
|
154
|
+
.mockRejectedValueOnce(new Error('error 1'))
|
|
155
|
+
.mockRejectedValueOnce(new Error('error 2'))
|
|
156
|
+
.mockRejectedValueOnce(new Error('error 3'));
|
|
157
|
+
const runPromise = loop.run(conn, 'cmd:Test', 'workers', onMessage);
|
|
158
|
+
// Advance past first backoff (~1s + jitter)
|
|
159
|
+
yield jest.advanceTimersByTimeAsync(2500);
|
|
160
|
+
// Advance past second backoff (~2s + jitter)
|
|
161
|
+
yield jest.advanceTimersByTimeAsync(3500);
|
|
162
|
+
// Advance past third backoff (~4s + jitter)
|
|
163
|
+
yield jest.advanceTimersByTimeAsync(5500);
|
|
164
|
+
loop.running = false;
|
|
165
|
+
conn.disconnect();
|
|
166
|
+
yield jest.advanceTimersByTimeAsync(1000);
|
|
167
|
+
yield runPromise;
|
|
168
|
+
expect(logger.error).toHaveBeenCalledTimes(3);
|
|
169
|
+
jest.useRealTimers();
|
|
170
|
+
}));
|
|
171
|
+
it('powinien zakończyć od razu przy błędzie gdy running = false', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
172
|
+
const conn = createMockConnection();
|
|
173
|
+
const onMessage = jest.fn().mockResolvedValue(undefined);
|
|
174
|
+
// Symuluj disconnect error
|
|
175
|
+
conn.xreadgroup.mockRejectedValueOnce(new Error('Connection is closed.'));
|
|
176
|
+
loop.running = false;
|
|
177
|
+
const runPromise = loop.run(conn, 'cmd:Test', 'workers', onMessage);
|
|
178
|
+
yield runPromise;
|
|
179
|
+
// Nie powinien logować błędu (running = false = graceful shutdown)
|
|
180
|
+
expect(logger.error).not.toHaveBeenCalled();
|
|
181
|
+
}));
|
|
182
|
+
it('powinien limitować concurrency do skonfigurowanego limitu', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
183
|
+
const smallLoop = new consumer_loop_1.ConsumerLoop({
|
|
184
|
+
consumerId: 'test-consumer',
|
|
185
|
+
batchSize: 10,
|
|
186
|
+
concurrency: 2,
|
|
187
|
+
logger,
|
|
188
|
+
});
|
|
189
|
+
const conn = createMockConnection();
|
|
190
|
+
const handlerResolves = [];
|
|
191
|
+
const onMessage = jest.fn().mockImplementation(() => new Promise((resolve) => {
|
|
192
|
+
handlerResolves.push(resolve);
|
|
193
|
+
}));
|
|
194
|
+
// Zwróć 5 wiadomości naraz
|
|
195
|
+
conn.xreadgroup.mockResolvedValueOnce([
|
|
196
|
+
[
|
|
197
|
+
'cmd:Test',
|
|
198
|
+
[
|
|
199
|
+
['msg-1', ['data', 'p1']],
|
|
200
|
+
['msg-2', ['data', 'p2']],
|
|
201
|
+
],
|
|
202
|
+
],
|
|
203
|
+
]);
|
|
204
|
+
const runPromise = smallLoop.run(conn, 'cmd:Test', 'workers', onMessage);
|
|
205
|
+
yield new Promise((r) => setTimeout(r, 20));
|
|
206
|
+
// Callback powinien być wywołany 2 razy (concurrency = 2)
|
|
207
|
+
expect(onMessage).toHaveBeenCalledTimes(2);
|
|
208
|
+
// Cleanup
|
|
209
|
+
handlerResolves.forEach((r) => r());
|
|
210
|
+
smallLoop.running = false;
|
|
211
|
+
conn.disconnect();
|
|
212
|
+
yield runPromise;
|
|
213
|
+
}));
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
//# sourceMappingURL=consumer-loop.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consumer-loop.spec.js","sourceRoot":"","sources":["../../../src/command-bus/transport/consumer-loop.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Moduł transportu dla CommandBus
|
|
3
|
+
*
|
|
4
|
+
* Eksportuje interfejsy segregowane (ISP) i fasadę RedisStreamsTransport
|
|
5
|
+
* oraz poszczególne komponenty dla zaawansowanego użycia
|
|
6
|
+
*/
|
|
7
|
+
export type { ITransport, IStreamProducer, IStreamConsumer, IRpcTransport, IClosable, ConsumerHandler, RpcMessageMetadata, } from './transport.interface';
|
|
8
|
+
export { default as RedisStreamsTransport } from './redis-streams-transport';
|
|
9
|
+
export type { RedisStreamsTransportOptions } from './redis-streams-transport';
|
|
10
|
+
export { RedisCodec } from './redis-codec';
|
|
11
|
+
export { StreamProducer } from './stream-producer';
|
|
12
|
+
export { StreamConsumer } from './stream-consumer';
|
|
13
|
+
export type { StreamConsumerOptions } from './stream-consumer';
|
|
14
|
+
export { MessageProcessor } from './message-processor';
|
|
15
|
+
export type { MessageProcessorOptions, RpcResponder } from './message-processor';
|
|
16
|
+
export { ConsumerLoop } from './consumer-loop';
|
|
17
|
+
export type { ConsumerLoopOptions, MessageCallback } from './consumer-loop';
|
|
18
|
+
export { RpcHandler } from './rpc-handler';
|
|
19
|
+
export type { RpcEnvelope } from './rpc-handler';
|
|
20
|
+
export { PendingRecovery } from './pending-recovery';
|
|
21
|
+
export type { PendingRecoveryOptions } from './pending-recovery';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.PendingRecovery = exports.RpcHandler = exports.ConsumerLoop = exports.MessageProcessor = exports.StreamConsumer = exports.StreamProducer = exports.RedisCodec = exports.RedisStreamsTransport = void 0;
|
|
7
|
+
var redis_streams_transport_1 = require("./redis-streams-transport");
|
|
8
|
+
Object.defineProperty(exports, "RedisStreamsTransport", { enumerable: true, get: function () { return __importDefault(redis_streams_transport_1).default; } });
|
|
9
|
+
var redis_codec_1 = require("./redis-codec");
|
|
10
|
+
Object.defineProperty(exports, "RedisCodec", { enumerable: true, get: function () { return redis_codec_1.RedisCodec; } });
|
|
11
|
+
var stream_producer_1 = require("./stream-producer");
|
|
12
|
+
Object.defineProperty(exports, "StreamProducer", { enumerable: true, get: function () { return stream_producer_1.StreamProducer; } });
|
|
13
|
+
var stream_consumer_1 = require("./stream-consumer");
|
|
14
|
+
Object.defineProperty(exports, "StreamConsumer", { enumerable: true, get: function () { return stream_consumer_1.StreamConsumer; } });
|
|
15
|
+
var message_processor_1 = require("./message-processor");
|
|
16
|
+
Object.defineProperty(exports, "MessageProcessor", { enumerable: true, get: function () { return message_processor_1.MessageProcessor; } });
|
|
17
|
+
var consumer_loop_1 = require("./consumer-loop");
|
|
18
|
+
Object.defineProperty(exports, "ConsumerLoop", { enumerable: true, get: function () { return consumer_loop_1.ConsumerLoop; } });
|
|
19
|
+
var rpc_handler_1 = require("./rpc-handler");
|
|
20
|
+
Object.defineProperty(exports, "RpcHandler", { enumerable: true, get: function () { return rpc_handler_1.RpcHandler; } });
|
|
21
|
+
var pending_recovery_1 = require("./pending-recovery");
|
|
22
|
+
Object.defineProperty(exports, "PendingRecovery", { enumerable: true, get: function () { return pending_recovery_1.PendingRecovery; } });
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/command-bus/transport/index.ts"],"names":[],"mappings":";;;;;;AAeA,qEAA6E;AAApE,iJAAA,OAAO,OAAyB;AAEzC,6CAA2C;AAAlC,yGAAA,UAAU,OAAA;AACnB,qDAAmD;AAA1C,iHAAA,cAAc,OAAA;AACvB,qDAAmD;AAA1C,iHAAA,cAAc,OAAA;AAEvB,yDAAuD;AAA9C,qHAAA,gBAAgB,OAAA;AAEzB,iDAA+C;AAAtC,6GAAA,YAAY,OAAA;AAErB,6CAA2C;AAAlC,yGAAA,UAAU,OAAA;AAEnB,uDAAqD;AAA5C,mHAAA,eAAe,OAAA"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { ILogger } from '../../shared/types';
|
|
2
|
+
import type { ISerializer } from '../serialization/serializer.interface';
|
|
3
|
+
import type { ConsumerHandler } from './transport.interface';
|
|
4
|
+
import type RedisConnectionPool from '../../shared/redis/connection-pool';
|
|
5
|
+
/**
|
|
6
|
+
* Typ callbacka do wysyłania odpowiedzi RPC
|
|
7
|
+
*/
|
|
8
|
+
export type RpcResponder = (responseKey: string, data: Buffer, ttl: number) => Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Konfiguracja MessageProcessor
|
|
11
|
+
*/
|
|
12
|
+
export interface MessageProcessorOptions {
|
|
13
|
+
pool: RedisConnectionPool;
|
|
14
|
+
serializer: ISerializer;
|
|
15
|
+
logger: ILogger;
|
|
16
|
+
batchSize: number;
|
|
17
|
+
maxRetained: number;
|
|
18
|
+
rpcRespond: RpcResponder;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Procesor wiadomości — parsuje, waliduje, przetwarza i potwierdza wiadomości
|
|
22
|
+
*
|
|
23
|
+
* Odpowiedzialności:
|
|
24
|
+
* - Parsowanie fields → fieldMap
|
|
25
|
+
* - Walidacja danych wejściowych (H4)
|
|
26
|
+
* - Detekcja RPC via marker field
|
|
27
|
+
* - Wywołanie handlera
|
|
28
|
+
* - XACK + throttled XTRIM (H2: per-stream counters)
|
|
29
|
+
* - Wysyłanie odpowiedzi RPC
|
|
30
|
+
*
|
|
31
|
+
* NIE odpowiada za deduplicację — to odpowiedzialność koordynatora (StreamConsumer)
|
|
32
|
+
*/
|
|
33
|
+
export declare class MessageProcessor {
|
|
34
|
+
private readonly pool;
|
|
35
|
+
private readonly serializer;
|
|
36
|
+
private readonly logger;
|
|
37
|
+
private readonly batchSize;
|
|
38
|
+
private readonly maxRetained;
|
|
39
|
+
private readonly rpcRespond;
|
|
40
|
+
/** H2: Liczniki wiadomości od ostatniego XTRIM — per strumień */
|
|
41
|
+
private readonly xtrimCounters;
|
|
42
|
+
constructor(options: MessageProcessorOptions);
|
|
43
|
+
/**
|
|
44
|
+
* Czyści stan counters — wywoływane przy close()
|
|
45
|
+
*/
|
|
46
|
+
reset(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Przetwarza wiadomość — parsuje fields, wykrywa RPC, wywołuje handler, XACK/XTRIM
|
|
49
|
+
*
|
|
50
|
+
* Błędy propagują do callera (koordynatora) — nie łapie wewnętrznie
|
|
51
|
+
*
|
|
52
|
+
* @param streamName - Nazwa strumienia Redis
|
|
53
|
+
* @param groupName - Nazwa grupy konsumentów
|
|
54
|
+
* @param messageId - ID wiadomości
|
|
55
|
+
* @param fields - Tablica pól ze strumienia [key, value, key, value, ...]
|
|
56
|
+
* @param handler - Handler przetwarzający dane komendy
|
|
57
|
+
*/
|
|
58
|
+
process(streamName: string, groupName: string, messageId: string, fields: string[], handler: ConsumerHandler): Promise<void>;
|
|
59
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.MessageProcessor = void 0;
|
|
13
|
+
const redis_codec_1 = require("./redis-codec");
|
|
14
|
+
/**
|
|
15
|
+
* Procesor wiadomości — parsuje, waliduje, przetwarza i potwierdza wiadomości
|
|
16
|
+
*
|
|
17
|
+
* Odpowiedzialności:
|
|
18
|
+
* - Parsowanie fields → fieldMap
|
|
19
|
+
* - Walidacja danych wejściowych (H4)
|
|
20
|
+
* - Detekcja RPC via marker field
|
|
21
|
+
* - Wywołanie handlera
|
|
22
|
+
* - XACK + throttled XTRIM (H2: per-stream counters)
|
|
23
|
+
* - Wysyłanie odpowiedzi RPC
|
|
24
|
+
*
|
|
25
|
+
* NIE odpowiada za deduplicację — to odpowiedzialność koordynatora (StreamConsumer)
|
|
26
|
+
*/
|
|
27
|
+
class MessageProcessor {
|
|
28
|
+
constructor(options) {
|
|
29
|
+
/** H2: Liczniki wiadomości od ostatniego XTRIM — per strumień */
|
|
30
|
+
this.xtrimCounters = new Map();
|
|
31
|
+
this.pool = options.pool;
|
|
32
|
+
this.serializer = options.serializer;
|
|
33
|
+
this.logger = options.logger;
|
|
34
|
+
this.batchSize = options.batchSize;
|
|
35
|
+
this.maxRetained = options.maxRetained;
|
|
36
|
+
this.rpcRespond = options.rpcRespond;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Czyści stan counters — wywoływane przy close()
|
|
40
|
+
*/
|
|
41
|
+
reset() {
|
|
42
|
+
this.xtrimCounters.clear();
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Przetwarza wiadomość — parsuje fields, wykrywa RPC, wywołuje handler, XACK/XTRIM
|
|
46
|
+
*
|
|
47
|
+
* Błędy propagują do callera (koordynatora) — nie łapie wewnętrznie
|
|
48
|
+
*
|
|
49
|
+
* @param streamName - Nazwa strumienia Redis
|
|
50
|
+
* @param groupName - Nazwa grupy konsumentów
|
|
51
|
+
* @param messageId - ID wiadomości
|
|
52
|
+
* @param fields - Tablica pól ze strumienia [key, value, key, value, ...]
|
|
53
|
+
* @param handler - Handler przetwarzający dane komendy
|
|
54
|
+
*/
|
|
55
|
+
process(streamName, groupName, messageId, fields, handler) {
|
|
56
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
57
|
+
var _a;
|
|
58
|
+
// H4: Walidacja fields array — zapobiega crash na pustych/nieparzystych danych
|
|
59
|
+
if (!fields || fields.length < 2) {
|
|
60
|
+
this.logger.warn('Nieprawidłowa wiadomość — brak danych', { streamName, messageId });
|
|
61
|
+
yield this.pool.next().xack(streamName, groupName, messageId);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const fieldMap = new Map();
|
|
65
|
+
for (let i = 0; i < fields.length; i += 2) {
|
|
66
|
+
if (i + 1 < fields.length) {
|
|
67
|
+
fieldMap.set(fields[i], fields[i + 1]);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const rawData = fieldMap.get('data');
|
|
71
|
+
// H4: Walidacja rawData przed decode
|
|
72
|
+
if (rawData === undefined || rawData === null) {
|
|
73
|
+
this.logger.warn('Wiadomość bez pola data', { streamName, messageId });
|
|
74
|
+
yield this.pool.next().xack(streamName, groupName, messageId);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const dataBuffer = redis_codec_1.RedisCodec.decode(rawData);
|
|
78
|
+
let commandData;
|
|
79
|
+
let rpcMetadata;
|
|
80
|
+
if (fieldMap.has('rpc')) {
|
|
81
|
+
const envelope = this.serializer.deserialize(dataBuffer);
|
|
82
|
+
commandData = redis_codec_1.RedisCodec.decode(envelope.commandData);
|
|
83
|
+
rpcMetadata = envelope.rpc;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
commandData = dataBuffer;
|
|
87
|
+
}
|
|
88
|
+
const result = yield handler(commandData);
|
|
89
|
+
// H2: XACK + throttled XTRIM per strumień
|
|
90
|
+
const count = ((_a = this.xtrimCounters.get(streamName)) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
91
|
+
this.xtrimCounters.set(streamName, count);
|
|
92
|
+
const conn = this.pool.next();
|
|
93
|
+
if (count >= this.batchSize) {
|
|
94
|
+
const pipeline = conn.pipeline();
|
|
95
|
+
pipeline.xack(streamName, groupName, messageId);
|
|
96
|
+
pipeline.xtrim(streamName, 'MAXLEN', '~', this.maxRetained);
|
|
97
|
+
yield pipeline.exec();
|
|
98
|
+
this.xtrimCounters.set(streamName, 0);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
yield conn.xack(streamName, groupName, messageId);
|
|
102
|
+
}
|
|
103
|
+
if (rpcMetadata) {
|
|
104
|
+
const responseBuffer = this.serializer.serialize({ result, error: null });
|
|
105
|
+
yield this.rpcRespond(rpcMetadata.responseKey, responseBuffer, 60);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
exports.MessageProcessor = MessageProcessor;
|
|
111
|
+
//# sourceMappingURL=message-processor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-processor.js","sourceRoot":"","sources":["../../../src/command-bus/transport/message-processor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const message_processor_1 = require("./message-processor");
|
|
13
|
+
const redis_codec_1 = require("./redis-codec");
|
|
14
|
+
/**
|
|
15
|
+
* Mock helpers
|
|
16
|
+
*/
|
|
17
|
+
function createMockLogger() {
|
|
18
|
+
return {
|
|
19
|
+
log: jest.fn(),
|
|
20
|
+
error: jest.fn(),
|
|
21
|
+
warn: jest.fn(),
|
|
22
|
+
debug: jest.fn(),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function createMockSerializer() {
|
|
26
|
+
return {
|
|
27
|
+
serialize: jest.fn((data) => Buffer.from(JSON.stringify(data))),
|
|
28
|
+
deserialize: jest.fn((buf) => JSON.parse(buf.toString())),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function createMockConnection() {
|
|
32
|
+
return {
|
|
33
|
+
xack: jest.fn().mockResolvedValue(1),
|
|
34
|
+
pipeline: jest.fn().mockReturnValue({
|
|
35
|
+
xack: jest.fn().mockReturnThis(),
|
|
36
|
+
xtrim: jest.fn().mockReturnThis(),
|
|
37
|
+
exec: jest.fn().mockResolvedValue([
|
|
38
|
+
[null, 1],
|
|
39
|
+
[null, 0],
|
|
40
|
+
]),
|
|
41
|
+
}),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function createMockPool() {
|
|
45
|
+
const conn = createMockConnection();
|
|
46
|
+
return {
|
|
47
|
+
pool: { next: jest.fn().mockReturnValue(conn) },
|
|
48
|
+
conn,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
describe('MessageProcessor', () => {
|
|
52
|
+
let processor;
|
|
53
|
+
let logger;
|
|
54
|
+
let serializer;
|
|
55
|
+
let mockConn;
|
|
56
|
+
let mockPool;
|
|
57
|
+
const rpcRespond = jest.fn().mockResolvedValue(undefined);
|
|
58
|
+
beforeEach(() => {
|
|
59
|
+
jest.clearAllMocks();
|
|
60
|
+
logger = createMockLogger();
|
|
61
|
+
serializer = createMockSerializer();
|
|
62
|
+
const poolMock = createMockPool();
|
|
63
|
+
mockPool = poolMock.pool;
|
|
64
|
+
mockConn = poolMock.conn;
|
|
65
|
+
processor = new message_processor_1.MessageProcessor({
|
|
66
|
+
pool: mockPool,
|
|
67
|
+
serializer,
|
|
68
|
+
logger,
|
|
69
|
+
batchSize: 10,
|
|
70
|
+
maxRetained: 10000,
|
|
71
|
+
rpcRespond,
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
describe('process()', () => {
|
|
75
|
+
it('powinien przetworzyć wiadomość i wywołać handler', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
76
|
+
const handler = jest.fn().mockResolvedValue('ok');
|
|
77
|
+
const testPayload = redis_codec_1.RedisCodec.encode(Buffer.from('test-data'));
|
|
78
|
+
const fields = ['data', testPayload];
|
|
79
|
+
yield processor.process('cmd:Test', 'workers', 'msg-1', fields, handler);
|
|
80
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
81
|
+
expect(handler).toHaveBeenCalledWith(expect.any(Buffer));
|
|
82
|
+
}));
|
|
83
|
+
it('powinien XACK wiadomość po przetworzeniu', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
84
|
+
const handler = jest.fn().mockResolvedValue('ok');
|
|
85
|
+
const fields = ['data', redis_codec_1.RedisCodec.encode(Buffer.from('data'))];
|
|
86
|
+
yield processor.process('cmd:Test', 'workers', 'msg-1', fields, handler);
|
|
87
|
+
expect(mockConn.xack).toHaveBeenCalledWith('cmd:Test', 'workers', 'msg-1');
|
|
88
|
+
}));
|
|
89
|
+
it('powinien odrzucić wiadomość z pustym fields array (H4)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
90
|
+
const handler = jest.fn();
|
|
91
|
+
yield processor.process('cmd:Test', 'workers', 'msg-empty', [], handler);
|
|
92
|
+
expect(handler).not.toHaveBeenCalled();
|
|
93
|
+
expect(logger.warn).toHaveBeenCalledWith('Nieprawidłowa wiadomość — brak danych', expect.objectContaining({ messageId: 'msg-empty' }));
|
|
94
|
+
expect(mockConn.xack).toHaveBeenCalledWith('cmd:Test', 'workers', 'msg-empty');
|
|
95
|
+
}));
|
|
96
|
+
it('powinien odrzucić wiadomość z jednym elementem (H4)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
97
|
+
const handler = jest.fn();
|
|
98
|
+
yield processor.process('cmd:Test', 'workers', 'msg-odd', ['key-only'], handler);
|
|
99
|
+
expect(handler).not.toHaveBeenCalled();
|
|
100
|
+
expect(logger.warn).toHaveBeenCalledWith('Nieprawidłowa wiadomość — brak danych', expect.objectContaining({ messageId: 'msg-odd' }));
|
|
101
|
+
}));
|
|
102
|
+
it('powinien odrzucić wiadomość bez pola data (H4)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
103
|
+
const handler = jest.fn();
|
|
104
|
+
yield processor.process('cmd:Test', 'workers', 'msg-nodata', ['other', 'value'], handler);
|
|
105
|
+
expect(handler).not.toHaveBeenCalled();
|
|
106
|
+
expect(logger.warn).toHaveBeenCalledWith('Wiadomość bez pola data', expect.objectContaining({ messageId: 'msg-nodata' }));
|
|
107
|
+
}));
|
|
108
|
+
it('powinien obsłużyć nieparzystą liczbę elementów (H4)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
109
|
+
const handler = jest.fn().mockResolvedValue('ok');
|
|
110
|
+
const fields = ['data', redis_codec_1.RedisCodec.encode(Buffer.from('data')), 'extra-key'];
|
|
111
|
+
yield processor.process('cmd:Test', 'workers', 'msg-odd-fields', fields, handler);
|
|
112
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
113
|
+
}));
|
|
114
|
+
it('powinien propagować błąd handlera do callera', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
115
|
+
const handler = jest.fn().mockRejectedValue(new Error('Handler error'));
|
|
116
|
+
const fields = ['data', redis_codec_1.RedisCodec.encode(Buffer.from('data'))];
|
|
117
|
+
yield expect(processor.process('cmd:Test', 'workers', 'msg-err', fields, handler)).rejects.toThrow('Handler error');
|
|
118
|
+
}));
|
|
119
|
+
});
|
|
120
|
+
describe('RPC detection', () => {
|
|
121
|
+
it('powinien wykryć RPC wiadomość przez marker i odpowiedzieć', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
122
|
+
const handler = jest.fn().mockResolvedValue('result-42');
|
|
123
|
+
const envelope = {
|
|
124
|
+
commandData: redis_codec_1.RedisCodec.encode(Buffer.from('cmd-data')),
|
|
125
|
+
rpc: { correlationId: 'corr-1', responseKey: 'rpc:res:corr-1' },
|
|
126
|
+
};
|
|
127
|
+
const envelopePayload = redis_codec_1.RedisCodec.encode(Buffer.from(JSON.stringify(envelope)));
|
|
128
|
+
const fields = ['data', envelopePayload, 'rpc', '1'];
|
|
129
|
+
yield processor.process('cmd:Test', 'workers', 'msg-rpc', fields, handler);
|
|
130
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
131
|
+
expect(rpcRespond).toHaveBeenCalledWith('rpc:res:corr-1', expect.any(Buffer), 60);
|
|
132
|
+
}));
|
|
133
|
+
it('powinien nie wywoływać rpcRespond dla zwykłej wiadomości', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
134
|
+
const handler = jest.fn().mockResolvedValue('ok');
|
|
135
|
+
const fields = ['data', redis_codec_1.RedisCodec.encode(Buffer.from('normal-data'))];
|
|
136
|
+
yield processor.process('cmd:Test', 'workers', 'msg-normal', fields, handler);
|
|
137
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
138
|
+
expect(rpcRespond).not.toHaveBeenCalled();
|
|
139
|
+
}));
|
|
140
|
+
});
|
|
141
|
+
describe('XTRIM per-stream (H2)', () => {
|
|
142
|
+
it('powinien zliczać XTRIM niezależnie per strumień', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
143
|
+
const handler = jest.fn().mockResolvedValue('ok');
|
|
144
|
+
const fields = ['data', redis_codec_1.RedisCodec.encode(Buffer.from('data'))];
|
|
145
|
+
// 9 wiadomości na stream A — nie powinno triggerować XTRIM (batchSize=10)
|
|
146
|
+
for (let i = 0; i < 9; i++) {
|
|
147
|
+
yield processor.process('cmd:StreamA', 'workers', `a-${i}`, fields, handler);
|
|
148
|
+
}
|
|
149
|
+
// 1 wiadomość na stream B
|
|
150
|
+
yield processor.process('cmd:StreamB', 'workers', 'b-0', fields, handler);
|
|
151
|
+
// Pipeline NIE powinien być wywołany — żaden stream nie osiągnął batchSize
|
|
152
|
+
const xackCalls = mockConn.xack.mock.calls;
|
|
153
|
+
expect(xackCalls.length).toBe(10);
|
|
154
|
+
// 10. wiadomość na stream A — powinno triggerować XTRIM
|
|
155
|
+
yield processor.process('cmd:StreamA', 'workers', 'a-9', fields, handler);
|
|
156
|
+
expect(mockConn.pipeline).toHaveBeenCalled();
|
|
157
|
+
}));
|
|
158
|
+
it('powinien wykonać pipeline XACK+XTRIM co batchSize', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
159
|
+
const handler = jest.fn().mockResolvedValue('ok');
|
|
160
|
+
const fields = ['data', redis_codec_1.RedisCodec.encode(Buffer.from('data'))];
|
|
161
|
+
for (let i = 0; i < 10; i++) {
|
|
162
|
+
yield processor.process('cmd:Test', 'workers', `msg-${i}`, fields, handler);
|
|
163
|
+
}
|
|
164
|
+
expect(mockConn.pipeline).toHaveBeenCalledTimes(1);
|
|
165
|
+
}));
|
|
166
|
+
it('powinien zresetować counter po XTRIM', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
167
|
+
const handler = jest.fn().mockResolvedValue('ok');
|
|
168
|
+
const fields = ['data', redis_codec_1.RedisCodec.encode(Buffer.from('data'))];
|
|
169
|
+
// Pierwsze 10 — trigger XTRIM
|
|
170
|
+
for (let i = 0; i < 10; i++) {
|
|
171
|
+
yield processor.process('cmd:Test', 'workers', `batch1-${i}`, fields, handler);
|
|
172
|
+
}
|
|
173
|
+
expect(mockConn.pipeline).toHaveBeenCalledTimes(1);
|
|
174
|
+
// Następne 9 — NIE trigger
|
|
175
|
+
for (let i = 0; i < 9; i++) {
|
|
176
|
+
yield processor.process('cmd:Test', 'workers', `batch2-${i}`, fields, handler);
|
|
177
|
+
}
|
|
178
|
+
expect(mockConn.pipeline).toHaveBeenCalledTimes(1);
|
|
179
|
+
// 20. wiadomość — trigger XTRIM ponownie
|
|
180
|
+
yield processor.process('cmd:Test', 'workers', 'batch2-9', fields, handler);
|
|
181
|
+
expect(mockConn.pipeline).toHaveBeenCalledTimes(2);
|
|
182
|
+
}));
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
//# sourceMappingURL=message-processor.spec.js.map
|