pp-command-bus 1.5.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.
Files changed (193) 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 +75 -21
  8. package/dist/command-bus/config/command-bus-config.js +99 -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 +39 -52
  13. package/dist/command-bus/index.js +133 -126
  14. package/dist/command-bus/index.js.map +1 -1
  15. package/dist/command-bus/logging/command-logger.d.ts +2 -0
  16. package/dist/command-bus/logging/command-logger.js +7 -0
  17. package/dist/command-bus/logging/command-logger.js.map +1 -1
  18. package/dist/command-bus/logging/command-logger.spec.js +36 -0
  19. package/dist/command-bus/logging/command-logger.spec.js.map +1 -1
  20. package/dist/command-bus/serialization/index.d.ts +6 -0
  21. package/dist/command-bus/serialization/index.js +9 -0
  22. package/dist/command-bus/serialization/index.js.map +1 -0
  23. package/dist/command-bus/serialization/msgpack-serializer.d.ts +26 -0
  24. package/dist/command-bus/serialization/msgpack-serializer.js +70 -0
  25. package/dist/command-bus/serialization/msgpack-serializer.js.map +1 -0
  26. package/dist/command-bus/serialization/msgpack-serializer.spec.js +223 -0
  27. package/dist/command-bus/serialization/msgpack-serializer.spec.js.map +1 -0
  28. package/dist/command-bus/serialization/serializer.interface.d.ts +21 -0
  29. package/dist/command-bus/serialization/serializer.interface.js +3 -0
  30. package/dist/command-bus/serialization/serializer.interface.js.map +1 -0
  31. package/dist/command-bus/transport/consumer-loop.d.ts +45 -0
  32. package/dist/command-bus/transport/consumer-loop.js +90 -0
  33. package/dist/command-bus/transport/consumer-loop.js.map +1 -0
  34. package/dist/command-bus/transport/consumer-loop.spec.js +216 -0
  35. package/dist/command-bus/transport/consumer-loop.spec.js.map +1 -0
  36. package/dist/command-bus/transport/index.d.ts +21 -0
  37. package/dist/command-bus/transport/index.js +23 -0
  38. package/dist/command-bus/transport/index.js.map +1 -0
  39. package/dist/command-bus/transport/message-processor.d.ts +59 -0
  40. package/dist/command-bus/transport/message-processor.js +111 -0
  41. package/dist/command-bus/transport/message-processor.js.map +1 -0
  42. package/dist/command-bus/transport/message-processor.spec.js +185 -0
  43. package/dist/command-bus/transport/message-processor.spec.js.map +1 -0
  44. package/dist/command-bus/transport/pending-recovery.d.ts +54 -0
  45. package/dist/command-bus/transport/pending-recovery.js +139 -0
  46. package/dist/command-bus/transport/pending-recovery.js.map +1 -0
  47. package/dist/command-bus/transport/pending-recovery.spec.js +176 -0
  48. package/dist/command-bus/transport/pending-recovery.spec.js.map +1 -0
  49. package/dist/command-bus/transport/redis-codec.d.ts +24 -0
  50. package/dist/command-bus/transport/redis-codec.js +33 -0
  51. package/dist/command-bus/transport/redis-codec.js.map +1 -0
  52. package/dist/command-bus/transport/redis-codec.spec.js +53 -0
  53. package/dist/command-bus/transport/redis-codec.spec.js.map +1 -0
  54. package/dist/command-bus/transport/redis-streams-transport.d.ts +91 -0
  55. package/dist/command-bus/transport/redis-streams-transport.js +134 -0
  56. package/dist/command-bus/transport/redis-streams-transport.js.map +1 -0
  57. package/dist/command-bus/transport/redis-streams-transport.spec.js +420 -0
  58. package/dist/command-bus/transport/redis-streams-transport.spec.js.map +1 -0
  59. package/dist/command-bus/transport/rpc-handler.d.ts +39 -0
  60. package/dist/command-bus/transport/rpc-handler.js +87 -0
  61. package/dist/command-bus/transport/rpc-handler.js.map +1 -0
  62. package/dist/command-bus/transport/rpc-handler.spec.js +157 -0
  63. package/dist/command-bus/transport/rpc-handler.spec.js.map +1 -0
  64. package/dist/command-bus/transport/stream-consumer.d.ts +89 -0
  65. package/dist/command-bus/transport/stream-consumer.js +181 -0
  66. package/dist/command-bus/transport/stream-consumer.js.map +1 -0
  67. package/dist/command-bus/transport/stream-consumer.spec.js +284 -0
  68. package/dist/command-bus/transport/stream-consumer.spec.js.map +1 -0
  69. package/dist/command-bus/transport/stream-producer.d.ts +23 -0
  70. package/dist/command-bus/transport/stream-producer.js +70 -0
  71. package/dist/command-bus/transport/stream-producer.js.map +1 -0
  72. package/dist/command-bus/transport/stream-producer.spec.js +125 -0
  73. package/dist/command-bus/transport/stream-producer.spec.js.map +1 -0
  74. package/dist/command-bus/transport/transport.interface.d.ts +87 -0
  75. package/dist/command-bus/transport/transport.interface.js +3 -0
  76. package/dist/command-bus/transport/transport.interface.js.map +1 -0
  77. package/dist/command-bus/types/index.d.ts +0 -84
  78. package/dist/examples/rpc.demo.js +1 -1
  79. package/dist/examples/rpc.demo.js.map +1 -1
  80. package/dist/index.d.ts +8 -5
  81. package/dist/index.js +6 -4
  82. package/dist/index.js.map +1 -1
  83. package/dist/pp-command-bus-2.0.0.tgz +0 -0
  84. package/dist/shared/redis/connection-pool.d.ts +54 -0
  85. package/dist/shared/redis/connection-pool.js +117 -0
  86. package/dist/shared/redis/connection-pool.js.map +1 -0
  87. package/dist/shared/redis/connection-pool.spec.js +114 -0
  88. package/dist/shared/redis/connection-pool.spec.js.map +1 -0
  89. package/dist/shared/redis/index.d.ts +5 -3
  90. package/dist/shared/redis/index.js +6 -4
  91. package/dist/shared/redis/index.js.map +1 -1
  92. package/dist/shared/redis/rpc-connection-pool.d.ts +61 -0
  93. package/dist/shared/redis/rpc-connection-pool.js +154 -0
  94. package/dist/shared/redis/rpc-connection-pool.js.map +1 -0
  95. package/dist/shared/redis/rpc-connection-pool.spec.d.ts +1 -0
  96. package/dist/shared/redis/rpc-connection-pool.spec.js +173 -0
  97. package/dist/shared/redis/rpc-connection-pool.spec.js.map +1 -0
  98. package/dist/shared/types.d.ts +0 -4
  99. package/dist/shared/utils/error-utils.d.ts +8 -0
  100. package/dist/shared/utils/error-utils.js +14 -0
  101. package/dist/shared/utils/error-utils.js.map +1 -0
  102. package/package.json +12 -12
  103. package/dist/command-bus/config/auto-config-optimizer.d.ts +0 -35
  104. package/dist/command-bus/config/auto-config-optimizer.js +0 -52
  105. package/dist/command-bus/config/auto-config-optimizer.js.map +0 -1
  106. package/dist/command-bus/config/auto-config-optimizer.spec.js +0 -42
  107. package/dist/command-bus/config/auto-config-optimizer.spec.js.map +0 -1
  108. package/dist/command-bus/job/index.d.ts +0 -6
  109. package/dist/command-bus/job/index.js +0 -15
  110. package/dist/command-bus/job/index.js.map +0 -1
  111. package/dist/command-bus/job/job-options-builder.d.ts +0 -21
  112. package/dist/command-bus/job/job-options-builder.js +0 -58
  113. package/dist/command-bus/job/job-options-builder.js.map +0 -1
  114. package/dist/command-bus/job/job-options-builder.spec.js +0 -156
  115. package/dist/command-bus/job/job-options-builder.spec.js.map +0 -1
  116. package/dist/command-bus/job/job-processor.d.ts +0 -39
  117. package/dist/command-bus/job/job-processor.js +0 -203
  118. package/dist/command-bus/job/job-processor.js.map +0 -1
  119. package/dist/command-bus/job/job-processor.spec.js +0 -436
  120. package/dist/command-bus/job/job-processor.spec.js.map +0 -1
  121. package/dist/command-bus/queue/index.d.ts +0 -5
  122. package/dist/command-bus/queue/index.js +0 -13
  123. package/dist/command-bus/queue/index.js.map +0 -1
  124. package/dist/command-bus/queue/queue-manager.d.ts +0 -56
  125. package/dist/command-bus/queue/queue-manager.js +0 -163
  126. package/dist/command-bus/queue/queue-manager.js.map +0 -1
  127. package/dist/command-bus/queue/queue-manager.spec.js +0 -371
  128. package/dist/command-bus/queue/queue-manager.spec.js.map +0 -1
  129. package/dist/command-bus/rpc/index.d.ts +0 -11
  130. package/dist/command-bus/rpc/index.js +0 -19
  131. package/dist/command-bus/rpc/index.js.map +0 -1
  132. package/dist/command-bus/rpc/payload-compression.service.d.ts +0 -50
  133. package/dist/command-bus/rpc/payload-compression.service.js +0 -215
  134. package/dist/command-bus/rpc/payload-compression.service.js.map +0 -1
  135. package/dist/command-bus/rpc/payload-compression.service.spec.js +0 -376
  136. package/dist/command-bus/rpc/payload-compression.service.spec.js.map +0 -1
  137. package/dist/command-bus/rpc/rpc-coordinator.d.ts +0 -96
  138. package/dist/command-bus/rpc/rpc-coordinator.js +0 -500
  139. package/dist/command-bus/rpc/rpc-coordinator.js.map +0 -1
  140. package/dist/command-bus/rpc/rpc-coordinator.spec.js +0 -621
  141. package/dist/command-bus/rpc/rpc-coordinator.spec.js.map +0 -1
  142. package/dist/command-bus/rpc/rpc-job-cancellation.service.d.ts +0 -82
  143. package/dist/command-bus/rpc/rpc-job-cancellation.service.js +0 -180
  144. package/dist/command-bus/rpc/rpc-job-cancellation.service.js.map +0 -1
  145. package/dist/command-bus/rpc/rpc-job-cancellation.service.spec.js +0 -286
  146. package/dist/command-bus/rpc/rpc-job-cancellation.service.spec.js.map +0 -1
  147. package/dist/command-bus/worker/index.d.ts +0 -10
  148. package/dist/command-bus/worker/index.js +0 -19
  149. package/dist/command-bus/worker/index.js.map +0 -1
  150. package/dist/command-bus/worker/worker-benchmark.d.ts +0 -71
  151. package/dist/command-bus/worker/worker-benchmark.js +0 -202
  152. package/dist/command-bus/worker/worker-benchmark.js.map +0 -1
  153. package/dist/command-bus/worker/worker-benchmark.spec.js +0 -310
  154. package/dist/command-bus/worker/worker-benchmark.spec.js.map +0 -1
  155. package/dist/command-bus/worker/worker-metrics-collector.d.ts +0 -98
  156. package/dist/command-bus/worker/worker-metrics-collector.js +0 -242
  157. package/dist/command-bus/worker/worker-metrics-collector.js.map +0 -1
  158. package/dist/command-bus/worker/worker-orchestrator.d.ts +0 -70
  159. package/dist/command-bus/worker/worker-orchestrator.js +0 -339
  160. package/dist/command-bus/worker/worker-orchestrator.js.map +0 -1
  161. package/dist/command-bus/worker/worker-orchestrator.spec.js +0 -712
  162. package/dist/command-bus/worker/worker-orchestrator.spec.js.map +0 -1
  163. package/dist/examples/auto-config.demo.d.ts +0 -9
  164. package/dist/examples/auto-config.demo.js +0 -106
  165. package/dist/examples/auto-config.demo.js.map +0 -1
  166. package/dist/examples/rpc-compression.demo.d.ts +0 -5
  167. package/dist/examples/rpc-compression.demo.js +0 -358
  168. package/dist/examples/rpc-compression.demo.js.map +0 -1
  169. package/dist/examples/rpc-resilience.demo.d.ts +0 -15
  170. package/dist/examples/rpc-resilience.demo.js +0 -233
  171. package/dist/examples/rpc-resilience.demo.js.map +0 -1
  172. package/dist/pp-command-bus-1.5.0.tgz +0 -0
  173. package/dist/shared/config/base-config.d.ts +0 -54
  174. package/dist/shared/config/base-config.js +0 -114
  175. package/dist/shared/config/base-config.js.map +0 -1
  176. package/dist/shared/config/base-config.spec.js +0 -204
  177. package/dist/shared/config/base-config.spec.js.map +0 -1
  178. package/dist/shared/config/index.d.ts +0 -1
  179. package/dist/shared/config/index.js +0 -9
  180. package/dist/shared/config/index.js.map +0 -1
  181. package/dist/shared/redis/redis-connection-factory.d.ts +0 -66
  182. package/dist/shared/redis/redis-connection-factory.js +0 -113
  183. package/dist/shared/redis/redis-connection-factory.js.map +0 -1
  184. /package/dist/command-bus/{config/auto-config-optimizer.spec.d.ts → serialization/msgpack-serializer.spec.d.ts} +0 -0
  185. /package/dist/command-bus/{job/job-options-builder.spec.d.ts → transport/consumer-loop.spec.d.ts} +0 -0
  186. /package/dist/command-bus/{job/job-processor.spec.d.ts → transport/message-processor.spec.d.ts} +0 -0
  187. /package/dist/command-bus/{queue/queue-manager.spec.d.ts → transport/pending-recovery.spec.d.ts} +0 -0
  188. /package/dist/command-bus/{rpc/payload-compression.service.spec.d.ts → transport/redis-codec.spec.d.ts} +0 -0
  189. /package/dist/command-bus/{rpc/rpc-coordinator.spec.d.ts → transport/redis-streams-transport.spec.d.ts} +0 -0
  190. /package/dist/command-bus/{rpc/rpc-job-cancellation.service.spec.d.ts → transport/rpc-handler.spec.d.ts} +0 -0
  191. /package/dist/command-bus/{worker/worker-benchmark.spec.d.ts → transport/stream-consumer.spec.d.ts} +0 -0
  192. /package/dist/command-bus/{worker/worker-orchestrator.spec.d.ts → transport/stream-producer.spec.d.ts} +0 -0
  193. /package/dist/shared/{config/base-config.spec.d.ts → redis/connection-pool.spec.d.ts} +0 -0
