@onekeyfe/hd-core 1.2.0-alpha.3 → 1.2.0-alpha.5
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.
- package/__tests__/preInitialize.test.ts +33 -0
- package/__tests__/protocol-v2.test.ts +1443 -480
- package/dist/api/FileRead.d.ts.map +1 -1
- package/dist/api/FileWrite.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +22 -2
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +1 -0
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/firmware/getBinary.d.ts +4 -0
- package/dist/api/firmware/getBinary.d.ts.map +1 -1
- package/dist/api/firmware/progressThrottle.d.ts +3 -0
- package/dist/api/firmware/progressThrottle.d.ts.map +1 -0
- package/dist/api/index.d.ts +5 -10
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceFactoryInfoGet.d.ts +6 -0
- package/dist/api/protocol-v2/DeviceFactoryInfoGet.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceFactoryInfoSet.d.ts +7 -0
- package/dist/api/protocol-v2/DeviceFactoryInfoSet.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts +1 -1
- package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts +3 -2
- package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
- package/dist/api/protocol-v2/{DeviceGetDeviceInfo.d.ts → DeviceInfoGet.d.ts} +10 -10
- package/dist/api/protocol-v2/DeviceInfoGet.d.ts.map +1 -0
- package/dist/api/protocol-v2/{FilesystemFixPermission.d.ts → FilesystemPermissionFix.d.ts} +2 -2
- package/dist/api/protocol-v2/FilesystemPermissionFix.d.ts.map +1 -0
- package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts +6 -0
- package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -0
- package/dist/api/protocol-v2/helpers.d.ts +27 -18
- package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
- package/dist/api/ton/TonSignData.d.ts +1 -5
- package/dist/api/ton/TonSignData.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/data-manager/DataManager.d.ts +1 -1
- package/dist/data-manager/DataManager.d.ts.map +1 -1
- package/dist/data-manager/connectSettings.d.ts.map +1 -1
- package/dist/device/Device.d.ts +4 -1
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
- package/dist/deviceProfile/buildDeviceProfile.d.ts.map +1 -1
- package/dist/index.d.ts +82 -54
- package/dist/index.js +1615 -1537
- package/dist/inject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/features.d.ts +12 -14
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/firmware.d.ts +11 -9
- package/dist/protocols/protocol-v2/firmware.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdate.d.ts +1 -6
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/api/getDeviceInfo.d.ts +2 -0
- package/dist/types/api/getDeviceInfo.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +6 -11
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +12 -17
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/device.d.ts +1 -0
- package/dist/types/device.d.ts.map +1 -1
- package/dist/types/params.d.ts +1 -0
- package/dist/types/params.d.ts.map +1 -1
- package/dist/types/settings.d.ts +29 -2
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/FileRead.ts +14 -1
- package/src/api/FileWrite.ts +14 -1
- package/src/api/FirmwareUpdateV4.ts +975 -201
- package/src/api/GetOnekeyFeatures.ts +1 -1
- package/src/api/device/DeviceRebootToBoardloader.ts +4 -4
- package/src/api/device/DeviceRebootToBootloader.ts +4 -4
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +18 -4
- package/src/api/firmware/progressThrottle.ts +44 -0
- package/src/api/index.ts +5 -10
- package/src/api/protocol-v2/{FactoryGetDeviceInfo.ts → DeviceFactoryInfoGet.ts} +3 -3
- package/src/api/protocol-v2/DeviceFactoryInfoSet.ts +32 -0
- package/src/api/protocol-v2/DeviceFirmwareUpdate.ts +3 -5
- package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +39 -5
- package/src/api/protocol-v2/{DeviceGetDeviceInfo.ts → DeviceInfoGet.ts} +52 -22
- package/src/api/protocol-v2/DeviceReboot.ts +1 -1
- package/src/api/protocol-v2/{FilesystemFixPermission.ts → FilesystemPermissionFix.ts} +2 -2
- package/src/api/protocol-v2/{GetProtoVersion.ts → ProtocolInfoRequest.ts} +2 -2
- package/src/api/protocol-v2/helpers.ts +64 -43
- package/src/api/ton/TonSignData.ts +1 -5
- package/src/core/index.ts +19 -5
- package/src/data/messages/messages-protocol-v2.json +485 -1069
- package/src/data-manager/DataManager.ts +7 -0
- package/src/data-manager/connectSettings.ts +0 -6
- package/src/device/Device.ts +25 -14
- package/src/deviceProfile/buildDeviceFeatures.ts +60 -63
- package/src/deviceProfile/buildDeviceProfile.ts +77 -58
- package/src/inject.ts +10 -16
- package/src/protocols/protocol-v2/features.ts +26 -63
- package/src/protocols/protocol-v2/firmware.ts +12 -36
- package/src/types/api/firmwareUpdate.ts +10 -20
- package/src/types/api/getDeviceInfo.ts +2 -0
- package/src/types/api/index.ts +10 -20
- package/src/types/api/protocolV2.ts +25 -43
- package/src/types/device.ts +1 -0
- package/src/types/params.ts +4 -0
- package/src/types/settings.ts +44 -35
- package/src/utils/deviceFeaturesUtils.ts +45 -10
- package/dist/api/protocol-v2/DevReboot.d.ts +0 -7
- package/dist/api/protocol-v2/DevReboot.d.ts.map +0 -1
- package/dist/api/protocol-v2/DeviceGetDeviceInfo.d.ts.map +0 -1
- package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts +0 -6
- package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts.map +0 -1
- package/dist/api/protocol-v2/FactoryDeviceInfoSettings.d.ts +0 -7
- package/dist/api/protocol-v2/FactoryDeviceInfoSettings.d.ts.map +0 -1
- package/dist/api/protocol-v2/FactoryGetDeviceInfo.d.ts +0 -6
- package/dist/api/protocol-v2/FactoryGetDeviceInfo.d.ts.map +0 -1
- package/dist/api/protocol-v2/FilesystemFixPermission.d.ts.map +0 -1
- package/dist/api/protocol-v2/GetProtoVersion.d.ts +0 -6
- package/dist/api/protocol-v2/GetProtoVersion.d.ts.map +0 -1
- package/src/api/protocol-v2/DevReboot.ts +0 -24
- package/src/api/protocol-v2/DeviceGetOnboardingStatus.ts +0 -16
- package/src/api/protocol-v2/FactoryDeviceInfoSettings.ts +0 -29
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
-
import {
|
|
2
|
+
import { DeviceRebootType } from '@onekeyfe/hd-transport';
|
|
3
3
|
|
|
4
|
-
import
|
|
4
|
+
import * as firmwareBinaryApi from '../src/api/firmware/getBinary';
|
|
5
5
|
import DnxGetAddress from '../src/api/dynex/DnxGetAddress';
|
|
6
6
|
import DnxSignTransaction from '../src/api/dynex/DnxSignTransaction';
|
|
7
7
|
import DirList from '../src/api/DirList';
|
|
8
8
|
import FileRead from '../src/api/FileRead';
|
|
9
9
|
import FileWrite from '../src/api/FileWrite';
|
|
10
|
-
import
|
|
10
|
+
import DeviceFactoryInfoGet from '../src/api/protocol-v2/DeviceFactoryInfoGet';
|
|
11
|
+
import DeviceFactoryInfoSet from '../src/api/protocol-v2/DeviceFactoryInfoSet';
|
|
11
12
|
import DeviceFirmwareUpdate from '../src/api/protocol-v2/DeviceFirmwareUpdate';
|
|
12
|
-
import
|
|
13
|
-
import
|
|
13
|
+
import DeviceGetFirmwareUpdateStatus from '../src/api/protocol-v2/DeviceGetFirmwareUpdateStatus';
|
|
14
|
+
import DeviceInfoGet from '../src/api/protocol-v2/DeviceInfoGet';
|
|
14
15
|
import DeviceReboot from '../src/api/protocol-v2/DeviceReboot';
|
|
16
|
+
import FilesystemPermissionFix from '../src/api/protocol-v2/FilesystemPermissionFix';
|
|
17
|
+
import ProtocolInfoRequest from '../src/api/protocol-v2/ProtocolInfoRequest';
|
|
15
18
|
import EVMSignTypedData from '../src/api/evm/EVMSignTypedData';
|
|
16
19
|
import EVMSignMessageEIP712 from '../src/api/evm/EVMSignMessageEIP712';
|
|
17
20
|
import FirmwareUpdateV3 from '../src/api/FirmwareUpdateV3';
|
|
@@ -35,8 +38,6 @@ import TonSignData from '../src/api/ton/TonSignData';
|
|
|
35
38
|
import TonSignMessage from '../src/api/ton/TonSignMessage';
|
|
36
39
|
import TonSignProof from '../src/api/ton/TonSignProof';
|
|
37
40
|
import TronGetAddress from '../src/api/tron/TronGetAddress';
|
|
38
|
-
import TronSignMessage from '../src/api/tron/TronSignMessage';
|
|
39
|
-
import XrpSignTransaction from '../src/api/xrp/XrpSignTransaction';
|
|
40
41
|
import StellarGetAddress from '../src/api/stellar/StellarGetAddress';
|
|
41
42
|
import BenfenSignMessage from '../src/api/benfen/BenfenSignMessage';
|
|
42
43
|
import { getBitcoinForkVersionRange } from '../src/api/btc/helpers/versionLimit';
|
|
@@ -45,17 +46,14 @@ import { Device } from '../src/device/Device';
|
|
|
45
46
|
import { UI_REQUEST } from '../src/events/ui-request';
|
|
46
47
|
import {
|
|
47
48
|
PROTOCOL_V2_DEVICE_INFO_TIMEOUT_MS,
|
|
48
|
-
buildMockProtocolV2DeviceInfo,
|
|
49
49
|
requestProtocolV2DeviceInfo,
|
|
50
|
-
setProtocolV2DeviceInfoMock,
|
|
51
50
|
} from '../src/protocols/protocol-v2/features';
|
|
52
|
-
import {
|
|
53
|
-
buildProfileFromProtocolV2,
|
|
54
|
-
buildProtocolV2FeaturesPayload,
|
|
55
|
-
} from '../src/deviceProfile';
|
|
51
|
+
import { buildProfileFromProtocolV2, buildProtocolV2FeaturesPayload } from '../src/deviceProfile';
|
|
56
52
|
import {
|
|
57
53
|
getDeviceType,
|
|
58
54
|
getFirmwareType,
|
|
55
|
+
getFirmwareUpdateField,
|
|
56
|
+
getFirmwareUpdateFieldArray,
|
|
59
57
|
getMethodVersionRange,
|
|
60
58
|
isMethodVersionRangeUnsupported,
|
|
61
59
|
} from '../src/utils';
|
|
@@ -74,12 +72,6 @@ jest.mock('../src/data/config', () => ({
|
|
|
74
72
|
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
|
|
75
73
|
}));
|
|
76
74
|
|
|
77
|
-
// DeviceGetDeviceInfo mock 开关默认开启(早期工程板固件没有该消息)。
|
|
78
|
-
// 本测试文件验证的是真实 wire 调用行为,统一关闭;mock 行为单独有用例覆盖。
|
|
79
|
-
beforeAll(() => {
|
|
80
|
-
setProtocolV2DeviceInfoMock(false);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
75
|
const descriptor = {
|
|
84
76
|
id: 'ble-id',
|
|
85
77
|
path: 'usb-path',
|
|
@@ -101,8 +93,7 @@ function stubDevice<T extends Record<string, any>>(device: T): T {
|
|
|
101
93
|
d.getCurrentSafetyChecks ??= () => d.features?.safetyChecks;
|
|
102
94
|
d.getCurrentDeviceId ??= () => d.features?.deviceId || undefined;
|
|
103
95
|
d.getCurrentSerialNo ??= () => d.features?.serialNo || '';
|
|
104
|
-
d.getCurrentPassphraseProtection ??= () =>
|
|
105
|
-
d.features?.passphraseProtection;
|
|
96
|
+
d.getCurrentPassphraseProtection ??= () => d.features?.passphraseProtection;
|
|
106
97
|
d.getCurrentMethodVersionRange ??= (fn: (model: any) => any) => {
|
|
107
98
|
const deviceType = d.getCurrentDeviceType();
|
|
108
99
|
const range = fn(deviceType);
|
|
@@ -121,7 +112,19 @@ function normalizeProtocolV2Features(_descriptor: unknown, deviceInfo?: Protocol
|
|
|
121
112
|
return buildProtocolV2FeaturesPayload(deviceInfo);
|
|
122
113
|
}
|
|
123
114
|
|
|
124
|
-
|
|
115
|
+
const protocolV2BootloaderDeviceInfo: ProtocolV2DeviceInfo = {
|
|
116
|
+
protocol_version: 1,
|
|
117
|
+
hw: {
|
|
118
|
+
serial_no: 'PR9999999999',
|
|
119
|
+
},
|
|
120
|
+
fw: {
|
|
121
|
+
bootloader: {
|
|
122
|
+
version: '0.0.1',
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
async function requestProtocolV2Features({
|
|
125
128
|
commands,
|
|
126
129
|
descriptor: inputDescriptor,
|
|
127
130
|
}: {
|
|
@@ -133,63 +136,38 @@ async function requestProtocolV2LegacyFeatures({
|
|
|
133
136
|
}
|
|
134
137
|
|
|
135
138
|
describe('Protocol V2 feature adapter', () => {
|
|
136
|
-
test('skips DeviceGetDeviceInfo wire call and returns mock when mock is enabled', async () => {
|
|
137
|
-
const typedCall = jest.fn();
|
|
138
|
-
setProtocolV2DeviceInfoMock(true);
|
|
139
|
-
try {
|
|
140
|
-
const deviceInfo = await requestProtocolV2DeviceInfo({
|
|
141
|
-
commands: { typedCall } as unknown as DeviceCommands,
|
|
142
|
-
});
|
|
143
|
-
expect(typedCall).not.toHaveBeenCalled();
|
|
144
|
-
expect(deviceInfo).toEqual(buildMockProtocolV2DeviceInfo());
|
|
145
|
-
const profile = buildProfileFromProtocolV2({
|
|
146
|
-
deviceInfo,
|
|
147
|
-
});
|
|
148
|
-
expect(profile).toMatchObject({
|
|
149
|
-
protocol: 'V2',
|
|
150
|
-
deviceId: '',
|
|
151
|
-
serialNo: '',
|
|
152
|
-
status: { initialized: true },
|
|
153
|
-
versions: { firmware: '0.1.0' },
|
|
154
|
-
});
|
|
155
|
-
} finally {
|
|
156
|
-
setProtocolV2DeviceInfoMock(false);
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
|
|
160
139
|
test('normalizes Protocol V2 DeviceInfo into existing Features fields', () => {
|
|
161
140
|
const features = normalizeProtocolV2Features(descriptor as any, {
|
|
162
141
|
protocol_version: 1,
|
|
163
142
|
hw: {
|
|
164
143
|
serial_no: 'PR2SERIAL',
|
|
165
|
-
device_id: 'PRO2-DEVICE-ID',
|
|
166
144
|
},
|
|
167
145
|
fw: {
|
|
168
|
-
|
|
146
|
+
application_data: {
|
|
169
147
|
version: '0.1.0',
|
|
170
148
|
hash: [1, 2, 255],
|
|
171
149
|
},
|
|
172
|
-
|
|
150
|
+
bootloader: {
|
|
173
151
|
version: '0.2.0',
|
|
174
152
|
build_id: 'boot-build',
|
|
175
153
|
hash: new Uint8Array([10, 11]),
|
|
176
154
|
},
|
|
177
|
-
|
|
155
|
+
application: {
|
|
178
156
|
version: '1.2.3',
|
|
179
157
|
build_id: 'app-build',
|
|
180
158
|
hash: 'abc123',
|
|
181
159
|
},
|
|
182
160
|
},
|
|
183
|
-
|
|
184
|
-
|
|
161
|
+
coprocessor: {
|
|
162
|
+
application: {
|
|
185
163
|
version: '4.5.6',
|
|
186
164
|
build_id: 'bt-build',
|
|
187
165
|
hash: [12, 13],
|
|
188
166
|
},
|
|
189
|
-
|
|
167
|
+
bt_adv_name: 'Pro2 BLE',
|
|
190
168
|
},
|
|
191
169
|
se1: {
|
|
192
|
-
|
|
170
|
+
application: {
|
|
193
171
|
version: '7.8.9',
|
|
194
172
|
build_id: 'se-build',
|
|
195
173
|
hash: [14, 15],
|
|
@@ -197,18 +175,19 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
197
175
|
state: 85,
|
|
198
176
|
},
|
|
199
177
|
se2: {
|
|
200
|
-
|
|
178
|
+
application: {
|
|
201
179
|
version: '8.0.0',
|
|
202
180
|
},
|
|
203
181
|
state: 0,
|
|
204
182
|
},
|
|
205
183
|
status: {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
bt_enable: true,
|
|
184
|
+
device_id: 'PRO2-DEVICE-ID',
|
|
185
|
+
unlocked: true,
|
|
209
186
|
init_states: false,
|
|
210
187
|
backup_required: true,
|
|
211
|
-
|
|
188
|
+
passphrase_enabled: true,
|
|
189
|
+
attach_to_pin_enabled: true,
|
|
190
|
+
unlocked_by_attach_to_pin: true,
|
|
212
191
|
},
|
|
213
192
|
});
|
|
214
193
|
|
|
@@ -228,24 +207,47 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
228
207
|
expect(features.verify?.bleHash).toBe('0c0d');
|
|
229
208
|
expect(features.se01Version).toBe('7.8.9');
|
|
230
209
|
expect(features.verify?.se01Hash).toBe('0e0f');
|
|
231
|
-
expect(features.label).
|
|
232
|
-
expect(features.language).
|
|
210
|
+
expect(features.label).toBeNull();
|
|
211
|
+
expect(features.language).toBeNull();
|
|
233
212
|
expect(features.initialized).toBe(false);
|
|
213
|
+
expect(features.unlocked).toBe(true);
|
|
234
214
|
expect(features.backupRequired).toBe(true);
|
|
235
215
|
expect(features.passphraseProtection).toBe(true);
|
|
236
|
-
expect(features.
|
|
216
|
+
expect(features.attachToPinEnabled).toBe(true);
|
|
217
|
+
expect(features.unlockedAttachPin).toBe(true);
|
|
218
|
+
expect(features.bleEnabled).toBeNull();
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test('marks Protocol V2 DeviceInfo without status as bootloader mode', () => {
|
|
222
|
+
const deviceInfo = {
|
|
223
|
+
protocol_version: 1,
|
|
224
|
+
hw: {
|
|
225
|
+
serial_no: 'PR2BOOT',
|
|
226
|
+
},
|
|
227
|
+
fw: {
|
|
228
|
+
bootloader: {
|
|
229
|
+
version: '0.2.0',
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
const features = normalizeProtocolV2Features(descriptor as any, deviceInfo);
|
|
234
|
+
const profile = buildProfileFromProtocolV2({ deviceInfo });
|
|
235
|
+
|
|
236
|
+
expect(features.mode).toBe('bootloader');
|
|
237
|
+
expect(features.bootloaderMode).toBe(true);
|
|
238
|
+
expect(features.initialized).toBeNull();
|
|
239
|
+
expect(profile.status.mode).toBe('bootloader');
|
|
240
|
+
expect(profile.status.bootloaderMode).toBe(true);
|
|
237
241
|
});
|
|
238
242
|
|
|
239
|
-
test('uses
|
|
243
|
+
test('uses DeviceSessionGet for Protocol V2 passphrase sessions', async () => {
|
|
240
244
|
const features = normalizeProtocolV2Features(descriptor as any);
|
|
241
|
-
// Pro2 版本线独立于 Pro 系列:V2 设备无论固件版本都支持 GetPassphraseState
|
|
242
245
|
features.firmwareVersion = '1.2.3';
|
|
243
246
|
const typedCall = jest.fn().mockResolvedValue({
|
|
244
|
-
type: '
|
|
247
|
+
type: 'DeviceSession',
|
|
245
248
|
message: {
|
|
246
|
-
|
|
249
|
+
btc_test_address: 'state-1',
|
|
247
250
|
session_id: 'session-1',
|
|
248
|
-
unlocked_attach_pin: false,
|
|
249
251
|
},
|
|
250
252
|
});
|
|
251
253
|
const commands = { typedCall } as unknown as DeviceCommands;
|
|
@@ -256,25 +258,18 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
256
258
|
await getPassphraseState(device, {
|
|
257
259
|
expectPassphraseState: 'state-1',
|
|
258
260
|
});
|
|
259
|
-
expect(typedCall).toHaveBeenLastCalledWith('
|
|
260
|
-
passphrase_state: 'state-1',
|
|
261
|
-
});
|
|
261
|
+
expect(typedCall).toHaveBeenLastCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
262
262
|
|
|
263
263
|
await getPassphraseState(device, {
|
|
264
264
|
expectPassphraseState: 'state-2',
|
|
265
265
|
allowCreateAttachPin: true,
|
|
266
266
|
});
|
|
267
|
-
expect(typedCall).toHaveBeenLastCalledWith('
|
|
268
|
-
passphrase_state: 'state-2',
|
|
269
|
-
allow_create_attach_pin: true,
|
|
270
|
-
});
|
|
267
|
+
expect(typedCall).toHaveBeenLastCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
271
268
|
|
|
272
269
|
await getPassphraseState(device, {
|
|
273
270
|
onlyMainPin: true,
|
|
274
271
|
});
|
|
275
|
-
expect(typedCall).toHaveBeenLastCalledWith('
|
|
276
|
-
_only_main_pin: true,
|
|
277
|
-
});
|
|
272
|
+
expect(typedCall).toHaveBeenLastCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
278
273
|
});
|
|
279
274
|
|
|
280
275
|
test('returns unified GetPassphraseState object payload for existing Pro devices', async () => {
|
|
@@ -302,7 +297,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
302
297
|
},
|
|
303
298
|
});
|
|
304
299
|
method.device = stubDevice({
|
|
305
|
-
originalDescriptor: { ...descriptor, protocolType: '
|
|
300
|
+
originalDescriptor: { ...descriptor, protocolType: 'V1' },
|
|
306
301
|
features,
|
|
307
302
|
commands: { typedCall },
|
|
308
303
|
updateInternalState,
|
|
@@ -335,11 +330,10 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
335
330
|
unlockedAttachPin: true,
|
|
336
331
|
};
|
|
337
332
|
const typedCall = jest.fn().mockResolvedValue({
|
|
338
|
-
type: '
|
|
333
|
+
type: 'DeviceSession',
|
|
339
334
|
message: {
|
|
340
|
-
|
|
335
|
+
btc_test_address: 'state-pro2',
|
|
341
336
|
session_id: 'session-pro2',
|
|
342
|
-
unlocked_attach_pin: false,
|
|
343
337
|
},
|
|
344
338
|
});
|
|
345
339
|
const updateInternalState = jest.fn();
|
|
@@ -364,7 +358,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
364
358
|
await expect(method.run()).resolves.toEqual({
|
|
365
359
|
passphraseState: 'state-pro2',
|
|
366
360
|
sessionId: 'session-pro2',
|
|
367
|
-
unlockedAttachPin:
|
|
361
|
+
unlockedAttachPin: true,
|
|
368
362
|
passphraseProtection: true,
|
|
369
363
|
});
|
|
370
364
|
expect(getFeatures).not.toHaveBeenCalled();
|
|
@@ -380,10 +374,9 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
380
374
|
unlockedAttachPin: false,
|
|
381
375
|
};
|
|
382
376
|
const typedCall = jest.fn().mockResolvedValue({
|
|
383
|
-
type: '
|
|
377
|
+
type: 'DeviceSession',
|
|
384
378
|
message: {
|
|
385
|
-
|
|
386
|
-
unlocked_attach_pin: false,
|
|
379
|
+
btc_test_address: 'state-pro2-new',
|
|
387
380
|
},
|
|
388
381
|
});
|
|
389
382
|
const clearInternalState = jest.fn();
|
|
@@ -424,19 +417,18 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
424
417
|
test('stores Pro2 passphrase session cache without synthetic device id', async () => {
|
|
425
418
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
426
419
|
const typedCall = jest.fn().mockResolvedValue({
|
|
427
|
-
type: '
|
|
420
|
+
type: 'DeviceSession',
|
|
428
421
|
message: {
|
|
429
|
-
|
|
422
|
+
btc_test_address: 'state-profile',
|
|
430
423
|
session_id: 'session-profile',
|
|
431
|
-
unlocked_attach_pin: false,
|
|
432
424
|
},
|
|
433
425
|
});
|
|
434
426
|
|
|
435
427
|
(device as any).features = {
|
|
436
428
|
...normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any, {
|
|
437
429
|
hw: { serial_no: 'PR2SERIAL' },
|
|
438
|
-
fw: {
|
|
439
|
-
status: {
|
|
430
|
+
fw: { application: { version: '4.15.0' } },
|
|
431
|
+
status: { passphrase_enabled: true },
|
|
440
432
|
}),
|
|
441
433
|
unlocked: true,
|
|
442
434
|
};
|
|
@@ -446,17 +438,16 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
446
438
|
|
|
447
439
|
device.passphraseState = 'state-profile';
|
|
448
440
|
expect(device.getInternalState()).toBe('session-profile');
|
|
449
|
-
expect(device.getInternalState('
|
|
441
|
+
expect(device.getInternalState('CACHED-ID')).toBeUndefined();
|
|
450
442
|
});
|
|
451
443
|
|
|
452
444
|
test('stores Pro2 passphrase sessions without selecting them implicitly', async () => {
|
|
453
445
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
454
446
|
const typedCall = jest.fn().mockResolvedValue({
|
|
455
|
-
type: '
|
|
447
|
+
type: 'DeviceSession',
|
|
456
448
|
message: {
|
|
457
|
-
|
|
449
|
+
btc_test_address: 'state-auto',
|
|
458
450
|
session_id: 'session-auto',
|
|
459
|
-
unlocked_attach_pin: false,
|
|
460
451
|
},
|
|
461
452
|
});
|
|
462
453
|
|
|
@@ -485,18 +476,15 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
485
476
|
expect(device.getInternalState()).toBeUndefined();
|
|
486
477
|
device.passphraseState = 'state-auto';
|
|
487
478
|
expect(device.getInternalState()).toBe('session-auto');
|
|
488
|
-
expect(typedCall).toHaveBeenLastCalledWith('
|
|
489
|
-
passphrase_state: undefined,
|
|
490
|
-
});
|
|
479
|
+
expect(typedCall).toHaveBeenLastCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
491
480
|
});
|
|
492
481
|
|
|
493
482
|
test('does not mark Pro2 passphrase enabled from a main PIN session alone', async () => {
|
|
494
483
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
495
484
|
const typedCall = jest.fn().mockResolvedValue({
|
|
496
|
-
type: '
|
|
485
|
+
type: 'DeviceSession',
|
|
497
486
|
message: {
|
|
498
487
|
session_id: 'main-pin-session',
|
|
499
|
-
unlocked_attach_pin: false,
|
|
500
488
|
},
|
|
501
489
|
});
|
|
502
490
|
|
|
@@ -507,7 +495,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
507
495
|
} as any,
|
|
508
496
|
{
|
|
509
497
|
status: {
|
|
510
|
-
|
|
498
|
+
passphrase_enabled: false,
|
|
511
499
|
},
|
|
512
500
|
}
|
|
513
501
|
);
|
|
@@ -525,19 +513,16 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
525
513
|
expect(device.features?.passphraseProtection).toBe(false);
|
|
526
514
|
expect(device.features?.sessionId).toBeNull();
|
|
527
515
|
expect(device.getInternalState()).toBeUndefined();
|
|
528
|
-
expect(typedCall).toHaveBeenLastCalledWith('
|
|
529
|
-
_only_main_pin: true,
|
|
530
|
-
});
|
|
516
|
+
expect(typedCall).toHaveBeenLastCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
531
517
|
});
|
|
532
518
|
|
|
533
519
|
test('does not let skipPassphraseCheck hide Pro2 passphrase state mismatch', async () => {
|
|
534
520
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
535
521
|
const typedCall = jest.fn().mockResolvedValue({
|
|
536
|
-
type: '
|
|
522
|
+
type: 'DeviceSession',
|
|
537
523
|
message: {
|
|
538
|
-
|
|
524
|
+
btc_test_address: 'wrong-state',
|
|
539
525
|
session_id: 'wrong-session',
|
|
540
|
-
unlocked_attach_pin: false,
|
|
541
526
|
},
|
|
542
527
|
});
|
|
543
528
|
|
|
@@ -554,9 +539,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
554
539
|
);
|
|
555
540
|
|
|
556
541
|
expect(device.getInternalState()).toBeUndefined();
|
|
557
|
-
expect(typedCall).toHaveBeenNthCalledWith(1, '
|
|
558
|
-
passphrase_state: 'expected-state',
|
|
559
|
-
});
|
|
542
|
+
expect(typedCall).toHaveBeenNthCalledWith(1, 'DeviceSessionGet', 'DeviceSession', {});
|
|
560
543
|
});
|
|
561
544
|
|
|
562
545
|
test('marks fallback features as unavailable when DeviceInfo is missing', () => {
|
|
@@ -569,7 +552,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
569
552
|
expect(features.firmwarePresent).toBeNull();
|
|
570
553
|
});
|
|
571
554
|
|
|
572
|
-
test('uses Protocol V2
|
|
555
|
+
test('uses Protocol V2 status.device_id and does not fall back to serial_no', () => {
|
|
573
556
|
const features = normalizeProtocolV2Features(
|
|
574
557
|
{
|
|
575
558
|
id: 'PR2000000000',
|
|
@@ -579,6 +562,8 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
579
562
|
{
|
|
580
563
|
hw: {
|
|
581
564
|
serial_no: 'PR9999999999',
|
|
565
|
+
},
|
|
566
|
+
status: {
|
|
582
567
|
device_id: 'DEVICE-ID-9999',
|
|
583
568
|
},
|
|
584
569
|
}
|
|
@@ -610,17 +595,17 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
610
595
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
611
596
|
(device as any).features = {
|
|
612
597
|
...normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any),
|
|
613
|
-
serialNo: '
|
|
614
|
-
label: '
|
|
615
|
-
bleName: '
|
|
598
|
+
serialNo: 'CACHED-SERIAL',
|
|
599
|
+
label: 'Cached Label',
|
|
600
|
+
bleName: 'Cached BLE',
|
|
616
601
|
passphraseProtection: true,
|
|
617
602
|
};
|
|
618
603
|
|
|
619
604
|
expect(device.toMessageObject()).toMatchObject({
|
|
620
|
-
uuid: '
|
|
605
|
+
uuid: 'CACHED-SERIAL',
|
|
621
606
|
deviceId: null,
|
|
622
|
-
bleName: '
|
|
623
|
-
label: '
|
|
607
|
+
bleName: 'Cached BLE',
|
|
608
|
+
label: 'Cached Label',
|
|
624
609
|
deviceType: 'pro2',
|
|
625
610
|
});
|
|
626
611
|
expect(device.getCurrentPassphraseProtection()).toBe(true);
|
|
@@ -632,52 +617,53 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
632
617
|
(cached as any).features = {
|
|
633
618
|
...normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any),
|
|
634
619
|
deviceId: null,
|
|
635
|
-
serialNo: '
|
|
620
|
+
serialNo: 'CACHED-SERIAL',
|
|
636
621
|
};
|
|
637
622
|
|
|
638
623
|
const current = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
639
624
|
current.updateFromCache(cached);
|
|
640
625
|
|
|
641
626
|
expect(current.getCurrentDeviceId()).toBeUndefined();
|
|
642
|
-
expect(current.getCurrentSerialNo()).toBe('
|
|
627
|
+
expect(current.getCurrentSerialNo()).toBe('CACHED-SERIAL');
|
|
643
628
|
expect(current.toMessageObject()).toMatchObject({
|
|
644
|
-
uuid: '
|
|
629
|
+
uuid: 'CACHED-SERIAL',
|
|
645
630
|
deviceId: null,
|
|
646
631
|
});
|
|
647
632
|
});
|
|
648
633
|
|
|
649
|
-
test('initializes Protocol V2 features from lightweight
|
|
634
|
+
test('initializes Protocol V2 features from lightweight DeviceInfoGet', async () => {
|
|
650
635
|
const commands = {
|
|
651
636
|
typedCall: jest.fn().mockResolvedValueOnce({
|
|
652
637
|
type: 'DeviceInfo',
|
|
653
638
|
message: {
|
|
654
639
|
hw: { serial_no: 'PR2SERIAL' },
|
|
655
640
|
status: {
|
|
641
|
+
device_id: 'PRO2-STATUS-ID',
|
|
656
642
|
init_states: true,
|
|
657
|
-
|
|
643
|
+
passphrase_enabled: true,
|
|
658
644
|
},
|
|
659
645
|
},
|
|
660
646
|
}),
|
|
661
647
|
};
|
|
662
648
|
|
|
663
|
-
const features = await
|
|
649
|
+
const features = await requestProtocolV2Features({
|
|
664
650
|
commands: commands as unknown as DeviceCommands,
|
|
665
651
|
descriptor: descriptor as any,
|
|
666
652
|
});
|
|
667
653
|
|
|
668
|
-
expect(features.deviceId).
|
|
654
|
+
expect(features.deviceId).toBe('PRO2-STATUS-ID');
|
|
669
655
|
expect(features.initialized).toBe(true);
|
|
670
656
|
expect(features.passphraseProtection).toBe(true);
|
|
671
657
|
expect(commands.typedCall).toHaveBeenCalledTimes(1);
|
|
672
658
|
expect(commands.typedCall).toHaveBeenNthCalledWith(
|
|
673
659
|
1,
|
|
674
|
-
'
|
|
660
|
+
'DeviceInfoGet',
|
|
675
661
|
'DeviceInfo',
|
|
676
662
|
{
|
|
677
663
|
targets: {
|
|
678
664
|
hw: true,
|
|
679
665
|
fw: true,
|
|
680
|
-
|
|
666
|
+
coprocessor: true,
|
|
681
667
|
status: true,
|
|
682
668
|
},
|
|
683
669
|
types: {
|
|
@@ -689,13 +675,13 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
689
675
|
);
|
|
690
676
|
});
|
|
691
677
|
|
|
692
|
-
test('fails initialization when Protocol V2
|
|
678
|
+
test('fails initialization when Protocol V2 DeviceInfoGet fails', async () => {
|
|
693
679
|
const commands = {
|
|
694
680
|
typedCall: jest.fn().mockRejectedValueOnce(new Error('DeviceInfo not supported')),
|
|
695
681
|
};
|
|
696
682
|
|
|
697
683
|
await expect(
|
|
698
|
-
|
|
684
|
+
requestProtocolV2Features({
|
|
699
685
|
commands: commands as unknown as DeviceCommands,
|
|
700
686
|
descriptor: descriptor as any,
|
|
701
687
|
})
|
|
@@ -708,20 +694,21 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
708
694
|
message: {
|
|
709
695
|
hw: { serial_no: 'PR2SERIAL' },
|
|
710
696
|
fw: {
|
|
711
|
-
|
|
697
|
+
application: {
|
|
712
698
|
version: '5.6.7',
|
|
713
699
|
},
|
|
714
700
|
},
|
|
715
|
-
|
|
716
|
-
|
|
701
|
+
coprocessor: {
|
|
702
|
+
application: {
|
|
717
703
|
version: '8.9.10',
|
|
718
704
|
},
|
|
719
|
-
|
|
705
|
+
bt_adv_name: 'Raw Pro2 BLE',
|
|
720
706
|
},
|
|
721
707
|
status: {
|
|
708
|
+
device_id: 'PRO2-BASIC-ID',
|
|
709
|
+
unlocked_by_attach_to_pin: true,
|
|
722
710
|
init_states: true,
|
|
723
|
-
|
|
724
|
-
passphrase_protection: true,
|
|
711
|
+
passphrase_enabled: true,
|
|
725
712
|
},
|
|
726
713
|
},
|
|
727
714
|
});
|
|
@@ -738,9 +725,9 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
738
725
|
originalDescriptor: { ...descriptor, protocolType: 'V2' },
|
|
739
726
|
features: {
|
|
740
727
|
...normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any),
|
|
741
|
-
device_id: '
|
|
742
|
-
serial_no: '
|
|
743
|
-
label: '
|
|
728
|
+
device_id: 'CACHED-ID',
|
|
729
|
+
serial_no: 'CACHED-SERIAL',
|
|
730
|
+
label: 'Cached Pro2',
|
|
744
731
|
onekey_firmware_version: '1.2.3',
|
|
745
732
|
onekey_ble_version: '2.3.4',
|
|
746
733
|
},
|
|
@@ -751,13 +738,13 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
751
738
|
const result = await method.run();
|
|
752
739
|
|
|
753
740
|
expect(typedCall).toHaveBeenCalledWith(
|
|
754
|
-
'
|
|
741
|
+
'DeviceInfoGet',
|
|
755
742
|
'DeviceInfo',
|
|
756
743
|
{
|
|
757
744
|
targets: {
|
|
758
745
|
hw: true,
|
|
759
746
|
fw: true,
|
|
760
|
-
|
|
747
|
+
coprocessor: true,
|
|
761
748
|
status: true,
|
|
762
749
|
},
|
|
763
750
|
types: {
|
|
@@ -770,12 +757,13 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
770
757
|
expect(result).toMatchObject({
|
|
771
758
|
protocol: 'V2',
|
|
772
759
|
deviceType: 'pro2',
|
|
773
|
-
deviceId: '',
|
|
760
|
+
deviceId: 'PRO2-BASIC-ID',
|
|
774
761
|
serialNo: 'PR2SERIAL',
|
|
775
|
-
label:
|
|
762
|
+
label: null,
|
|
776
763
|
bleName: 'Raw Pro2 BLE',
|
|
777
764
|
status: {
|
|
778
765
|
initialized: true,
|
|
766
|
+
unlockedAttachPin: true,
|
|
779
767
|
passphraseProtection: true,
|
|
780
768
|
},
|
|
781
769
|
versions: {
|
|
@@ -785,7 +773,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
785
773
|
});
|
|
786
774
|
});
|
|
787
775
|
|
|
788
|
-
test('does not fill Protocol V2 DeviceProfile from
|
|
776
|
+
test('does not fill Protocol V2 DeviceProfile from cached V1-shaped fields', async () => {
|
|
789
777
|
const typedCall = jest.fn().mockResolvedValueOnce({
|
|
790
778
|
type: 'DeviceInfo',
|
|
791
779
|
message: {
|
|
@@ -806,10 +794,10 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
806
794
|
originalDescriptor: { ...descriptor, protocolType: 'V2' },
|
|
807
795
|
features: {
|
|
808
796
|
...normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any),
|
|
809
|
-
device_id: '
|
|
810
|
-
serial_no: '
|
|
811
|
-
label: '
|
|
812
|
-
ble_name: '
|
|
797
|
+
device_id: 'STALE-ID',
|
|
798
|
+
serial_no: 'STALE-SERIAL',
|
|
799
|
+
label: 'Stale Pro2',
|
|
800
|
+
ble_name: 'Stale BLE',
|
|
813
801
|
passphrase_protection: true,
|
|
814
802
|
onekey_firmware_version: '1.2.3',
|
|
815
803
|
onekey_ble_version: '2.3.4',
|
|
@@ -821,7 +809,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
821
809
|
|
|
822
810
|
expect(result).toMatchObject({
|
|
823
811
|
protocol: 'V2',
|
|
824
|
-
//
|
|
812
|
+
// 身份字段不得取自缓存的 V1-shaped features(STALE-ID / STALE-SERIAL),
|
|
825
813
|
// hw.serial_no 缺失时也不回退 descriptor.path。
|
|
826
814
|
deviceId: '',
|
|
827
815
|
serialNo: '',
|
|
@@ -864,13 +852,13 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
864
852
|
await method.run();
|
|
865
853
|
|
|
866
854
|
expect(typedCall).toHaveBeenCalledWith(
|
|
867
|
-
'
|
|
855
|
+
'DeviceInfoGet',
|
|
868
856
|
'DeviceInfo',
|
|
869
857
|
{
|
|
870
858
|
targets: {
|
|
871
859
|
hw: true,
|
|
872
860
|
fw: true,
|
|
873
|
-
|
|
861
|
+
coprocessor: true,
|
|
874
862
|
se1: true,
|
|
875
863
|
se2: true,
|
|
876
864
|
se3: true,
|
|
@@ -894,12 +882,12 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
894
882
|
message: {
|
|
895
883
|
hw: { serial_no: 'PR2SERIAL' },
|
|
896
884
|
se1: {
|
|
897
|
-
|
|
898
|
-
|
|
885
|
+
application: { version: '1.0.1' },
|
|
886
|
+
bootloader: { version: '1.0.0' },
|
|
899
887
|
},
|
|
900
888
|
se2: {
|
|
901
|
-
|
|
902
|
-
|
|
889
|
+
application: { version: '2.0.1' },
|
|
890
|
+
bootloader: { version: '2.0.0' },
|
|
903
891
|
},
|
|
904
892
|
status: { init_states: true },
|
|
905
893
|
},
|
|
@@ -921,13 +909,13 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
921
909
|
const result = await method.run();
|
|
922
910
|
|
|
923
911
|
expect(typedCall).toHaveBeenCalledWith(
|
|
924
|
-
'
|
|
912
|
+
'DeviceInfoGet',
|
|
925
913
|
'DeviceInfo',
|
|
926
914
|
{
|
|
927
915
|
targets: {
|
|
928
916
|
hw: true,
|
|
929
917
|
fw: true,
|
|
930
|
-
|
|
918
|
+
coprocessor: true,
|
|
931
919
|
se1: true,
|
|
932
920
|
se2: true,
|
|
933
921
|
se3: true,
|
|
@@ -966,10 +954,23 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
966
954
|
expect(checkedTypes).toEqual(['pro2']);
|
|
967
955
|
});
|
|
968
956
|
|
|
957
|
+
test('uses firmware-v1 as the Pro2 remote firmware config field', () => {
|
|
958
|
+
const features = normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any);
|
|
959
|
+
|
|
960
|
+
expect(
|
|
961
|
+
getFirmwareUpdateField({
|
|
962
|
+
features,
|
|
963
|
+
updateType: 'firmware',
|
|
964
|
+
firmwareType: 'universal',
|
|
965
|
+
})
|
|
966
|
+
).toBe('firmware-v1');
|
|
967
|
+
expect(getFirmwareUpdateFieldArray(features, 'firmware')).toEqual(['firmware-v1']);
|
|
968
|
+
});
|
|
969
|
+
|
|
969
970
|
test('marks known unsupported public-chain methods as unsupported on Protocol V2', () => {
|
|
970
971
|
const features = normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any, {
|
|
971
972
|
fw: {
|
|
972
|
-
|
|
973
|
+
application: {
|
|
973
974
|
version: '9.9.9',
|
|
974
975
|
},
|
|
975
976
|
},
|
|
@@ -1019,7 +1020,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1019
1020
|
expect(isMethodVersionRangeUnsupported(neuraiRange)).toBe(true);
|
|
1020
1021
|
});
|
|
1021
1022
|
|
|
1022
|
-
test('does not block
|
|
1023
|
+
test('does not block batch public key support checks on Protocol V2', async () => {
|
|
1023
1024
|
const paths = [{ address_n: [0x8000002c, 0x80000000, 0x80000000] }] as any;
|
|
1024
1025
|
const typedCall = jest.fn().mockResolvedValue({
|
|
1025
1026
|
type: 'EcdsaPublicKeys',
|
|
@@ -1033,7 +1034,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1033
1034
|
originalDescriptor: { protocolType: 'V2' },
|
|
1034
1035
|
features: normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any, {
|
|
1035
1036
|
fw: {
|
|
1036
|
-
|
|
1037
|
+
application: {
|
|
1037
1038
|
version: '4.14.0',
|
|
1038
1039
|
},
|
|
1039
1040
|
},
|
|
@@ -1055,7 +1056,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1055
1056
|
});
|
|
1056
1057
|
});
|
|
1057
1058
|
|
|
1058
|
-
test('does not call
|
|
1059
|
+
test('does not call V1 OnekeyGetFeatures for Protocol V2 devices', async () => {
|
|
1059
1060
|
const method = new GetOnekeyFeatures({
|
|
1060
1061
|
id: 1,
|
|
1061
1062
|
payload: {
|
|
@@ -1067,9 +1068,9 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1067
1068
|
message: {
|
|
1068
1069
|
protocol_version: 1,
|
|
1069
1070
|
hw: { serial_no: 'PR2SERIAL' },
|
|
1070
|
-
fw: {
|
|
1071
|
-
|
|
1072
|
-
status: {
|
|
1071
|
+
fw: { application: { version: '1.2.3', build_id: 'app-build' } },
|
|
1072
|
+
coprocessor: { bt_adv_name: 'Pro2 BLE' },
|
|
1073
|
+
status: { init_states: true },
|
|
1073
1074
|
},
|
|
1074
1075
|
});
|
|
1075
1076
|
|
|
@@ -1080,10 +1081,10 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1080
1081
|
|
|
1081
1082
|
const message = await method.run();
|
|
1082
1083
|
|
|
1083
|
-
// V2 走
|
|
1084
|
+
// V2 走 DeviceInfoGet 完整请求(含 SE 与 hash/build_id),而不是 V1 OnekeyGetFeatures
|
|
1084
1085
|
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
1085
1086
|
expect(typedCall).toHaveBeenCalledWith(
|
|
1086
|
-
'
|
|
1087
|
+
'DeviceInfoGet',
|
|
1087
1088
|
'DeviceInfo',
|
|
1088
1089
|
expect.objectContaining({
|
|
1089
1090
|
targets: expect.objectContaining({ se1: true, se2: true, se3: true, se4: true }),
|
|
@@ -1106,9 +1107,10 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1106
1107
|
type: 'DeviceInfo',
|
|
1107
1108
|
message: {
|
|
1108
1109
|
hw: { serial_no: 'PR2SERIAL' },
|
|
1109
|
-
fw: {
|
|
1110
|
+
fw: { application: { version: '1.2.3' } },
|
|
1110
1111
|
status: {
|
|
1111
|
-
|
|
1112
|
+
device_id: 'PRO2-REFRESH-ID',
|
|
1113
|
+
passphrase_enabled: true,
|
|
1112
1114
|
},
|
|
1113
1115
|
},
|
|
1114
1116
|
})
|
|
@@ -1118,8 +1120,8 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1118
1120
|
message: {
|
|
1119
1121
|
hw: { serial_no: 'PR2SERIAL' },
|
|
1120
1122
|
status: {
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
+
device_id: 'PRO2-REFRESH-ID',
|
|
1124
|
+
passphrase_enabled: false,
|
|
1123
1125
|
},
|
|
1124
1126
|
},
|
|
1125
1127
|
});
|
|
@@ -1130,27 +1132,27 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1130
1132
|
await device.initialize();
|
|
1131
1133
|
|
|
1132
1134
|
expect(device.features).toMatchObject({
|
|
1133
|
-
deviceId:
|
|
1135
|
+
deviceId: 'PRO2-REFRESH-ID',
|
|
1134
1136
|
firmwareVersion: '1.2.3',
|
|
1135
1137
|
passphraseProtection: false,
|
|
1136
|
-
label:
|
|
1138
|
+
label: null,
|
|
1137
1139
|
});
|
|
1138
1140
|
expect((device as any).profile).toBeUndefined();
|
|
1139
1141
|
// status 字段被第二次刷新更新
|
|
1140
1142
|
expect(device.features?.passphraseProtection).toBe(false);
|
|
1141
|
-
expect(device.features?.label).
|
|
1143
|
+
expect(device.features?.label).toBeNull();
|
|
1142
1144
|
// 轻量刷新不含 fw target,已有版本信息按字段级合并保留
|
|
1143
1145
|
expect(device.features?.firmwareVersion).toBe('1.2.3');
|
|
1144
1146
|
expect(typedCall).toHaveBeenCalledTimes(2);
|
|
1145
1147
|
expect(typedCall).toHaveBeenNthCalledWith(
|
|
1146
1148
|
1,
|
|
1147
|
-
'
|
|
1149
|
+
'DeviceInfoGet',
|
|
1148
1150
|
'DeviceInfo',
|
|
1149
1151
|
{
|
|
1150
1152
|
targets: {
|
|
1151
1153
|
hw: true,
|
|
1152
1154
|
fw: true,
|
|
1153
|
-
|
|
1155
|
+
coprocessor: true,
|
|
1154
1156
|
status: true,
|
|
1155
1157
|
},
|
|
1156
1158
|
types: {
|
|
@@ -1162,15 +1164,15 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1162
1164
|
timeoutMs: PROTOCOL_V2_DEVICE_INFO_TIMEOUT_MS,
|
|
1163
1165
|
}
|
|
1164
1166
|
);
|
|
1165
|
-
// 第二次为 status-only 轻量请求(hw/
|
|
1167
|
+
// 第二次为 status-only 轻量请求(hw/coprocessor 仅用于身份字段,避免顶层覆盖清空)
|
|
1166
1168
|
expect(typedCall).toHaveBeenNthCalledWith(
|
|
1167
1169
|
2,
|
|
1168
|
-
'
|
|
1170
|
+
'DeviceInfoGet',
|
|
1169
1171
|
'DeviceInfo',
|
|
1170
1172
|
{
|
|
1171
1173
|
targets: {
|
|
1172
1174
|
hw: true,
|
|
1173
|
-
|
|
1175
|
+
coprocessor: true,
|
|
1174
1176
|
status: true,
|
|
1175
1177
|
},
|
|
1176
1178
|
types: {
|
|
@@ -1184,7 +1186,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1184
1186
|
);
|
|
1185
1187
|
});
|
|
1186
1188
|
|
|
1187
|
-
test('refreshes Protocol V2 features without falling back to
|
|
1189
|
+
test('refreshes Protocol V2 features without falling back to V1 GetFeatures', async () => {
|
|
1188
1190
|
const device = Device.fromDescriptor({
|
|
1189
1191
|
path: 'usb-path',
|
|
1190
1192
|
protocolType: 'V2',
|
|
@@ -1195,7 +1197,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1195
1197
|
type: 'DeviceInfo',
|
|
1196
1198
|
message: {
|
|
1197
1199
|
hw: { serial_no: 'PR2SERIAL' },
|
|
1198
|
-
fw: {
|
|
1200
|
+
fw: { application: { version: '1.2.3' } },
|
|
1199
1201
|
status: { init_states: true },
|
|
1200
1202
|
},
|
|
1201
1203
|
})
|
|
@@ -1203,8 +1205,8 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1203
1205
|
type: 'DeviceInfo',
|
|
1204
1206
|
message: {
|
|
1205
1207
|
hw: { serial_no: 'PR2SERIAL' },
|
|
1206
|
-
fw: {
|
|
1207
|
-
status: { init_states: true,
|
|
1208
|
+
fw: { application: { version: '1.2.4' } },
|
|
1209
|
+
status: { init_states: true, passphrase_enabled: true },
|
|
1208
1210
|
},
|
|
1209
1211
|
});
|
|
1210
1212
|
|
|
@@ -1227,13 +1229,13 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1227
1229
|
expect(typedCall).toHaveBeenCalledTimes(2);
|
|
1228
1230
|
expect(typedCall).toHaveBeenNthCalledWith(
|
|
1229
1231
|
2,
|
|
1230
|
-
'
|
|
1232
|
+
'DeviceInfoGet',
|
|
1231
1233
|
'DeviceInfo',
|
|
1232
1234
|
{
|
|
1233
1235
|
targets: {
|
|
1234
1236
|
hw: true,
|
|
1235
1237
|
fw: true,
|
|
1236
|
-
|
|
1238
|
+
coprocessor: true,
|
|
1237
1239
|
status: true,
|
|
1238
1240
|
},
|
|
1239
1241
|
types: {
|
|
@@ -1246,14 +1248,14 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1246
1248
|
expect(typedCall).not.toHaveBeenCalledWith('GetFeatures', 'Features', {});
|
|
1247
1249
|
});
|
|
1248
1250
|
|
|
1249
|
-
test('keeps Protocol V2 features available for
|
|
1251
|
+
test('keeps Protocol V2 features available for method internals such as evmSignTypedData', async () => {
|
|
1250
1252
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
1251
1253
|
const typedCall = jest.fn().mockResolvedValueOnce({
|
|
1252
1254
|
type: 'DeviceInfo',
|
|
1253
1255
|
message: {
|
|
1254
1256
|
hw: { serial_no: 'PR2SERIAL' },
|
|
1255
|
-
fw: {
|
|
1256
|
-
status: { init_states: true,
|
|
1257
|
+
fw: { application: { version: '1.2.4' } },
|
|
1258
|
+
status: { init_states: true, passphrase_enabled: true },
|
|
1257
1259
|
},
|
|
1258
1260
|
});
|
|
1259
1261
|
(device as any).commands = { typedCall };
|
|
@@ -1307,7 +1309,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1307
1309
|
{ ...descriptor, protocolType: 'V2' } as any,
|
|
1308
1310
|
{
|
|
1309
1311
|
hw: { serial_no: 'PR2SERIAL' },
|
|
1310
|
-
fw: {
|
|
1312
|
+
fw: { application: { version: '1.2.3' } },
|
|
1311
1313
|
status: { init_states: true },
|
|
1312
1314
|
}
|
|
1313
1315
|
);
|
|
@@ -1331,14 +1333,15 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1331
1333
|
{ ...descriptor, protocolType: 'V2' } as any,
|
|
1332
1334
|
{
|
|
1333
1335
|
hw: { serial_no: 'PR2SERIAL' },
|
|
1334
|
-
fw: {
|
|
1335
|
-
status: {
|
|
1336
|
+
fw: { application: { version: '4.15.0' } },
|
|
1337
|
+
status: { passphrase_enabled: false },
|
|
1336
1338
|
}
|
|
1337
1339
|
);
|
|
1338
1340
|
const typedCall = jest.fn().mockResolvedValue({
|
|
1339
1341
|
type: 'UnLockDeviceResponse',
|
|
1340
1342
|
message: {
|
|
1341
1343
|
unlocked: true,
|
|
1344
|
+
unlocked_attach_pin: true,
|
|
1342
1345
|
passphrase_protection: true,
|
|
1343
1346
|
},
|
|
1344
1347
|
});
|
|
@@ -1348,6 +1351,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1348
1351
|
|
|
1349
1352
|
expect((device as any).profile).toBeUndefined();
|
|
1350
1353
|
expect(device.features?.passphraseProtection).toBe(true);
|
|
1354
|
+
expect(device.features?.unlockedAttachPin).toBe(true);
|
|
1351
1355
|
});
|
|
1352
1356
|
});
|
|
1353
1357
|
|
|
@@ -1384,25 +1388,7 @@ describe('API compatibility handling', () => {
|
|
|
1384
1388
|
);
|
|
1385
1389
|
});
|
|
1386
1390
|
|
|
1387
|
-
test('
|
|
1388
|
-
const method = new TronSignMessage({
|
|
1389
|
-
id: 1,
|
|
1390
|
-
payload: {
|
|
1391
|
-
method: 'tronSignMessage',
|
|
1392
|
-
path: "m/44'/195'/0'/0/0",
|
|
1393
|
-
messageHex: '0x1234',
|
|
1394
|
-
messageType: 'V1',
|
|
1395
|
-
},
|
|
1396
|
-
});
|
|
1397
|
-
|
|
1398
|
-
expect(() => method.init()).toThrow(
|
|
1399
|
-
expect.objectContaining({
|
|
1400
|
-
errorCode: HardwareErrorCode.DeviceNotSupportMethod,
|
|
1401
|
-
})
|
|
1402
|
-
);
|
|
1403
|
-
});
|
|
1404
|
-
|
|
1405
|
-
test('does not mark Pro2 Tron, Solana, TON, SUI and Polkadot methods as unsupported', () => {
|
|
1391
|
+
test('does not mark supported Pro2 Tron, Solana, TON, SUI and Polkadot methods as unsupported', () => {
|
|
1406
1392
|
const features = {
|
|
1407
1393
|
deviceType: 'pro2',
|
|
1408
1394
|
} as Features;
|
|
@@ -1523,9 +1509,7 @@ describe('API compatibility handling', () => {
|
|
|
1523
1509
|
});
|
|
1524
1510
|
expect(
|
|
1525
1511
|
getMethodVersionRange(features, type => tonSignDataMethod.getVersionRange()[type])
|
|
1526
|
-
).
|
|
1527
|
-
min: '0.0.0',
|
|
1528
|
-
});
|
|
1512
|
+
).toBeUndefined();
|
|
1529
1513
|
expect(
|
|
1530
1514
|
getMethodVersionRange(features, type => suiGetAddressMethod.getVersionRange()[type])
|
|
1531
1515
|
).toEqual({
|
|
@@ -1619,15 +1603,6 @@ describe('API compatibility handling', () => {
|
|
|
1619
1603
|
});
|
|
1620
1604
|
});
|
|
1621
1605
|
|
|
1622
|
-
test('includes TON signData in the Protocol V2 protobuf schema', () => {
|
|
1623
|
-
const protocolV2Messages = DataManager.getProtobufMessages('v2Schema') as any;
|
|
1624
|
-
|
|
1625
|
-
expect(protocolV2Messages.nested.TonSignData).toBeDefined();
|
|
1626
|
-
expect(protocolV2Messages.nested.TonSignedData).toBeDefined();
|
|
1627
|
-
expect(protocolV2Messages.nested.MessageType.values.MessageType_TonSignData).toBe(11908);
|
|
1628
|
-
expect(protocolV2Messages.nested.MessageType.values.MessageType_TonSignedData).toBe(11909);
|
|
1629
|
-
});
|
|
1630
|
-
|
|
1631
1606
|
test('uses chunk transfer for large Sui transactions on Protocol V2', async () => {
|
|
1632
1607
|
const rawTx = '0x'.concat('ab'.repeat(5000));
|
|
1633
1608
|
const typedCall = jest.fn(() => ({
|
|
@@ -1672,54 +1647,6 @@ describe('API compatibility handling', () => {
|
|
|
1672
1647
|
expect(params.data_initial_chunk).toHaveLength(2048);
|
|
1673
1648
|
});
|
|
1674
1649
|
|
|
1675
|
-
test('accepts string XRP payment amount values', () => {
|
|
1676
|
-
const method = new XrpSignTransaction({
|
|
1677
|
-
id: 1,
|
|
1678
|
-
payload: {
|
|
1679
|
-
method: 'xrpSignTransaction',
|
|
1680
|
-
path: "m/44'/144'/0'/0/0",
|
|
1681
|
-
transaction: {
|
|
1682
|
-
fee: '100000',
|
|
1683
|
-
flags: 2147483648,
|
|
1684
|
-
sequence: 25,
|
|
1685
|
-
maxLedgerVersion: 8820051,
|
|
1686
|
-
payment: {
|
|
1687
|
-
amount: '100000000',
|
|
1688
|
-
destination: 'rBKz5MC2iXdoS3XgnNSYmF69K1Yo4NS3Ws',
|
|
1689
|
-
},
|
|
1690
|
-
},
|
|
1691
|
-
},
|
|
1692
|
-
});
|
|
1693
|
-
|
|
1694
|
-
expect(() => method.init()).not.toThrow();
|
|
1695
|
-
expect(method.params.payment?.amount).toBe('100000000');
|
|
1696
|
-
});
|
|
1697
|
-
|
|
1698
|
-
test('accepts Conflux base32 recipient addresses without hex formatting them', () => {
|
|
1699
|
-
const to = 'cfx:aak2rra2njvd77ezwjvx04kkds9fzagfe6ku8scz91';
|
|
1700
|
-
const method = new ConfluxSignTransaction({
|
|
1701
|
-
id: 1,
|
|
1702
|
-
payload: {
|
|
1703
|
-
method: 'confluxSignTransaction',
|
|
1704
|
-
path: "m/44'/503'/0'/0/0",
|
|
1705
|
-
transaction: {
|
|
1706
|
-
to,
|
|
1707
|
-
value: '0x0',
|
|
1708
|
-
data: '0x',
|
|
1709
|
-
chainId: 1,
|
|
1710
|
-
nonce: '0x00',
|
|
1711
|
-
epochHeight: '0x00',
|
|
1712
|
-
gasLimit: '0x5208',
|
|
1713
|
-
storageLimit: '0x5208',
|
|
1714
|
-
gasPrice: '0xbebc200',
|
|
1715
|
-
},
|
|
1716
|
-
},
|
|
1717
|
-
});
|
|
1718
|
-
|
|
1719
|
-
expect(() => method.init()).not.toThrow();
|
|
1720
|
-
expect(method.formattedTx?.to).toBe(to);
|
|
1721
|
-
});
|
|
1722
|
-
|
|
1723
1650
|
test('returns a typed unsupported error for Dynex signing on Protocol V2', async () => {
|
|
1724
1651
|
const method = new DnxSignTransaction({
|
|
1725
1652
|
id: 1,
|
|
@@ -1775,7 +1702,41 @@ describe('API compatibility handling', () => {
|
|
|
1775
1702
|
});
|
|
1776
1703
|
|
|
1777
1704
|
describe('Protocol V2 firmware update targets', () => {
|
|
1778
|
-
|
|
1705
|
+
const OKPP_HEADER_SIZE = 0x52a0;
|
|
1706
|
+
|
|
1707
|
+
const buildOkppHeader = ({
|
|
1708
|
+
type = 'RESC',
|
|
1709
|
+
version = [1, 0, 0],
|
|
1710
|
+
payloadHash = 'ab'.repeat(64),
|
|
1711
|
+
headerHash = 'cd'.repeat(64),
|
|
1712
|
+
}: {
|
|
1713
|
+
type?: string;
|
|
1714
|
+
version?: [number, number, number];
|
|
1715
|
+
payloadHash?: string;
|
|
1716
|
+
headerHash?: string;
|
|
1717
|
+
} = {}) => {
|
|
1718
|
+
const header = new Uint8Array(OKPP_HEADER_SIZE);
|
|
1719
|
+
const view = new DataView(header.buffer);
|
|
1720
|
+
'OKPP'.split('').forEach((char, index) => {
|
|
1721
|
+
header[index] = char.charCodeAt(0);
|
|
1722
|
+
});
|
|
1723
|
+
view.setUint32(0x04, 1, true);
|
|
1724
|
+
type.split('').forEach((char, index) => {
|
|
1725
|
+
header[0x08 + index] = char.charCodeAt(0);
|
|
1726
|
+
});
|
|
1727
|
+
view.setUint32(0x0c, OKPP_HEADER_SIZE, true);
|
|
1728
|
+
view.setUint32(0x10, version[0] * 0x10000 + version[1] * 0x100 + version[2], true);
|
|
1729
|
+
const writeHex = (offset: number, hex: string) => {
|
|
1730
|
+
for (let i = 0; i < hex.length / 2; i++) {
|
|
1731
|
+
header[offset + i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
1732
|
+
}
|
|
1733
|
+
};
|
|
1734
|
+
writeHex(0x200, payloadHash);
|
|
1735
|
+
writeHex(0x240, headerHash);
|
|
1736
|
+
return header;
|
|
1737
|
+
};
|
|
1738
|
+
|
|
1739
|
+
test('keeps Protocol V2 firmware updates off the firmwareUpdateV3 path', async () => {
|
|
1779
1740
|
const method = new FirmwareUpdateV3({
|
|
1780
1741
|
id: 1,
|
|
1781
1742
|
payload: {
|
|
@@ -1789,25 +1750,26 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
1789
1750
|
await expect(method.run()).rejects.toThrow('firmwareUpdateV4');
|
|
1790
1751
|
});
|
|
1791
1752
|
|
|
1792
|
-
test('uses Protocol V2 features after BLE final reconnect without
|
|
1753
|
+
test('uses Protocol V2 features after BLE final reconnect without V1 Initialize', async () => {
|
|
1793
1754
|
const method = new FirmwareUpdateV4({
|
|
1794
1755
|
id: 1,
|
|
1795
1756
|
payload: {
|
|
1796
1757
|
method: 'firmwareUpdateV4',
|
|
1758
|
+
coprocessorBinary: new Uint8Array([1, 2, 3]).buffer,
|
|
1797
1759
|
},
|
|
1798
1760
|
});
|
|
1799
1761
|
const acquire = jest.fn().mockResolvedValue({ uuid: 'ble-session' });
|
|
1800
1762
|
const typedCall = jest.fn().mockImplementation((name: string) => {
|
|
1801
|
-
if (name === '
|
|
1763
|
+
if (name === 'DeviceInfoGet') {
|
|
1802
1764
|
return Promise.resolve({
|
|
1803
1765
|
type: 'DeviceInfo',
|
|
1804
1766
|
message: {
|
|
1805
1767
|
fw: {
|
|
1806
|
-
|
|
1807
|
-
|
|
1768
|
+
bootloader: { version: '0.0.0' },
|
|
1769
|
+
application: { version: '0.0.0' },
|
|
1808
1770
|
},
|
|
1809
|
-
|
|
1810
|
-
|
|
1771
|
+
coprocessor: {
|
|
1772
|
+
application: { version: '0.0.0' },
|
|
1811
1773
|
},
|
|
1812
1774
|
},
|
|
1813
1775
|
});
|
|
@@ -1821,7 +1783,13 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
1821
1783
|
originalDescriptor: { id: 'ble-id', path: 'ble-path', protocolType: 'V2' },
|
|
1822
1784
|
deviceConnector: { acquire },
|
|
1823
1785
|
getCommands: () => commands,
|
|
1824
|
-
|
|
1786
|
+
updateProtocolV2Features: jest.fn(() => ({
|
|
1787
|
+
bootloaderMode: false,
|
|
1788
|
+
mode: 'normal',
|
|
1789
|
+
firmwareVersion: '0.0.0',
|
|
1790
|
+
bootloaderVersion: '0.0.0',
|
|
1791
|
+
bleVersion: '0.0.0',
|
|
1792
|
+
})),
|
|
1825
1793
|
});
|
|
1826
1794
|
|
|
1827
1795
|
const versions = await (method as any).waitForProtocolV2FinalFeatures();
|
|
@@ -1830,13 +1798,13 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
1830
1798
|
// 更新完成判定使用 VERSIONS 请求(含 SE targets),scope 与请求内容一致
|
|
1831
1799
|
expect(typedCall).toHaveBeenNthCalledWith(
|
|
1832
1800
|
1,
|
|
1833
|
-
'
|
|
1801
|
+
'DeviceInfoGet',
|
|
1834
1802
|
'DeviceInfo',
|
|
1835
1803
|
{
|
|
1836
1804
|
targets: {
|
|
1837
1805
|
hw: true,
|
|
1838
1806
|
fw: true,
|
|
1839
|
-
|
|
1807
|
+
coprocessor: true,
|
|
1840
1808
|
se1: true,
|
|
1841
1809
|
se2: true,
|
|
1842
1810
|
se3: true,
|
|
@@ -1859,7 +1827,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
1859
1827
|
});
|
|
1860
1828
|
});
|
|
1861
1829
|
|
|
1862
|
-
test('
|
|
1830
|
+
test('enters Protocol V2 bootloader before upload', async () => {
|
|
1863
1831
|
const method = new FirmwareUpdateV4({
|
|
1864
1832
|
id: 1,
|
|
1865
1833
|
payload: {
|
|
@@ -1872,14 +1840,15 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
1872
1840
|
originalDescriptor: { id: 'ble-id', path: 'ble-path', protocolType: 'V2' },
|
|
1873
1841
|
features: { capabilities: [] },
|
|
1874
1842
|
});
|
|
1875
|
-
(method as any).
|
|
1876
|
-
(method as any).
|
|
1843
|
+
(method as any).prepareResourceBinaries = jest.fn().mockResolvedValue([]);
|
|
1844
|
+
(method as any).prepareBootloaderBinary = jest.fn().mockReturnValue(null);
|
|
1845
|
+
(method as any).collectExplicitTargetBinaries = jest.fn().mockReturnValue([
|
|
1877
1846
|
{
|
|
1878
|
-
fileName: '
|
|
1847
|
+
fileName: 'coprocessor.bin',
|
|
1879
1848
|
binary: new Uint8Array([1, 2, 3]).buffer,
|
|
1849
|
+
targetId: 6,
|
|
1880
1850
|
},
|
|
1881
1851
|
]);
|
|
1882
|
-
(method as any).prepareBootloaderBinary = jest.fn().mockResolvedValue(null);
|
|
1883
1852
|
(method as any).executeProtocolV2Update = jest.fn().mockResolvedValue(undefined);
|
|
1884
1853
|
(method as any).exitProtocolV2BootloaderToNormal = jest.fn().mockResolvedValue(undefined);
|
|
1885
1854
|
(method as any).waitForProtocolV2FinalFeatures = jest.fn().mockResolvedValue({
|
|
@@ -1893,17 +1862,18 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
1893
1862
|
|
|
1894
1863
|
await method.run();
|
|
1895
1864
|
|
|
1865
|
+
expect((method as any).enterProtocolV2BootloaderMode).toHaveBeenCalledTimes(1);
|
|
1896
1866
|
expect((method as any).executeProtocolV2Update).toHaveBeenCalledWith({
|
|
1897
|
-
|
|
1867
|
+
resourceBinaryMap: [],
|
|
1898
1868
|
fwBinaryMap: [
|
|
1899
1869
|
{
|
|
1900
|
-
fileName: '
|
|
1870
|
+
fileName: 'coprocessor.bin',
|
|
1901
1871
|
binary: expect.any(ArrayBuffer),
|
|
1872
|
+
targetId: 6,
|
|
1902
1873
|
},
|
|
1903
1874
|
],
|
|
1904
1875
|
bootloaderBinary: null,
|
|
1905
1876
|
});
|
|
1906
|
-
expect((method as any).enterProtocolV2BootloaderMode).not.toHaveBeenCalled();
|
|
1907
1877
|
});
|
|
1908
1878
|
|
|
1909
1879
|
test('reboots Protocol V2 normal-mode device to bootloader before transfer', async () => {
|
|
@@ -1913,31 +1883,70 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
1913
1883
|
method: 'firmwareUpdateV4',
|
|
1914
1884
|
},
|
|
1915
1885
|
});
|
|
1916
|
-
const acquire = jest.fn().mockResolvedValue(undefined);
|
|
1917
1886
|
(method as any).device = stubDevice({
|
|
1918
1887
|
originalDescriptor: { id: 'usb-id', path: 'usb-path', protocolType: 'V2' },
|
|
1919
1888
|
features: { bootloader_mode: false, capabilities: [] },
|
|
1920
1889
|
isBootloader: () => false,
|
|
1921
|
-
acquire,
|
|
1922
1890
|
});
|
|
1923
1891
|
(method as any).protocolV2Reboot = jest.fn().mockResolvedValue({
|
|
1924
1892
|
message: 'Device rebooted successfully',
|
|
1925
1893
|
});
|
|
1926
|
-
(method as any).checkDeviceToBootloader = jest.fn(
|
|
1927
|
-
|
|
1894
|
+
(method as any).checkDeviceToBootloader = jest.fn();
|
|
1895
|
+
const typedCall = jest.fn().mockResolvedValue({
|
|
1896
|
+
type: 'DeviceInfo',
|
|
1897
|
+
message: protocolV2BootloaderDeviceInfo,
|
|
1898
|
+
});
|
|
1899
|
+
const reconnectProtocolV2Device = jest.fn().mockImplementation(() => {
|
|
1900
|
+
(method as any).device.isBootloader = () => true;
|
|
1901
|
+
return Promise.resolve();
|
|
1928
1902
|
});
|
|
1903
|
+
(method as any).reconnectProtocolV2Device = reconnectProtocolV2Device;
|
|
1904
|
+
(method as any).device.getCommands = () => ({ typedCall });
|
|
1929
1905
|
method.postTipMessage = jest.fn();
|
|
1930
1906
|
|
|
1931
1907
|
await (method as any).enterProtocolV2BootloaderMode();
|
|
1932
1908
|
|
|
1933
1909
|
expect(method.postTipMessage).toHaveBeenCalledWith('AutoRebootToBootloader');
|
|
1934
|
-
expect((method as any).protocolV2Reboot).toHaveBeenCalledWith(
|
|
1910
|
+
expect((method as any).protocolV2Reboot).toHaveBeenCalledWith(DeviceRebootType.Bootloader);
|
|
1935
1911
|
expect(method.postTipMessage).toHaveBeenCalledWith('GoToBootloaderSuccess');
|
|
1936
|
-
expect((method as any).checkDeviceToBootloader).
|
|
1937
|
-
expect(
|
|
1912
|
+
expect((method as any).checkDeviceToBootloader).not.toHaveBeenCalled();
|
|
1913
|
+
expect(reconnectProtocolV2Device).toHaveBeenCalledTimes(1);
|
|
1914
|
+
});
|
|
1915
|
+
|
|
1916
|
+
test('polls until Protocol V2 bootloader descriptor is ready after reboot', async () => {
|
|
1917
|
+
const method = new FirmwareUpdateV4({
|
|
1918
|
+
id: 1,
|
|
1919
|
+
payload: {
|
|
1920
|
+
method: 'firmwareUpdateV4',
|
|
1921
|
+
},
|
|
1922
|
+
});
|
|
1923
|
+
(method as any).device = stubDevice({
|
|
1924
|
+
originalDescriptor: { id: 'usb-id', path: 'app-path', protocolType: 'V2' },
|
|
1925
|
+
features: { bootloader_mode: false, capabilities: [] },
|
|
1926
|
+
isBootloader: () => false,
|
|
1927
|
+
});
|
|
1928
|
+
const typedCall = jest.fn().mockResolvedValue({
|
|
1929
|
+
type: 'DeviceInfo',
|
|
1930
|
+
message: protocolV2BootloaderDeviceInfo,
|
|
1931
|
+
});
|
|
1932
|
+
const reconnectProtocolV2Device = jest
|
|
1933
|
+
.fn()
|
|
1934
|
+
.mockRejectedValueOnce(new Error('Device not found'))
|
|
1935
|
+
.mockRejectedValueOnce(new Error('Device not found'))
|
|
1936
|
+
.mockImplementation(() => {
|
|
1937
|
+
(method as any).device.isBootloader = () => true;
|
|
1938
|
+
return Promise.resolve();
|
|
1939
|
+
});
|
|
1940
|
+
(method as any).reconnectProtocolV2Device = reconnectProtocolV2Device;
|
|
1941
|
+
(method as any).device.getCommands = () => ({ typedCall });
|
|
1942
|
+
|
|
1943
|
+
await (method as any).waitForProtocolV2BootloaderMode(60 * 1000, 0);
|
|
1944
|
+
|
|
1945
|
+
expect(reconnectProtocolV2Device).toHaveBeenCalledTimes(3);
|
|
1946
|
+
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
1938
1947
|
});
|
|
1939
1948
|
|
|
1940
|
-
test('reboots Protocol V2 firmware flow back to normal
|
|
1949
|
+
test('reboots Protocol V2 firmware flow back to normal after install status is finished', async () => {
|
|
1941
1950
|
const method = new FirmwareUpdateV4({
|
|
1942
1951
|
id: 1,
|
|
1943
1952
|
payload: {
|
|
@@ -1952,7 +1961,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
1952
1961
|
await (method as any).exitProtocolV2BootloaderToNormal();
|
|
1953
1962
|
|
|
1954
1963
|
expect(method.postTipMessage).toHaveBeenCalledWith('SwitchFirmwareReconnectDevice');
|
|
1955
|
-
expect((method as any).protocolV2Reboot).toHaveBeenCalledWith(
|
|
1964
|
+
expect((method as any).protocolV2Reboot).toHaveBeenCalledWith(DeviceRebootType.Normal);
|
|
1956
1965
|
});
|
|
1957
1966
|
|
|
1958
1967
|
test('treats iOS BLE RxError 6 during Protocol V2 reboot as expected disconnect', async () => {
|
|
@@ -1972,7 +1981,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
1972
1981
|
getCommands: () => ({ typedCall }),
|
|
1973
1982
|
});
|
|
1974
1983
|
|
|
1975
|
-
await expect((method as any).protocolV2Reboot(
|
|
1984
|
+
await expect((method as any).protocolV2Reboot(DeviceRebootType.Normal)).resolves.toEqual({
|
|
1976
1985
|
message: 'Device rebooted successfully',
|
|
1977
1986
|
});
|
|
1978
1987
|
});
|
|
@@ -1992,7 +2001,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
1992
2001
|
getCommands: () => ({ typedCall }),
|
|
1993
2002
|
});
|
|
1994
2003
|
|
|
1995
|
-
await expect((method as any).protocolV2Reboot(
|
|
2004
|
+
await expect((method as any).protocolV2Reboot(DeviceRebootType.Normal)).resolves.toEqual({
|
|
1996
2005
|
message: 'Device rebooted successfully',
|
|
1997
2006
|
});
|
|
1998
2007
|
});
|
|
@@ -2020,7 +2029,30 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2020
2029
|
|
|
2021
2030
|
await expect(
|
|
2022
2031
|
(method as any).protocolV2StartFirmwareUpdate({
|
|
2023
|
-
targets: [{ target_id:
|
|
2032
|
+
targets: [{ target_id: 4, path: 'vol1:firmware.bin' }],
|
|
2033
|
+
})
|
|
2034
|
+
).resolves.toBeUndefined();
|
|
2035
|
+
|
|
2036
|
+
expect(method.postTipMessage).toHaveBeenCalledWith('FirmwareUpdating');
|
|
2037
|
+
});
|
|
2038
|
+
|
|
2039
|
+
test('continues Protocol V2 install polling when update request transfer times out', async () => {
|
|
2040
|
+
const method = new FirmwareUpdateV4({
|
|
2041
|
+
id: 1,
|
|
2042
|
+
payload: {
|
|
2043
|
+
method: 'firmwareUpdateV4',
|
|
2044
|
+
},
|
|
2045
|
+
});
|
|
2046
|
+
const typedCall = jest.fn().mockRejectedValue(new Error('LIBUSB_TRANSFER_TIMED_OUT'));
|
|
2047
|
+
|
|
2048
|
+
(method as any).device = stubDevice({
|
|
2049
|
+
getCommands: () => ({ typedCall }),
|
|
2050
|
+
});
|
|
2051
|
+
method.postTipMessage = jest.fn();
|
|
2052
|
+
|
|
2053
|
+
await expect(
|
|
2054
|
+
(method as any).protocolV2StartFirmwareUpdate({
|
|
2055
|
+
targets: [{ target_id: 1, path: 'vol1:resource.bin' }],
|
|
2024
2056
|
})
|
|
2025
2057
|
).resolves.toBeUndefined();
|
|
2026
2058
|
|
|
@@ -2034,19 +2066,31 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2034
2066
|
method: 'firmwareUpdateV4',
|
|
2035
2067
|
},
|
|
2036
2068
|
});
|
|
2037
|
-
const typedCall = jest
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2069
|
+
const typedCall = jest.fn().mockImplementation((name: string) => {
|
|
2070
|
+
if (name === 'DeviceFirmwareUpdateStatusGet') {
|
|
2071
|
+
const callCount = typedCall.mock.calls.filter(call => call[0] === name).length;
|
|
2072
|
+
if (callCount === 1) {
|
|
2073
|
+
return Promise.reject(
|
|
2074
|
+
new Error(
|
|
2075
|
+
'Device protocol mismatch: expected V2, but device did not respond to expected protocol'
|
|
2076
|
+
)
|
|
2077
|
+
);
|
|
2078
|
+
}
|
|
2079
|
+
return Promise.resolve({
|
|
2080
|
+
type: 'DeviceFirmwareUpdateStatus',
|
|
2081
|
+
message: {
|
|
2082
|
+
records: [{ target_id: 6, status: 2 }],
|
|
2083
|
+
},
|
|
2084
|
+
});
|
|
2085
|
+
}
|
|
2086
|
+
if (name === 'DeviceInfoGet') {
|
|
2087
|
+
return Promise.reject(new Error('DeviceInfo not ready'));
|
|
2088
|
+
}
|
|
2089
|
+
if (name === 'Ping') {
|
|
2090
|
+
return Promise.resolve({ type: 'Success', message: { message: 'ready' } });
|
|
2091
|
+
}
|
|
2092
|
+
return Promise.reject(new Error(`unexpected call ${name}`));
|
|
2093
|
+
});
|
|
2050
2094
|
const reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
|
|
2051
2095
|
|
|
2052
2096
|
(method as any).device = stubDevice({
|
|
@@ -2056,14 +2100,75 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2056
2100
|
method.postProgressMessage = jest.fn();
|
|
2057
2101
|
|
|
2058
2102
|
await (method as any).waitForProtocolV2FirmwareUpdateComplete([
|
|
2059
|
-
{ target_id:
|
|
2103
|
+
{ target_id: 6, path: 'vol1:ble-firmware.bin' },
|
|
2060
2104
|
]);
|
|
2061
2105
|
|
|
2062
2106
|
expect(reconnectProtocolV2Device).toHaveBeenCalledTimes(1);
|
|
2063
|
-
expect(typedCall).
|
|
2107
|
+
expect(typedCall.mock.calls.map(call => call[0])).toEqual([
|
|
2108
|
+
'DeviceFirmwareUpdateStatusGet',
|
|
2109
|
+
'DeviceInfoGet',
|
|
2110
|
+
'Ping',
|
|
2111
|
+
'DeviceFirmwareUpdateStatusGet',
|
|
2112
|
+
]);
|
|
2113
|
+
});
|
|
2114
|
+
|
|
2115
|
+
test('treats Protocol V2 normal mode after reconnect as firmware update complete', async () => {
|
|
2116
|
+
const method = new FirmwareUpdateV4({
|
|
2117
|
+
id: 1,
|
|
2118
|
+
payload: {
|
|
2119
|
+
method: 'firmwareUpdateV4',
|
|
2120
|
+
},
|
|
2121
|
+
});
|
|
2122
|
+
const typedCall = jest.fn().mockImplementation((name: string) => {
|
|
2123
|
+
if (name === 'DeviceFirmwareUpdateStatusGet') {
|
|
2124
|
+
return Promise.reject(new Error('Device disconnected'));
|
|
2125
|
+
}
|
|
2126
|
+
if (name === 'DeviceInfoGet') {
|
|
2127
|
+
return Promise.resolve({
|
|
2128
|
+
type: 'DeviceInfo',
|
|
2129
|
+
message: {
|
|
2130
|
+
hw: { serial_no: 'PR9999999999' },
|
|
2131
|
+
fw: {
|
|
2132
|
+
bootloader: { version: '9.9.9' },
|
|
2133
|
+
application: { version: '9.9.9' },
|
|
2134
|
+
},
|
|
2135
|
+
status: {
|
|
2136
|
+
device_id: 'PRO2-DEVICE-ID',
|
|
2137
|
+
init_states: true,
|
|
2138
|
+
},
|
|
2139
|
+
},
|
|
2140
|
+
});
|
|
2141
|
+
}
|
|
2142
|
+
return Promise.reject(new Error(`unexpected call ${name}`));
|
|
2143
|
+
});
|
|
2144
|
+
const reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
|
|
2145
|
+
const updateProtocolV2Features = jest.fn((deviceInfo: ProtocolV2DeviceInfo) =>
|
|
2146
|
+
normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any, deviceInfo)
|
|
2147
|
+
);
|
|
2148
|
+
|
|
2149
|
+
(method as any).device = stubDevice({
|
|
2150
|
+
getCommands: () => ({ typedCall }),
|
|
2151
|
+
updateProtocolV2Features,
|
|
2152
|
+
});
|
|
2153
|
+
(method as any).reconnectProtocolV2Device = reconnectProtocolV2Device;
|
|
2154
|
+
method.postProgressMessage = jest.fn();
|
|
2155
|
+
|
|
2156
|
+
await expect(
|
|
2157
|
+
(method as any).waitForProtocolV2FirmwareUpdateComplete([
|
|
2158
|
+
{ target_id: 3, path: 'vol1:bootloader.bin' },
|
|
2159
|
+
{ target_id: 4, path: 'vol1:application_p1.bin' },
|
|
2160
|
+
])
|
|
2161
|
+
).resolves.toBeUndefined();
|
|
2162
|
+
|
|
2163
|
+
expect(reconnectProtocolV2Device).toHaveBeenCalledTimes(1);
|
|
2164
|
+
expect(updateProtocolV2Features).toHaveBeenCalledTimes(1);
|
|
2165
|
+
expect(typedCall.mock.calls.map(call => call[0])).toEqual([
|
|
2166
|
+
'DeviceFirmwareUpdateStatusGet',
|
|
2167
|
+
'DeviceInfoGet',
|
|
2168
|
+
]);
|
|
2064
2169
|
});
|
|
2065
2170
|
|
|
2066
|
-
test('uses
|
|
2171
|
+
test('uses SDK decoded enum names for Protocol V2 install polling', () => {
|
|
2067
2172
|
const method = new FirmwareUpdateV4({
|
|
2068
2173
|
id: 1,
|
|
2069
2174
|
payload: {
|
|
@@ -2072,18 +2177,36 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2072
2177
|
});
|
|
2073
2178
|
method.postProgressMessage = jest.fn();
|
|
2074
2179
|
|
|
2075
|
-
const expectedTargetIds = new Set([
|
|
2180
|
+
const expectedTargetIds = new Set([4]);
|
|
2076
2181
|
|
|
2182
|
+
expect(
|
|
2183
|
+
(method as any).assertProtocolV2TargetStatus([{ target_id: 4, status: 2 }], expectedTargetIds)
|
|
2184
|
+
).toBe(true);
|
|
2077
2185
|
expect(
|
|
2078
2186
|
(method as any).assertProtocolV2TargetStatus(
|
|
2079
|
-
[{ target_id:
|
|
2187
|
+
[{ target_id: 4, status: 'FW_MGMT_UPDATER_TASK_STATUS_FINISHED' }],
|
|
2080
2188
|
expectedTargetIds
|
|
2081
2189
|
)
|
|
2082
2190
|
).toBe(true);
|
|
2191
|
+
expect(
|
|
2192
|
+
(method as any).assertProtocolV2TargetStatus(
|
|
2193
|
+
[
|
|
2194
|
+
{
|
|
2195
|
+
target_id: 'FW_MGMT_TARGET_CRATE',
|
|
2196
|
+
status: 'FW_MGMT_UPDATER_TASK_STATUS_FINISHED',
|
|
2197
|
+
},
|
|
2198
|
+
{ target_id: 'FW_MGMT_TARGET_SE04', status: 'FW_MGMT_UPDATER_TASK_STATUS_FINISHED' },
|
|
2199
|
+
],
|
|
2200
|
+
new Set([1, 10])
|
|
2201
|
+
)
|
|
2202
|
+
).toBe(true);
|
|
2083
2203
|
|
|
2204
|
+
expect(
|
|
2205
|
+
(method as any).assertProtocolV2TargetStatus([{ target_id: 4, status: 1 }], expectedTargetIds)
|
|
2206
|
+
).toBe(false);
|
|
2084
2207
|
expect(
|
|
2085
2208
|
(method as any).assertProtocolV2TargetStatus(
|
|
2086
|
-
[{ target_id:
|
|
2209
|
+
[{ target_id: 4, status: 'FW_MGMT_UPDATER_TASK_STATUS_IN_PROGRESS' }],
|
|
2087
2210
|
expectedTargetIds
|
|
2088
2211
|
)
|
|
2089
2212
|
).toBe(false);
|
|
@@ -2091,7 +2214,17 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2091
2214
|
|
|
2092
2215
|
try {
|
|
2093
2216
|
(method as any).assertProtocolV2TargetStatus(
|
|
2094
|
-
[{ target_id:
|
|
2217
|
+
[{ target_id: 4, status: 3 }],
|
|
2218
|
+
expectedTargetIds
|
|
2219
|
+
);
|
|
2220
|
+
throw new Error('Expected Protocol V2 failed firmware status to throw');
|
|
2221
|
+
} catch (error: any) {
|
|
2222
|
+
expect(error.errorCode).toBe(HardwareErrorCode.FirmwareError);
|
|
2223
|
+
}
|
|
2224
|
+
|
|
2225
|
+
try {
|
|
2226
|
+
(method as any).assertProtocolV2TargetStatus(
|
|
2227
|
+
[{ target_id: 4, status: 'FW_MGMT_UPDATER_TASK_STATUS_FAILED_VERIFY' }],
|
|
2095
2228
|
expectedTargetIds
|
|
2096
2229
|
);
|
|
2097
2230
|
throw new Error('Expected Protocol V2 failed firmware status to throw');
|
|
@@ -2100,7 +2233,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2100
2233
|
}
|
|
2101
2234
|
});
|
|
2102
2235
|
|
|
2103
|
-
test('passes resource, bootloader,
|
|
2236
|
+
test('passes resource, bootloader, coprocessor, SE and app files to DeviceFirmwareUpdate targets', async () => {
|
|
2104
2237
|
const method = new FirmwareUpdateV4({
|
|
2105
2238
|
id: 1,
|
|
2106
2239
|
payload: {
|
|
@@ -2121,21 +2254,29 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2121
2254
|
.mockResolvedValue(undefined);
|
|
2122
2255
|
|
|
2123
2256
|
await (method as any).executeProtocolV2Update({
|
|
2124
|
-
|
|
2125
|
-
|
|
2257
|
+
resourceBinaryMap: [
|
|
2258
|
+
{
|
|
2259
|
+
fileName: 'resource.bin',
|
|
2260
|
+
binary: new Uint8Array([1, 2, 3]).buffer,
|
|
2261
|
+
targetId: 1,
|
|
2262
|
+
},
|
|
2263
|
+
],
|
|
2126
2264
|
bootloaderBinary: new Uint8Array([4, 5]).buffer,
|
|
2127
2265
|
fwBinaryMap: [
|
|
2128
2266
|
{
|
|
2129
|
-
fileName: '
|
|
2267
|
+
fileName: 'coprocessor.bin',
|
|
2130
2268
|
binary: new Uint8Array([6]).buffer,
|
|
2269
|
+
targetId: 6,
|
|
2131
2270
|
},
|
|
2132
2271
|
{
|
|
2133
|
-
fileName: '
|
|
2272
|
+
fileName: 'se01.bin',
|
|
2134
2273
|
binary: new Uint8Array([7]).buffer,
|
|
2274
|
+
targetId: 7,
|
|
2135
2275
|
},
|
|
2136
2276
|
{
|
|
2137
|
-
fileName: '
|
|
2277
|
+
fileName: 'application_p1.bin',
|
|
2138
2278
|
binary: new Uint8Array([8]).buffer,
|
|
2279
|
+
targetId: 4,
|
|
2139
2280
|
},
|
|
2140
2281
|
],
|
|
2141
2282
|
});
|
|
@@ -2143,18 +2284,19 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2143
2284
|
expect(writtenPaths).toEqual([
|
|
2144
2285
|
'vol1:resource.bin',
|
|
2145
2286
|
'vol1:bootloader.bin',
|
|
2146
|
-
'vol1:
|
|
2147
|
-
'vol1:
|
|
2148
|
-
'vol1:
|
|
2287
|
+
'vol1:coprocessor.bin',
|
|
2288
|
+
'vol1:se01.bin',
|
|
2289
|
+
'vol1:application_p1.bin',
|
|
2149
2290
|
]);
|
|
2150
|
-
|
|
2151
|
-
|
|
2291
|
+
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenNthCalledWith(1, {
|
|
2292
|
+
targets: [{ target_id: 1, path: 'vol1:resource.bin' }],
|
|
2293
|
+
});
|
|
2294
|
+
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenNthCalledWith(2, {
|
|
2152
2295
|
targets: [
|
|
2153
|
-
{ target_id:
|
|
2154
|
-
{ target_id:
|
|
2155
|
-
{ target_id:
|
|
2156
|
-
{ target_id:
|
|
2157
|
-
{ target_id: 0, path: 'vol1:firmware.bin' },
|
|
2296
|
+
{ target_id: 3, path: 'vol1:bootloader.bin' },
|
|
2297
|
+
{ target_id: 6, path: 'vol1:coprocessor.bin' },
|
|
2298
|
+
{ target_id: 7, path: 'vol1:se01.bin' },
|
|
2299
|
+
{ target_id: 4, path: 'vol1:application_p1.bin' },
|
|
2158
2300
|
],
|
|
2159
2301
|
});
|
|
2160
2302
|
expect(method.postProgressMessage).toHaveBeenCalledWith(100, 'transferData');
|
|
@@ -2167,7 +2309,6 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2167
2309
|
payload: {
|
|
2168
2310
|
method: 'firmwareUpdateV4',
|
|
2169
2311
|
platform: 'web',
|
|
2170
|
-
romloaderBinary: new Uint8Array([1]).buffer,
|
|
2171
2312
|
applicationP2Binary: new Uint8Array([2]).buffer,
|
|
2172
2313
|
se04Binary: new Uint8Array([3]).buffer,
|
|
2173
2314
|
},
|
|
@@ -2176,9 +2317,8 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2176
2317
|
|
|
2177
2318
|
const explicit = (method as any).collectExplicitTargetBinaries();
|
|
2178
2319
|
expect(explicit).toEqual([
|
|
2179
|
-
{ fileName: '
|
|
2180
|
-
{ fileName: '
|
|
2181
|
-
{ fileName: 'se04.bin', binary: expect.anything(), targetId: 6 },
|
|
2320
|
+
{ fileName: 'application_p2.bin', binary: expect.anything(), targetId: 5 },
|
|
2321
|
+
{ fileName: 'se04.bin', binary: expect.anything(), targetId: 10 },
|
|
2182
2322
|
]);
|
|
2183
2323
|
|
|
2184
2324
|
method.postTipMessage = jest.fn();
|
|
@@ -2194,43 +2334,759 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2194
2334
|
.mockResolvedValue(undefined);
|
|
2195
2335
|
|
|
2196
2336
|
await (method as any).executeProtocolV2Update({
|
|
2197
|
-
|
|
2337
|
+
resourceBinaryMap: [],
|
|
2198
2338
|
bootloaderBinary: null,
|
|
2199
2339
|
fwBinaryMap: explicit,
|
|
2200
2340
|
});
|
|
2201
2341
|
|
|
2202
2342
|
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenCalledWith({
|
|
2203
2343
|
targets: [
|
|
2204
|
-
{ target_id:
|
|
2205
|
-
{ target_id:
|
|
2206
|
-
{ target_id: 6, path: 'vol1:se04.bin' },
|
|
2344
|
+
{ target_id: 5, path: 'vol1:application_p2.bin' },
|
|
2345
|
+
{ target_id: 10, path: 'vol1:se04.bin' },
|
|
2207
2346
|
],
|
|
2208
2347
|
});
|
|
2209
2348
|
});
|
|
2210
2349
|
|
|
2211
|
-
test('
|
|
2350
|
+
test('maps explicit Pro2 resource crate binaries to separate CRATE targets', async () => {
|
|
2351
|
+
const firstResource = new Uint8Array([1]).buffer;
|
|
2352
|
+
const secondResource = new Uint8Array([2]).buffer;
|
|
2212
2353
|
const method = new FirmwareUpdateV4({
|
|
2213
2354
|
id: 1,
|
|
2214
2355
|
payload: {
|
|
2215
2356
|
method: 'firmwareUpdateV4',
|
|
2357
|
+
platform: 'web',
|
|
2358
|
+
resourceBinaries: [firstResource, secondResource],
|
|
2216
2359
|
},
|
|
2217
2360
|
});
|
|
2218
|
-
|
|
2219
|
-
(
|
|
2220
|
-
_name: string,
|
|
2221
|
-
_resType: string,
|
|
2222
|
-
params: { file: { offset: number; data: { byteLength: number } } }
|
|
2223
|
-
) =>
|
|
2224
|
-
Promise.resolve({
|
|
2225
|
-
type: 'FilesystemFile',
|
|
2226
|
-
message: {
|
|
2227
|
-
processed_byte: params.file.offset + params.file.data.byteLength,
|
|
2228
|
-
},
|
|
2229
|
-
})
|
|
2230
|
-
);
|
|
2361
|
+
method.init();
|
|
2231
2362
|
|
|
2232
|
-
(method as any).
|
|
2233
|
-
|
|
2363
|
+
const resourceTargets = await (method as any).prepareResourceBinaries('universal', {
|
|
2364
|
+
deviceType: 'pro2',
|
|
2365
|
+
firmwareVersion: '1.0.0',
|
|
2366
|
+
capabilities: [],
|
|
2367
|
+
});
|
|
2368
|
+
|
|
2369
|
+
expect(resourceTargets).toEqual([
|
|
2370
|
+
{
|
|
2371
|
+
fileName: 'resource-1.bin',
|
|
2372
|
+
binary: firstResource,
|
|
2373
|
+
targetId: 1,
|
|
2374
|
+
},
|
|
2375
|
+
{
|
|
2376
|
+
fileName: 'resource-2.bin',
|
|
2377
|
+
binary: secondResource,
|
|
2378
|
+
targetId: 1,
|
|
2379
|
+
},
|
|
2380
|
+
]);
|
|
2381
|
+
expect((method as any).hasExplicitProtocolV2Payload([])).toBe(true);
|
|
2382
|
+
});
|
|
2383
|
+
|
|
2384
|
+
test('rejects romloaderBinary before sending a Protocol V2 update request', () => {
|
|
2385
|
+
const method = new FirmwareUpdateV4({
|
|
2386
|
+
id: 1,
|
|
2387
|
+
payload: {
|
|
2388
|
+
method: 'firmwareUpdateV4',
|
|
2389
|
+
platform: 'web',
|
|
2390
|
+
romloaderBinary: new Uint8Array([1]).buffer,
|
|
2391
|
+
},
|
|
2392
|
+
});
|
|
2393
|
+
method.init();
|
|
2394
|
+
|
|
2395
|
+
expect(() => (method as any).collectExplicitTargetBinaries()).toThrow(
|
|
2396
|
+
'FW_MGMT_TARGET_ROMLOADER is not accepted'
|
|
2397
|
+
);
|
|
2398
|
+
});
|
|
2399
|
+
|
|
2400
|
+
test('maps Pro2 remote firmware-v1 components to Protocol V2 targets in installOrder', async () => {
|
|
2401
|
+
const method = new FirmwareUpdateV4({
|
|
2402
|
+
id: 1,
|
|
2403
|
+
payload: {
|
|
2404
|
+
method: 'firmwareUpdateV4',
|
|
2405
|
+
platform: 'web',
|
|
2406
|
+
},
|
|
2407
|
+
});
|
|
2408
|
+
method.init();
|
|
2409
|
+
|
|
2410
|
+
const binaries = new Map(
|
|
2411
|
+
[
|
|
2412
|
+
'bootloader',
|
|
2413
|
+
'applicationP1',
|
|
2414
|
+
'applicationP2',
|
|
2415
|
+
'coprocessor',
|
|
2416
|
+
'se01',
|
|
2417
|
+
'se02',
|
|
2418
|
+
'se03',
|
|
2419
|
+
'se04',
|
|
2420
|
+
].map((key, index) => [
|
|
2421
|
+
`https://example.com/${key}.pp.bin`,
|
|
2422
|
+
new Uint8Array([index + 1]).buffer,
|
|
2423
|
+
])
|
|
2424
|
+
);
|
|
2425
|
+
const getSysResourceBinarySpy = jest
|
|
2426
|
+
.spyOn(firmwareBinaryApi, 'getSysResourceBinary')
|
|
2427
|
+
.mockImplementation(url =>
|
|
2428
|
+
Promise.resolve({
|
|
2429
|
+
binary: binaries.get(url) ?? new Uint8Array([0]).buffer,
|
|
2430
|
+
})
|
|
2431
|
+
);
|
|
2432
|
+
const getFirmwareLatestReleaseSpy = jest
|
|
2433
|
+
.spyOn(DataManager, 'getFirmwareLatestRelease')
|
|
2434
|
+
.mockReturnValue({
|
|
2435
|
+
required: false,
|
|
2436
|
+
version: [1, 0, 0],
|
|
2437
|
+
url: 'https://example.com/applicationP1.pp.bin',
|
|
2438
|
+
bootloaderResource: 'https://example.com/bootloader.pp.bin',
|
|
2439
|
+
bootloaderVersion: [1, 0, 0],
|
|
2440
|
+
displayBootloaderVersion: [1, 0, 0],
|
|
2441
|
+
upgradeType: 'payload-package-set',
|
|
2442
|
+
installOrder: [
|
|
2443
|
+
'bootloader',
|
|
2444
|
+
'applicationP1',
|
|
2445
|
+
'applicationP2',
|
|
2446
|
+
'coprocessor',
|
|
2447
|
+
'se01',
|
|
2448
|
+
'se02',
|
|
2449
|
+
'se03',
|
|
2450
|
+
'se04',
|
|
2451
|
+
],
|
|
2452
|
+
components: {
|
|
2453
|
+
bootloader: {
|
|
2454
|
+
target: 'BOOTLOADER',
|
|
2455
|
+
url: 'https://example.com/bootloader.pp.bin',
|
|
2456
|
+
},
|
|
2457
|
+
applicationP1: {
|
|
2458
|
+
target: 'APPLICATION_P1',
|
|
2459
|
+
url: 'https://example.com/applicationP1.pp.bin',
|
|
2460
|
+
},
|
|
2461
|
+
applicationP2: {
|
|
2462
|
+
target: 'APPLICATION_P2',
|
|
2463
|
+
url: 'https://example.com/applicationP2.pp.bin',
|
|
2464
|
+
},
|
|
2465
|
+
coprocessor: {
|
|
2466
|
+
target: 'COPROCESSOR',
|
|
2467
|
+
url: 'https://example.com/coprocessor.pp.bin',
|
|
2468
|
+
},
|
|
2469
|
+
se01: { target: 'SE01', url: 'https://example.com/se01.pp.bin' },
|
|
2470
|
+
se02: { target: 'SE02', url: 'https://example.com/se02.pp.bin' },
|
|
2471
|
+
se03: { target: 'SE03', url: 'https://example.com/se03.pp.bin' },
|
|
2472
|
+
se04: { target: 'SE04', url: 'https://example.com/se04.pp.bin' },
|
|
2473
|
+
},
|
|
2474
|
+
fingerprint: '',
|
|
2475
|
+
changelog: {
|
|
2476
|
+
'zh-CN': '',
|
|
2477
|
+
'en-US': '',
|
|
2478
|
+
},
|
|
2479
|
+
});
|
|
2480
|
+
|
|
2481
|
+
const remoteBinaries = await (method as any).prepareRemoteProtocolV2Binaries('universal', {
|
|
2482
|
+
deviceType: 'pro2',
|
|
2483
|
+
firmwareVersion: '0.0.0',
|
|
2484
|
+
});
|
|
2485
|
+
|
|
2486
|
+
expect(remoteBinaries.bootloaderBinary).toBe(
|
|
2487
|
+
binaries.get('https://example.com/bootloader.pp.bin')
|
|
2488
|
+
);
|
|
2489
|
+
expect(remoteBinaries.fwBinaryMap).toEqual([
|
|
2490
|
+
{ fileName: 'application_p1.bin', binary: expect.anything(), targetId: 4 },
|
|
2491
|
+
{ fileName: 'application_p2.bin', binary: expect.anything(), targetId: 5 },
|
|
2492
|
+
{ fileName: 'coprocessor.bin', binary: expect.anything(), targetId: 6 },
|
|
2493
|
+
{ fileName: 'se01.bin', binary: expect.anything(), targetId: 7 },
|
|
2494
|
+
{ fileName: 'se02.bin', binary: expect.anything(), targetId: 8 },
|
|
2495
|
+
{ fileName: 'se03.bin', binary: expect.anything(), targetId: 9 },
|
|
2496
|
+
{ fileName: 'se04.bin', binary: expect.anything(), targetId: 10 },
|
|
2497
|
+
]);
|
|
2498
|
+
expect(getSysResourceBinarySpy.mock.calls.map(call => call[0])).toEqual([
|
|
2499
|
+
'https://example.com/bootloader.pp.bin',
|
|
2500
|
+
'https://example.com/applicationP1.pp.bin',
|
|
2501
|
+
'https://example.com/applicationP2.pp.bin',
|
|
2502
|
+
'https://example.com/coprocessor.pp.bin',
|
|
2503
|
+
'https://example.com/se01.pp.bin',
|
|
2504
|
+
'https://example.com/se02.pp.bin',
|
|
2505
|
+
'https://example.com/se03.pp.bin',
|
|
2506
|
+
'https://example.com/se04.pp.bin',
|
|
2507
|
+
]);
|
|
2508
|
+
|
|
2509
|
+
getSysResourceBinarySpy.mockRestore();
|
|
2510
|
+
getFirmwareLatestReleaseSpy.mockRestore();
|
|
2511
|
+
});
|
|
2512
|
+
|
|
2513
|
+
test('skips Pro2 resource crate download when resourceManifest matches device OKPP headers', async () => {
|
|
2514
|
+
const method = new FirmwareUpdateV4({
|
|
2515
|
+
id: 1,
|
|
2516
|
+
payload: {
|
|
2517
|
+
method: 'firmwareUpdateV4',
|
|
2518
|
+
platform: 'web',
|
|
2519
|
+
},
|
|
2520
|
+
});
|
|
2521
|
+
method.init();
|
|
2522
|
+
|
|
2523
|
+
const payloadHash = 'ab'.repeat(64);
|
|
2524
|
+
const header = buildOkppHeader({ payloadHash });
|
|
2525
|
+
const typedCall = jest.fn(
|
|
2526
|
+
(name: string, _resType: string, params: { file: { offset: number }; chunk_len: number }) => {
|
|
2527
|
+
if (name === 'FilesystemPathInfoQuery') {
|
|
2528
|
+
return Promise.resolve({
|
|
2529
|
+
type: 'FilesystemPathInfo',
|
|
2530
|
+
message: { exist: true, directory: false, size: header.byteLength },
|
|
2531
|
+
});
|
|
2532
|
+
}
|
|
2533
|
+
if (name === 'FilesystemFileRead') {
|
|
2534
|
+
const { offset } = params.file;
|
|
2535
|
+
const readLen = params.chunk_len;
|
|
2536
|
+
return Promise.resolve({
|
|
2537
|
+
type: 'FilesystemFile',
|
|
2538
|
+
message: { data: header.slice(offset, offset + readLen) },
|
|
2539
|
+
});
|
|
2540
|
+
}
|
|
2541
|
+
return Promise.reject(new Error(`unexpected call ${name}`));
|
|
2542
|
+
}
|
|
2543
|
+
);
|
|
2544
|
+
(method as any).device = stubDevice({
|
|
2545
|
+
getCommands: () => ({ typedCall }),
|
|
2546
|
+
});
|
|
2547
|
+
|
|
2548
|
+
const getSysResourceBinarySpy = jest
|
|
2549
|
+
.spyOn(firmwareBinaryApi, 'getSysResourceBinary')
|
|
2550
|
+
.mockResolvedValue({ binary: new Uint8Array([9]).buffer });
|
|
2551
|
+
const getFirmwareLatestReleaseSpy = jest
|
|
2552
|
+
.spyOn(DataManager, 'getFirmwareLatestRelease')
|
|
2553
|
+
.mockReturnValue({
|
|
2554
|
+
required: false,
|
|
2555
|
+
version: [1, 0, 0],
|
|
2556
|
+
url: '',
|
|
2557
|
+
upgradeType: 'payload-package-set',
|
|
2558
|
+
installOrder: ['resource'],
|
|
2559
|
+
components: {
|
|
2560
|
+
resource: {
|
|
2561
|
+
target: 'CRATE',
|
|
2562
|
+
url: 'https://example.com/pro2-resource.crate.okpkg',
|
|
2563
|
+
},
|
|
2564
|
+
},
|
|
2565
|
+
resourceManifest: {
|
|
2566
|
+
format: 'okpkg-crate',
|
|
2567
|
+
target: 'CRATE',
|
|
2568
|
+
version: [1, 0, 0],
|
|
2569
|
+
packages: [
|
|
2570
|
+
{
|
|
2571
|
+
path: 'resource/images/images.okpkg',
|
|
2572
|
+
type: 'RESC',
|
|
2573
|
+
payloadHash,
|
|
2574
|
+
},
|
|
2575
|
+
],
|
|
2576
|
+
},
|
|
2577
|
+
fingerprint: '',
|
|
2578
|
+
changelog: {
|
|
2579
|
+
'zh-CN': '',
|
|
2580
|
+
'en-US': '',
|
|
2581
|
+
},
|
|
2582
|
+
});
|
|
2583
|
+
|
|
2584
|
+
const remoteBinaries = await (method as any).prepareRemoteProtocolV2Binaries('universal', {
|
|
2585
|
+
deviceType: 'pro2',
|
|
2586
|
+
firmwareVersion: '1.0.0',
|
|
2587
|
+
capabilities: [],
|
|
2588
|
+
});
|
|
2589
|
+
|
|
2590
|
+
expect(remoteBinaries).toEqual({
|
|
2591
|
+
resourceBinaryMap: [],
|
|
2592
|
+
bootloaderBinary: null,
|
|
2593
|
+
fwBinaryMap: [],
|
|
2594
|
+
installItems: [],
|
|
2595
|
+
});
|
|
2596
|
+
expect(getSysResourceBinarySpy).not.toHaveBeenCalled();
|
|
2597
|
+
expect(typedCall.mock.calls[0][2]).toEqual({
|
|
2598
|
+
path: 'vol0:/resource/images/images.okpkg',
|
|
2599
|
+
});
|
|
2600
|
+
|
|
2601
|
+
getSysResourceBinarySpy.mockRestore();
|
|
2602
|
+
getFirmwareLatestReleaseSpy.mockRestore();
|
|
2603
|
+
});
|
|
2604
|
+
|
|
2605
|
+
test('downloads Pro2 resource crate when resourceManifest package is missing', async () => {
|
|
2606
|
+
const method = new FirmwareUpdateV4({
|
|
2607
|
+
id: 1,
|
|
2608
|
+
payload: {
|
|
2609
|
+
method: 'firmwareUpdateV4',
|
|
2610
|
+
platform: 'web',
|
|
2611
|
+
},
|
|
2612
|
+
});
|
|
2613
|
+
method.init();
|
|
2614
|
+
|
|
2615
|
+
const resourceBinary = new Uint8Array([9]).buffer;
|
|
2616
|
+
const typedCall = jest.fn().mockResolvedValue({
|
|
2617
|
+
type: 'FilesystemPathInfo',
|
|
2618
|
+
message: { exist: false, directory: false, size: 0 },
|
|
2619
|
+
});
|
|
2620
|
+
(method as any).device = stubDevice({
|
|
2621
|
+
getCommands: () => ({ typedCall }),
|
|
2622
|
+
});
|
|
2623
|
+
|
|
2624
|
+
const getSysResourceBinarySpy = jest
|
|
2625
|
+
.spyOn(firmwareBinaryApi, 'getSysResourceBinary')
|
|
2626
|
+
.mockResolvedValue({ binary: resourceBinary });
|
|
2627
|
+
const getFirmwareLatestReleaseSpy = jest
|
|
2628
|
+
.spyOn(DataManager, 'getFirmwareLatestRelease')
|
|
2629
|
+
.mockReturnValue({
|
|
2630
|
+
required: false,
|
|
2631
|
+
version: [1, 0, 0],
|
|
2632
|
+
url: '',
|
|
2633
|
+
upgradeType: 'payload-package-set',
|
|
2634
|
+
installOrder: ['resource'],
|
|
2635
|
+
components: {
|
|
2636
|
+
resource: {
|
|
2637
|
+
target: 'CRATE',
|
|
2638
|
+
url: 'https://example.com/pro2-resource.crate.okpkg',
|
|
2639
|
+
resourceManifest: {
|
|
2640
|
+
format: 'okpkg-crate',
|
|
2641
|
+
target: 'CRATE',
|
|
2642
|
+
version: [1, 0, 0],
|
|
2643
|
+
packages: [{ path: 'resource/images/images.okpkg', type: 'RESC' }],
|
|
2644
|
+
},
|
|
2645
|
+
},
|
|
2646
|
+
},
|
|
2647
|
+
fingerprint: '',
|
|
2648
|
+
changelog: {
|
|
2649
|
+
'zh-CN': '',
|
|
2650
|
+
'en-US': '',
|
|
2651
|
+
},
|
|
2652
|
+
});
|
|
2653
|
+
|
|
2654
|
+
const remoteBinaries = await (method as any).prepareRemoteProtocolV2Binaries('universal', {
|
|
2655
|
+
deviceType: 'pro2',
|
|
2656
|
+
firmwareVersion: '1.0.0',
|
|
2657
|
+
capabilities: [],
|
|
2658
|
+
});
|
|
2659
|
+
|
|
2660
|
+
expect(remoteBinaries.resourceBinaryMap).toEqual([
|
|
2661
|
+
{
|
|
2662
|
+
fileName: 'resource-resource.bin',
|
|
2663
|
+
binary: resourceBinary,
|
|
2664
|
+
targetId: 1,
|
|
2665
|
+
},
|
|
2666
|
+
]);
|
|
2667
|
+
expect(getSysResourceBinarySpy).toHaveBeenCalledWith(
|
|
2668
|
+
'https://example.com/pro2-resource.crate.okpkg'
|
|
2669
|
+
);
|
|
2670
|
+
|
|
2671
|
+
getSysResourceBinarySpy.mockRestore();
|
|
2672
|
+
getFirmwareLatestReleaseSpy.mockRestore();
|
|
2673
|
+
});
|
|
2674
|
+
|
|
2675
|
+
test('maps Pro2 CRATE remote component to resource binary target', async () => {
|
|
2676
|
+
const method = new FirmwareUpdateV4({
|
|
2677
|
+
id: 1,
|
|
2678
|
+
payload: {
|
|
2679
|
+
method: 'firmwareUpdateV4',
|
|
2680
|
+
platform: 'web',
|
|
2681
|
+
},
|
|
2682
|
+
});
|
|
2683
|
+
method.init();
|
|
2684
|
+
|
|
2685
|
+
const resourceBinary = new Uint8Array([1, 2, 3]).buffer;
|
|
2686
|
+
const getSysResourceBinarySpy = jest
|
|
2687
|
+
.spyOn(firmwareBinaryApi, 'getSysResourceBinary')
|
|
2688
|
+
.mockResolvedValue({ binary: resourceBinary });
|
|
2689
|
+
const getFirmwareLatestReleaseSpy = jest
|
|
2690
|
+
.spyOn(DataManager, 'getFirmwareLatestRelease')
|
|
2691
|
+
.mockReturnValue({
|
|
2692
|
+
required: false,
|
|
2693
|
+
version: [1, 0, 0],
|
|
2694
|
+
url: '',
|
|
2695
|
+
upgradeType: 'payload-package-set',
|
|
2696
|
+
installOrder: ['resource'],
|
|
2697
|
+
components: {
|
|
2698
|
+
resource: {
|
|
2699
|
+
target: 'CRATE',
|
|
2700
|
+
url: 'https://example.com/pro2-resource.crate.okpkg',
|
|
2701
|
+
},
|
|
2702
|
+
},
|
|
2703
|
+
fingerprint: '',
|
|
2704
|
+
changelog: {
|
|
2705
|
+
'zh-CN': '',
|
|
2706
|
+
'en-US': '',
|
|
2707
|
+
},
|
|
2708
|
+
});
|
|
2709
|
+
|
|
2710
|
+
const remoteBinaries = await (method as any).prepareRemoteProtocolV2Binaries('universal', {
|
|
2711
|
+
deviceType: 'pro2',
|
|
2712
|
+
firmwareVersion: '0.0.0',
|
|
2713
|
+
capabilities: [],
|
|
2714
|
+
});
|
|
2715
|
+
|
|
2716
|
+
expect(remoteBinaries).toEqual({
|
|
2717
|
+
resourceBinaryMap: [
|
|
2718
|
+
{
|
|
2719
|
+
fileName: 'resource-resource.bin',
|
|
2720
|
+
binary: resourceBinary,
|
|
2721
|
+
targetId: 1,
|
|
2722
|
+
},
|
|
2723
|
+
],
|
|
2724
|
+
bootloaderBinary: null,
|
|
2725
|
+
fwBinaryMap: [],
|
|
2726
|
+
installItems: [
|
|
2727
|
+
{
|
|
2728
|
+
fileName: 'resource-resource.bin',
|
|
2729
|
+
binary: resourceBinary,
|
|
2730
|
+
targetId: 1,
|
|
2731
|
+
kind: 'resource',
|
|
2732
|
+
},
|
|
2733
|
+
],
|
|
2734
|
+
});
|
|
2735
|
+
|
|
2736
|
+
getSysResourceBinarySpy.mockRestore();
|
|
2737
|
+
getFirmwareLatestReleaseSpy.mockRestore();
|
|
2738
|
+
});
|
|
2739
|
+
|
|
2740
|
+
test('downloads only unmatched Pro2 resource crate components by component manifests', async () => {
|
|
2741
|
+
const method = new FirmwareUpdateV4({
|
|
2742
|
+
id: 1,
|
|
2743
|
+
payload: {
|
|
2744
|
+
method: 'firmwareUpdateV4',
|
|
2745
|
+
platform: 'web',
|
|
2746
|
+
},
|
|
2747
|
+
});
|
|
2748
|
+
method.init();
|
|
2749
|
+
|
|
2750
|
+
const imagesPayloadHash = 'ab'.repeat(64);
|
|
2751
|
+
const imagesHeader = buildOkppHeader({ payloadHash: imagesPayloadHash });
|
|
2752
|
+
const fontsBinary = new Uint8Array([7, 8, 9]).buffer;
|
|
2753
|
+
const typedCall = jest.fn(
|
|
2754
|
+
(
|
|
2755
|
+
name: string,
|
|
2756
|
+
_resType: string,
|
|
2757
|
+
params: { path?: string; file?: { offset: number }; chunk_len?: number }
|
|
2758
|
+
) => {
|
|
2759
|
+
if (name === 'FilesystemPathInfoQuery') {
|
|
2760
|
+
const exist = params.path === 'vol0:/resource/images/images.okpkg';
|
|
2761
|
+
return Promise.resolve({
|
|
2762
|
+
type: 'FilesystemPathInfo',
|
|
2763
|
+
message: { exist, directory: false, size: exist ? imagesHeader.byteLength : 0 },
|
|
2764
|
+
});
|
|
2765
|
+
}
|
|
2766
|
+
if (name === 'FilesystemFileRead') {
|
|
2767
|
+
const offset = params.file?.offset ?? 0;
|
|
2768
|
+
const readLen = params.chunk_len ?? 0;
|
|
2769
|
+
return Promise.resolve({
|
|
2770
|
+
type: 'FilesystemFile',
|
|
2771
|
+
message: { data: imagesHeader.slice(offset, offset + readLen) },
|
|
2772
|
+
});
|
|
2773
|
+
}
|
|
2774
|
+
return Promise.reject(new Error(`unexpected call ${name}`));
|
|
2775
|
+
}
|
|
2776
|
+
);
|
|
2777
|
+
(method as any).device = stubDevice({
|
|
2778
|
+
getCommands: () => ({ typedCall }),
|
|
2779
|
+
});
|
|
2780
|
+
|
|
2781
|
+
const getSysResourceBinarySpy = jest
|
|
2782
|
+
.spyOn(firmwareBinaryApi, 'getSysResourceBinary')
|
|
2783
|
+
.mockResolvedValue({ binary: fontsBinary });
|
|
2784
|
+
const getFirmwareLatestReleaseSpy = jest
|
|
2785
|
+
.spyOn(DataManager, 'getFirmwareLatestRelease')
|
|
2786
|
+
.mockReturnValue({
|
|
2787
|
+
required: false,
|
|
2788
|
+
version: [1, 0, 0],
|
|
2789
|
+
url: '',
|
|
2790
|
+
upgradeType: 'payload-package-set',
|
|
2791
|
+
installOrder: ['resourceImages', 'resourceFonts'],
|
|
2792
|
+
components: {
|
|
2793
|
+
resourceImages: {
|
|
2794
|
+
target: 'CRATE',
|
|
2795
|
+
url: 'https://example.com/pro2-resource-images.crate.okpkg',
|
|
2796
|
+
resourceManifest: {
|
|
2797
|
+
format: 'okpkg-crate',
|
|
2798
|
+
target: 'CRATE',
|
|
2799
|
+
version: [1, 0, 0],
|
|
2800
|
+
packages: [
|
|
2801
|
+
{
|
|
2802
|
+
path: 'resource/images/images.okpkg',
|
|
2803
|
+
type: 'RESC',
|
|
2804
|
+
payloadHash: imagesPayloadHash,
|
|
2805
|
+
},
|
|
2806
|
+
],
|
|
2807
|
+
},
|
|
2808
|
+
},
|
|
2809
|
+
resourceFonts: {
|
|
2810
|
+
target: 'CRATE',
|
|
2811
|
+
url: 'https://example.com/pro2-resource-fonts.crate.okpkg',
|
|
2812
|
+
resourceManifest: {
|
|
2813
|
+
format: 'okpkg-crate',
|
|
2814
|
+
target: 'CRATE',
|
|
2815
|
+
version: [1, 0, 0],
|
|
2816
|
+
packages: [{ path: 'resource/font/noto.okpkg', type: 'RESC' }],
|
|
2817
|
+
},
|
|
2818
|
+
},
|
|
2819
|
+
},
|
|
2820
|
+
fingerprint: '',
|
|
2821
|
+
changelog: {
|
|
2822
|
+
'zh-CN': '',
|
|
2823
|
+
'en-US': '',
|
|
2824
|
+
},
|
|
2825
|
+
});
|
|
2826
|
+
|
|
2827
|
+
const remoteBinaries = await (method as any).prepareRemoteProtocolV2Binaries('universal', {
|
|
2828
|
+
deviceType: 'pro2',
|
|
2829
|
+
firmwareVersion: '1.0.0',
|
|
2830
|
+
capabilities: [],
|
|
2831
|
+
});
|
|
2832
|
+
|
|
2833
|
+
expect(remoteBinaries.resourceBinaryMap).toEqual([
|
|
2834
|
+
{
|
|
2835
|
+
fileName: 'resource-resourceFonts.bin',
|
|
2836
|
+
binary: fontsBinary,
|
|
2837
|
+
targetId: 1,
|
|
2838
|
+
},
|
|
2839
|
+
]);
|
|
2840
|
+
expect(getSysResourceBinarySpy).toHaveBeenCalledTimes(1);
|
|
2841
|
+
expect(getSysResourceBinarySpy).toHaveBeenCalledWith(
|
|
2842
|
+
'https://example.com/pro2-resource-fonts.crate.okpkg'
|
|
2843
|
+
);
|
|
2844
|
+
|
|
2845
|
+
getSysResourceBinarySpy.mockRestore();
|
|
2846
|
+
getFirmwareLatestReleaseSpy.mockRestore();
|
|
2847
|
+
});
|
|
2848
|
+
|
|
2849
|
+
test('installs multiple Pro2 resource crates as separate update requests', async () => {
|
|
2850
|
+
const method = new FirmwareUpdateV4({
|
|
2851
|
+
id: 1,
|
|
2852
|
+
payload: {
|
|
2853
|
+
method: 'firmwareUpdateV4',
|
|
2854
|
+
},
|
|
2855
|
+
});
|
|
2856
|
+
|
|
2857
|
+
const operations: string[] = [];
|
|
2858
|
+
method.postTipMessage = jest.fn();
|
|
2859
|
+
method.postProgressMessage = jest.fn();
|
|
2860
|
+
(method as any).protocolV2CommonUpdateProcess = jest
|
|
2861
|
+
.fn()
|
|
2862
|
+
.mockImplementation(params => {
|
|
2863
|
+
operations.push(`write:${params.filePath}`);
|
|
2864
|
+
return Promise.resolve(
|
|
2865
|
+
Number(params.processedSize ?? 0) + Number(params.payload.byteLength)
|
|
2866
|
+
);
|
|
2867
|
+
});
|
|
2868
|
+
(method as any).protocolV2StartFirmwareUpdate = jest.fn().mockImplementation(({ targets }) => {
|
|
2869
|
+
operations.push(
|
|
2870
|
+
`install:${targets.map((target: { path: string }) => target.path).join(',')}`
|
|
2871
|
+
);
|
|
2872
|
+
return Promise.resolve(undefined);
|
|
2873
|
+
});
|
|
2874
|
+
(method as any).waitForProtocolV2FirmwareUpdateComplete = jest
|
|
2875
|
+
.fn()
|
|
2876
|
+
.mockResolvedValue(undefined);
|
|
2877
|
+
|
|
2878
|
+
await (method as any).executeProtocolV2Update({
|
|
2879
|
+
resourceBinaryMap: [
|
|
2880
|
+
{
|
|
2881
|
+
fileName: 'resource-images.bin',
|
|
2882
|
+
binary: new Uint8Array([1]).buffer,
|
|
2883
|
+
targetId: 1,
|
|
2884
|
+
},
|
|
2885
|
+
{
|
|
2886
|
+
fileName: 'resource-fonts.bin',
|
|
2887
|
+
binary: new Uint8Array([2]).buffer,
|
|
2888
|
+
targetId: 1,
|
|
2889
|
+
},
|
|
2890
|
+
],
|
|
2891
|
+
bootloaderBinary: null,
|
|
2892
|
+
fwBinaryMap: [
|
|
2893
|
+
{
|
|
2894
|
+
fileName: 'application_p1.bin',
|
|
2895
|
+
binary: new Uint8Array([3]).buffer,
|
|
2896
|
+
targetId: 4,
|
|
2897
|
+
},
|
|
2898
|
+
],
|
|
2899
|
+
});
|
|
2900
|
+
|
|
2901
|
+
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenNthCalledWith(1, {
|
|
2902
|
+
targets: [{ target_id: 1, path: 'vol1:resource-images.bin' }],
|
|
2903
|
+
});
|
|
2904
|
+
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenNthCalledWith(2, {
|
|
2905
|
+
targets: [{ target_id: 1, path: 'vol1:resource-fonts.bin' }],
|
|
2906
|
+
});
|
|
2907
|
+
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenNthCalledWith(3, {
|
|
2908
|
+
targets: [{ target_id: 4, path: 'vol1:application_p1.bin' }],
|
|
2909
|
+
});
|
|
2910
|
+
expect(operations).toEqual([
|
|
2911
|
+
'write:vol1:resource-images.bin',
|
|
2912
|
+
'write:vol1:resource-fonts.bin',
|
|
2913
|
+
'write:vol1:application_p1.bin',
|
|
2914
|
+
'install:vol1:resource-images.bin',
|
|
2915
|
+
'install:vol1:resource-fonts.bin',
|
|
2916
|
+
'install:vol1:application_p1.bin',
|
|
2917
|
+
]);
|
|
2918
|
+
});
|
|
2919
|
+
|
|
2920
|
+
test('skips Pro2 firmware components when configured versions are already installed', async () => {
|
|
2921
|
+
const method = new FirmwareUpdateV4({
|
|
2922
|
+
id: 1,
|
|
2923
|
+
payload: {
|
|
2924
|
+
method: 'firmwareUpdateV4',
|
|
2925
|
+
platform: 'web',
|
|
2926
|
+
},
|
|
2927
|
+
});
|
|
2928
|
+
method.init();
|
|
2929
|
+
|
|
2930
|
+
const getSysResourceBinarySpy = jest
|
|
2931
|
+
.spyOn(firmwareBinaryApi, 'getSysResourceBinary')
|
|
2932
|
+
.mockResolvedValue({ binary: new Uint8Array([1]).buffer });
|
|
2933
|
+
const getFirmwareLatestReleaseSpy = jest
|
|
2934
|
+
.spyOn(DataManager, 'getFirmwareLatestRelease')
|
|
2935
|
+
.mockReturnValue({
|
|
2936
|
+
required: false,
|
|
2937
|
+
version: [1, 0, 0],
|
|
2938
|
+
url: 'https://example.com/applicationP1.pp.bin',
|
|
2939
|
+
bootloaderVersion: [1, 0, 0],
|
|
2940
|
+
upgradeType: 'payload-package-set',
|
|
2941
|
+
installOrder: ['bootloader', 'applicationP1', 'applicationP2'],
|
|
2942
|
+
components: {
|
|
2943
|
+
bootloader: {
|
|
2944
|
+
target: 'BOOTLOADER',
|
|
2945
|
+
url: 'https://example.com/bootloader.pp.bin',
|
|
2946
|
+
},
|
|
2947
|
+
applicationP1: {
|
|
2948
|
+
target: 'APPLICATION_P1',
|
|
2949
|
+
url: 'https://example.com/applicationP1.pp.bin',
|
|
2950
|
+
},
|
|
2951
|
+
applicationP2: {
|
|
2952
|
+
target: 'APPLICATION_P2',
|
|
2953
|
+
url: 'https://example.com/applicationP2.pp.bin',
|
|
2954
|
+
},
|
|
2955
|
+
},
|
|
2956
|
+
fingerprint: '',
|
|
2957
|
+
changelog: {
|
|
2958
|
+
'zh-CN': '',
|
|
2959
|
+
'en-US': '',
|
|
2960
|
+
},
|
|
2961
|
+
});
|
|
2962
|
+
|
|
2963
|
+
const remoteBinaries = await (method as any).prepareRemoteProtocolV2Binaries('universal', {
|
|
2964
|
+
deviceType: 'pro2',
|
|
2965
|
+
firmwareVersion: '1.0.0',
|
|
2966
|
+
bootloaderVersion: '1.0.0',
|
|
2967
|
+
capabilities: [],
|
|
2968
|
+
});
|
|
2969
|
+
|
|
2970
|
+
expect(remoteBinaries).toEqual({
|
|
2971
|
+
resourceBinaryMap: [],
|
|
2972
|
+
bootloaderBinary: null,
|
|
2973
|
+
fwBinaryMap: [],
|
|
2974
|
+
installItems: [],
|
|
2975
|
+
});
|
|
2976
|
+
expect(getSysResourceBinarySpy).not.toHaveBeenCalled();
|
|
2977
|
+
|
|
2978
|
+
getSysResourceBinarySpy.mockRestore();
|
|
2979
|
+
getFirmwareLatestReleaseSpy.mockRestore();
|
|
2980
|
+
});
|
|
2981
|
+
|
|
2982
|
+
test('uses Pro2 remote components when firmwareUpdateV4 has no explicit binaries', async () => {
|
|
2983
|
+
const method = new FirmwareUpdateV4({
|
|
2984
|
+
id: 1,
|
|
2985
|
+
payload: {
|
|
2986
|
+
method: 'firmwareUpdateV4',
|
|
2987
|
+
platform: 'web',
|
|
2988
|
+
},
|
|
2989
|
+
});
|
|
2990
|
+
method.init();
|
|
2991
|
+
|
|
2992
|
+
const remoteBinaries = {
|
|
2993
|
+
resourceBinaryMap: [],
|
|
2994
|
+
bootloaderBinary: new Uint8Array([1]).buffer,
|
|
2995
|
+
fwBinaryMap: [
|
|
2996
|
+
{
|
|
2997
|
+
fileName: 'application_p1.bin',
|
|
2998
|
+
binary: new Uint8Array([2]).buffer,
|
|
2999
|
+
targetId: 4,
|
|
3000
|
+
},
|
|
3001
|
+
],
|
|
3002
|
+
};
|
|
3003
|
+
|
|
3004
|
+
(method as any).device = stubDevice({
|
|
3005
|
+
originalDescriptor: { protocolType: 'V2' },
|
|
3006
|
+
features: { deviceType: 'pro2', firmwareVersion: '0.0.0', capabilities: [] },
|
|
3007
|
+
});
|
|
3008
|
+
(method as any).prepareResourceBinaries = jest.fn().mockResolvedValue([]);
|
|
3009
|
+
(method as any).prepareRemoteProtocolV2Binaries = jest.fn().mockResolvedValue(remoteBinaries);
|
|
3010
|
+
(method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(true);
|
|
3011
|
+
(method as any).executeProtocolV2Update = jest.fn().mockResolvedValue(undefined);
|
|
3012
|
+
(method as any).exitProtocolV2BootloaderToNormal = jest.fn().mockResolvedValue(undefined);
|
|
3013
|
+
(method as any).waitForProtocolV2FinalFeatures = jest.fn().mockResolvedValue({
|
|
3014
|
+
bootloaderVersion: '1.0.0',
|
|
3015
|
+
bleVersion: '0.0.0',
|
|
3016
|
+
firmwareVersion: '1.0.0',
|
|
3017
|
+
});
|
|
3018
|
+
method.postTipMessage = jest.fn();
|
|
3019
|
+
|
|
3020
|
+
await method.run();
|
|
3021
|
+
|
|
3022
|
+
expect((method as any).prepareRemoteProtocolV2Binaries).toHaveBeenCalledTimes(1);
|
|
3023
|
+
expect((method as any).executeProtocolV2Update).toHaveBeenCalledWith({
|
|
3024
|
+
resourceBinaryMap: remoteBinaries.resourceBinaryMap,
|
|
3025
|
+
bootloaderBinary: remoteBinaries.bootloaderBinary,
|
|
3026
|
+
fwBinaryMap: remoteBinaries.fwBinaryMap,
|
|
3027
|
+
});
|
|
3028
|
+
});
|
|
3029
|
+
|
|
3030
|
+
test('keeps explicit Protocol V2 binaries isolated from remote component auto-fill', async () => {
|
|
3031
|
+
const method = new FirmwareUpdateV4({
|
|
3032
|
+
id: 1,
|
|
3033
|
+
payload: {
|
|
3034
|
+
method: 'firmwareUpdateV4',
|
|
3035
|
+
platform: 'web',
|
|
3036
|
+
coprocessorBinary: new Uint8Array([1]).buffer,
|
|
3037
|
+
},
|
|
3038
|
+
});
|
|
3039
|
+
method.init();
|
|
3040
|
+
|
|
3041
|
+
(method as any).device = stubDevice({
|
|
3042
|
+
originalDescriptor: { protocolType: 'V2' },
|
|
3043
|
+
features: { deviceType: 'pro2', firmwareVersion: '0.0.0', capabilities: [] },
|
|
3044
|
+
});
|
|
3045
|
+
(method as any).prepareResourceBinaries = jest.fn().mockResolvedValue([]);
|
|
3046
|
+
(method as any).prepareRemoteProtocolV2Binaries = jest.fn();
|
|
3047
|
+
(method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(true);
|
|
3048
|
+
(method as any).executeProtocolV2Update = jest.fn().mockResolvedValue(undefined);
|
|
3049
|
+
(method as any).exitProtocolV2BootloaderToNormal = jest.fn().mockResolvedValue(undefined);
|
|
3050
|
+
(method as any).waitForProtocolV2FinalFeatures = jest.fn().mockResolvedValue({
|
|
3051
|
+
bootloaderVersion: '1.0.0',
|
|
3052
|
+
bleVersion: '0.0.0',
|
|
3053
|
+
firmwareVersion: '1.0.0',
|
|
3054
|
+
});
|
|
3055
|
+
method.postTipMessage = jest.fn();
|
|
3056
|
+
|
|
3057
|
+
await method.run();
|
|
3058
|
+
|
|
3059
|
+
expect((method as any).prepareRemoteProtocolV2Binaries).not.toHaveBeenCalled();
|
|
3060
|
+
expect((method as any).executeProtocolV2Update).toHaveBeenCalledWith({
|
|
3061
|
+
resourceBinaryMap: [],
|
|
3062
|
+
bootloaderBinary: null,
|
|
3063
|
+
fwBinaryMap: [{ fileName: 'coprocessor.bin', binary: expect.anything(), targetId: 6 }],
|
|
3064
|
+
});
|
|
3065
|
+
});
|
|
3066
|
+
|
|
3067
|
+
test('uses absolute processed_byte offsets and disables append for firmware file writes', async () => {
|
|
3068
|
+
const method = new FirmwareUpdateV4({
|
|
3069
|
+
id: 1,
|
|
3070
|
+
payload: {
|
|
3071
|
+
method: 'firmwareUpdateV4',
|
|
3072
|
+
},
|
|
3073
|
+
});
|
|
3074
|
+
const typedCall = jest.fn(
|
|
3075
|
+
(
|
|
3076
|
+
_name: string,
|
|
3077
|
+
_resType: string,
|
|
3078
|
+
params: { file: { offset: number; data: { byteLength: number } } }
|
|
3079
|
+
) =>
|
|
3080
|
+
Promise.resolve({
|
|
3081
|
+
type: 'FilesystemFile',
|
|
3082
|
+
message: {
|
|
3083
|
+
processed_byte: params.file.offset + params.file.data.byteLength,
|
|
3084
|
+
},
|
|
3085
|
+
})
|
|
3086
|
+
);
|
|
3087
|
+
|
|
3088
|
+
(method as any).device = stubDevice({
|
|
3089
|
+
getCommands: () => ({ typedCall }),
|
|
2234
3090
|
});
|
|
2235
3091
|
method.postProgressMessage = jest.fn();
|
|
2236
3092
|
|
|
@@ -2242,10 +3098,11 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2242
3098
|
});
|
|
2243
3099
|
|
|
2244
3100
|
const writePayloads = typedCall.mock.calls.map(call => call[2]);
|
|
2245
|
-
expect(writePayloads.map(payload => payload.file.offset)).toEqual([0,
|
|
2246
|
-
expect(writePayloads.map(payload => payload.file.data.byteLength)).toEqual([
|
|
3101
|
+
expect(writePayloads.map(payload => payload.file.offset)).toEqual([0, 4000]);
|
|
3102
|
+
expect(writePayloads.map(payload => payload.file.data.byteLength)).toEqual([4000, 97]);
|
|
2247
3103
|
expect(writePayloads.map(payload => payload.overwrite)).toEqual([true, false]);
|
|
2248
3104
|
expect(writePayloads.every(payload => payload.append === false)).toBe(true);
|
|
3105
|
+
expect(writePayloads.map(payload => payload.ui_percentage)).toEqual([0, 100]);
|
|
2249
3106
|
});
|
|
2250
3107
|
|
|
2251
3108
|
test('continues to DeviceFirmwareUpdate when FilesystemFileWrite returns processed chunk length', async () => {
|
|
@@ -2264,9 +3121,17 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2264
3121
|
},
|
|
2265
3122
|
});
|
|
2266
3123
|
}
|
|
2267
|
-
if (name === '
|
|
3124
|
+
if (name === 'DeviceFirmwareUpdateRequest') {
|
|
2268
3125
|
return Promise.resolve({ type: 'Success', message: { message: 'ok' } });
|
|
2269
3126
|
}
|
|
3127
|
+
if (name === 'DeviceFirmwareUpdateStatusGet') {
|
|
3128
|
+
return Promise.resolve({
|
|
3129
|
+
type: 'DeviceFirmwareUpdateStatus',
|
|
3130
|
+
message: {
|
|
3131
|
+
records: [{ target_id: 4, status: 2 }],
|
|
3132
|
+
},
|
|
3133
|
+
});
|
|
3134
|
+
}
|
|
2270
3135
|
return Promise.reject(new Error(`unexpected call ${name}`));
|
|
2271
3136
|
});
|
|
2272
3137
|
|
|
@@ -2277,21 +3142,22 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2277
3142
|
method.postTipMessage = jest.fn();
|
|
2278
3143
|
|
|
2279
3144
|
await (method as any).executeProtocolV2Update({
|
|
2280
|
-
|
|
3145
|
+
resourceBinaryMap: [],
|
|
2281
3146
|
bootloaderBinary: null,
|
|
2282
3147
|
fwBinaryMap: [
|
|
2283
3148
|
{
|
|
2284
3149
|
fileName: 'firmware.bin',
|
|
2285
3150
|
binary: new Uint8Array(4097).buffer,
|
|
3151
|
+
targetId: 4,
|
|
2286
3152
|
},
|
|
2287
3153
|
],
|
|
2288
3154
|
});
|
|
2289
3155
|
|
|
2290
3156
|
expect(typedCall).toHaveBeenCalledWith(
|
|
2291
|
-
'
|
|
2292
|
-
['Success', '
|
|
3157
|
+
'DeviceFirmwareUpdateRequest',
|
|
3158
|
+
['Success', 'DeviceFirmwareUpdateStatus'],
|
|
2293
3159
|
{
|
|
2294
|
-
targets: [{ target_id:
|
|
3160
|
+
targets: [{ target_id: 4, path: 'vol1:firmware.bin' }],
|
|
2295
3161
|
},
|
|
2296
3162
|
expect.any(Object)
|
|
2297
3163
|
);
|
|
@@ -2337,6 +3203,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2337
3203
|
const writePayloads = typedCall.mock.calls.map(call => call[2]);
|
|
2338
3204
|
expect(writePayloads.map(payload => payload.file.offset)).toEqual([0, 1800]);
|
|
2339
3205
|
expect(writePayloads.map(payload => payload.file.data.byteLength)).toEqual([1800, 1]);
|
|
3206
|
+
expect(writePayloads.map(payload => payload.ui_percentage)).toEqual([0, 100]);
|
|
2340
3207
|
});
|
|
2341
3208
|
|
|
2342
3209
|
test('consumes Protocol V2 install progress before final update success', async () => {
|
|
@@ -2355,18 +3222,18 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2355
3222
|
method.postTipMessage = jest.fn();
|
|
2356
3223
|
|
|
2357
3224
|
await (method as any).protocolV2StartFirmwareUpdate({
|
|
2358
|
-
targets: [{ target_id:
|
|
3225
|
+
targets: [{ target_id: 4, path: 'vol1:firmware.bin' }],
|
|
2359
3226
|
});
|
|
2360
3227
|
|
|
2361
3228
|
const callOptions = typedCall.mock.calls[0][3];
|
|
2362
|
-
expect(typedCall.mock.calls[0][1]).toEqual(['Success', '
|
|
2363
|
-
expect(callOptions.intermediateTypes).toEqual(['
|
|
3229
|
+
expect(typedCall.mock.calls[0][1]).toEqual(['Success', 'DeviceFirmwareUpdateStatus']);
|
|
3230
|
+
expect(callOptions.intermediateTypes).toEqual(['DeviceFirmwareUpdateStatus']);
|
|
2364
3231
|
callOptions.onIntermediateResponse({
|
|
2365
|
-
type: '
|
|
2366
|
-
message: { target_id:
|
|
3232
|
+
type: 'DeviceFirmwareUpdateStatus',
|
|
3233
|
+
message: { records: [{ target_id: 4, status: 1 }] },
|
|
2367
3234
|
});
|
|
2368
3235
|
|
|
2369
|
-
expect(method.postProgressMessage).toHaveBeenCalledWith(
|
|
3236
|
+
expect(method.postProgressMessage).toHaveBeenCalledWith(99, 'installingFirmware');
|
|
2370
3237
|
});
|
|
2371
3238
|
|
|
2372
3239
|
test('accepts Protocol V2 firmware update status as start response', async () => {
|
|
@@ -2377,8 +3244,8 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2377
3244
|
},
|
|
2378
3245
|
});
|
|
2379
3246
|
const typedCall = jest.fn().mockResolvedValue({
|
|
2380
|
-
type: '
|
|
2381
|
-
message: {
|
|
3247
|
+
type: 'DeviceFirmwareUpdateStatus',
|
|
3248
|
+
message: { records: [{ target_id: 4, status: 1 }] },
|
|
2382
3249
|
});
|
|
2383
3250
|
|
|
2384
3251
|
(method as any).device = stubDevice({
|
|
@@ -2388,29 +3255,29 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2388
3255
|
method.postTipMessage = jest.fn();
|
|
2389
3256
|
|
|
2390
3257
|
await (method as any).protocolV2StartFirmwareUpdate({
|
|
2391
|
-
targets: [{ target_id:
|
|
3258
|
+
targets: [{ target_id: 4, path: 'vol1:firmware.bin' }],
|
|
2392
3259
|
});
|
|
2393
3260
|
|
|
2394
|
-
expect(typedCall.mock.calls[0][1]).toEqual(['Success', '
|
|
3261
|
+
expect(typedCall.mock.calls[0][1]).toEqual(['Success', 'DeviceFirmwareUpdateStatus']);
|
|
2395
3262
|
expect(method.postTipMessage).toHaveBeenCalledWith('FirmwareUpdating');
|
|
2396
3263
|
});
|
|
2397
3264
|
});
|
|
2398
3265
|
|
|
2399
3266
|
describe('Protocol V2 firmware update method', () => {
|
|
2400
|
-
test('returns
|
|
3267
|
+
test('returns DeviceFirmwareUpdateStatus from low-level update trigger', async () => {
|
|
2401
3268
|
const method = new DeviceFirmwareUpdate({
|
|
2402
3269
|
id: 1,
|
|
2403
3270
|
payload: {
|
|
2404
3271
|
method: 'deviceFirmwareUpdate',
|
|
2405
|
-
targetId:
|
|
3272
|
+
targetId: 4,
|
|
2406
3273
|
path: 'vol0:firmware.bin',
|
|
2407
3274
|
},
|
|
2408
3275
|
});
|
|
2409
3276
|
method.init();
|
|
2410
3277
|
|
|
2411
3278
|
const typedCall = jest.fn().mockResolvedValue({
|
|
2412
|
-
type: '
|
|
2413
|
-
message: {
|
|
3279
|
+
type: 'DeviceFirmwareUpdateStatus',
|
|
3280
|
+
message: { records: [{ target_id: 4, status: 1 }] },
|
|
2414
3281
|
});
|
|
2415
3282
|
|
|
2416
3283
|
(method as any).device = stubDevice({
|
|
@@ -2418,11 +3285,11 @@ describe('Protocol V2 firmware update method', () => {
|
|
|
2418
3285
|
});
|
|
2419
3286
|
|
|
2420
3287
|
await expect(method.run()).resolves.toEqual({
|
|
2421
|
-
|
|
3288
|
+
records: [{ target_id: 4, status: 1 }],
|
|
2422
3289
|
});
|
|
2423
|
-
expect(typedCall.mock.calls[0][1]).toEqual(['Success', '
|
|
3290
|
+
expect(typedCall.mock.calls[0][1]).toEqual(['Success', 'DeviceFirmwareUpdateStatus']);
|
|
2424
3291
|
expect(typedCall.mock.calls[0][2]).toEqual({
|
|
2425
|
-
targets: [{ target_id:
|
|
3292
|
+
targets: [{ target_id: 4, path: 'vol0:firmware.bin' }],
|
|
2426
3293
|
});
|
|
2427
3294
|
});
|
|
2428
3295
|
|
|
@@ -2431,7 +3298,7 @@ describe('Protocol V2 firmware update method', () => {
|
|
|
2431
3298
|
id: 1,
|
|
2432
3299
|
payload: {
|
|
2433
3300
|
method: 'deviceFirmwareUpdate',
|
|
2434
|
-
targetId:
|
|
3301
|
+
targetId: 4,
|
|
2435
3302
|
path: 'vol0:firmware.bin',
|
|
2436
3303
|
},
|
|
2437
3304
|
});
|
|
@@ -2485,7 +3352,7 @@ describe('Protocol V2 firmware update method', () => {
|
|
|
2485
3352
|
targets: [
|
|
2486
3353
|
{
|
|
2487
3354
|
target_id: undefined,
|
|
2488
|
-
targetId:
|
|
3355
|
+
targetId: 4,
|
|
2489
3356
|
path: 'vol0:firmware.bin',
|
|
2490
3357
|
},
|
|
2491
3358
|
],
|
|
@@ -2498,32 +3365,72 @@ describe('Protocol V2 firmware update method', () => {
|
|
|
2498
3365
|
|
|
2499
3366
|
await method.run();
|
|
2500
3367
|
expect(typedCall.mock.calls[0][2]).toEqual({
|
|
2501
|
-
targets: [{ target_id:
|
|
3368
|
+
targets: [{ target_id: 4, path: 'vol0:firmware.bin' }],
|
|
2502
3369
|
});
|
|
2503
3370
|
});
|
|
2504
|
-
});
|
|
2505
3371
|
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
const
|
|
2509
|
-
const method = new DevReboot({
|
|
3372
|
+
test('accepts firmware target enum names from low-level update params', async () => {
|
|
3373
|
+
const typedCall = jest.fn().mockResolvedValue({ message: {} });
|
|
3374
|
+
const method = new DeviceFirmwareUpdate({
|
|
2510
3375
|
id: 1,
|
|
2511
3376
|
payload: {
|
|
2512
|
-
method: '
|
|
2513
|
-
|
|
3377
|
+
method: 'deviceFirmwareUpdate',
|
|
3378
|
+
targetId: 'FW_MGMT_TARGET_CRATE',
|
|
3379
|
+
path: 'vol0:resource.bin',
|
|
2514
3380
|
},
|
|
2515
3381
|
});
|
|
2516
3382
|
method.init();
|
|
2517
|
-
(method as any).device = stubDevice({
|
|
3383
|
+
(method as any).device = stubDevice({
|
|
3384
|
+
commands: { typedCall },
|
|
3385
|
+
});
|
|
2518
3386
|
|
|
2519
3387
|
await method.run();
|
|
3388
|
+
expect(typedCall.mock.calls[0][2]).toEqual({
|
|
3389
|
+
targets: [{ target_id: 1, path: 'vol0:resource.bin' }],
|
|
3390
|
+
});
|
|
3391
|
+
});
|
|
2520
3392
|
|
|
2521
|
-
|
|
2522
|
-
|
|
3393
|
+
test('passes firmware update status fields through to Protocol V2', async () => {
|
|
3394
|
+
const typedCall = jest.fn().mockResolvedValue({
|
|
3395
|
+
message: {
|
|
3396
|
+
records: [{ target_id: 4, status: 2, payload_version: 1, path: 'vol1:application_p1.bin' }],
|
|
3397
|
+
},
|
|
2523
3398
|
});
|
|
3399
|
+
const method = new DeviceGetFirmwareUpdateStatus({
|
|
3400
|
+
id: 1,
|
|
3401
|
+
payload: {
|
|
3402
|
+
method: 'deviceGetFirmwareUpdateStatus',
|
|
3403
|
+
fields: {
|
|
3404
|
+
status: true,
|
|
3405
|
+
payload_version: true,
|
|
3406
|
+
path: true,
|
|
3407
|
+
},
|
|
3408
|
+
},
|
|
3409
|
+
});
|
|
3410
|
+
method.init();
|
|
3411
|
+
(method as any).device = stubDevice({
|
|
3412
|
+
commands: { typedCall },
|
|
3413
|
+
});
|
|
3414
|
+
|
|
3415
|
+
await expect(method.run()).resolves.toEqual({
|
|
3416
|
+
records: [{ target_id: 4, status: 2, payload_version: 1, path: 'vol1:application_p1.bin' }],
|
|
3417
|
+
});
|
|
3418
|
+
expect(typedCall).toHaveBeenCalledWith(
|
|
3419
|
+
'DeviceFirmwareUpdateStatusGet',
|
|
3420
|
+
'DeviceFirmwareUpdateStatus',
|
|
3421
|
+
{
|
|
3422
|
+
fields: {
|
|
3423
|
+
status: true,
|
|
3424
|
+
payload_version: true,
|
|
3425
|
+
path: true,
|
|
3426
|
+
},
|
|
3427
|
+
}
|
|
3428
|
+
);
|
|
2524
3429
|
});
|
|
3430
|
+
});
|
|
2525
3431
|
|
|
2526
|
-
|
|
3432
|
+
describe('Protocol V2 reboot methods', () => {
|
|
3433
|
+
test('sends DeviceReboot from deviceReboot', async () => {
|
|
2527
3434
|
const typedCall = jest.fn().mockResolvedValue({ message: { message: 'ok' } });
|
|
2528
3435
|
const method = new DeviceReboot({
|
|
2529
3436
|
id: 1,
|
|
@@ -2537,18 +3444,86 @@ describe('Protocol V2 reboot methods', () => {
|
|
|
2537
3444
|
|
|
2538
3445
|
await method.run();
|
|
2539
3446
|
|
|
2540
|
-
expect(typedCall).toHaveBeenCalledWith('
|
|
3447
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceReboot', 'Success', {
|
|
2541
3448
|
reboot_type: 2,
|
|
2542
3449
|
});
|
|
2543
3450
|
});
|
|
2544
3451
|
});
|
|
2545
3452
|
|
|
3453
|
+
describe('Protocol V2 current low-level methods', () => {
|
|
3454
|
+
test('sends ProtocolInfoRequest from protocolInfoRequest', async () => {
|
|
3455
|
+
const typedCall = jest.fn().mockResolvedValue({ message: { version: 1 } });
|
|
3456
|
+
const method = new ProtocolInfoRequest({
|
|
3457
|
+
id: 1,
|
|
3458
|
+
payload: { method: 'protocolInfoRequest' },
|
|
3459
|
+
});
|
|
3460
|
+
method.init();
|
|
3461
|
+
(method as any).device = stubDevice({ commands: { typedCall } });
|
|
3462
|
+
|
|
3463
|
+
await method.run();
|
|
3464
|
+
|
|
3465
|
+
expect(typedCall).toHaveBeenCalledWith('ProtocolInfoRequest', 'ProtocolInfo', {});
|
|
3466
|
+
});
|
|
3467
|
+
|
|
3468
|
+
test('sends DeviceFactoryInfoSet and DeviceFactoryInfoGet', async () => {
|
|
3469
|
+
const typedCall = jest.fn().mockResolvedValue({ message: {} });
|
|
3470
|
+
const setMethod = new DeviceFactoryInfoSet({
|
|
3471
|
+
id: 1,
|
|
3472
|
+
payload: {
|
|
3473
|
+
method: 'deviceFactoryInfoSet',
|
|
3474
|
+
serial_number: 'PR2SERIAL',
|
|
3475
|
+
burn_in_completed: true,
|
|
3476
|
+
},
|
|
3477
|
+
});
|
|
3478
|
+
setMethod.init();
|
|
3479
|
+
(setMethod as any).device = stubDevice({ commands: { typedCall } });
|
|
3480
|
+
|
|
3481
|
+
await setMethod.run();
|
|
3482
|
+
|
|
3483
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceFactoryInfoSet', 'Success', {
|
|
3484
|
+
info: {
|
|
3485
|
+
version: undefined,
|
|
3486
|
+
serial_number: 'PR2SERIAL',
|
|
3487
|
+
burn_in_completed: true,
|
|
3488
|
+
factory_test_completed: undefined,
|
|
3489
|
+
manufacture_time: undefined,
|
|
3490
|
+
},
|
|
3491
|
+
});
|
|
3492
|
+
|
|
3493
|
+
typedCall.mockResolvedValueOnce({ message: { serial_number: 'PR2SERIAL' } });
|
|
3494
|
+
const getMethod = new DeviceFactoryInfoGet({
|
|
3495
|
+
id: 2,
|
|
3496
|
+
payload: { method: 'deviceFactoryInfoGet' },
|
|
3497
|
+
});
|
|
3498
|
+
getMethod.init();
|
|
3499
|
+
(getMethod as any).device = stubDevice({ commands: { typedCall } });
|
|
3500
|
+
|
|
3501
|
+
await getMethod.run();
|
|
3502
|
+
|
|
3503
|
+
expect(typedCall).toHaveBeenLastCalledWith('DeviceFactoryInfoGet', 'DeviceFactoryInfo', {});
|
|
3504
|
+
});
|
|
3505
|
+
|
|
3506
|
+
test('sends FilesystemPermissionFix from filesystemPermissionFix', async () => {
|
|
3507
|
+
const typedCall = jest.fn().mockResolvedValue({ message: {} });
|
|
3508
|
+
const method = new FilesystemPermissionFix({
|
|
3509
|
+
id: 1,
|
|
3510
|
+
payload: { method: 'filesystemPermissionFix' },
|
|
3511
|
+
});
|
|
3512
|
+
method.init();
|
|
3513
|
+
(method as any).device = stubDevice({ commands: { typedCall } });
|
|
3514
|
+
|
|
3515
|
+
await method.run();
|
|
3516
|
+
|
|
3517
|
+
expect(typedCall).toHaveBeenCalledWith('FilesystemPermissionFix', 'Success', {});
|
|
3518
|
+
});
|
|
3519
|
+
});
|
|
3520
|
+
|
|
2546
3521
|
describe('Protocol V2 raw device info method', () => {
|
|
2547
3522
|
const buildMethod = (payload: Record<string, unknown> = {}) => {
|
|
2548
|
-
const method = new
|
|
3523
|
+
const method = new DeviceInfoGet({
|
|
2549
3524
|
id: 1,
|
|
2550
3525
|
payload: {
|
|
2551
|
-
method: '
|
|
3526
|
+
method: 'deviceInfoGet',
|
|
2552
3527
|
...payload,
|
|
2553
3528
|
},
|
|
2554
3529
|
});
|
|
@@ -2558,7 +3533,7 @@ describe('Protocol V2 raw device info method', () => {
|
|
|
2558
3533
|
|
|
2559
3534
|
test('passes requested targets/types through and returns the raw DeviceInfo message', async () => {
|
|
2560
3535
|
const method = buildMethod({
|
|
2561
|
-
targets: { hw: true, se1: true
|
|
3536
|
+
targets: { hw: true, se1: true },
|
|
2562
3537
|
types: { version: true, hash: true },
|
|
2563
3538
|
});
|
|
2564
3539
|
const typedCall = jest.fn().mockResolvedValue({
|
|
@@ -2574,9 +3549,8 @@ describe('Protocol V2 raw device info method', () => {
|
|
|
2574
3549
|
protocol_version: 1,
|
|
2575
3550
|
hw: { serial_no: 'PR2SERIAL' },
|
|
2576
3551
|
});
|
|
2577
|
-
// 未知 key(junk)被过滤,已选 key 原样透传,不构建 DeviceProfile
|
|
2578
3552
|
expect(typedCall).toHaveBeenCalledWith(
|
|
2579
|
-
'
|
|
3553
|
+
'DeviceInfoGet',
|
|
2580
3554
|
'DeviceInfo',
|
|
2581
3555
|
{
|
|
2582
3556
|
targets: { hw: true, se1: true },
|
|
@@ -2586,6 +3560,20 @@ describe('Protocol V2 raw device info method', () => {
|
|
|
2586
3560
|
);
|
|
2587
3561
|
});
|
|
2588
3562
|
|
|
3563
|
+
test('rejects removed target/type names before transport call', () => {
|
|
3564
|
+
expect(() =>
|
|
3565
|
+
buildMethod({
|
|
3566
|
+
targets: { hw: true, bt: true },
|
|
3567
|
+
})
|
|
3568
|
+
).toThrow('targets');
|
|
3569
|
+
|
|
3570
|
+
expect(() =>
|
|
3571
|
+
buildMethod({
|
|
3572
|
+
types: { version: true, digest: true },
|
|
3573
|
+
})
|
|
3574
|
+
).toThrow('types');
|
|
3575
|
+
});
|
|
3576
|
+
|
|
2589
3577
|
test('defaults to the basic targets/types when none are given', async () => {
|
|
2590
3578
|
const method = buildMethod();
|
|
2591
3579
|
const typedCall = jest.fn().mockResolvedValue({ type: 'DeviceInfo', message: {} });
|
|
@@ -2597,10 +3585,10 @@ describe('Protocol V2 raw device info method', () => {
|
|
|
2597
3585
|
await method.run();
|
|
2598
3586
|
|
|
2599
3587
|
expect(typedCall).toHaveBeenCalledWith(
|
|
2600
|
-
'
|
|
3588
|
+
'DeviceInfoGet',
|
|
2601
3589
|
'DeviceInfo',
|
|
2602
3590
|
{
|
|
2603
|
-
targets: { hw: true, fw: true,
|
|
3591
|
+
targets: { hw: true, fw: true, coprocessor: true, status: true },
|
|
2604
3592
|
types: { version: true, specific: true },
|
|
2605
3593
|
},
|
|
2606
3594
|
{ timeoutMs: PROTOCOL_V2_DEVICE_INFO_TIMEOUT_MS }
|
|
@@ -2620,48 +3608,6 @@ describe('Protocol V2 raw device info method', () => {
|
|
|
2620
3608
|
});
|
|
2621
3609
|
});
|
|
2622
3610
|
|
|
2623
|
-
describe('Protocol V2 onboarding status method', () => {
|
|
2624
|
-
test('returns OnboardingStatus from low-level status query', async () => {
|
|
2625
|
-
const method = new DeviceGetOnboardingStatus({
|
|
2626
|
-
id: 1,
|
|
2627
|
-
payload: {
|
|
2628
|
-
method: 'deviceGetOnboardingStatus',
|
|
2629
|
-
},
|
|
2630
|
-
});
|
|
2631
|
-
method.init();
|
|
2632
|
-
|
|
2633
|
-
const typedCall = jest.fn().mockResolvedValue({
|
|
2634
|
-
type: 'OnboardingStatus',
|
|
2635
|
-
message: {
|
|
2636
|
-
step: 3,
|
|
2637
|
-
setup: {
|
|
2638
|
-
restore: {
|
|
2639
|
-
mnemonic: true,
|
|
2640
|
-
},
|
|
2641
|
-
},
|
|
2642
|
-
detail_code: 7,
|
|
2643
|
-
detail_str: 'Recovery Phrase',
|
|
2644
|
-
},
|
|
2645
|
-
});
|
|
2646
|
-
|
|
2647
|
-
(method as any).device = stubDevice({
|
|
2648
|
-
commands: { typedCall },
|
|
2649
|
-
});
|
|
2650
|
-
|
|
2651
|
-
await expect(method.run()).resolves.toEqual({
|
|
2652
|
-
step: 3,
|
|
2653
|
-
setup: {
|
|
2654
|
-
restore: {
|
|
2655
|
-
mnemonic: true,
|
|
2656
|
-
},
|
|
2657
|
-
},
|
|
2658
|
-
detail_code: 7,
|
|
2659
|
-
detail_str: 'Recovery Phrase',
|
|
2660
|
-
});
|
|
2661
|
-
expect(typedCall).toHaveBeenCalledWith('GetOnboardingStatus', 'OnboardingStatus', {});
|
|
2662
|
-
});
|
|
2663
|
-
});
|
|
2664
|
-
|
|
2665
3611
|
describe('Protocol V2 file write method', () => {
|
|
2666
3612
|
test('rejects invalid write parameters before transport call', () => {
|
|
2667
3613
|
expect(() => {
|
|
@@ -2711,17 +3657,22 @@ describe('Protocol V2 file write method', () => {
|
|
|
2711
3657
|
method.init();
|
|
2712
3658
|
await method.run();
|
|
2713
3659
|
|
|
2714
|
-
expect(typedCall).toHaveBeenCalledWith(
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
3660
|
+
expect(typedCall).toHaveBeenCalledWith(
|
|
3661
|
+
'FilesystemFileWrite',
|
|
3662
|
+
'FilesystemFile',
|
|
3663
|
+
{
|
|
3664
|
+
file: {
|
|
3665
|
+
path: 'vol1:test.bin',
|
|
3666
|
+
offset: 1,
|
|
3667
|
+
total_size: 2,
|
|
3668
|
+
data: new Uint8Array([1]),
|
|
3669
|
+
},
|
|
3670
|
+
overwrite: false,
|
|
3671
|
+
append: false,
|
|
3672
|
+
ui_percentage: 100,
|
|
2720
3673
|
},
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
ui_percentage: 99,
|
|
2724
|
-
}, { timeoutMs: undefined });
|
|
3674
|
+
{ timeoutMs: undefined }
|
|
3675
|
+
);
|
|
2725
3676
|
expect(method.postMessage).toHaveBeenCalledWith({
|
|
2726
3677
|
event: 'UI_EVENT',
|
|
2727
3678
|
type: UI_REQUEST.DEVICE_PROGRESS,
|
|
@@ -2754,28 +3705,40 @@ describe('Protocol V2 file write method', () => {
|
|
|
2754
3705
|
const result = await method.run();
|
|
2755
3706
|
|
|
2756
3707
|
expect(typedCall).toHaveBeenCalledTimes(2);
|
|
2757
|
-
expect(typedCall).toHaveBeenNthCalledWith(
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
3708
|
+
expect(typedCall).toHaveBeenNthCalledWith(
|
|
3709
|
+
1,
|
|
3710
|
+
'FilesystemFileWrite',
|
|
3711
|
+
'FilesystemFile',
|
|
3712
|
+
{
|
|
3713
|
+
file: {
|
|
3714
|
+
path: 'vol1:test.bin',
|
|
3715
|
+
offset: 0,
|
|
3716
|
+
total_size: 4097,
|
|
3717
|
+
data: data.slice(0, 4000),
|
|
3718
|
+
},
|
|
3719
|
+
overwrite: true,
|
|
3720
|
+
append: false,
|
|
3721
|
+
ui_percentage: 0,
|
|
3722
|
+
},
|
|
3723
|
+
{ timeoutMs: undefined }
|
|
3724
|
+
);
|
|
3725
|
+
expect(typedCall).toHaveBeenNthCalledWith(
|
|
3726
|
+
2,
|
|
3727
|
+
'FilesystemFileWrite',
|
|
3728
|
+
'FilesystemFile',
|
|
3729
|
+
{
|
|
3730
|
+
file: {
|
|
3731
|
+
path: 'vol1:test.bin',
|
|
3732
|
+
offset: 4000,
|
|
3733
|
+
total_size: 4097,
|
|
3734
|
+
data: data.slice(4000),
|
|
3735
|
+
},
|
|
3736
|
+
overwrite: false,
|
|
3737
|
+
append: false,
|
|
3738
|
+
ui_percentage: 100,
|
|
3739
|
+
},
|
|
3740
|
+
{ timeoutMs: undefined }
|
|
3741
|
+
);
|
|
2779
3742
|
expect(result).toMatchObject({
|
|
2780
3743
|
path: 'vol1:test.bin',
|
|
2781
3744
|
processed_byte: 4097,
|
|
@@ -2785,8 +3748,8 @@ describe('Protocol V2 file write method', () => {
|
|
|
2785
3748
|
event: 'UI_EVENT',
|
|
2786
3749
|
type: UI_REQUEST.DEVICE_PROGRESS,
|
|
2787
3750
|
payload: expect.objectContaining({
|
|
2788
|
-
progress:
|
|
2789
|
-
transferredBytes:
|
|
3751
|
+
progress: 97,
|
|
3752
|
+
transferredBytes: 4000,
|
|
2790
3753
|
totalBytes: 4097,
|
|
2791
3754
|
elapsedMs: expect.any(Number),
|
|
2792
3755
|
}),
|
|
@@ -2911,7 +3874,7 @@ describe('Protocol V2 file read method', () => {
|
|
|
2911
3874
|
total_size: 0,
|
|
2912
3875
|
},
|
|
2913
3876
|
chunk_len: 64,
|
|
2914
|
-
ui_percentage:
|
|
3877
|
+
ui_percentage: 0,
|
|
2915
3878
|
});
|
|
2916
3879
|
expect(typedCall).toHaveBeenNthCalledWith(3, 'FilesystemFileRead', 'FilesystemFile', {
|
|
2917
3880
|
file: {
|
|
@@ -2920,7 +3883,7 @@ describe('Protocol V2 file read method', () => {
|
|
|
2920
3883
|
total_size: 0,
|
|
2921
3884
|
},
|
|
2922
3885
|
chunk_len: 1,
|
|
2923
|
-
ui_percentage:
|
|
3886
|
+
ui_percentage: 100,
|
|
2924
3887
|
});
|
|
2925
3888
|
expect(result.data.byteLength).toBe(65);
|
|
2926
3889
|
expect(result.data[0]).toBe(1);
|