@onekeyfe/hd-transport 1.2.0-alpha.20 → 1.2.0-alpha.22

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 (35) hide show
  1. package/__tests__/messages.test.js +32 -7
  2. package/__tests__/protocol-v2-link-manager.test.js +27 -34
  3. package/__tests__/protocol-v2.test.js +242 -56
  4. package/dist/constants.d.ts +1 -0
  5. package/dist/constants.d.ts.map +1 -1
  6. package/dist/index.d.ts +101 -19
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +166 -106
  9. package/dist/protocols/index.d.ts +5 -2
  10. package/dist/protocols/index.d.ts.map +1 -1
  11. package/dist/protocols/v2/decode.d.ts +11 -1
  12. package/dist/protocols/v2/decode.d.ts.map +1 -1
  13. package/dist/protocols/v2/errors.d.ts +8 -0
  14. package/dist/protocols/v2/errors.d.ts.map +1 -0
  15. package/dist/protocols/v2/frame-assembler.d.ts.map +1 -1
  16. package/dist/protocols/v2/index.d.ts +1 -0
  17. package/dist/protocols/v2/index.d.ts.map +1 -1
  18. package/dist/protocols/v2/link-manager.d.ts +0 -1
  19. package/dist/protocols/v2/link-manager.d.ts.map +1 -1
  20. package/dist/protocols/v2/session.d.ts +4 -5
  21. package/dist/protocols/v2/session.d.ts.map +1 -1
  22. package/dist/types/messages.d.ts +34 -5
  23. package/dist/types/messages.d.ts.map +1 -1
  24. package/messages-protocol-v2.json +77 -4
  25. package/package.json +2 -2
  26. package/scripts/protobuf-types.js +7 -1
  27. package/src/constants.ts +8 -0
  28. package/src/protocols/index.ts +18 -4
  29. package/src/protocols/v2/decode.ts +49 -19
  30. package/src/protocols/v2/errors.ts +23 -0
  31. package/src/protocols/v2/frame-assembler.ts +6 -4
  32. package/src/protocols/v2/index.ts +1 -0
  33. package/src/protocols/v2/link-manager.ts +0 -2
  34. package/src/protocols/v2/session.ts +124 -122
  35. package/src/types/messages.ts +46 -6
