@onekeyfe/hd-transport-react-native 1.2.0-alpha.7 → 1.2.0-alpha.70

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.
@@ -0,0 +1,132 @@
1
+ import ReactNativeBleTransport, { PROTOCOL_REPROBE_FALLBACK_ATTEMPTS } from '../index';
2
+ import protocolV1Schema from './protocolV1SchemaFixture';
3
+
4
+ jest.mock(
5
+ 'react-native',
6
+ () => ({
7
+ Platform: { OS: 'ios', select: (spec: Record<string, unknown>) => spec.ios },
8
+ PermissionsAndroid: {
9
+ PERMISSIONS: {},
10
+ RESULTS: {},
11
+ request: jest.fn(),
12
+ requestMultiple: jest.fn(),
13
+ },
14
+ }),
15
+ { virtual: true }
16
+ );
17
+
18
+ jest.mock('react-native-ble-plx', () => ({
19
+ BleATTErrorCode: { InvalidHandle: 1, UnlikelyError: 14 },
20
+ BleError: Error,
21
+ BleErrorCode: { DeviceDisconnected: 201, OperationStartFailed: 601 },
22
+ BleManager: jest.fn(),
23
+ ScanMode: { LowLatency: 2 },
24
+ }));
25
+
26
+ jest.mock('@onekeyfe/react-native-ble-utils', () => ({
27
+ __esModule: true,
28
+ default: {
29
+ getConnectedPeripherals: jest.fn(() => Promise.resolve([])),
30
+ getBondedPeripherals: jest.fn(() => Promise.resolve([])),
31
+ pairDevice: jest.fn(() => Promise.resolve()),
32
+ },
33
+ }));
34
+
35
+ const UUID = 'reprobe-device';
36
+
37
+ /** Drive detectProtocol with stubbed probes so we can observe which are attempted. */
38
+ function createHarness({ v1, v2 }: { v1: boolean; v2: boolean }) {
39
+ const transport = new ReactNativeBleTransport({});
40
+ transport.configure(protocolV1Schema);
41
+ const probeV1 = jest.fn(() => {
42
+ if (v1) {
43
+ (transport as any).deviceProtocol.set(UUID, 'V1');
44
+ }
45
+ return Promise.resolve(v1);
46
+ });
47
+ const probeV2 = jest.fn(() => {
48
+ if (v2) {
49
+ (transport as any).deviceProtocol.set(UUID, 'V2');
50
+ }
51
+ return Promise.resolve(v2);
52
+ });
53
+ (transport as any).probeProtocolV1 = probeV1;
54
+ (transport as any).probeProtocolV2 = probeV2;
55
+ (transport as any).resetProbeStateAfterProtocolProbe = jest.fn(() => Promise.resolve());
56
+ return { transport, probeV1, probeV2 };
57
+ }
58
+
59
+ const detect = (transport: ReactNativeBleTransport) =>
60
+ (transport as any).detectProtocol(UUID, undefined, undefined, () =>
61
+ Promise.resolve()
62
+ ) as Promise<string>;
63
+
64
+ describe('protocol re-probe after a known device goes away', () => {
65
+ test('an unknown device still probes both protocols', async () => {
66
+ const { transport, probeV1, probeV2 } = createHarness({ v1: false, v2: false });
67
+
68
+ await expect(detect(transport)).rejects.toBeDefined();
69
+
70
+ expect(probeV1).toHaveBeenCalledTimes(1);
71
+ expect(probeV2).toHaveBeenCalledTimes(1);
72
+ });
73
+
74
+ test('a device already known to speak V1 does not pay the V2 probe while it is away', async () => {
75
+ const known = createHarness({ v1: true, v2: false });
76
+ // First detection confirms V1 (this is the session that just talked to the device).
77
+ await expect(detect(known.transport)).resolves.toBe('V1');
78
+
79
+ // The device now reboots after a firmware install: V1 stops answering.
80
+ (known.transport as any).deviceProtocol.delete(UUID);
81
+ const probeV1 = jest.fn(() => Promise.resolve(false));
82
+ const probeV2 = jest.fn(() => Promise.resolve(false));
83
+ (known.transport as any).probeProtocolV1 = probeV1;
84
+ (known.transport as any).probeProtocolV2 = probeV2;
85
+
86
+ await expect(detect(known.transport)).rejects.toBeDefined();
87
+
88
+ expect(probeV1).toHaveBeenCalledTimes(1);
89
+ // The 10s Protocol V2 ping is pure waste for a device we just spoke V1 to.
90
+ expect(probeV2).not.toHaveBeenCalled();
91
+ });
92
+
93
+ test('after repeated failures it re-probes every protocol again', async () => {
94
+ const known = createHarness({ v1: true, v2: false });
95
+ await expect(detect(known.transport)).resolves.toBe('V1');
96
+
97
+ (known.transport as any).deviceProtocol.delete(UUID);
98
+ const probeV1 = jest.fn(() => Promise.resolve(false));
99
+ const probeV2 = jest.fn(() => Promise.resolve(false));
100
+ (known.transport as any).probeProtocolV1 = probeV1;
101
+ (known.transport as any).probeProtocolV2 = probeV2;
102
+
103
+ for (let attempt = 0; attempt < PROTOCOL_REPROBE_FALLBACK_ATTEMPTS; attempt += 1) {
104
+ // eslint-disable-next-line no-await-in-loop
105
+ await expect(detect(known.transport)).rejects.toBeDefined();
106
+ }
107
+ expect(probeV2).not.toHaveBeenCalled();
108
+
109
+ // The device may genuinely have changed protocol, so the shortcut must expire.
110
+ await expect(detect(known.transport)).rejects.toBeDefined();
111
+ expect(probeV2).toHaveBeenCalled();
112
+ });
113
+
114
+ test('a successful detection clears the failure streak', async () => {
115
+ const known = createHarness({ v1: true, v2: false });
116
+ await expect(detect(known.transport)).resolves.toBe('V1');
117
+ (known.transport as any).deviceProtocol.delete(UUID);
118
+
119
+ const failingV1 = jest.fn(() => Promise.resolve(false));
120
+ (known.transport as any).probeProtocolV1 = failingV1;
121
+ await expect(detect(known.transport)).rejects.toBeDefined();
122
+
123
+ // Device comes back.
124
+ (known.transport as any).probeProtocolV1 = jest.fn(() => {
125
+ (known.transport as any).deviceProtocol.set(UUID, 'V1');
126
+ return Promise.resolve(true);
127
+ });
128
+ await expect(detect(known.transport)).resolves.toBe('V1');
129
+
130
+ expect((known.transport as any).protocolReprobeFailures.get(UUID)).toBeUndefined();
131
+ });
132
+ });
@@ -0,0 +1,39 @@
1
+ const protocolV1Schema = {
2
+ nested: {
3
+ Initialize: {
4
+ fields: {
5
+ session_id: { type: 'bytes', id: 1 },
6
+ derive_cardano: { type: 'bool', id: 3 },
7
+ },
8
+ },
9
+ GetFeatures: { fields: {} },
10
+ Success: {
11
+ fields: {
12
+ message: { type: 'string', id: 1 },
13
+ },
14
+ },
15
+ Ping: {
16
+ fields: {
17
+ message: { type: 'string', id: 1 },
18
+ button_protection: { type: 'bool', id: 2 },
19
+ },
20
+ },
21
+ FirmwareUpload: {
22
+ fields: {
23
+ payload: { rule: 'required', type: 'bytes', id: 1 },
24
+ hash: { type: 'bytes', id: 2 },
25
+ },
26
+ },
27
+ MessageType: {
28
+ values: {
29
+ MessageType_Initialize: 0,
30
+ MessageType_Ping: 1,
31
+ MessageType_Success: 2,
32
+ MessageType_FirmwareUpload: 7,
33
+ MessageType_GetFeatures: 55,
34
+ },
35
+ },
36
+ },
37
+ };
38
+
39
+ export default protocolV1Schema;