@@ -1,621 +0,0 @@
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 rpc_coordinator_1 = __importDefault(require("./rpc-coordinator"));
16
- const command_1 = __importDefault(require("../command"));
17
- describe('RpcCoordinator - Shared Subscriber Pattern z Redis Pub/Sub', () => {
18
- let rpcCoordinator;
19
- let mockLogger;
20
- let mockRedisConnection;
21
- let mockSharedSubscriber;
22
- let mockCompressionService;
23
- let mockCancellationService;
24
- let mockRemoveJobCallback;
25
- let processInstanceId;
26
- // Mock Command class for testing
27
- class TestCommand extends command_1.default {
28
- constructor(payload) {
29
- super(payload);
30
- }
31
- }
32
- beforeEach(() => {
33
- var _a, _b;
34
- jest.clearAllMocks();
35
- mockLogger = {
36
- log: jest.fn(),
37
- error: jest.fn(),
38
- warn: jest.fn(),
39
- debug: jest.fn(),
40
- };
41
- // Mock Shared Subscriber
42
- mockSharedSubscriber = {
43
- psubscribe: jest
44
- .fn()
45
- .mockImplementation((_pattern, callback) => {
46
- // Wywołaj callback natychmiast jeśli został przekazany
47
- if (callback) {
48
- setImmediate(() => callback(null, 1));
49
- }
50
- return Promise.resolve(undefined);
51
- }),
52
- punsubscribe: jest.fn().mockResolvedValue(undefined),
53
- on: jest.fn().mockReturnThis(),
54
- once: jest.fn().mockImplementation((event, handler) => {
55
- // Automatycznie wywołaj handler 'ready' natychmiast
56
- if (event === 'ready') {
57
- setImmediate(() => handler());
58
- }
59
- return mockSharedSubscriber;
60
- }),
61
- removeAllListeners: jest.fn().mockReturnThis(),
62
- quit: jest.fn().mockResolvedValue(undefined),
63
- };
64
- // Mock Redis connection
65
- mockRedisConnection = {
66
- duplicate: jest.fn().mockReturnValue(mockSharedSubscriber),
67
- };
68
- // Mock PayloadCompressionService
69
- mockCompressionService = {
70
- compress: jest.fn().mockResolvedValue({ data: '{}', compressed: false }),
71
- decompress: jest.fn().mockImplementation((message) => {
72
- return Promise.resolve(JSON.parse(message));
73
- }),
74
- };
75
- // Mock RpcJobCancellationService
76
- mockCancellationService = {
77
- markAsCancelled: jest.fn().mockResolvedValue(undefined),
78
- isCancelled: jest.fn().mockResolvedValue(false),
79
- clearCancellation: jest.fn().mockResolvedValue(undefined),
80
- tryRemoveJob: jest.fn().mockResolvedValue(false),
81
- };
82
- // Mock RemoveJobCallback
83
- mockRemoveJobCallback = jest.fn().mockResolvedValue(false);
84
- rpcCoordinator = new rpc_coordinator_1.default(mockLogger, mockRedisConnection, mockCompressionService, mockCancellationService, mockRemoveJobCallback);
85
- // Ekstraktuj processInstanceId z logów debug
86
- const debugCalls = mockLogger.debug.mock.calls;
87
- const initCall = debugCalls.find((call) => call[0] === 'Inicjalizacja RpcCoordinator');
88
- processInstanceId = (_b = (_a = initCall === null || initCall === void 0 ? void 0 : initCall[1]) === null || _a === void 0 ? void 0 : _a.processInstanceId) !== null && _b !== void 0 ? _b : 'test-process-id';
89
- });
90
- afterEach(() => __awaiter(void 0, void 0, void 0, function* () {
91
- // Zamknij RpcCoordinator
92
- yield rpcCoordinator.close();
93
- // Wyczyść wszystkie pending promises
94
- yield new Promise((resolve) => setImmediate(resolve));
95
- }));
96
- describe('Konstrukcja i inicjalizacja', () => {
97
- it('powinno utworzyć shared subscriber przy konstrukcji', () => __awaiter(void 0, void 0, void 0, function* () {
98
- // Verify: duplicate() called once dla shared subscriber
99
- expect(mockRedisConnection.duplicate).toHaveBeenCalledTimes(1);
100
- // Poczekaj na async ready event
101
- yield new Promise((resolve) => setImmediate(resolve));
102
- // Verify: psubscribe z pattern zawierającym processInstanceId
103
- expect(mockSharedSubscriber.psubscribe).toHaveBeenCalledTimes(1);
104
- const pattern = mockSharedSubscriber.psubscribe.mock.calls[0][0];
105
- expect(pattern).toMatch(/^rpc:response:[0-9a-f-]{36}:\*$/); // UUID pattern
106
- expect(pattern).toBe(`rpc:response:${processInstanceId}:*`);
107
- }));
108
- it('powinno zarejestrować handlery pmessage i error', () => {
109
- // Verify: pmessage handler zarejestrowany
110
- const pmessageCalls = mockSharedSubscriber.on.mock.calls.filter((call) => call[0] === 'pmessage');
111
- expect(pmessageCalls).toHaveLength(1);
112
- // Verify: error handler zarejestrowany
113
- const errorCalls = mockSharedSubscriber.on.mock.calls.filter((call) => call[0] === 'error');
114
- expect(errorCalls).toHaveLength(1);
115
- });
116
- });
117
- describe('Gotowość subscribera - waitForSubscriberReady', () => {
118
- it('powinno poczekać na gotowość subscribera przed rejestrowaniem call', () => __awaiter(void 0, void 0, void 0, function* () {
119
- // Given
120
- const correlationId = 'ready-test-123';
121
- const commandName = 'TestCommand';
122
- const timeout = 100;
123
- const responseChannel = `rpc:response:${processInstanceId}:${correlationId}`;
124
- // Symuluj odpowiedź RPC natychmiast (subscriber już gotowy dzięki mock)
125
- setTimeout(() => {
126
- var _a;
127
- const pmessageHandler = (_a = mockSharedSubscriber.on.mock.calls.find((call) => call[0] === 'pmessage')) === null || _a === void 0 ? void 0 : _a[1];
128
- if (pmessageHandler) {
129
- const wrapper = {
130
- data: JSON.stringify({
131
- correlationId,
132
- result: { success: true },
133
- error: null,
134
- }),
135
- compressed: false,
136
- };
137
- pmessageHandler(`rpc:response:${processInstanceId}:*`, responseChannel, JSON.stringify(wrapper));
138
- }
139
- }, 10);
140
- // When
141
- const promise = rpcCoordinator.registerCall(correlationId, commandName, timeout);
142
- // Then
143
- yield expect(promise).resolves.toEqual({ success: true });
144
- }));
145
- it('powinno rzucić wyjątek timeout gdy subscriber nie jest gotowy w ciągu 5s', () => __awaiter(void 0, void 0, void 0, function* () {
146
- // Use fake timers dla tego testu
147
- jest.useFakeTimers();
148
- try {
149
- // Given: Utwórz nowy coordinator z subscriber który NIGDY nie będzie gotowy
150
- const slowSubscriber = {
151
- psubscribe: jest.fn().mockImplementation(() => {
152
- // NIE wywołujemy callback - subscriber nigdy nie będzie gotowy
153
- return Promise.resolve(undefined);
154
- }),
155
- on: jest.fn().mockReturnThis(),
156
- once: jest.fn().mockReturnThis(), // NIE wywołujemy 'ready' event
157
- removeAllListeners: jest.fn().mockReturnThis(),
158
- quit: jest.fn().mockResolvedValue(undefined),
159
- };
160
- const slowRedisConnection = {
161
- duplicate: jest.fn().mockReturnValue(slowSubscriber),
162
- };
163
- const slowCoordinator = new rpc_coordinator_1.default(mockLogger, slowRedisConnection, mockCompressionService);
164
- const correlationId = 'timeout-subscriber-test';
165
- const commandName = 'TestCommand';
166
- const timeout = 100; // Timeout dla RPC call (nie subscriber ready)
167
- // When
168
- const promise = slowCoordinator.registerCall(correlationId, commandName, timeout);
169
- // Fast-forward time
170
- jest.advanceTimersByTime(5000);
171
- // Then
172
- yield expect(promise).rejects.toThrow('Subscriber timeout: nie udało się nawiązać połączenia w ciągu 5000ms');
173
- // Cleanup
174
- yield slowCoordinator.close().catch(() => {
175
- /* ignore */
176
- });
177
- }
178
- finally {
179
- // Przywróć rzeczywiste timery
180
- jest.useRealTimers();
181
- }
182
- }));
183
- });
184
- describe('registerCall - shared subscriber', () => {
185
- it('powinno użyć shared subscriber zamiast tworzyć nowe połączenie', () => __awaiter(void 0, void 0, void 0, function* () {
186
- // Given
187
- const correlationId = 'test-correlation-123';
188
- const commandName = 'TestCommand';
189
- const timeout = 100;
190
- const responseChannel = `rpc:response:${processInstanceId}:${correlationId}`;
191
- // Symuluj odpowiedź RPC przez pmessage handler
192
- setTimeout(() => {
193
- var _a;
194
- const pmessageHandler = (_a = mockSharedSubscriber.on.mock.calls.find((call) => call[0] === 'pmessage')) === null || _a === void 0 ? void 0 : _a[1];
195
- if (pmessageHandler) {
196
- // Wrapper z metadanymi kompresji
197
- const wrapper = {
198
- data: JSON.stringify({
199
- correlationId,
200
- result: { success: true },
201
- error: null,
202
- }),
203
- compressed: false,
204
- };
205
- pmessageHandler(`rpc:response:${processInstanceId}:*`, responseChannel, JSON.stringify(wrapper));
206
- }
207
- }, 10);
208
- // When
209
- const promise = rpcCoordinator.registerCall(correlationId, commandName, timeout);
210
- // Then
211
- yield expect(promise).resolves.toEqual({ success: true });
212
- // Verify: NIE wywołano duplicate() ponownie (używa shared subscriber)
213
- expect(mockRedisConnection.duplicate).toHaveBeenCalledTimes(1);
214
- // Verify: NIE wywołano subscribe/unsubscribe per call (używa psubscribe)
215
- expect(mockSharedSubscriber.psubscribe).toHaveBeenCalledTimes(1); // Tylko przy konstrukcji
216
- }));
217
- it('powinno reject przy timeout', () => __awaiter(void 0, void 0, void 0, function* () {
218
- // Given
219
- const correlationId = 'timeout-test';
220
- const commandName = 'TestCommand';
221
- const timeout = 50; // Krótki timeout
222
- // When & Then
223
- yield expect(rpcCoordinator.registerCall(correlationId, commandName, timeout)).rejects.toThrow('RPC timeout');
224
- }));
225
- it('powinno reject przy błędzie w odpowiedzi', () => __awaiter(void 0, void 0, void 0, function* () {
226
- // Given
227
- const correlationId = 'error-test';
228
- const commandName = 'TestCommand';
229
- const timeout = 100;
230
- const responseChannel = `rpc:response:${processInstanceId}:${correlationId}`;
231
- // Symuluj odpowiedź z błędem
232
- setTimeout(() => {
233
- var _a;
234
- const pmessageHandler = (_a = mockSharedSubscriber.on.mock.calls.find((call) => call[0] === 'pmessage')) === null || _a === void 0 ? void 0 : _a[1];
235
- if (pmessageHandler) {
236
- const wrapper = {
237
- data: JSON.stringify({
238
- correlationId,
239
- result: null,
240
- error: 'Test error',
241
- }),
242
- compressed: false,
243
- };
244
- pmessageHandler(`rpc:response:${processInstanceId}:*`, responseChannel, JSON.stringify(wrapper));
245
- }
246
- }, 10);
247
- // When & Then
248
- yield expect(rpcCoordinator.registerCall(correlationId, commandName, timeout)).rejects.toThrow('Test error');
249
- }));
250
- });
251
- describe('prepareRpcCommand', () => {
252
- it('powinno dodać metadata z responseChannel zawierającym processInstanceId', () => {
253
- // Given
254
- const command = new TestCommand({ value: 42 });
255
- const responseChannel = `rpc:response:${processInstanceId}:${command.__id}`;
256
- // When
257
- const result = rpcCoordinator.prepareRpcCommand(command);
258
- // Then
259
- expect(result.__rpcMetadata).toBeDefined();
260
- expect(result.__rpcMetadata.correlationId).toBe(command.__id);
261
- expect(result.__rpcMetadata.responseChannel).toBe(responseChannel);
262
- expect(result.__rpcMetadata.responseChannel).toContain(processInstanceId);
263
- expect(result.__rpcMetadata.timestamp).toBeDefined();
264
- });
265
- });
266
- describe('Izolacja processInstanceId', () => {
267
- it('powinno ignorować wiadomości z innym processInstanceId', () => __awaiter(void 0, void 0, void 0, function* () {
268
- // Given
269
- const correlationId = 'isolation-test';
270
- const commandName = 'TestCommand';
271
- const timeout = 100;
272
- const wrongProcessId = 'wrong-process-id-123';
273
- // Symuluj odpowiedź z INNYM processInstanceId
274
- setTimeout(() => {
275
- var _a;
276
- const pmessageHandler = (_a = mockSharedSubscriber.on.mock.calls.find((call) => call[0] === 'pmessage')) === null || _a === void 0 ? void 0 : _a[1];
277
- if (pmessageHandler) {
278
- const wrapper = {
279
- data: JSON.stringify({
280
- correlationId,
281
- result: { wrong: true },
282
- error: null,
283
- }),
284
- compressed: false,
285
- };
286
- pmessageHandler(`rpc:response:${wrongProcessId}:*`, `rpc:response:${wrongProcessId}:${correlationId}`, JSON.stringify(wrapper));
287
- }
288
- }, 10);
289
- // When
290
- const promise = rpcCoordinator.registerCall(correlationId, commandName, timeout);
291
- // Then: Powinno timeout (wiadomość zignorowana)
292
- yield expect(promise).rejects.toThrow('RPC timeout');
293
- // Verify: Log warning o wiadomości dla innego procesu
294
- expect(mockLogger.warn).toHaveBeenCalledWith('Otrzymano wiadomość dla innego procesu', expect.objectContaining({
295
- processInstanceId,
296
- receivedProcessId: wrongProcessId,
297
- correlationId,
298
- }));
299
- }));
300
- it('powinno ignorować wiadomości dla nieistniejących pending calls', () => __awaiter(void 0, void 0, void 0, function* () {
301
- var _a;
302
- // Given
303
- const nonExistentCorrelationId = 'non-existent-123';
304
- const responseChannel = `rpc:response:${processInstanceId}:${nonExistentCorrelationId}`;
305
- // Symuluj odpowiedź dla nieistniejącego call
306
- const pmessageHandler = (_a = mockSharedSubscriber.on.mock.calls.find((call) => call[0] === 'pmessage')) === null || _a === void 0 ? void 0 : _a[1];
307
- if (pmessageHandler) {
308
- const wrapper = {
309
- data: JSON.stringify({
310
- correlationId: nonExistentCorrelationId,
311
- result: { data: 'test' },
312
- error: null,
313
- }),
314
- compressed: false,
315
- };
316
- pmessageHandler(`rpc:response:${processInstanceId}:*`, responseChannel, JSON.stringify(wrapper));
317
- }
318
- // Wait for async handling
319
- yield new Promise((resolve) => setTimeout(resolve, 50));
320
- // Verify: Log warning o nieistniejącym pending call
321
- expect(mockLogger.warn).toHaveBeenCalledWith('Otrzymano odpowiedź dla nieistniejącego RPC call', expect.objectContaining({
322
- processInstanceId,
323
- correlationId: nonExistentCorrelationId,
324
- }));
325
- }));
326
- });
327
- describe('Concurrent RPC calls - Shared Subscriber', () => {
328
- it('powinno obsłużyć 16 jednoczesnych RPC calls używając 1 shared subscriber', () => __awaiter(void 0, void 0, void 0, function* () {
329
- // Given
330
- const concurrentCalls = 16;
331
- const timeout = 200;
332
- const responseDelay = 50;
333
- // When: Wystartuj 16 równoległych RPC calls
334
- const promises = Array.from({ length: concurrentCalls }, (_, index) => {
335
- const correlationId = `concurrent-test-${index}`;
336
- const commandName = 'ConcurrentTestCommand';
337
- const responseChannel = `rpc:response:${processInstanceId}:${correlationId}`;
338
- // Symuluj odpowiedź RPC dla każdego call
339
- setTimeout(() => {
340
- var _a;
341
- const pmessageHandler = (_a = mockSharedSubscriber.on.mock.calls.find((call) => call[0] === 'pmessage')) === null || _a === void 0 ? void 0 : _a[1];
342
- if (pmessageHandler) {
343
- const wrapper = {
344
- data: JSON.stringify({
345
- correlationId,
346
- result: { index, success: true },
347
- error: null,
348
- }),
349
- compressed: false,
350
- };
351
- pmessageHandler(`rpc:response:${processInstanceId}:*`, responseChannel, JSON.stringify(wrapper));
352
- }
353
- }, responseDelay);
354
- return rpcCoordinator.registerCall(correlationId, commandName, timeout);
355
- });
356
- // Then: Wszystkie 16 calls powinno się zakończyć sukcesem
357
- const results = yield Promise.all(promises);
358
- // Weryfikacja: wszystkie wyniki są poprawne
359
- expect(results).toHaveLength(concurrentCalls);
360
- results.forEach((result, index) => {
361
- expect(result).toEqual({ index, success: true });
362
- });
363
- // Weryfikacja: TYLKO 1 duplicate() (shared subscriber)
364
- expect(mockRedisConnection.duplicate).toHaveBeenCalledTimes(1);
365
- // Weryfikacja: TYLKO 1 psubscribe (przy konstrukcji)
366
- expect(mockSharedSubscriber.psubscribe).toHaveBeenCalledTimes(1);
367
- }), 10000);
368
- it('powinno obsłużyć race condition - odpowiedź publikowana natychmiast po rejestracji', () => __awaiter(void 0, void 0, void 0, function* () {
369
- var _a;
370
- // Given: Worker bardzo szybki - publikuje odpowiedź natychmiast po rejestracji
371
- const correlationId = 'race-condition-test';
372
- const commandName = 'FastCommand';
373
- const timeout = 100;
374
- const responseChannel = `rpc:response:${processInstanceId}:${correlationId}`;
375
- // When: Rejestruj call i natychmiast po tym publikuj odpowiedź
376
- const resultPromise = rpcCoordinator.registerCall(correlationId, commandName, timeout);
377
- // Poczekaj minimalny czas na rejestrację pending call w Map
378
- // (registerCall() jest async ze względu na waitForSubscriberReady)
379
- yield new Promise((resolve) => setTimeout(resolve, 5));
380
- // Symuluj NATYCHMIASTOWĄ odpowiedź po rejestracji
381
- const pmessageHandler = (_a = mockSharedSubscriber.on.mock.calls.find((call) => call[0] === 'pmessage')) === null || _a === void 0 ? void 0 : _a[1];
382
- if (pmessageHandler) {
383
- const wrapper = {
384
- data: JSON.stringify({
385
- correlationId,
386
- result: { veryFast: true },
387
- error: null,
388
- }),
389
- compressed: false,
390
- };
391
- pmessageHandler(`rpc:response:${processInstanceId}:*`, responseChannel, JSON.stringify(wrapper));
392
- }
393
- const result = yield resultPromise;
394
- // Then: Powinna dotrzeć odpowiedź (shared subscriber zawsze gotowy)
395
- expect(result).toEqual({ veryFast: true });
396
- // Verify: Brak timeout
397
- expect(mockLogger.error).not.toHaveBeenCalledWith('RPC timeout', expect.any(Object));
398
- }));
399
- });
400
- describe('close()', () => {
401
- it('powinno reject wszystkie pending calls przy zamykaniu', () => __awaiter(void 0, void 0, void 0, function* () {
402
- // Given: Zarejestruj 3 pending calls
403
- const promises = Array.from({ length: 3 }, (_, index) => {
404
- const correlationId = `close-test-${index}`;
405
- return rpcCoordinator
406
- .registerCall(correlationId, 'TestCommand', 1000)
407
- .catch((error) => ({ rejected: true, error: error.message }));
408
- });
409
- // Wait for calls to register
410
- yield new Promise((resolve) => setTimeout(resolve, 50));
411
- // When: Zamknij coordinator
412
- yield rpcCoordinator.close();
413
- // Then: Wszystkie calls zostały rejected
414
- const results = yield Promise.all(promises);
415
- results.forEach((result) => {
416
- expect(result).toEqual({
417
- rejected: true,
418
- error: 'RpcCoordinator is closing',
419
- });
420
- });
421
- // Verify: Zamknięcie shared subscriber
422
- expect(mockSharedSubscriber.punsubscribe).toHaveBeenCalledWith(`rpc:response:${processInstanceId}:*`);
423
- expect(mockSharedSubscriber.quit).toHaveBeenCalled();
424
- }));
425
- });
426
- describe('setJobId() - powiązanie pending call z jobem BullMQ', () => {
427
- it('powinno ustawić jobId i queueName dla istniejącego pending call', () => __awaiter(void 0, void 0, void 0, function* () {
428
- // Given
429
- const correlationId = 'set-job-id-test';
430
- const commandName = 'TestCommand';
431
- const timeout = 200;
432
- const jobId = 'job-123';
433
- const queueName = 'TestQueue';
434
- const responseChannel = `rpc:response:${processInstanceId}:${correlationId}`;
435
- // Symuluj odpowiedź RPC
436
- setTimeout(() => {
437
- var _a;
438
- const pmessageHandler = (_a = mockSharedSubscriber.on.mock.calls.find((call) => call[0] === 'pmessage')) === null || _a === void 0 ? void 0 : _a[1];
439
- if (pmessageHandler) {
440
- const wrapper = {
441
- data: JSON.stringify({
442
- correlationId,
443
- result: { success: true },
444
- error: null,
445
- }),
446
- compressed: false,
447
- };
448
- pmessageHandler(`rpc:response:${processInstanceId}:*`, responseChannel, JSON.stringify(wrapper));
449
- }
450
- }, 50);
451
- // When
452
- const promise = rpcCoordinator.registerCall(correlationId, commandName, timeout);
453
- // Poczekaj na rejestrację pending call
454
- yield new Promise((resolve) => setTimeout(resolve, 10));
455
- // Ustaw jobId
456
- rpcCoordinator.setJobId(correlationId, jobId, queueName);
457
- // Then
458
- yield expect(promise).resolves.toEqual({ success: true });
459
- }));
460
- it('powinno ignorować setJobId dla nieistniejącego pending call', () => {
461
- // Given
462
- const nonExistentCorrelationId = 'non-existent-123';
463
- const jobId = 'job-456';
464
- const queueName = 'TestQueue';
465
- // When & Then - nie powinno rzucić błędu
466
- expect(() => {
467
- rpcCoordinator.setJobId(nonExistentCorrelationId, jobId, queueName);
468
- }).not.toThrow();
469
- // Verify: zalogowano debug
470
- expect(mockLogger.debug).toHaveBeenCalledWith(expect.stringContaining('Próba ustawienia jobId dla nieistniejącego pending call'), expect.objectContaining({ correlationId: nonExistentCorrelationId }));
471
- });
472
- });
473
- describe('Cancellation przy timeout', () => {
474
- it('powinno wywołać markAsCancelled przy timeout', () => __awaiter(void 0, void 0, void 0, function* () {
475
- // Given
476
- const correlationId = 'cancellation-test';
477
- const commandName = 'TestCommand';
478
- const timeout = 50;
479
- // When
480
- yield expect(rpcCoordinator.registerCall(correlationId, commandName, timeout)).rejects.toThrow('RPC timeout');
481
- // Poczekaj na async handleTimeout
482
- yield new Promise((resolve) => setTimeout(resolve, 10));
483
- // Then
484
- expect(mockCancellationService.markAsCancelled).toHaveBeenCalledWith(correlationId);
485
- }));
486
- it('powinno wywołać tryRemoveJob gdy jobId jest ustawione', () => __awaiter(void 0, void 0, void 0, function* () {
487
- // Given
488
- const correlationId = 'remove-job-test';
489
- const commandName = 'TestCommand';
490
- const timeout = 100;
491
- const jobId = 'job-to-remove-789';
492
- const queueName = 'TestQueueRemove';
493
- // When
494
- const promise = rpcCoordinator.registerCall(correlationId, commandName, timeout);
495
- // Poczekaj na rejestrację i ustaw jobId
496
- yield new Promise((resolve) => setTimeout(resolve, 10));
497
- rpcCoordinator.setJobId(correlationId, jobId, queueName);
498
- // Czekaj na timeout
499
- yield expect(promise).rejects.toThrow('RPC timeout');
500
- // Poczekaj na async handleTimeout
501
- yield new Promise((resolve) => setTimeout(resolve, 20));
502
- // Then
503
- expect(mockCancellationService.markAsCancelled).toHaveBeenCalledWith(correlationId);
504
- expect(mockCancellationService.tryRemoveJob).toHaveBeenCalledWith(jobId, queueName, mockRemoveJobCallback);
505
- }));
506
- it('powinno wywołać clearCancellation gdy job został usunięty', () => __awaiter(void 0, void 0, void 0, function* () {
507
- // Given
508
- const correlationId = 'clear-after-remove-test';
509
- const commandName = 'TestCommand';
510
- const timeout = 100;
511
- const jobId = 'job-removed-successfully';
512
- const queueName = 'TestQueueClear';
513
- // Symuluj pomyślne usunięcie joba
514
- mockCancellationService.tryRemoveJob.mockResolvedValue(true);
515
- // When
516
- const promise = rpcCoordinator.registerCall(correlationId, commandName, timeout);
517
- yield new Promise((resolve) => setTimeout(resolve, 10));
518
- rpcCoordinator.setJobId(correlationId, jobId, queueName);
519
- yield expect(promise).rejects.toThrow('RPC timeout');
520
- yield new Promise((resolve) => setTimeout(resolve, 20));
521
- // Then
522
- expect(mockCancellationService.clearCancellation).toHaveBeenCalledWith(correlationId);
523
- }));
524
- it('NIE powinno wywołać clearCancellation gdy job nie został usunięty', () => __awaiter(void 0, void 0, void 0, function* () {
525
- // Given
526
- const correlationId = 'no-clear-test';
527
- const commandName = 'TestCommand';
528
- const timeout = 100;
529
- const jobId = 'job-not-removed';
530
- const queueName = 'TestQueueNoClear';
531
- // Symuluj nieudane usunięcie joba (już przetwarzany)
532
- mockCancellationService.tryRemoveJob.mockResolvedValue(false);
533
- // When
534
- const promise = rpcCoordinator.registerCall(correlationId, commandName, timeout);
535
- yield new Promise((resolve) => setTimeout(resolve, 10));
536
- rpcCoordinator.setJobId(correlationId, jobId, queueName);
537
- yield expect(promise).rejects.toThrow('RPC timeout');
538
- yield new Promise((resolve) => setTimeout(resolve, 20));
539
- // Then - clearCancellation NIE wywołane
540
- expect(mockCancellationService.clearCancellation).not.toHaveBeenCalled();
541
- }));
542
- it('NIE powinno wywołać tryRemoveJob gdy jobId nie jest ustawione', () => __awaiter(void 0, void 0, void 0, function* () {
543
- // Given
544
- const correlationId = 'no-job-id-test';
545
- const commandName = 'TestCommand';
546
- const timeout = 50;
547
- // When - NIE ustawiamy jobId
548
- yield expect(rpcCoordinator.registerCall(correlationId, commandName, timeout)).rejects.toThrow('RPC timeout');
549
- yield new Promise((resolve) => setTimeout(resolve, 10));
550
- // Then - markAsCancelled wywołane, ale tryRemoveJob NIE
551
- expect(mockCancellationService.markAsCancelled).toHaveBeenCalledWith(correlationId);
552
- expect(mockCancellationService.tryRemoveJob).not.toHaveBeenCalled();
553
- }));
554
- it('powinno obsłużyć błędy cancellation service gracefully', () => __awaiter(void 0, void 0, void 0, function* () {
555
- // Given
556
- const correlationId = 'error-handling-test';
557
- const commandName = 'TestCommand';
558
- const timeout = 50;
559
- // Symuluj błąd w cancellation service
560
- mockCancellationService.markAsCancelled.mockRejectedValue(new Error('Redis error'));
561
- // When - nie powinno zmienić zachowania timeout
562
- yield expect(rpcCoordinator.registerCall(correlationId, commandName, timeout)).rejects.toThrow('RPC timeout');
563
- // Then - błąd obsłużony, nie propagowany
564
- // (test przechodzi jeśli nie ma unhandled rejection)
565
- }));
566
- });
567
- describe('Logowanie reconnect', () => {
568
- it('powinno logować INFO po udanym reconnect', () => {
569
- var _a, _b;
570
- // Given: Pobierz handlery eventów
571
- const onCalls = mockSharedSubscriber.on.mock.calls;
572
- const reconnectingHandler = (_a = onCalls.find((call) => call[0] === 'reconnecting')) === null || _a === void 0 ? void 0 : _a[1];
573
- const readyHandler = (_b = onCalls.find((call) => call[0] === 'ready')) === null || _b === void 0 ? void 0 : _b[1];
574
- expect(reconnectingHandler).toBeDefined();
575
- expect(readyHandler).toBeDefined();
576
- // When: Symuluj reconnect
577
- reconnectingHandler();
578
- readyHandler();
579
- // Then: Powinno zalogować INFO (logger.log) o ponownym połączeniu
580
- expect(mockLogger.log).toHaveBeenCalledWith('Shared subscriber RPC - ponowne łączenie', expect.objectContaining({
581
- processInstanceId,
582
- }));
583
- expect(mockLogger.log).toHaveBeenCalledWith('Shared subscriber RPC - ponownie połączony', expect.objectContaining({
584
- processInstanceId,
585
- reconnected: true,
586
- }));
587
- });
588
- it('NIE powinno logować INFO przy pierwszym ready (bez reconnecting)', () => {
589
- var _a;
590
- // Given: Pobierz handler ready
591
- const onCalls = mockSharedSubscriber.on.mock.calls;
592
- const readyHandler = (_a = onCalls.find((call) => call[0] === 'ready')) === null || _a === void 0 ? void 0 : _a[1];
593
- expect(readyHandler).toBeDefined();
594
- // Wyczyść poprzednie wywołania
595
- mockLogger.log.mockClear();
596
- // When: Wywołaj ready BEZ wcześniejszego reconnecting
597
- readyHandler();
598
- // Then: NIE powinno logować "ponownie połączony"
599
- expect(mockLogger.log).not.toHaveBeenCalledWith('Shared subscriber RPC - ponownie połączony', expect.any(Object));
600
- });
601
- it('powinno resetować flagę isReconnecting po logowaniu', () => {
602
- var _a, _b;
603
- // Given: Pobierz handlery eventów
604
- const onCalls = mockSharedSubscriber.on.mock.calls;
605
- const reconnectingHandler = (_a = onCalls.find((call) => call[0] === 'reconnecting')) === null || _a === void 0 ? void 0 : _a[1];
606
- const readyHandler = (_b = onCalls.find((call) => call[0] === 'ready')) === null || _b === void 0 ? void 0 : _b[1];
607
- expect(reconnectingHandler).toBeDefined();
608
- expect(readyHandler).toBeDefined();
609
- // When: Symuluj reconnect
610
- reconnectingHandler();
611
- readyHandler();
612
- // Wyczyść wywołania
613
- mockLogger.log.mockClear();
614
- // Drugie wywołanie ready (bez reconnecting)
615
- readyHandler();
616
- // Then: Drugie ready NIE loguje INFO (flaga zresetowana)
617
- expect(mockLogger.log).not.toHaveBeenCalledWith('Shared subscriber RPC - ponownie połączony', expect.any(Object));
618
- });
619
- });
620
- });
621
- //# sourceMappingURL=rpc-coordinator.spec.js.map