@@ -101,17 +101,44 @@ describe('messages', () => {
101
101
  expect(v2Messages.nested.MessageType.values).toMatchObject({
102
102
  MessageType_DeviceStatusGet: 60602,
103
103
  MessageType_DeviceStatus: 60603,
104
- MessageType_DeviceSessionGet: 60606,
105
104
  MessageType_DeviceSession: 60607,
106
105
  MessageType_DeviceSessionAskPin: 60608,
106
+ MessageType_DeviceSessionOpen: 60609,
107
107
  });
108
108
  expect(v2Messages.nested.DeviceStatusGet).toEqual({ fields: {} });
109
- expect(v2Messages.nested.DeviceSessionGet.fields.session_id).toMatchObject({
109
+ expect(v2Messages.nested.ProtocolInfoRequest.fields.eventless_wallet_session).toMatchObject({
110
+ id: 1,
111
+ type: 'bool',
112
+ options: { default: false },
113
+ });
114
+ expect(v2Messages.nested).not.toHaveProperty('DeviceSessionGet');
115
+ expect(v2Messages.nested.DeviceSessionResume.fields.session_id).toMatchObject({
116
+ rule: 'required',
110
117
  id: 1,
111
118
  type: 'bytes',
112
119
  });
113
- expect(v2Messages.nested).not.toHaveProperty('DeviceSessionOpen');
114
- expect(v2Messages.nested).not.toHaveProperty('DeviceSessionResume');
120
+ expect(v2Messages.nested.DeviceSessionOpen.fields).toMatchObject({
121
+ resume: { id: 1, type: 'DeviceSessionResume' },
122
+ select: { id: 2, type: 'DeviceSessionSelect' },
123
+ });
124
+ expect(v2Messages.nested.DeviceSessionSelect.fields).toMatchObject({
125
+ host_passphrase: { id: 1, type: 'DeviceSessionHostPassphrase' },
126
+ passphrase_on_device: { id: 2, type: 'DeviceSessionPassphraseOnDevice' },
127
+ attach_pin_on_device: { id: 3, type: 'DeviceSessionAttachPinOnDevice' },
128
+ });
129
+ expect(v2Messages.nested.DeviceSessionHostPassphrase.fields.passphrase).toMatchObject({
130
+ rule: 'required',
131
+ id: 1,
132
+ type: 'string',
133
+ });
134
+ expect(v2Messages.nested.DeviceSessionErrorCode.values).toEqual({
135
+ DeviceSessionError_None: 0,
136
+ DeviceSessionError_UserCancelled: 1,
137
+ DeviceSessionError_InvalidSession: 2,
138
+ DeviceSessionError_AttachPinUnavailable: 3,
139
+ DeviceSessionError_PassphraseDisabled: 4,
140
+ DeviceSessionError_Busy: 5,
141
+ });
115
142
  expect(v2Messages.nested).not.toHaveProperty('DeviceWalletSelect');
116
143
  expect(v2Messages.nested).not.toHaveProperty('DeviceWalletType');
117
144
  expect(v2Messages.nested).not.toHaveProperty('DeviceHiddenWalletSelect');
@@ -126,9 +153,7 @@ describe('messages', () => {
126
153
  expect(v2Messages.nested.MessageType.values).not.toHaveProperty(
127
154
  'MessageType_DeviceSessionPinResult'
128
155
  );
129
- expect(v2Messages.nested.MessageType.values).not.toHaveProperty(
130
- 'MessageType_DeviceSessionOpen'
131
- );
156
+ expect(v2Messages.nested.MessageType.values).not.toHaveProperty('MessageType_DeviceSessionGet');
132
157
  });
133
158
 
134
159
  test('Protocol V2 onboarding status matches the current firmware-pro2 schema', () => {
@@ -55,7 +55,12 @@ const rewriteSeq = (frame, seq) => {
55
55
  const createAdapterFactory = sentSeqs => {
56
56
  const adapters = [];
57
57
  let generation = 0;
58
- const success = ProtocolV2.encodeFrame(schemas, 'Success', { message: 'ok' });
58
+ const success = ProtocolV2.encodeFrame(
59
+ schemas,
60
+ 'Success',
61
+ { message: 'ok' },
62
+ { router: 1, packetSrc: 0, seq: 1 }
63
+ );
59
64
 
60
65
  return {
61
66
  adapters,
@@ -108,12 +113,15 @@ describe('ProtocolV2LinkManager', () => {
108
113
 
109
114
  await manager.call('device-a', createAdapter, 'Ping', { message: '1' }, { timeoutMs: 123 });
110
115
 
111
- expect(adapters[0].prepareCall).toHaveBeenCalledWith({
112
- messageName: 'Ping',
113
- timeoutMs: 123,
114
- highVolume: false,
115
- generation: 1,
116
- });
116
+ expect(adapters[0].prepareCall).toHaveBeenCalledWith(
117
+ expect.objectContaining({
118
+ messageName: 'Ping',
119
+ timeoutMs: 123,
120
+ highVolume: false,
121
+ generation: 1,
122
+ signal: expect.any(AbortSignal),
123
+ })
124
+ );
117
125
  });
118
126
 
119
127
  test('invalidates a link-fatal call before the next call creates a new link', async () => {
@@ -158,31 +166,6 @@ describe('ProtocolV2LinkManager', () => {
158
166
  expect(sentSeqs).toEqual([1, 2]);
159
167
  });
160
168
 
161
- test('times out a stalled write and resets the link before releasing the call queue', async () => {
162
- const adapter = {
163
- router: 1,
164
- generation: 1,
165
- prepareCall: jest.fn(),
166
- writeFrame: jest.fn(() => new Promise(() => {})),
167
- readFrame: jest.fn(),
168
- reset: jest.fn(() => Promise.resolve()),
169
- writeTimeoutMs: 10,
170
- };
171
- const manager = new ProtocolV2LinkManager({
172
- getSchemas: () => schemas,
173
- classifyError: error =>
174
- String(error).includes('write timeout') ? 'link-fatal' : 'recoverable',
175
- });
176
-
177
- await expect(
178
- manager.call('device-a', () => adapter, 'Ping', { message: 'pending' })
179
- ).rejects.toThrow('Protocol V2 write timeout after 10ms for Ping');
180
-
181
- expect(adapter.reset).toHaveBeenCalledWith(expect.stringContaining('write timeout'));
182
- expect(adapter.readFrame).not.toHaveBeenCalled();
183
- expect(manager.callQueues.size).toBe(0);
184
- });
185
-
186
169
  test('invalidates every active link while retaining per-device cursors', async () => {
187
170
  const sentSeqs = [];
188
171
  const { adapters, createAdapter } = createAdapterFactory(sentSeqs);
@@ -225,7 +208,12 @@ describe('ProtocolV2LinkManager', () => {
225
208
  const deviceABlocked = new Promise(resolve => {
226
209
  releaseDeviceA = resolve;
227
210
  });
228
- const success = ProtocolV2.encodeFrame(schemas, 'Success', { message: 'ok' });
211
+ const success = ProtocolV2.encodeFrame(
212
+ schemas,
213
+ 'Success',
214
+ { message: 'ok' },
215
+ { router: 1, packetSrc: 0, seq: 1 }
216
+ );
229
217
  const createAdapter = key => {
230
218
  let requestSeq = 0;
231
219
  return {
@@ -323,7 +311,12 @@ describe('ProtocolV2LinkManager', () => {
323
311
  const writeBlocked = new Promise(resolve => {
324
312
  releaseWrite = resolve;
325
313
  });
326
- const success = ProtocolV2.encodeFrame(schemas, 'Success', { message: 'ok' });
314
+ const success = ProtocolV2.encodeFrame(
315
+ schemas,
316
+ 'Success',
317
+ { message: 'ok' },
318
+ { router: 1, packetSrc: 0, seq: 1 }
319
+ );
327
320
  const adapter = {
328
321
  router: 1,
329
322
  generation: 1,
@@ -2,13 +2,17 @@ const { ProtocolV2 } = require('../src/protocols');
2
2
  const { parseConfigure } = require('../src/serialization/protobuf/messages');
3
3
  const {
4
4
  ProtocolV2FrameAssembler,
5
+ ProtocolV2LinkError,
5
6
  ProtocolV2SequenceCursor,
6
7
  ProtocolV2Session,
7
8
  hexToBytes,
8
9
  probeProtocolV2,
9
10
  } = require('../src/protocols/v2/session');
10
11
  const protocolV2 = require('../src/protocols/v2');
11
- const { PROTOCOL_V2_FRAME_MAX_BYTES } = require('../src/constants');
12
+ const {
13
+ PROTOCOL_V2_DEFAULT_RESPONSE_TIMEOUT_MS,
14
+ PROTOCOL_V2_FRAME_MAX_BYTES,
15
+ } = require('../src/constants');
12
16
 
13
17
  const protocolV1Messages = parseConfigure({
14
18
  nested: {
@@ -162,6 +166,18 @@ const protocolV2Messages = parseConfigure({
162
166
  },
163
167
  },
164
168
  },
169
+ DeviceSession: {
170
+ fields: {
171
+ session_id: {
172
+ type: 'bytes',
173
+ id: 1,
174
+ },
175
+ btc_test_address: {
176
+ type: 'string',
177
+ id: 2,
178
+ },
179
+ },
180
+ },
165
181
  FileWrite: {
166
182
  fields: {},
167
183
  },
@@ -195,6 +211,7 @@ const protocolV2Messages = parseConfigure({
195
211
  MessageType_FileWrite: 60805,
196
212
  MessageType_DeviceFirmwareUpdateRequest: 61000,
197
213
  MessageType_DeviceFirmwareUpdateStatus: 61002,
214
+ MessageType_DeviceSession: 60607,
198
215
  MessageType_PartialNested: 62000,
199
216
  },
200
217
  },
@@ -208,6 +225,7 @@ const schemas = {
208
225
 
209
226
  const rewriteSeq = (frame, seq) => {
210
227
  const copy = new Uint8Array(frame);
228
+ copy[4] = 1;
211
229
  copy[6] = seq;
212
230
  copy[copy.length - 1] = protocolV2.crc8(copy, copy.length - 1);
213
231
  return copy;
@@ -259,6 +277,34 @@ describe('Protocol V2 framing and session', () => {
259
277
  expect(decoded.message).toEqual({ message: 'ok' });
260
278
  });
261
279
 
280
+ test('round-trips a DeviceSession response with a 32-byte session id', () => {
281
+ const sessionId = '01'.repeat(32);
282
+ const frame = ProtocolV2.encodeFrame(schemas, 'DeviceSession', {
283
+ session_id: sessionId,
284
+ btc_test_address: 'tb1qwallet',
285
+ });
286
+
287
+ const decoded = ProtocolV2.decodeFrame(schemas, frame);
288
+
289
+ expect(decoded.type).toBe('DeviceSession');
290
+ expect(decoded.message).toEqual({
291
+ session_id: sessionId,
292
+ btc_test_address: 'tb1qwallet',
293
+ });
294
+ });
295
+
296
+ test('reports malformed DeviceSession protobuf as a schema-compatible transport error', () => {
297
+ const malformedPayload = new Uint8Array(32);
298
+ malformedPayload[0] = 0x0a;
299
+ malformedPayload[1] = 101;
300
+ const frame = protocolV2.encodeProtobufFrame(60607, malformedPayload);
301
+
302
+ expect(() => ProtocolV2.decodeFrame(schemas, frame)).toThrow(
303
+ 'Protocol V2 protobuf decode failed for "DeviceSession" (60607, 32-byte payload); ' +
304
+ 'the payload is malformed or incompatible with the active SDK schema.'
305
+ );
306
+ });
307
+
262
308
  test('decodes missing optional nested messages as null', () => {
263
309
  const frame = ProtocolV2.encodeFrame(schemas, 'PartialNested', {
264
310
  label: 'only label',
@@ -388,7 +434,7 @@ describe('Protocol V2 framing and session', () => {
388
434
  logger,
389
435
  logPrefix: 'ProtocolV2 Test',
390
436
  writeFrame: () => Promise.resolve(),
391
- readFrame: () => Promise.resolve(response),
437
+ readFrame: () => Promise.resolve(rewriteSeq(response, 1)),
392
438
  });
393
439
 
394
440
  await expect(session.call('Ping', { message: 'hello' })).rejects.toThrow();
@@ -410,7 +456,10 @@ describe('Protocol V2 framing and session', () => {
410
456
  const response = ProtocolV2.encodeFrame(schemas, 'Success', {
411
457
  message: 'ok',
412
458
  });
413
- const readFrame = jest.fn().mockResolvedValueOnce(ack).mockResolvedValueOnce(response);
459
+ const readFrame = jest
460
+ .fn()
461
+ .mockResolvedValueOnce(ack)
462
+ .mockResolvedValueOnce(rewriteSeq(response, 1));
414
463
  const session = new ProtocolV2Session({
415
464
  schemas,
416
465
  router: 1,
@@ -427,56 +476,93 @@ describe('Protocol V2 framing and session', () => {
427
476
  expect(readFrame).toHaveBeenCalledTimes(2);
428
477
  });
429
478
 
430
- test('session ignores an ACK whose sequence does not match the request', async () => {
431
- const mismatchedAck = new Uint8Array(8);
432
- mismatchedAck[0] = 0x5a;
433
- mismatchedAck[1] = 8;
434
- mismatchedAck[4] = 1;
435
- mismatchedAck[5] = 1;
436
- mismatchedAck[6] = 2;
437
- mismatchedAck[3] = protocolV2.crc8(mismatchedAck, 3);
438
- mismatchedAck[7] = protocolV2.crc8(mismatchedAck, 7);
439
- const readFrame = jest
440
- .fn()
441
- .mockResolvedValueOnce(mismatchedAck)
442
- .mockImplementation(() => new Promise(() => {}));
479
+ test('session accepts a terminal response before an ACK', async () => {
480
+ const response = ProtocolV2.encodeFrame(schemas, 'Success', {
481
+ message: 'ok',
482
+ });
483
+ const session = new ProtocolV2Session({
484
+ schemas,
485
+ router: 1,
486
+ writeFrame: () => Promise.resolve(),
487
+ readFrame: () => Promise.resolve(rewriteSeq(response, 1)),
488
+ });
489
+
490
+ await expect(
491
+ session.call('Ping', { message: 'hello' }, { expectedTypes: ['Success'] })
492
+ ).resolves.toEqual({
493
+ type: 'Success',
494
+ message: {
495
+ message: 'ok',
496
+ },
497
+ });
498
+ });
499
+
500
+ test('session uses the unified response timeout before the first ACK', async () => {
501
+ const response = ProtocolV2.encodeFrame(schemas, 'Success', {
502
+ message: 'ok',
503
+ });
504
+ const readFrame = jest.fn(() => Promise.resolve(rewriteSeq(response, 1)));
443
505
  const session = new ProtocolV2Session({
444
506
  schemas,
445
507
  router: 1,
446
- deliveryTimeoutMs: 10,
447
508
  writeFrame: () => Promise.resolve(),
448
509
  readFrame,
449
510
  });
450
511
 
451
- await expect(session.call('Ping', { message: 'hello' })).rejects.toThrow(
452
- 'Protocol V2 delivery timeout after 10ms for Ping'
453
- );
454
- expect(readFrame).toHaveBeenCalledTimes(2);
512
+ await expect(
513
+ session.call(
514
+ 'Ping',
515
+ { message: 'hello' },
516
+ {
517
+ timeoutMs: 12_345,
518
+ expectedTypes: ['Success'],
519
+ }
520
+ )
521
+ ).resolves.toEqual({
522
+ type: 'Success',
523
+ message: {
524
+ message: 'ok',
525
+ },
526
+ });
527
+ expect(readFrame).toHaveBeenCalledWith(expect.objectContaining({ timeoutMs: 12_345 }));
455
528
  });
456
529
 
457
- test('session keeps waiting for a response after a matching delivery ACK', async () => {
530
+ test('session rejects an ACK that belongs to another request sequence', async () => {
458
531
  const ack = new Uint8Array(8);
459
532
  ack[0] = 0x5a;
460
533
  ack[1] = 8;
461
534
  ack[4] = 1;
462
535
  ack[5] = 1;
463
- ack[6] = 1;
536
+ ack[6] = 2;
464
537
  ack[3] = protocolV2.crc8(ack, 3);
465
538
  ack[7] = protocolV2.crc8(ack, 7);
466
- const readFrame = jest
467
- .fn()
468
- .mockResolvedValueOnce(ack)
469
- .mockImplementation(() => new Promise(() => {}));
470
539
  const session = new ProtocolV2Session({
471
540
  schemas,
472
541
  router: 1,
473
- deliveryTimeoutMs: 10,
474
542
  writeFrame: () => Promise.resolve(),
475
- readFrame,
543
+ readFrame: () => Promise.resolve(ack),
476
544
  });
477
545
 
478
- await expect(session.call('Ping', { message: 'hello' }, { timeoutMs: 25 })).rejects.toThrow(
479
- 'Protocol V2 response timeout after 25ms for Ping'
546
+ await expect(session.call('Ping', { message: 'hello' })).rejects.toEqual(
547
+ expect.objectContaining({
548
+ name: 'ProtocolV2LinkError',
549
+ code: 'ack-sequence',
550
+ message: 'Protocol V2 ACK sequence mismatch: expected 1, got 2',
551
+ })
552
+ );
553
+ });
554
+
555
+ test('session rejects a response from another routing channel', async () => {
556
+ const response = ProtocolV2.encodeFrame(schemas, 'Success', { message: 'wrong route' });
557
+ const session = new ProtocolV2Session({
558
+ schemas,
559
+ router: 1,
560
+ writeFrame: () => Promise.resolve(),
561
+ readFrame: () => Promise.resolve(response),
562
+ });
563
+
564
+ await expect(session.call('Ping', { message: 'hello' })).rejects.toBeInstanceOf(
565
+ ProtocolV2LinkError
480
566
  );
481
567
  });
482
568
 
@@ -490,7 +576,7 @@ describe('Protocol V2 framing and session', () => {
490
576
  router: 1,
491
577
  maxFrameBytes: 2048,
492
578
  writeFrame,
493
- readFrame: () => Promise.resolve(response),
579
+ readFrame: () => Promise.resolve(rewriteSeq(response, 1)),
494
580
  });
495
581
 
496
582
  await expect(session.call('Ping', { message: 'x'.repeat(2048) })).rejects.toThrow(
@@ -499,28 +585,40 @@ describe('Protocol V2 framing and session', () => {
499
585
  expect(writeFrame).not.toHaveBeenCalled();
500
586
  });
501
587
 
502
- test('session starts response timeout after the frame is written', async () => {
503
- const response = ProtocolV2.encodeFrame(schemas, 'Success', {
504
- message: 'ok',
505
- });
588
+ test('session watchdog covers a write that never settles', async () => {
589
+ const readFrame = jest.fn();
590
+ let writeSignal;
506
591
  const session = new ProtocolV2Session({
507
592
  schemas,
508
593
  router: 1,
509
- writeFrame: () =>
510
- new Promise(resolve => {
511
- setTimeout(resolve, 30);
512
- }),
513
- readFrame: () => Promise.resolve(response),
594
+ writeFrame: (_frame, context) => {
595
+ writeSignal = context.signal;
596
+ return new Promise(() => {});
597
+ },
598
+ readFrame,
514
599
  });
515
600
 
516
601
  await expect(
517
602
  session.call('Ping', { message: 'hello' }, { timeoutMs: 10, expectedTypes: ['Success'] })
518
- ).resolves.toEqual({
519
- type: 'Success',
520
- message: {
521
- message: 'ok',
522
- },
603
+ ).rejects.toThrow('Protocol V2 response timeout after 10ms for Ping');
604
+ expect(writeSignal.aborted).toBe(true);
605
+ expect(readFrame).not.toHaveBeenCalled();
606
+ });
607
+
608
+ test('session watchdog covers prepareCall before writing', async () => {
609
+ const writeFrame = jest.fn();
610
+ const session = new ProtocolV2Session({
611
+ schemas,
612
+ router: 1,
613
+ prepareCall: () => new Promise(() => {}),
614
+ writeFrame,
615
+ readFrame: jest.fn(),
523
616
  });
617
+
618
+ await expect(session.call('Ping', { message: 'hello' }, { timeoutMs: 10 })).rejects.toThrow(
619
+ 'Protocol V2 response timeout after 10ms for Ping'
620
+ );
621
+ expect(writeFrame).not.toHaveBeenCalled();
524
622
  });
525
623
 
526
624
  test('session logs a device-owned response seq without rejecting the frame', async () => {
@@ -550,6 +648,41 @@ describe('Protocol V2 framing and session', () => {
550
648
  expect(logger.debug).not.toHaveBeenCalled();
551
649
  });
552
650
 
651
+ test('session accepts gaps in the firmware-global response sequence', async () => {
652
+ const response = ProtocolV2.encodeFrame(schemas, 'Success', { message: 'ok' });
653
+ const readFrame = jest
654
+ .fn()
655
+ .mockResolvedValueOnce(rewriteSeq(response, 1))
656
+ .mockResolvedValueOnce(rewriteSeq(response, 4));
657
+ const session = new ProtocolV2Session({
658
+ schemas,
659
+ router: 1,
660
+ writeFrame: () => Promise.resolve(),
661
+ readFrame,
662
+ });
663
+
664
+ await session.call('Ping', { message: 'first' });
665
+ await expect(session.call('Ping', { message: 'second' })).resolves.toMatchObject({
666
+ type: 'Success',
667
+ });
668
+ });
669
+
670
+ test('session rejects a duplicate device response sequence', async () => {
671
+ const response = ProtocolV2.encodeFrame(schemas, 'Success', { message: 'ok' });
672
+ const readFrame = jest.fn(() => Promise.resolve(rewriteSeq(response, 7)));
673
+ const session = new ProtocolV2Session({
674
+ schemas,
675
+ router: 1,
676
+ writeFrame: () => Promise.resolve(),
677
+ readFrame,
678
+ });
679
+
680
+ await session.call('Ping', { message: 'first' });
681
+ await expect(session.call('Ping', { message: 'second' })).rejects.toThrow(
682
+ 'Protocol V2 duplicate response sequence: 7'
683
+ );
684
+ });
685
+
553
686
  test('session does not log transmit or receive payload details', async () => {
554
687
  const response = ProtocolV2.encodeFrame(schemas, 'Success', {
555
688
  message: 'accepted',
@@ -561,7 +694,7 @@ describe('Protocol V2 framing and session', () => {
561
694
  schemas,
562
695
  router: 1,
563
696
  writeFrame: () => Promise.resolve(),
564
- readFrame: () => Promise.resolve(response),
697
+ readFrame: () => Promise.resolve(rewriteSeq(response, 1)),
565
698
  logger,
566
699
  logPrefix: 'ProtocolV2 Test',
567
700
  });
@@ -587,7 +720,7 @@ describe('Protocol V2 framing and session', () => {
587
720
  schemas,
588
721
  router: 1,
589
722
  writeFrame: () => Promise.resolve(),
590
- readFrame: () => Promise.resolve(response),
723
+ readFrame: () => Promise.resolve(rewriteSeq(response, 1)),
591
724
  logger,
592
725
  });
593
726
 
@@ -612,7 +745,10 @@ describe('Protocol V2 framing and session', () => {
612
745
  const logger = {
613
746
  debug: jest.fn(),
614
747
  };
615
- const readFrame = jest.fn().mockResolvedValueOnce(stale).mockResolvedValueOnce(response);
748
+ const readFrame = jest
749
+ .fn()
750
+ .mockResolvedValueOnce(rewriteSeq(stale, 1))
751
+ .mockResolvedValueOnce(rewriteSeq(response, 2));
616
752
  const session = new ProtocolV2Session({
617
753
  schemas,
618
754
  router: 1,
@@ -649,7 +785,9 @@ describe('Protocol V2 framing and session', () => {
649
785
  const [writtenFrame] = written;
650
786
  const { seq } = protocolV2.decodeFrame(writtenFrame);
651
787
  return Promise.resolve(
652
- readFrame.mock.calls.length === 1 ? rewriteSeq(progress, seq) : rewriteSeq(success, seq)
788
+ readFrame.mock.calls.length === 1
789
+ ? rewriteSeq(progress, seq)
790
+ : rewriteSeq(success, protocolV2.nextProtoSeq(seq))
653
791
  );
654
792
  });
655
793
  const session = new ProtocolV2Session({
@@ -826,7 +964,7 @@ describe('Protocol V2 framing and session', () => {
826
964
  }
827
965
  return Promise.resolve();
828
966
  },
829
- readFrame: () => Promise.resolve(response),
967
+ readFrame: () => Promise.resolve(rewriteSeq(response, 1)),
830
968
  });
831
969
 
832
970
  await expect(session.call('Ping', { message: '1' })).rejects.toThrow('transport write failed');
@@ -895,6 +1033,7 @@ describe('Protocol V2 framing and session', () => {
895
1033
  const writeContexts = [];
896
1034
  const readContexts = [];
897
1035
  const response = ProtocolV2.encodeFrame(schemas, 'Success', { message: 'ok' });
1036
+ let responseSeq = 0;
898
1037
  const session = new ProtocolV2Session({
899
1038
  schemas,
900
1039
  router: 1,
@@ -905,18 +1044,65 @@ describe('Protocol V2 framing and session', () => {
905
1044
  },
906
1045
  readFrame: context => {
907
1046
  readContexts.push(context);
908
- return Promise.resolve(response);
1047
+ responseSeq = protocolV2.nextProtoSeq(responseSeq);
1048
+ return Promise.resolve(rewriteSeq(response, responseSeq));
909
1049
  },
910
1050
  });
911
1051
 
912
1052
  await session.call('Ping', { message: 'ping' }, { timeoutMs: 123 });
913
1053
  await session.call('FileWrite', {}, { timeoutMs: 456 });
1054
+ await session.call('Ping', { message: 'default-timeout' });
914
1055
 
915
- expect(writeContexts).toEqual([
916
- { messageName: 'Ping', timeoutMs: 123, highVolume: false, generation: 7 },
917
- { messageName: 'FileWrite', timeoutMs: 456, highVolume: true, generation: 7 },
1056
+ const normalizeContext = ({ signal, ...context }) => ({
1057
+ ...context,
1058
+ signalAborted: signal.aborted,
1059
+ });
1060
+ expect(writeContexts.map(normalizeContext)).toEqual([
1061
+ {
1062
+ messageName: 'Ping',
1063
+ timeoutMs: 123,
1064
+ highVolume: false,
1065
+ generation: 7,
1066
+ signalAborted: false,
1067
+ },
1068
+ {
1069
+ messageName: 'FileWrite',
1070
+ timeoutMs: 456,
1071
+ highVolume: true,
1072
+ generation: 7,
1073
+ signalAborted: false,
1074
+ },
1075
+ {
1076
+ messageName: 'Ping',
1077
+ timeoutMs: PROTOCOL_V2_DEFAULT_RESPONSE_TIMEOUT_MS,
1078
+ highVolume: false,
1079
+ generation: 7,
1080
+ signalAborted: false,
1081
+ },
1082
+ ]);
1083
+ expect(readContexts.map(normalizeContext)).toEqual([
1084
+ {
1085
+ messageName: 'Ping',
1086
+ timeoutMs: 123,
1087
+ highVolume: false,
1088
+ generation: 7,
1089
+ signalAborted: false,
1090
+ },
1091
+ {
1092
+ messageName: 'FileWrite',
1093
+ timeoutMs: 456,
1094
+ highVolume: true,
1095
+ generation: 7,
1096
+ signalAborted: false,
1097
+ },
1098
+ {
1099
+ messageName: 'Ping',
1100
+ timeoutMs: PROTOCOL_V2_DEFAULT_RESPONSE_TIMEOUT_MS,
1101
+ highVolume: false,
1102
+ generation: 7,
1103
+ signalAborted: false,
1104
+ },
918
1105
  ]);
919
- expect(readContexts).toEqual(writeContexts);
920
1106
  });
921
1107
 
922
1108
  test('assembler throws and resets on frames with an impossible length field', () => {
@@ -14,4 +14,5 @@ export declare const PROTOCOL_V2_CHANNEL_USB = 0;
14
14
  export declare const PROTOCOL_V2_CHANNEL_BLE_UART = 1;
15
15
  export declare const PROTOCOL_V2_CHANNEL_SOCKET = 2;
16
16
  export declare const PROTOCOL_V2_PACKET_SRC_COMMAND = 0;
17
+ export declare const PROTOCOL_V2_DEFAULT_RESPONSE_TIMEOUT_MS: number;
17
18
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,qBAAqB,KAAO,CAAC;AAG1C,eAAO,MAAM,uBAAuB,KAAO,CAAC;AAG5C,eAAO,MAAM,8BAA8B,KAAK,CAAC;AAGjD,eAAO,MAAM,2BAA2B,QAAqC,CAAC;AAG9E,eAAO,MAAM,+BAA+B,QAAQ,CAAC;AAGrD,eAAO,MAAM,gCAAgC,QAA0C,CAAC;AAKxF,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAGhD,eAAO,MAAM,kCAAkC,OAAO,CAAC;AAGvD,eAAO,MAAM,+BAA+B,OAAO,CAAC;AAGpD,eAAO,MAAM,oCAAoC,MAAM,CAAC;AAGxD,eAAO,MAAM,+BAA+B,OAAO,CAAC;AAGpD,eAAO,MAAM,2BAA2B,OAAqC,CAAC;AAM9E,eAAO,MAAM,uBAAuB,IAAI,CAAC;AACzC,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAC9C,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAG5C,eAAO,MAAM,8BAA8B,IAAI,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,qBAAqB,KAAO,CAAC;AAG1C,eAAO,MAAM,uBAAuB,KAAO,CAAC;AAG5C,eAAO,MAAM,8BAA8B,KAAK,CAAC;AAGjD,eAAO,MAAM,2BAA2B,QAAqC,CAAC;AAG9E,eAAO,MAAM,+BAA+B,QAAQ,CAAC;AAGrD,eAAO,MAAM,gCAAgC,QAA0C,CAAC;AAKxF,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAGhD,eAAO,MAAM,kCAAkC,OAAO,CAAC;AAGvD,eAAO,MAAM,+BAA+B,OAAO,CAAC;AAGpD,eAAO,MAAM,oCAAoC,MAAM,CAAC;AAGxD,eAAO,MAAM,+BAA+B,OAAO,CAAC;AAGpD,eAAO,MAAM,2BAA2B,OAAqC,CAAC;AAM9E,eAAO,MAAM,uBAAuB,IAAI,CAAC;AACzC,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAC9C,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAG5C,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAQhD,eAAO,MAAM,uCAAuC,QAAgB,CAAC"}