pp-command-bus 1.5.0 → 2.0.1

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.
Files changed (205) hide show
  1. package/README.md +400 -1219
  2. package/dist/command-bus/command-bus.spec.js +138 -359
  3. package/dist/command-bus/command-bus.spec.js.map +1 -1
  4. package/dist/command-bus/command.d.ts +3 -4
  5. package/dist/command-bus/command.js +3 -32
  6. package/dist/command-bus/command.js.map +1 -1
  7. package/dist/command-bus/config/command-bus-config.d.ts +80 -21
  8. package/dist/command-bus/config/command-bus-config.js +104 -58
  9. package/dist/command-bus/config/command-bus-config.js.map +1 -1
  10. package/dist/command-bus/config/command-bus-config.spec.js +174 -100
  11. package/dist/command-bus/config/command-bus-config.spec.js.map +1 -1
  12. package/dist/command-bus/index.d.ts +41 -50
  13. package/dist/command-bus/index.js +143 -127
  14. package/dist/command-bus/index.js.map +1 -1
  15. package/dist/command-bus/interceptors/index.d.ts +5 -0
  16. package/dist/command-bus/interceptors/index.js +22 -0
  17. package/dist/command-bus/interceptors/index.js.map +1 -0
  18. package/dist/command-bus/interceptors/interceptor.interface.d.ts +35 -0
  19. package/dist/command-bus/interceptors/interceptor.interface.js +3 -0
  20. package/dist/command-bus/interceptors/interceptor.interface.js.map +1 -0
  21. package/dist/command-bus/interceptors/performance-interceptor.d.ts +24 -0
  22. package/dist/command-bus/interceptors/performance-interceptor.js +86 -0
  23. package/dist/command-bus/interceptors/performance-interceptor.js.map +1 -0
  24. package/dist/command-bus/interceptors/performance-interceptor.spec.js +124 -0
  25. package/dist/command-bus/interceptors/performance-interceptor.spec.js.map +1 -0
  26. package/dist/command-bus/logging/command-logger.d.ts +2 -0
  27. package/dist/command-bus/logging/command-logger.js +7 -0
  28. package/dist/command-bus/logging/command-logger.js.map +1 -1
  29. package/dist/command-bus/logging/command-logger.spec.js +36 -0
  30. package/dist/command-bus/logging/command-logger.spec.js.map +1 -1
  31. package/dist/command-bus/serialization/index.d.ts +6 -0
  32. package/dist/command-bus/serialization/index.js +9 -0
  33. package/dist/command-bus/serialization/index.js.map +1 -0
  34. package/dist/command-bus/serialization/msgpack-serializer.d.ts +26 -0
  35. package/dist/command-bus/serialization/msgpack-serializer.js +70 -0
  36. package/dist/command-bus/serialization/msgpack-serializer.js.map +1 -0
  37. package/dist/command-bus/serialization/msgpack-serializer.spec.js +223 -0
  38. package/dist/command-bus/serialization/msgpack-serializer.spec.js.map +1 -0
  39. package/dist/command-bus/serialization/serializer.interface.d.ts +21 -0
  40. package/dist/command-bus/serialization/serializer.interface.js +3 -0
  41. package/dist/command-bus/serialization/serializer.interface.js.map +1 -0
  42. package/dist/command-bus/transport/consumer-loop.d.ts +45 -0
  43. package/dist/command-bus/transport/consumer-loop.js +90 -0
  44. package/dist/command-bus/transport/consumer-loop.js.map +1 -0
  45. package/dist/command-bus/transport/consumer-loop.spec.js +216 -0
  46. package/dist/command-bus/transport/consumer-loop.spec.js.map +1 -0
  47. package/dist/command-bus/transport/index.d.ts +21 -0
  48. package/dist/command-bus/transport/index.js +23 -0
  49. package/dist/command-bus/transport/index.js.map +1 -0
  50. package/dist/command-bus/transport/message-processor.d.ts +70 -0
  51. package/dist/command-bus/transport/message-processor.js +158 -0
  52. package/dist/command-bus/transport/message-processor.js.map +1 -0
  53. package/dist/command-bus/transport/message-processor.spec.js +185 -0
  54. package/dist/command-bus/transport/message-processor.spec.js.map +1 -0
  55. package/dist/command-bus/transport/pending-recovery.d.ts +54 -0
  56. package/dist/command-bus/transport/pending-recovery.js +139 -0
  57. package/dist/command-bus/transport/pending-recovery.js.map +1 -0
  58. package/dist/command-bus/transport/pending-recovery.spec.js +176 -0
  59. package/dist/command-bus/transport/pending-recovery.spec.js.map +1 -0
  60. package/dist/command-bus/transport/redis-codec.d.ts +24 -0
  61. package/dist/command-bus/transport/redis-codec.js +33 -0
  62. package/dist/command-bus/transport/redis-codec.js.map +1 -0
  63. package/dist/command-bus/transport/redis-codec.spec.js +53 -0
  64. package/dist/command-bus/transport/redis-codec.spec.js.map +1 -0
  65. package/dist/command-bus/transport/redis-streams-transport.d.ts +94 -0
  66. package/dist/command-bus/transport/redis-streams-transport.js +143 -0
  67. package/dist/command-bus/transport/redis-streams-transport.js.map +1 -0
  68. package/dist/command-bus/transport/redis-streams-transport.spec.js +420 -0
  69. package/dist/command-bus/transport/redis-streams-transport.spec.js.map +1 -0
  70. package/dist/command-bus/transport/rpc-handler.d.ts +39 -0
  71. package/dist/command-bus/transport/rpc-handler.js +87 -0
  72. package/dist/command-bus/transport/rpc-handler.js.map +1 -0
  73. package/dist/command-bus/transport/rpc-handler.spec.js +157 -0
  74. package/dist/command-bus/transport/rpc-handler.spec.js.map +1 -0
  75. package/dist/command-bus/transport/stream-consumer.d.ts +91 -0
  76. package/dist/command-bus/transport/stream-consumer.js +182 -0
  77. package/dist/command-bus/transport/stream-consumer.js.map +1 -0
  78. package/dist/command-bus/transport/stream-consumer.spec.js +284 -0
  79. package/dist/command-bus/transport/stream-consumer.spec.js.map +1 -0
  80. package/dist/command-bus/transport/stream-producer.d.ts +23 -0
  81. package/dist/command-bus/transport/stream-producer.js +70 -0
  82. package/dist/command-bus/transport/stream-producer.js.map +1 -0
  83. package/dist/command-bus/transport/stream-producer.spec.js +125 -0
  84. package/dist/command-bus/transport/stream-producer.spec.js.map +1 -0
  85. package/dist/command-bus/transport/transport.interface.d.ts +87 -0
  86. package/dist/command-bus/transport/transport.interface.js +3 -0
  87. package/dist/command-bus/transport/transport.interface.js.map +1 -0
  88. package/dist/command-bus/types/index.d.ts +0 -84
  89. package/dist/examples/rpc.demo.js +1 -1
  90. package/dist/examples/rpc.demo.js.map +1 -1
  91. package/dist/index.d.ts +8 -5
  92. package/dist/index.js +6 -4
  93. package/dist/index.js.map +1 -1
  94. package/dist/pp-command-bus-2.0.1.tgz +0 -0
  95. package/dist/shared/redis/connection-pool.d.ts +54 -0
  96. package/dist/shared/redis/connection-pool.js +123 -0
  97. package/dist/shared/redis/connection-pool.js.map +1 -0
  98. package/dist/shared/redis/connection-pool.spec.d.ts +1 -0
  99. package/dist/shared/redis/connection-pool.spec.js +114 -0
  100. package/dist/shared/redis/connection-pool.spec.js.map +1 -0
  101. package/dist/shared/redis/index.d.ts +5 -3
  102. package/dist/shared/redis/index.js +6 -4
  103. package/dist/shared/redis/index.js.map +1 -1
  104. package/dist/shared/redis/rpc-connection-pool.d.ts +61 -0
  105. package/dist/shared/redis/rpc-connection-pool.js +154 -0
  106. package/dist/shared/redis/rpc-connection-pool.js.map +1 -0
  107. package/dist/shared/redis/rpc-connection-pool.spec.d.ts +1 -0
  108. package/dist/shared/redis/rpc-connection-pool.spec.js +173 -0
  109. package/dist/shared/redis/rpc-connection-pool.spec.js.map +1 -0
  110. package/dist/shared/types.d.ts +0 -4
  111. package/dist/shared/utils/error-utils.d.ts +8 -0
  112. package/dist/shared/utils/error-utils.js +14 -0
  113. package/dist/shared/utils/error-utils.js.map +1 -0
  114. package/package.json +12 -12
  115. package/dist/command-bus/config/auto-config-optimizer.d.ts +0 -35
  116. package/dist/command-bus/config/auto-config-optimizer.js +0 -52
  117. package/dist/command-bus/config/auto-config-optimizer.js.map +0 -1
  118. package/dist/command-bus/config/auto-config-optimizer.spec.js +0 -42
  119. package/dist/command-bus/config/auto-config-optimizer.spec.js.map +0 -1
  120. package/dist/command-bus/job/index.d.ts +0 -6
  121. package/dist/command-bus/job/index.js +0 -15
  122. package/dist/command-bus/job/index.js.map +0 -1
  123. package/dist/command-bus/job/job-options-builder.d.ts +0 -21
  124. package/dist/command-bus/job/job-options-builder.js +0 -58
  125. package/dist/command-bus/job/job-options-builder.js.map +0 -1
  126. package/dist/command-bus/job/job-options-builder.spec.js +0 -156
  127. package/dist/command-bus/job/job-options-builder.spec.js.map +0 -1
  128. package/dist/command-bus/job/job-processor.d.ts +0 -39
  129. package/dist/command-bus/job/job-processor.js +0 -203
  130. package/dist/command-bus/job/job-processor.js.map +0 -1
  131. package/dist/command-bus/job/job-processor.spec.js +0 -436
  132. package/dist/command-bus/job/job-processor.spec.js.map +0 -1
  133. package/dist/command-bus/queue/index.d.ts +0 -5
  134. package/dist/command-bus/queue/index.js +0 -13
  135. package/dist/command-bus/queue/index.js.map +0 -1
  136. package/dist/command-bus/queue/queue-manager.d.ts +0 -56
  137. package/dist/command-bus/queue/queue-manager.js +0 -163
  138. package/dist/command-bus/queue/queue-manager.js.map +0 -1
  139. package/dist/command-bus/queue/queue-manager.spec.js +0 -371
  140. package/dist/command-bus/queue/queue-manager.spec.js.map +0 -1
  141. package/dist/command-bus/rpc/index.d.ts +0 -11
  142. package/dist/command-bus/rpc/index.js +0 -19
  143. package/dist/command-bus/rpc/index.js.map +0 -1
  144. package/dist/command-bus/rpc/payload-compression.service.d.ts +0 -50
  145. package/dist/command-bus/rpc/payload-compression.service.js +0 -215
  146. package/dist/command-bus/rpc/payload-compression.service.js.map +0 -1
  147. package/dist/command-bus/rpc/payload-compression.service.spec.js +0 -376
  148. package/dist/command-bus/rpc/payload-compression.service.spec.js.map +0 -1
  149. package/dist/command-bus/rpc/rpc-coordinator.d.ts +0 -96
  150. package/dist/command-bus/rpc/rpc-coordinator.js +0 -500
  151. package/dist/command-bus/rpc/rpc-coordinator.js.map +0 -1
  152. package/dist/command-bus/rpc/rpc-coordinator.spec.js +0 -621
  153. package/dist/command-bus/rpc/rpc-coordinator.spec.js.map +0 -1
  154. package/dist/command-bus/rpc/rpc-job-cancellation.service.d.ts +0 -82
  155. package/dist/command-bus/rpc/rpc-job-cancellation.service.js +0 -180
  156. package/dist/command-bus/rpc/rpc-job-cancellation.service.js.map +0 -1
  157. package/dist/command-bus/rpc/rpc-job-cancellation.service.spec.js +0 -286
  158. package/dist/command-bus/rpc/rpc-job-cancellation.service.spec.js.map +0 -1
  159. package/dist/command-bus/worker/index.d.ts +0 -10
  160. package/dist/command-bus/worker/index.js +0 -19
  161. package/dist/command-bus/worker/index.js.map +0 -1
  162. package/dist/command-bus/worker/worker-benchmark.d.ts +0 -71
  163. package/dist/command-bus/worker/worker-benchmark.js +0 -202
  164. package/dist/command-bus/worker/worker-benchmark.js.map +0 -1
  165. package/dist/command-bus/worker/worker-benchmark.spec.js +0 -310
  166. package/dist/command-bus/worker/worker-benchmark.spec.js.map +0 -1
  167. package/dist/command-bus/worker/worker-metrics-collector.d.ts +0 -98
  168. package/dist/command-bus/worker/worker-metrics-collector.js +0 -242
  169. package/dist/command-bus/worker/worker-metrics-collector.js.map +0 -1
  170. package/dist/command-bus/worker/worker-orchestrator.d.ts +0 -70
  171. package/dist/command-bus/worker/worker-orchestrator.js +0 -339
  172. package/dist/command-bus/worker/worker-orchestrator.js.map +0 -1
  173. package/dist/command-bus/worker/worker-orchestrator.spec.js +0 -712
  174. package/dist/command-bus/worker/worker-orchestrator.spec.js.map +0 -1
  175. package/dist/examples/auto-config.demo.d.ts +0 -9
  176. package/dist/examples/auto-config.demo.js +0 -106
  177. package/dist/examples/auto-config.demo.js.map +0 -1
  178. package/dist/examples/rpc-compression.demo.d.ts +0 -5
  179. package/dist/examples/rpc-compression.demo.js +0 -358
  180. package/dist/examples/rpc-compression.demo.js.map +0 -1
  181. package/dist/examples/rpc-resilience.demo.d.ts +0 -15
  182. package/dist/examples/rpc-resilience.demo.js +0 -233
  183. package/dist/examples/rpc-resilience.demo.js.map +0 -1
  184. package/dist/pp-command-bus-1.5.0.tgz +0 -0
  185. package/dist/shared/config/base-config.d.ts +0 -54
  186. package/dist/shared/config/base-config.js +0 -114
  187. package/dist/shared/config/base-config.js.map +0 -1
  188. package/dist/shared/config/base-config.spec.js +0 -204
  189. package/dist/shared/config/base-config.spec.js.map +0 -1
  190. package/dist/shared/config/index.d.ts +0 -1
  191. package/dist/shared/config/index.js +0 -9
  192. package/dist/shared/config/index.js.map +0 -1
  193. package/dist/shared/redis/redis-connection-factory.d.ts +0 -66
  194. package/dist/shared/redis/redis-connection-factory.js +0 -113
  195. package/dist/shared/redis/redis-connection-factory.js.map +0 -1
  196. /package/dist/command-bus/{config/auto-config-optimizer.spec.d.ts → interceptors/performance-interceptor.spec.d.ts} +0 -0
  197. /package/dist/command-bus/{job/job-options-builder.spec.d.ts → serialization/msgpack-serializer.spec.d.ts} +0 -0
  198. /package/dist/command-bus/{job/job-processor.spec.d.ts → transport/consumer-loop.spec.d.ts} +0 -0
  199. /package/dist/command-bus/{queue/queue-manager.spec.d.ts → transport/message-processor.spec.d.ts} +0 -0
  200. /package/dist/command-bus/{rpc/payload-compression.service.spec.d.ts → transport/pending-recovery.spec.d.ts} +0 -0
  201. /package/dist/command-bus/{rpc/rpc-coordinator.spec.d.ts → transport/redis-codec.spec.d.ts} +0 -0
  202. /package/dist/command-bus/{rpc/rpc-job-cancellation.service.spec.d.ts → transport/redis-streams-transport.spec.d.ts} +0 -0
  203. /package/dist/command-bus/{worker/worker-benchmark.spec.d.ts → transport/rpc-handler.spec.d.ts} +0 -0
  204. /package/dist/command-bus/{worker/worker-orchestrator.spec.d.ts → transport/stream-consumer.spec.d.ts} +0 -0
  205. /package/dist/{shared/config/base-config.spec.d.ts → command-bus/transport/stream-producer.spec.d.ts} +0 -0
