@push.rocks/smartrust 1.3.1 → 1.4.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.
@@ -1,440 +0,0 @@
1
- import { expect, tap } from '@git.zone/tstest/tapbundle';
2
- import * as path from 'path';
3
- import { RustBridge } from '../ts/classes.rustbridge.js';
4
- import type { ICommandDefinition } from '../ts/interfaces/index.js';
5
-
6
- const testDir = path.resolve(path.dirname(new URL(import.meta.url).pathname));
7
- const mockBinaryPath = path.join(testDir, 'helpers/mock-rust-binary.mjs');
8
-
9
- // Define the command types for our mock binary
10
- type TMockCommands = {
11
- echo: { params: Record<string, any>; result: Record<string, any> };
12
- largeEcho: { params: Record<string, any>; result: Record<string, any> };
13
- error: { params: {}; result: never };
14
- emitEvent: { params: { eventName: string; eventData: any }; result: null };
15
- slow: { params: {}; result: { delayed: boolean } };
16
- exit: { params: {}; result: null };
17
- streamEcho: { params: { count: number }; chunk: { index: number; value: string }; result: { totalChunks: number } };
18
- streamError: { params: {}; chunk: { index: number; value: string }; result: never };
19
- streamEmpty: { params: {}; chunk: never; result: { totalChunks: number } };
20
- };
21
-
22
- tap.test('should spawn and receive ready event', async () => {
23
- const bridge = new RustBridge<TMockCommands>({
24
- binaryName: 'node',
25
- binaryPath: 'node',
26
- cliArgs: [mockBinaryPath],
27
- readyTimeoutMs: 5000,
28
- });
29
-
30
- const result = await bridge.spawn();
31
- expect(result).toBeTrue();
32
- expect(bridge.running).toBeTrue();
33
-
34
- bridge.kill();
35
- expect(bridge.running).toBeFalse();
36
- });
37
-
38
- tap.test('should send command and receive response', async () => {
39
- const bridge = new RustBridge<TMockCommands>({
40
- binaryName: 'node',
41
- binaryPath: 'node',
42
- cliArgs: [mockBinaryPath],
43
- readyTimeoutMs: 5000,
44
- });
45
-
46
- await bridge.spawn();
47
-
48
- const result = await bridge.sendCommand('echo', { hello: 'world', num: 42 });
49
- expect(result).toEqual({ hello: 'world', num: 42 });
50
-
51
- bridge.kill();
52
- });
53
-
54
- tap.test('should handle error responses', async () => {
55
- const bridge = new RustBridge<TMockCommands>({
56
- binaryName: 'node',
57
- binaryPath: 'node',
58
- cliArgs: [mockBinaryPath],
59
- readyTimeoutMs: 5000,
60
- });
61
-
62
- await bridge.spawn();
63
-
64
- let threw = false;
65
- try {
66
- await bridge.sendCommand('error', {});
67
- } catch (err: any) {
68
- threw = true;
69
- expect(err.message).toInclude('Test error message');
70
- }
71
- expect(threw).toBeTrue();
72
-
73
- bridge.kill();
74
- });
75
-
76
- tap.test('should receive custom events from the binary', async () => {
77
- const bridge = new RustBridge<TMockCommands>({
78
- binaryName: 'node',
79
- binaryPath: 'node',
80
- cliArgs: [mockBinaryPath],
81
- readyTimeoutMs: 5000,
82
- });
83
-
84
- await bridge.spawn();
85
-
86
- const eventPromise = new Promise<any>((resolve) => {
87
- bridge.once('management:testEvent', (data) => {
88
- resolve(data);
89
- });
90
- });
91
-
92
- await bridge.sendCommand('emitEvent', {
93
- eventName: 'testEvent',
94
- eventData: { key: 'value' },
95
- });
96
-
97
- const eventData = await eventPromise;
98
- expect(eventData).toEqual({ key: 'value' });
99
-
100
- bridge.kill();
101
- });
102
-
103
- tap.test('should handle delayed responses', async () => {
104
- const bridge = new RustBridge<TMockCommands>({
105
- binaryName: 'node',
106
- binaryPath: 'node',
107
- cliArgs: [mockBinaryPath],
108
- readyTimeoutMs: 5000,
109
- requestTimeoutMs: 5000,
110
- });
111
-
112
- await bridge.spawn();
113
-
114
- const result = await bridge.sendCommand('slow', {});
115
- expect(result).toEqual({ delayed: true });
116
-
117
- bridge.kill();
118
- });
119
-
120
- tap.test('should handle multiple concurrent commands', async () => {
121
- const bridge = new RustBridge<TMockCommands>({
122
- binaryName: 'node',
123
- binaryPath: 'node',
124
- cliArgs: [mockBinaryPath],
125
- readyTimeoutMs: 5000,
126
- });
127
-
128
- await bridge.spawn();
129
-
130
- const results = await Promise.all([
131
- bridge.sendCommand('echo', { id: 1 }),
132
- bridge.sendCommand('echo', { id: 2 }),
133
- bridge.sendCommand('echo', { id: 3 }),
134
- ]);
135
-
136
- expect(results[0]).toEqual({ id: 1 });
137
- expect(results[1]).toEqual({ id: 2 });
138
- expect(results[2]).toEqual({ id: 3 });
139
-
140
- bridge.kill();
141
- });
142
-
143
- tap.test('should throw when sending command while not running', async () => {
144
- const bridge = new RustBridge<TMockCommands>({
145
- binaryName: 'node',
146
- binaryPath: 'node',
147
- cliArgs: [mockBinaryPath],
148
- });
149
-
150
- let threw = false;
151
- try {
152
- await bridge.sendCommand('echo', {});
153
- } catch (err: any) {
154
- threw = true;
155
- expect(err.message).toInclude('not running');
156
- }
157
- expect(threw).toBeTrue();
158
- });
159
-
160
- tap.test('should return false when binary not found', async () => {
161
- const bridge = new RustBridge<TMockCommands>({
162
- binaryName: 'nonexistent-binary-xyz',
163
- searchSystemPath: false,
164
- });
165
-
166
- const result = await bridge.spawn();
167
- expect(result).toBeFalse();
168
- expect(bridge.running).toBeFalse();
169
- });
170
-
171
- tap.test('should emit exit event when process exits', async () => {
172
- const bridge = new RustBridge<TMockCommands>({
173
- binaryName: 'node',
174
- binaryPath: 'node',
175
- cliArgs: [mockBinaryPath],
176
- readyTimeoutMs: 5000,
177
- });
178
-
179
- await bridge.spawn();
180
-
181
- const exitPromise = new Promise<number | null>((resolve) => {
182
- bridge.once('exit', (code) => {
183
- resolve(code);
184
- });
185
- });
186
-
187
- // Tell mock binary to exit
188
- await bridge.sendCommand('exit', {});
189
-
190
- const exitCode = await exitPromise;
191
- expect(exitCode).toEqual(0);
192
- expect(bridge.running).toBeFalse();
193
- });
194
-
195
- tap.test('should handle 1MB payload round-trip', async () => {
196
- const bridge = new RustBridge<TMockCommands>({
197
- binaryName: 'node',
198
- binaryPath: 'node',
199
- cliArgs: [mockBinaryPath],
200
- readyTimeoutMs: 5000,
201
- requestTimeoutMs: 30000,
202
- });
203
-
204
- await bridge.spawn();
205
-
206
- // Create a ~1MB payload
207
- const largeString = 'x'.repeat(1024 * 1024);
208
- const result = await bridge.sendCommand('largeEcho', { data: largeString });
209
- expect(result.data).toEqual(largeString);
210
- expect(result.data.length).toEqual(1024 * 1024);
211
-
212
- bridge.kill();
213
- });
214
-
215
- tap.test('should handle 10MB payload round-trip', async () => {
216
- const bridge = new RustBridge<TMockCommands>({
217
- binaryName: 'node',
218
- binaryPath: 'node',
219
- cliArgs: [mockBinaryPath],
220
- readyTimeoutMs: 5000,
221
- requestTimeoutMs: 60000,
222
- });
223
-
224
- await bridge.spawn();
225
-
226
- // Create a ~10MB payload
227
- const largeString = 'y'.repeat(10 * 1024 * 1024);
228
- const result = await bridge.sendCommand('largeEcho', { data: largeString });
229
- expect(result.data).toEqual(largeString);
230
- expect(result.data.length).toEqual(10 * 1024 * 1024);
231
-
232
- bridge.kill();
233
- });
234
-
235
- tap.test('should reject outbound messages exceeding maxPayloadSize', async () => {
236
- const bridge = new RustBridge<TMockCommands>({
237
- binaryName: 'node',
238
- binaryPath: 'node',
239
- cliArgs: [mockBinaryPath],
240
- readyTimeoutMs: 5000,
241
- maxPayloadSize: 1000,
242
- });
243
-
244
- await bridge.spawn();
245
-
246
- let threw = false;
247
- try {
248
- await bridge.sendCommand('largeEcho', { data: 'z'.repeat(2000) });
249
- } catch (err: any) {
250
- threw = true;
251
- expect(err.message).toInclude('maxPayloadSize');
252
- }
253
- expect(threw).toBeTrue();
254
-
255
- bridge.kill();
256
- });
257
-
258
- tap.test('should handle multiple large concurrent commands', async () => {
259
- const bridge = new RustBridge<TMockCommands>({
260
- binaryName: 'node',
261
- binaryPath: 'node',
262
- cliArgs: [mockBinaryPath],
263
- readyTimeoutMs: 5000,
264
- requestTimeoutMs: 30000,
265
- });
266
-
267
- await bridge.spawn();
268
-
269
- const size = 500 * 1024; // 500KB each
270
- const results = await Promise.all([
271
- bridge.sendCommand('largeEcho', { data: 'a'.repeat(size), id: 1 }),
272
- bridge.sendCommand('largeEcho', { data: 'b'.repeat(size), id: 2 }),
273
- bridge.sendCommand('largeEcho', { data: 'c'.repeat(size), id: 3 }),
274
- ]);
275
-
276
- expect(results[0].data.length).toEqual(size);
277
- expect(results[0].data[0]).toEqual('a');
278
- expect(results[1].data.length).toEqual(size);
279
- expect(results[1].data[0]).toEqual('b');
280
- expect(results[2].data.length).toEqual(size);
281
- expect(results[2].data[0]).toEqual('c');
282
-
283
- bridge.kill();
284
- });
285
-
286
- // === Streaming tests ===
287
-
288
- tap.test('streaming: should receive chunks via for-await-of and final result', async () => {
289
- const bridge = new RustBridge<TMockCommands>({
290
- binaryName: 'node',
291
- binaryPath: 'node',
292
- cliArgs: [mockBinaryPath],
293
- readyTimeoutMs: 5000,
294
- requestTimeoutMs: 10000,
295
- });
296
-
297
- await bridge.spawn();
298
-
299
- const stream = bridge.sendCommandStreaming('streamEcho', { count: 5 });
300
- const chunks: Array<{ index: number; value: string }> = [];
301
-
302
- for await (const chunk of stream) {
303
- chunks.push(chunk);
304
- }
305
-
306
- expect(chunks.length).toEqual(5);
307
- for (let i = 0; i < 5; i++) {
308
- expect(chunks[i].index).toEqual(i);
309
- expect(chunks[i].value).toEqual(`chunk_${i}`);
310
- }
311
-
312
- const result = await stream.result;
313
- expect(result.totalChunks).toEqual(5);
314
-
315
- bridge.kill();
316
- });
317
-
318
- tap.test('streaming: should handle zero chunks (immediate result)', async () => {
319
- const bridge = new RustBridge<TMockCommands>({
320
- binaryName: 'node',
321
- binaryPath: 'node',
322
- cliArgs: [mockBinaryPath],
323
- readyTimeoutMs: 5000,
324
- });
325
-
326
- await bridge.spawn();
327
-
328
- const stream = bridge.sendCommandStreaming('streamEmpty', {});
329
- const chunks: any[] = [];
330
-
331
- for await (const chunk of stream) {
332
- chunks.push(chunk);
333
- }
334
-
335
- expect(chunks.length).toEqual(0);
336
-
337
- const result = await stream.result;
338
- expect(result.totalChunks).toEqual(0);
339
-
340
- bridge.kill();
341
- });
342
-
343
- tap.test('streaming: should propagate error to iterator and .result', async () => {
344
- const bridge = new RustBridge<TMockCommands>({
345
- binaryName: 'node',
346
- binaryPath: 'node',
347
- cliArgs: [mockBinaryPath],
348
- readyTimeoutMs: 5000,
349
- requestTimeoutMs: 10000,
350
- });
351
-
352
- await bridge.spawn();
353
-
354
- const stream = bridge.sendCommandStreaming('streamError', {});
355
- const chunks: any[] = [];
356
- let iteratorError: Error | null = null;
357
-
358
- try {
359
- for await (const chunk of stream) {
360
- chunks.push(chunk);
361
- }
362
- } catch (err: any) {
363
- iteratorError = err;
364
- }
365
-
366
- // Should have received at least one chunk before error
367
- expect(chunks.length).toEqual(1);
368
- expect(chunks[0].value).toEqual('before_error');
369
-
370
- // Iterator should have thrown
371
- expect(iteratorError).toBeTruthy();
372
- expect(iteratorError!.message).toInclude('Stream error after chunk');
373
-
374
- // .result should also reject
375
- let resultError: Error | null = null;
376
- try {
377
- await stream.result;
378
- } catch (err: any) {
379
- resultError = err;
380
- }
381
- expect(resultError).toBeTruthy();
382
- expect(resultError!.message).toInclude('Stream error after chunk');
383
-
384
- bridge.kill();
385
- });
386
-
387
- tap.test('streaming: should fail when bridge is not running', async () => {
388
- const bridge = new RustBridge<TMockCommands>({
389
- binaryName: 'node',
390
- binaryPath: 'node',
391
- cliArgs: [mockBinaryPath],
392
- });
393
-
394
- const stream = bridge.sendCommandStreaming('streamEcho', { count: 3 });
395
-
396
- let resultError: Error | null = null;
397
- try {
398
- await stream.result;
399
- } catch (err: any) {
400
- resultError = err;
401
- }
402
- expect(resultError).toBeTruthy();
403
- expect(resultError!.message).toInclude('not running');
404
- });
405
-
406
- tap.test('streaming: should fail when killed mid-stream', async () => {
407
- const bridge = new RustBridge<TMockCommands>({
408
- binaryName: 'node',
409
- binaryPath: 'node',
410
- cliArgs: [mockBinaryPath],
411
- readyTimeoutMs: 5000,
412
- requestTimeoutMs: 30000,
413
- });
414
-
415
- await bridge.spawn();
416
-
417
- // Request many chunks so we can kill mid-stream
418
- const stream = bridge.sendCommandStreaming('streamEcho', { count: 100 });
419
- const chunks: any[] = [];
420
- let iteratorError: Error | null = null;
421
-
422
- // Kill after a short delay
423
- setTimeout(() => {
424
- bridge.kill();
425
- }, 50);
426
-
427
- try {
428
- for await (const chunk of stream) {
429
- chunks.push(chunk);
430
- }
431
- } catch (err: any) {
432
- iteratorError = err;
433
- }
434
-
435
- // Should have gotten some chunks but not all
436
- expect(iteratorError).toBeTruthy();
437
- expect(iteratorError!.message).toInclude('killed');
438
- });
439
-
440
- export default tap.start();