@@ -0,0 +1,420 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const redis_streams_transport_1 = __importDefault(require("./redis-streams-transport"));
16
+ /**
17
+ * Tworzy mock Redis connection z disconnect() który przerywa blocking operations
18
+ * Symuluje prawdziwe zachowanie: disconnect() zamyka socket → XREADGROUP BLOCK rejectuje
19
+ */
20
+ const createMockConnection = () => {
21
+ /** Rejecty pending blocking operations — disconnect() je wywołuje */
22
+ const pendingRejects = [];
23
+ return {
24
+ xadd: jest.fn().mockResolvedValue('1700000000000-0'),
25
+ xreadgroup: jest.fn().mockImplementation(() => new Promise((_resolve, reject) => {
26
+ pendingRejects.push(reject);
27
+ })),
28
+ xack: jest.fn().mockResolvedValue(1),
29
+ xgroup: jest.fn().mockResolvedValue('OK'),
30
+ xpending: jest.fn().mockResolvedValue([]),
31
+ xclaim: jest.fn().mockResolvedValue([]),
32
+ xtrim: jest.fn().mockResolvedValue(0),
33
+ brpop: jest.fn().mockResolvedValue(null),
34
+ lpush: jest.fn().mockResolvedValue(1),
35
+ expire: jest.fn().mockResolvedValue(1),
36
+ del: jest.fn().mockResolvedValue(1),
37
+ quit: jest.fn().mockResolvedValue('OK'),
38
+ on: jest.fn(),
39
+ disconnect: jest.fn().mockImplementation(() => {
40
+ // Przerwij pending blocking operations — symuluje zamknięcie socketa
41
+ const disconnectError = new Error('Connection is closed.');
42
+ while (pendingRejects.length > 0) {
43
+ const reject = pendingRejects.shift();
44
+ if (reject)
45
+ reject(disconnectError);
46
+ }
47
+ }),
48
+ pipeline: jest.fn().mockReturnValue({
49
+ xadd: jest.fn().mockReturnThis(),
50
+ xack: jest.fn().mockReturnThis(),
51
+ xtrim: jest.fn().mockReturnThis(),
52
+ lpush: jest.fn().mockReturnThis(),
53
+ expire: jest.fn().mockReturnThis(),
54
+ exec: jest.fn().mockResolvedValue([
55
+ [null, '1700000000000-0'],
56
+ [null, '1700000000001-0'],
57
+ ]),
58
+ }),
59
+ };
60
+ };
61
+ const createMockPool = (conn) => ({
62
+ next: jest.fn().mockReturnValue(conn),
63
+ createDedicated: jest.fn().mockReturnValue(conn),
64
+ close: jest.fn().mockResolvedValue(undefined),
65
+ size: 4,
66
+ });
67
+ const createMockRpcPool = () => {
68
+ const rpcConn = createMockConnection();
69
+ return {
70
+ acquire: jest.fn().mockResolvedValue(rpcConn),
71
+ release: jest.fn(),
72
+ close: jest.fn().mockResolvedValue(undefined),
73
+ activeCount: 0,
74
+ waitingCount: 0,
75
+ };
76
+ };
77
+ const createMockSerializer = () => ({
78
+ serialize: jest.fn((data) => Buffer.from(JSON.stringify(data))),
79
+ deserialize: jest.fn((buf) => JSON.parse(buf.toString())),
80
+ });
81
+ const createMockLogger = () => ({
82
+ log: jest.fn(),
83
+ error: jest.fn(),
84
+ warn: jest.fn(),
85
+ debug: jest.fn(),
86
+ });
87
+ describe('RedisStreamsTransport', () => {
88
+ let transport;
89
+ let mockConn;
90
+ let mockPool;
91
+ let mockRpcPool;
92
+ let mockSerializer;
93
+ let logger;
94
+ beforeEach(() => {
95
+ jest.useFakeTimers({ legacyFakeTimers: false });
96
+ jest.clearAllMocks();
97
+ mockConn = createMockConnection();
98
+ mockPool = createMockPool(mockConn);
99
+ mockRpcPool = createMockRpcPool();
100
+ mockSerializer = createMockSerializer();
101
+ logger = createMockLogger();
102
+ transport = new redis_streams_transport_1.default({
103
+ pool: mockPool,
104
+ rpcPool: mockRpcPool,
105
+ serializer: mockSerializer,
106
+ logger,
107
+ maxRetained: 10000,
108
+ maxAttempts: 3,
109
+ claimTimeout: 30000,
110
+ batchSize: 10,
111
+ concurrency: 100,
112
+ });
113
+ });
114
+ afterEach(() => __awaiter(void 0, void 0, void 0, function* () {
115
+ yield transport.close();
116
+ jest.useRealTimers();
117
+ }));
118
+ describe('enqueue()', () => {
119
+ it('powinien wysłać dane do strumienia przez XADD', () => __awaiter(void 0, void 0, void 0, function* () {
120
+ const data = Buffer.from('test-data');
121
+ const messageId = yield transport.enqueue('cmd:TestCommand', data);
122
+ // Base64: ioredis dekoduje string responses jako UTF-8, co korumpuje surowe bajty
123
+ expect(mockConn.xadd).toHaveBeenCalledWith('cmd:TestCommand', '*', 'data', data.toString('base64'));
124
+ expect(messageId).toBe('1700000000000-0');
125
+ }));
126
+ it('powinien używać połączenia z puli (round-robin)', () => __awaiter(void 0, void 0, void 0, function* () {
127
+ yield transport.enqueue('cmd:Test', Buffer.from('data'));
128
+ expect(mockPool.next).toHaveBeenCalledTimes(1);
129
+ }));
130
+ });
131
+ describe('enqueueBatch()', () => {
132
+ it('powinien wysłać wiele komend w jednym pipeline', () => __awaiter(void 0, void 0, void 0, function* () {
133
+ const entries = [
134
+ { streamName: 'cmd:A', data: Buffer.from('data-a') },
135
+ { streamName: 'cmd:B', data: Buffer.from('data-b') },
136
+ ];
137
+ const ids = yield transport.enqueueBatch(entries);
138
+ expect(mockPool.next).toHaveBeenCalled();
139
+ expect(ids).toHaveLength(2);
140
+ }));
141
+ it('powinien zwrócić pustą tablicę dla pustego wejścia', () => __awaiter(void 0, void 0, void 0, function* () {
142
+ const ids = yield transport.enqueueBatch([]);
143
+ expect(ids).toEqual([]);
144
+ }));
145
+ });
146
+ describe('consume()', () => {
147
+ it('powinien utworzyć consumer group i rozpocząć nasłuchiwanie', () => __awaiter(void 0, void 0, void 0, function* () {
148
+ jest.useRealTimers(); // Potrzebujemy real timers dla setTimeout
149
+ const handler = jest.fn().mockResolvedValue(undefined);
150
+ // Mockujemy XREADGROUP:
151
+ // 1. Pierwsza wiadomość — dane do przetworzenia
152
+ // 2. Potem blocking promise (symuluje BLOCK 5000)
153
+ const consumerConn = createMockConnection();
154
+ // Dane w strumieniu są base64-encoded (jak w prawdziwym Redis)
155
+ const testPayload = Buffer.from('test-payload').toString('base64');
156
+ consumerConn.xreadgroup.mockResolvedValueOnce([
157
+ ['cmd:TestCommand', [['1700000000000-0', ['data', testPayload]]]],
158
+ ]);
159
+ mockPool.createDedicated.mockReturnValue(consumerConn);
160
+ yield transport.consume('cmd:TestCommand', 'workers', handler);
161
+ // Poczekaj na przetworzenie wiadomości
162
+ yield new Promise((r) => setTimeout(r, 50));
163
+ // Powinien utworzyć consumer group
164
+ expect(consumerConn.xgroup).toHaveBeenCalledWith('CREATE', 'cmd:TestCommand', 'workers', '0', 'MKSTREAM');
165
+ // Powinien wywołać handler z danymi
166
+ expect(handler).toHaveBeenCalledTimes(1);
167
+ expect(handler).toHaveBeenCalledWith(expect.any(Buffer));
168
+ // XACK idzie przez pool connection (nie dedykowane consumer conn — XREADGROUP BLOCK)
169
+ expect(mockConn.xack).toHaveBeenCalledWith('cmd:TestCommand', 'workers', '1700000000000-0');
170
+ }));
171
+ it('powinien obsłużyć błąd tworzenia grupy jeśli już istnieje', () => __awaiter(void 0, void 0, void 0, function* () {
172
+ const handler = jest.fn().mockResolvedValue(undefined);
173
+ const consumerConn = createMockConnection();
174
+ consumerConn.xgroup.mockRejectedValueOnce(new Error('BUSYGROUP Consumer Group name already exists'));
175
+ mockPool.createDedicated.mockReturnValue(consumerConn);
176
+ // Nie powinien rzucić błędu
177
+ yield transport.consume('cmd:Test', 'workers', handler);
178
+ }));
179
+ });
180
+ describe('rpcCall()', () => {
181
+ it('powinien wysłać komendę i czekać na odpowiedź przez BRPOP', () => __awaiter(void 0, void 0, void 0, function* () {
182
+ const responseData = Buffer.from(JSON.stringify({ result: 42 }));
183
+ const rpcConn = createMockConnection();
184
+ // BRPOP zwraca base64-encoded string (rpcRespond koduje dane jako base64)
185
+ rpcConn.brpop.mockResolvedValue(['rpc:res:test-id', responseData.toString('base64')]);
186
+ mockRpcPool.acquire.mockResolvedValue(rpcConn);
187
+ const result = yield transport.rpcCall('TestCommand', Buffer.from('request-data'), 5000);
188
+ // Powinien wysłać komendę do strumienia
189
+ expect(mockConn.xadd).toHaveBeenCalled();
190
+ // Powinien pobrać połączenie z RPC pool
191
+ expect(mockRpcPool.acquire).toHaveBeenCalled();
192
+ // Powinien czekać na odpowiedź przez BRPOP
193
+ expect(rpcConn.brpop).toHaveBeenCalled();
194
+ // Powinien zwolnić połączenie po zakończeniu
195
+ expect(mockRpcPool.release).toHaveBeenCalledWith(rpcConn);
196
+ expect(result).toBeInstanceOf(Buffer);
197
+ }));
198
+ it('powinien rzucić błąd przy timeout (BRPOP zwraca null)', () => __awaiter(void 0, void 0, void 0, function* () {
199
+ const rpcConn = createMockConnection();
200
+ rpcConn.brpop.mockResolvedValue(null);
201
+ mockRpcPool.acquire.mockResolvedValue(rpcConn);
202
+ yield expect(transport.rpcCall('TestCommand', Buffer.from('data'), 1000)).rejects.toThrow('RPC timeout');
203
+ // Powinien zwolnić połączenie nawet przy błędzie
204
+ expect(mockRpcPool.release).toHaveBeenCalledWith(rpcConn);
205
+ }));
206
+ });
207
+ describe('rpcRespond()', () => {
208
+ it('powinien wysłać odpowiedź przez pipeline LPUSH + EXPIRE', () => __awaiter(void 0, void 0, void 0, function* () {
209
+ const responseData = Buffer.from('response');
210
+ yield transport.rpcRespond('rpc:res:test-id', responseData, 60);
211
+ // Powinien użyć pipeline
212
+ expect(mockConn.pipeline).toHaveBeenCalled();
213
+ }));
214
+ });
215
+ describe('XTRIM throttling (H1+H2)', () => {
216
+ it('powinien wykonać XTRIM co batchSize wiadomości', () => __awaiter(void 0, void 0, void 0, function* () {
217
+ jest.useRealTimers();
218
+ const handler = jest.fn().mockResolvedValue(undefined);
219
+ const consumerConn = createMockConnection();
220
+ // Symuluj 10 wiadomości (batchSize = 10)
221
+ const testPayload = Buffer.from('payload').toString('base64');
222
+ const messages = Array.from({ length: 10 }, (_, i) => [
223
+ `170000000000${i}-0`,
224
+ ['data', testPayload],
225
+ ]);
226
+ consumerConn.xreadgroup.mockResolvedValueOnce([['cmd:Test', messages]]);
227
+ mockPool.createDedicated.mockReturnValue(consumerConn);
228
+ yield transport.consume('cmd:Test', 'workers', handler);
229
+ // Poczekaj na przetworzenie
230
+ yield new Promise((r) => setTimeout(r, 100));
231
+ // Pipeline powinien być użyty dokładnie raz (10. wiadomość = batchSize)
232
+ // Wiadomości 1-9: sam XACK, wiadomość 10: pipeline XACK+XTRIM
233
+ const pipelineCalls = mockConn.pipeline.mock.calls.length;
234
+ expect(pipelineCalls).toBeGreaterThanOrEqual(1);
235
+ // XTRIM NIE powinien być wywołany bezpośrednio (fire-and-forget usunięty)
236
+ expect(mockConn.xtrim).not.toHaveBeenCalled();
237
+ }));
238
+ });
239
+ describe('RPC marker field (H3)', () => {
240
+ it('powinien dodać marker rpc do XADD dla RPC', () => __awaiter(void 0, void 0, void 0, function* () {
241
+ const rpcConn = createMockConnection();
242
+ const responseData = Buffer.from(JSON.stringify({ result: 42 }));
243
+ rpcConn.brpop.mockResolvedValue(['rpc:res:test-id', responseData.toString('base64')]);
244
+ mockRpcPool.acquire.mockResolvedValue(rpcConn);
245
+ yield transport.rpcCall('TestCommand', Buffer.from('request-data'), 5000);
246
+ // XADD powinien mieć 6 argumentów: streamName, *, 'data', value, 'rpc', '1'
247
+ const xaddCall = mockConn.xadd.mock.calls[0];
248
+ expect(xaddCall).toContain('rpc');
249
+ expect(xaddCall).toContain('1');
250
+ }));
251
+ it('powinien użyć markera rpc do identyfikacji RPC envelope', () => __awaiter(void 0, void 0, void 0, function* () {
252
+ jest.useRealTimers();
253
+ const handler = jest.fn().mockResolvedValue('rpc-result');
254
+ const consumerConn = createMockConnection();
255
+ // Symuluj RPC wiadomość z markerem
256
+ const envelope = {
257
+ commandData: Buffer.from('cmd-data').toString('base64'),
258
+ rpc: { correlationId: 'test', responseKey: 'rpc:res:test' },
259
+ };
260
+ const envelopePayload = Buffer.from(JSON.stringify(envelope)).toString('base64');
261
+ consumerConn.xreadgroup.mockResolvedValueOnce([
262
+ ['cmd:Test', [['msg-rpc', ['data', envelopePayload, 'rpc', '1']]]],
263
+ ]);
264
+ mockPool.createDedicated.mockReturnValue(consumerConn);
265
+ yield transport.consume('cmd:Test', 'workers', handler);
266
+ yield new Promise((r) => setTimeout(r, 50));
267
+ // Handler powinien być wywołany
268
+ expect(handler).toHaveBeenCalledTimes(1);
269
+ }));
270
+ it('powinien obsługiwać zwykłe komendy bez markera', () => __awaiter(void 0, void 0, void 0, function* () {
271
+ jest.useRealTimers();
272
+ const handler = jest.fn().mockResolvedValue(undefined);
273
+ const consumerConn = createMockConnection();
274
+ // Zwykła komenda — bez markera 'rpc'
275
+ const testPayload = Buffer.from('normal-command').toString('base64');
276
+ consumerConn.xreadgroup.mockResolvedValueOnce([
277
+ ['cmd:Test', [['msg-normal', ['data', testPayload]]]],
278
+ ]);
279
+ mockPool.createDedicated.mockReturnValue(consumerConn);
280
+ yield transport.consume('cmd:Test', 'workers', handler);
281
+ yield new Promise((r) => setTimeout(r, 50));
282
+ // Handler powinien być wywołany z surowymi danymi (nie RPC)
283
+ expect(handler).toHaveBeenCalledTimes(1);
284
+ expect(handler).toHaveBeenCalledWith(expect.any(Buffer));
285
+ }));
286
+ });
287
+ describe('close()', () => {
288
+ it('powinien zamknąć transport gracefully', () => __awaiter(void 0, void 0, void 0, function* () {
289
+ yield transport.close();
290
+ expect(mockPool.close).toHaveBeenCalled();
291
+ expect(mockRpcPool.close).toHaveBeenCalled();
292
+ }));
293
+ it('powinien być idempotentny (drugie close nie rzuca)', () => __awaiter(void 0, void 0, void 0, function* () {
294
+ yield transport.close();
295
+ yield transport.close();
296
+ }));
297
+ it('powinien czekać na zakończenie aktywnych processMessage przed zamknięciem puli', () => __awaiter(void 0, void 0, void 0, function* () {
298
+ jest.useRealTimers();
299
+ // Przygotuj consumer z wolnym handlerem
300
+ let handlerResolve;
301
+ const handlerPromise = new Promise((resolve) => {
302
+ handlerResolve = resolve;
303
+ });
304
+ const handler = jest.fn().mockReturnValue(handlerPromise);
305
+ const consumerConn = createMockConnection();
306
+ const testPayload = Buffer.from('test-payload').toString('base64');
307
+ // Pierwsza wiadomość → przetworzenie, kolejne → domyślne interruptible blocking
308
+ consumerConn.xreadgroup.mockResolvedValueOnce([
309
+ ['cmd:Test', [['1700000000000-0', ['data', testPayload]]]],
310
+ ]);
311
+ mockPool.createDedicated.mockReturnValue(consumerConn);
312
+ yield transport.consume('cmd:Test', 'workers', handler);
313
+ // Poczekaj na start przetwarzania
314
+ yield new Promise((r) => setTimeout(r, 50));
315
+ expect(handler).toHaveBeenCalledTimes(1);
316
+ // Zamknij transport — close powinno czekać na handler
317
+ const closePromise = transport.close();
318
+ // Handler jeszcze pracuje — pool.close() jeszcze nie powinien być wywołany
319
+ yield new Promise((r) => setTimeout(r, 20));
320
+ // Zakończ handler
321
+ handlerResolve();
322
+ yield closePromise;
323
+ // Pool zamknięty dopiero po zakończeniu handlera
324
+ expect(mockPool.close).toHaveBeenCalled();
325
+ }));
326
+ });
327
+ describe('recoverPending()', () => {
328
+ /**
329
+ * Pomocnik — wyciąga wewnętrzne komponenty fasady do testowania recovery
330
+ * Po refaktorze SRP: recoverPending jest w PendingRecovery, processMessage w StreamConsumer
331
+ */
332
+ const getInternals = (t) => {
333
+ const any = t;
334
+ return {
335
+ recovery: any['recovery'],
336
+ consumer: any['consumer'],
337
+ };
338
+ };
339
+ it('powinien przetworzyć wiadomość po XCLAIM', () => __awaiter(void 0, void 0, void 0, function* () {
340
+ jest.useRealTimers();
341
+ const handler = jest.fn().mockResolvedValue('ok');
342
+ const consumerConn = createMockConnection();
343
+ mockPool.createDedicated.mockReturnValue(consumerConn);
344
+ // Symuluj XPENDING z 1 stalled wiadomością
345
+ const testPayload = Buffer.from('recovered-data').toString('base64');
346
+ mockConn.xpending.mockResolvedValue([['msg-1', 'consumer-old', 60000, 1]]);
347
+ // XCLAIM zwraca dane wiadomości
348
+ mockConn.xclaim.mockResolvedValue([['msg-1', ['data', testPayload]]]);
349
+ yield transport.consume('cmd:Test', 'workers', handler);
350
+ // Wywołujemy recoverPending bezpośrednio przez wewnętrzny komponent recovery
351
+ const { recovery, consumer } = getInternals(transport);
352
+ yield recovery['recoverPending']('cmd:Test', 'workers', handler, consumer);
353
+ // Poczekaj na przetworzenie
354
+ yield new Promise((r) => setTimeout(r, 50));
355
+ // Handler powinien być wywołany z recovered danymi
356
+ expect(handler).toHaveBeenCalledWith(expect.any(Buffer));
357
+ // XACK powinno być wywołane
358
+ expect(mockConn.xack).toHaveBeenCalledWith('cmd:Test', 'workers', 'msg-1');
359
+ }));
360
+ it('powinien przenieść do DLQ po maxAttempts (D3)', () => __awaiter(void 0, void 0, void 0, function* () {
361
+ jest.useRealTimers();
362
+ const handler = jest.fn().mockResolvedValue('ok');
363
+ const consumerConn = createMockConnection();
364
+ mockPool.createDedicated.mockReturnValue(consumerConn);
365
+ const testPayload = Buffer.from('dead-data').toString('base64');
366
+ // Wiadomość z deliveryCount >= maxAttempts (3)
367
+ mockConn.xpending.mockResolvedValue([['msg-dead', 'consumer-old', 60000, 3]]);
368
+ // D3: XCLAIM teraz jest wywoływany żeby pobrać dane do DLQ
369
+ mockConn.xclaim.mockResolvedValue([['msg-dead', ['data', testPayload]]]);
370
+ yield transport.consume('cmd:Test', 'workers', handler);
371
+ const { recovery, consumer } = getInternals(transport);
372
+ yield recovery['recoverPending']('cmd:Test', 'workers', handler, consumer);
373
+ // XCLAIM powinno być wywołane (pobiera dane do DLQ)
374
+ expect(mockConn.xclaim).toHaveBeenCalled();
375
+ // XADD do DLQ strumienia
376
+ expect(mockConn.xadd).toHaveBeenCalledWith('dlq:cmd:Test', '*', 'data', testPayload, 'original_stream', 'cmd:Test', 'delivery_count', '3');
377
+ // XACK po przeniesieniu do DLQ
378
+ expect(mockConn.xack).toHaveBeenCalledWith('cmd:Test', 'workers', 'msg-dead');
379
+ // Handler NIE powinien być wywołany
380
+ expect(handler).not.toHaveBeenCalled();
381
+ // Powinien zalogować
382
+ expect(logger.error).toHaveBeenCalledWith('Wiadomość przeniesiona do Dead Letter Queue', expect.objectContaining({
383
+ messageId: 'msg-dead',
384
+ dlqStream: 'dlq:cmd:Test',
385
+ deliveryCount: 3,
386
+ }));
387
+ }));
388
+ it('nie powinien duplikować gdy wiadomość jest już przetwarzana', () => __awaiter(void 0, void 0, void 0, function* () {
389
+ jest.useRealTimers();
390
+ // Handler który trwa długo
391
+ let handlerCallCount = 0;
392
+ const handler = jest.fn().mockImplementation(() => __awaiter(void 0, void 0, void 0, function* () {
393
+ handlerCallCount++;
394
+ yield new Promise((r) => setTimeout(r, 100));
395
+ return 'ok';
396
+ }));
397
+ const consumerConn = createMockConnection();
398
+ const testPayload = Buffer.from('dedup-data').toString('base64');
399
+ // Consumer loop dostaje wiadomość — po niej domyślne interruptible blocking
400
+ consumerConn.xreadgroup.mockResolvedValueOnce([
401
+ ['cmd:Test', [['msg-dedup', ['data', testPayload]]]],
402
+ ]);
403
+ mockPool.createDedicated.mockReturnValue(consumerConn);
404
+ // XCLAIM też zwraca tę samą wiadomość
405
+ mockConn.xpending.mockResolvedValue([['msg-dedup', 'consumer-old', 60000, 1]]);
406
+ mockConn.xclaim.mockResolvedValue([['msg-dedup', ['data', testPayload]]]);
407
+ yield transport.consume('cmd:Test', 'workers', handler);
408
+ // Poczekaj aż consumer loop zacznie przetwarzać
409
+ yield new Promise((r) => setTimeout(r, 30));
410
+ // Recovery próbuje XCLAIM tej samej wiadomości — przez wewnętrzny komponent
411
+ const { recovery, consumer } = getInternals(transport);
412
+ yield recovery['recoverPending']('cmd:Test', 'workers', handler, consumer);
413
+ // Poczekaj na zakończenie
414
+ yield new Promise((r) => setTimeout(r, 200));
415
+ // Handler powinien być wywołany tylko RAZ (deduplicacja)
416
+ expect(handlerCallCount).toBe(1);
417
+ }));
418
+ });
419
+ });
420
+ //# sourceMappingURL=redis-streams-transport.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-streams-transport.spec.js","sourceRoot":"","sources":["../../../src/command-bus/transport/redis-streams-transport.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import type { ISerializer } from '../serialization/serializer.interface';
2
+ import type { IRpcTransport, RpcMessageMetadata } from './transport.interface';
3
+ import type RedisConnectionPool from '../../shared/redis/connection-pool';
4
+ import type RpcConnectionPool from '../../shared/redis/rpc-connection-pool';
5
+ /**
6
+ * Dane envelope dla RPC — wiadomość w strumieniu zawiera zarówno
7
+ * dane komendy jak i metadane RPC do routingu odpowiedzi
8
+ */
9
+ export interface RpcEnvelope {
10
+ /** Surowe dane komendy zakodowane jako base64 string */
11
+ commandData: string;
12
+ /** Metadane RPC */
13
+ rpc: RpcMessageMetadata;
14
+ }
15
+ /**
16
+ * Handler RPC — odpowiada za request/response przez Redis Streams + LPUSH/BRPOP
17
+ * Jedna odpowiedzialność: RPC call i respond
18
+ */
19
+ export declare class RpcHandler implements IRpcTransport {
20
+ private readonly pool;
21
+ private readonly rpcPool;
22
+ private readonly serializer;
23
+ constructor(pool: RedisConnectionPool, rpcPool: RpcConnectionPool, serializer: ISerializer);
24
+ /**
25
+ * RPC: wysyła request do strumienia i czeka na odpowiedź via BRPOP
26
+ *
27
+ * Flow (4 komendy Redis):
28
+ * 1. XADD — komenda z metadanymi RPC do strumienia
29
+ * 2. BRPOP — czekaj na odpowiedź na dedykowanym połączeniu
30
+ * 3. Worker: LPUSH — odpowiedź na klucz
31
+ * 4. DEL — cleanup klucza (TTL safety net)
32
+ */
33
+ rpcCall(commandName: string, data: Buffer, timeout: number): Promise<Buffer>;
34
+ /**
35
+ * RPC: odpowiada na request przez LPUSH + EXPIRE w jednym pipeline
36
+ * Wywoływane przez worker po przetworzeniu komendy RPC
37
+ */
38
+ rpcRespond(responseKey: string, data: Buffer, ttl: number): Promise<void>;
39
+ }
@@ -0,0 +1,87 @@
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.RpcHandler = void 0;
13
+ const crypto_1 = require("crypto");
14
+ const redis_codec_1 = require("./redis-codec");
15
+ /**
16
+ * Handler RPC — odpowiada za request/response przez Redis Streams + LPUSH/BRPOP
17
+ * Jedna odpowiedzialność: RPC call i respond
18
+ */
19
+ class RpcHandler {
20
+ constructor(pool, rpcPool, serializer) {
21
+ this.pool = pool;
22
+ this.rpcPool = rpcPool;
23
+ this.serializer = serializer;
24
+ }
25
+ /**
26
+ * RPC: wysyła request do strumienia i czeka na odpowiedź via BRPOP
27
+ *
28
+ * Flow (4 komendy Redis):
29
+ * 1. XADD — komenda z metadanymi RPC do strumienia
30
+ * 2. BRPOP — czekaj na odpowiedź na dedykowanym połączeniu
31
+ * 3. Worker: LPUSH — odpowiedź na klucz
32
+ * 4. DEL — cleanup klucza (TTL safety net)
33
+ */
34
+ rpcCall(commandName, data, timeout) {
35
+ return __awaiter(this, void 0, void 0, function* () {
36
+ // M1: Walidacja timeout — zapobiega infinite BRPOP przy 0/negatywnych/Infinity
37
+ if (!Number.isFinite(timeout) || timeout <= 0) {
38
+ throw new Error(`Nieprawidłowy timeout RPC: ${timeout}ms`);
39
+ }
40
+ const correlationId = (0, crypto_1.randomUUID)();
41
+ const responseKey = `rpc:res:${correlationId}`;
42
+ const envelope = {
43
+ commandData: redis_codec_1.RedisCodec.encode(data),
44
+ rpc: { correlationId, responseKey },
45
+ };
46
+ const envelopeBuffer = this.serializer.serialize(envelope);
47
+ // Wyślij do strumienia z markerem 'rpc' do identyfikacji
48
+ const poolConn = this.pool.next();
49
+ yield poolConn.xadd(`cmd:${commandName}`, '*', 'data', redis_codec_1.RedisCodec.encode(envelopeBuffer), 'rpc', '1');
50
+ // Pobierz dedykowane połączenie dla BRPOP (blocking)
51
+ const rpcConn = yield this.rpcPool.acquire();
52
+ try {
53
+ const timeoutSec = Math.max(1, Math.ceil(timeout / 1000));
54
+ const result = yield rpcConn.brpop(responseKey, timeoutSec);
55
+ if (!result) {
56
+ // C2: await DEL z fallback EXPIRE — zapobiega orphaned Redis keys
57
+ yield poolConn.del(responseKey).catch(() => {
58
+ // Fallback: EXPIRE zapewnia automatyczne usunięcie klucza po 120s
59
+ void poolConn.expire(responseKey, 120).catch(() => undefined);
60
+ });
61
+ // M2: bez correlationId w error message — zapobiega information leakage
62
+ throw new Error(`RPC timeout dla komendy ${commandName} (${timeout}ms)`);
63
+ }
64
+ const value = result[1];
65
+ return redis_codec_1.RedisCodec.decode(value);
66
+ }
67
+ finally {
68
+ this.rpcPool.release(rpcConn);
69
+ }
70
+ });
71
+ }
72
+ /**
73
+ * RPC: odpowiada na request przez LPUSH + EXPIRE w jednym pipeline
74
+ * Wywoływane przez worker po przetworzeniu komendy RPC
75
+ */
76
+ rpcRespond(responseKey, data, ttl) {
77
+ return __awaiter(this, void 0, void 0, function* () {
78
+ const conn = this.pool.next();
79
+ const pipeline = conn.pipeline();
80
+ pipeline.lpush(responseKey, redis_codec_1.RedisCodec.encode(data));
81
+ pipeline.expire(responseKey, ttl);
82
+ yield pipeline.exec();
83
+ });
84
+ }
85
+ }
86
+ exports.RpcHandler = RpcHandler;
87
+ //# sourceMappingURL=rpc-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc-handler.js","sourceRoot":"","sources":["../../../src/command-bus/transport/rpc-handler.ts"],"names":[],"mappings":"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"}