@onekeyfe/hd-core 1.2.0-alpha.54 → 1.2.0-alpha.55
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__/AllNetworkGetAddressBase.tracing.test.ts +231 -0
- package/__tests__/DeviceCommands.test.ts +45 -0
- package/__tests__/DeviceEventRegistration.test.ts +6 -0
- package/__tests__/check-all-firmware-release-protocol-v2.test.ts +198 -3
- package/__tests__/connectSettings.test.ts +5 -47
- package/__tests__/device-lifecycle-events.test.ts +105 -3
- package/__tests__/deviceUploadNft.test.ts +293 -0
- package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +31 -59
- package/__tests__/get-device-state.test.ts +132 -12
- package/__tests__/homescreen.test.ts +41 -1
- package/__tests__/kaspaSignTransaction.test.ts +3 -26
- package/__tests__/logBlockEvent.test.ts +1 -1
- package/__tests__/method-protocol-support.test.ts +19 -0
- package/__tests__/open-wallet-session.test.ts +568 -37
- package/__tests__/pro2Nft.test.ts +108 -0
- package/__tests__/protocol-binding.test.ts +89 -0
- package/__tests__/protocol-v2-resources.test.ts +286 -0
- package/__tests__/protocol-v2-ui-lifecycle.test.ts +91 -0
- package/__tests__/protocol-v2-unlock-policy.test.ts +201 -35
- package/__tests__/protocol-v2.test.ts +1350 -1012
- package/__tests__/protocolV2UiInteraction.test.ts +63 -0
- package/__tests__/public-pro2-api-boundary.test.ts +1 -0
- package/__tests__/search-devices.test.ts +12 -3
- package/__tests__/uiProgressThrottle.test.ts +74 -0
- package/__tests__/uiPromiseRegistry.test.ts +86 -0
- package/dist/api/BaseMethod.d.ts +3 -1
- package/dist/api/BaseMethod.d.ts.map +1 -1
- package/dist/api/CheckAllFirmwareRelease.d.ts +2 -1
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/DetectDeviceConnectProtocol.d.ts +7 -0
- package/dist/api/DetectDeviceConnectProtocol.d.ts.map +1 -0
- package/dist/api/FirmwareUpdate.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV2.d.ts +0 -11
- package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV3.d.ts +3 -7
- package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +11 -25
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/GetPassphraseState.d.ts.map +1 -1
- package/dist/api/OpenWalletSession.d.ts.map +1 -1
- package/dist/api/SearchDevices.d.ts.map +1 -1
- package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
- package/dist/api/device/DeviceUnlock.d.ts +2 -2
- package/dist/api/device/DeviceUnlock.d.ts.map +1 -1
- package/dist/api/device/DeviceUpdateBootloader.d.ts +1 -5
- package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +0 -9
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/firmware/getBinary.d.ts +0 -7
- package/dist/api/firmware/getBinary.d.ts.map +1 -1
- package/dist/api/firmware/updateBootloader.d.ts +0 -2
- package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
- package/dist/api/firmware/uploadFirmware.d.ts +1 -9
- package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
- package/dist/api/helpers/protocolV2FileWrite.d.ts +2 -0
- package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/kaspa/KaspaSignTransaction.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts +31 -0
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -0
- package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -1
- package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
- package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
- package/dist/core/deviceEventRegistration.d.ts +2 -0
- package/dist/core/deviceEventRegistration.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/uiPromiseRegistry.d.ts +4 -0
- package/dist/core/uiPromiseRegistry.d.ts.map +1 -0
- package/dist/data-manager/DataManager.d.ts +4 -2
- 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 +29 -6
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts +4 -4
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/device/DevicePool.d.ts.map +1 -1
- package/dist/events/device.d.ts +4 -0
- package/dist/events/device.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/events/ui-promise.d.ts +4 -2
- package/dist/events/ui-promise.d.ts.map +1 -1
- package/dist/events/ui-request.d.ts +31 -2
- package/dist/events/ui-request.d.ts.map +1 -1
- package/dist/events/ui-response.d.ts +10 -2
- package/dist/events/ui-response.d.ts.map +1 -1
- package/dist/index.d.ts +214 -231
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2616 -3527
- package/dist/inject.d.ts +6 -1
- package/dist/inject.d.ts.map +1 -1
- package/dist/lowLevelInject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/features.d.ts +0 -4
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/resources.d.ts +30 -0
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/uiInteraction.d.ts +7 -4
- package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockPolicy.d.ts +1 -3
- package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +22 -0
- package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/walletSession.d.ts +6 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/topLevelInject.d.ts.map +1 -1
- package/dist/types/api/checkAllFirmwareRelease.d.ts +2 -4
- package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
- package/dist/types/api/detectDeviceConnectProtocol.d.ts +4 -0
- package/dist/types/api/detectDeviceConnectProtocol.d.ts.map +1 -0
- package/dist/types/api/deviceUnlock.d.ts +5 -1
- package/dist/types/api/deviceUnlock.d.ts.map +1 -1
- package/dist/types/api/deviceUpdateBootloader.d.ts +0 -6
- package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
- package/dist/types/api/export.d.ts +2 -4
- package/dist/types/api/export.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdate.d.ts +2 -70
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +7 -10
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +3 -0
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- 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 +22 -13
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesCompat.d.ts +0 -1
- package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts +4 -0
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/homescreen.d.ts +4 -0
- package/dist/utils/homescreen.d.ts.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/dist/utils/pro2Nft.d.ts +31 -0
- package/dist/utils/pro2Nft.d.ts.map +1 -0
- package/dist/utils/pro2Wallpaper.d.ts +10 -0
- package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
- package/dist/utils/uiProgressThrottle.d.ts +3 -0
- package/dist/utils/uiProgressThrottle.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/api/BaseMethod.ts +8 -10
- package/src/api/CheckAllFirmwareRelease.ts +54 -57
- package/src/api/DetectDeviceConnectProtocol.ts +18 -0
- package/src/api/FirmwareUpdate.ts +7 -15
- package/src/api/FirmwareUpdateV2.ts +126 -316
- package/src/api/FirmwareUpdateV3.ts +63 -265
- package/src/api/FirmwareUpdateV4.ts +569 -965
- package/src/api/GetPassphraseState.ts +7 -1
- package/src/api/OpenWalletSession.ts +70 -17
- package/src/api/SearchDevices.ts +3 -1
- package/src/api/allnetwork/AllNetworkGetAddress.ts +21 -21
- package/src/api/allnetwork/AllNetworkGetAddressBase.ts +34 -11
- package/src/api/device/DeviceUnlock.ts +8 -3
- package/src/api/device/DeviceUpdateBootloader.ts +6 -122
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +16 -83
- package/src/api/firmware/updateBootloader.ts +4 -19
- package/src/api/firmware/uploadFirmware.ts +39 -144
- package/src/api/helpers/protocolV2FileWrite.ts +11 -4
- package/src/api/index.ts +2 -0
- package/src/api/kaspa/KaspaSignTransaction.ts +1 -12
- package/src/api/protocol-v2/DeviceUploadNft.ts +189 -0
- package/src/api/protocol-v2/ProtocolInfoRequest.ts +3 -1
- package/src/api/protocol-v2/helpers.ts +1 -0
- package/src/core/deviceEventRegistration.ts +4 -0
- package/src/core/index.ts +167 -83
- package/src/core/uiPromiseRegistry.ts +41 -0
- package/src/data/messages/messages-protocol-v2.json +25 -0
- package/src/data-manager/DataManager.ts +71 -39
- package/src/data-manager/connectSettings.ts +0 -11
- package/src/device/Device.ts +259 -43
- package/src/device/DeviceCommands.ts +7 -1
- package/src/device/DevicePool.ts +7 -2
- package/src/events/device.ts +4 -0
- package/src/events/logBlockEvent.ts +1 -0
- package/src/events/ui-promise.ts +4 -2
- package/src/events/ui-request.ts +36 -2
- package/src/events/ui-response.ts +12 -2
- package/src/index.ts +0 -5
- package/src/inject.ts +60 -24
- package/src/lowLevelInject.ts +7 -8
- package/src/protocols/protocol-v2/features.ts +10 -7
- package/src/protocols/protocol-v2/resources.ts +359 -0
- package/src/protocols/protocol-v2/uiInteraction.ts +46 -6
- package/src/protocols/protocol-v2/unlockPolicy.ts +1 -105
- package/src/protocols/protocol-v2/unlockPolicyRunner.ts +118 -0
- package/src/protocols/protocol-v2/walletSession.ts +214 -36
- package/src/topLevelInject.ts +7 -8
- package/src/types/api/checkAllFirmwareRelease.ts +2 -4
- package/src/types/api/detectDeviceConnectProtocol.ts +7 -0
- package/src/types/api/deviceUnlock.ts +12 -1
- package/src/types/api/deviceUpdateBootloader.ts +0 -6
- package/src/types/api/export.ts +1 -18
- package/src/types/api/firmwareUpdate.ts +3 -76
- package/src/types/api/index.ts +13 -14
- package/src/types/api/protocolV2.ts +13 -0
- package/src/types/device.ts +3 -0
- package/src/types/params.ts +7 -1
- package/src/types/settings.ts +42 -13
- package/src/utils/deviceFeaturesCompat.ts +0 -6
- package/src/utils/deviceFeaturesUtils.ts +8 -0
- package/src/utils/homescreen.ts +32 -6
- package/src/utils/index.ts +6 -1
- package/src/utils/pro2Nft.ts +142 -0
- package/src/utils/pro2Wallpaper.ts +35 -8
- package/src/utils/uiProgressThrottle.ts +63 -0
- package/__tests__/device-initialize-timeout.test.ts +0 -56
- package/__tests__/firmware-update/check-all-firmware-release.test.ts +0 -175
- package/__tests__/firmware-update/firmware-artifact-source.test.ts +0 -119
- package/__tests__/firmware-update/firmware-host-binding.test.ts +0 -70
- package/__tests__/firmware-update/firmware-preparation-error.test.ts +0 -27
- package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +0 -200
- package/__tests__/firmware-update/firmware-update-capabilities.test.ts +0 -13
- package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +0 -294
- package/__tests__/firmware-update/firmware-update-plan.test.ts +0 -593
- package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +0 -213
- package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +0 -58
- package/__tests__/firmware-update/firmware-upload-source.test.ts +0 -70
- package/dist/api/firmware/FirmwareArtifactSource.d.ts +0 -25
- package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +0 -1
- package/dist/api/firmware/FirmwareHostBinding.d.ts +0 -6
- package/dist/api/firmware/FirmwareHostBinding.d.ts.map +0 -1
- package/dist/api/firmware/FirmwarePreparationError.d.ts +0 -3
- package/dist/api/firmware/FirmwarePreparationError.d.ts.map +0 -1
- package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +0 -3
- package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +0 -1
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts +0 -25
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +0 -1
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +0 -32
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +0 -1
- package/dist/api/firmware/progressThrottle.d.ts +0 -3
- package/dist/api/firmware/progressThrottle.d.ts.map +0 -1
- package/dist/protocols/protocol-v2/lockedError.d.ts +0 -2
- package/dist/protocols/protocol-v2/lockedError.d.ts.map +0 -1
- package/dist/protocols/protocol-v2/unlockRetry.d.ts +0 -10
- package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +0 -1
- package/dist/types/api/firmwareUpdateCapabilities.d.ts +0 -9
- package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +0 -1
- package/dist/types/api/firmwareUpdatePlan.d.ts +0 -28
- package/dist/types/api/firmwareUpdatePlan.d.ts.map +0 -1
- package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +0 -42
- package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +0 -1
- package/src/api/firmware/FirmwareArtifactSource.ts +0 -278
- package/src/api/firmware/FirmwareHostBinding.ts +0 -100
- package/src/api/firmware/FirmwarePreparationError.ts +0 -12
- package/src/api/firmware/FirmwareUpdateCapabilities.ts +0 -9
- package/src/api/firmware/FirmwareUpdatePlan.ts +0 -772
- package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +0 -415
- package/src/api/firmware/progressThrottle.ts +0 -44
- package/src/protocols/protocol-v2/lockedError.ts +0 -10
- package/src/protocols/protocol-v2/unlockRetry.ts +0 -109
- package/src/types/api/firmwareUpdateCapabilities.ts +0 -9
- package/src/types/api/firmwareUpdatePlan.ts +0 -38
- package/src/types/api/firmwareUpdatePreparedPlan.ts +0 -53
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
1
2
|
import { TRANSPORT_EVENT } from '@onekeyfe/hd-transport';
|
|
2
3
|
|
|
3
4
|
import { initConnector, initCore } from '../src/core';
|
|
@@ -95,7 +96,7 @@ describe('public device lifecycle events', () => {
|
|
|
95
96
|
expect(messages.filter(message => message.type === DEVICE.DISCONNECT)).toHaveLength(1);
|
|
96
97
|
});
|
|
97
98
|
|
|
98
|
-
test('uses a cached protocol as a
|
|
99
|
+
test('uses a verified cached protocol as a strict expected protocol', async () => {
|
|
99
100
|
jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native' as never);
|
|
100
101
|
const device = Device.fromDescriptor({
|
|
101
102
|
id: 'ble-id',
|
|
@@ -103,16 +104,117 @@ describe('public device lifecycle events', () => {
|
|
|
103
104
|
commType: 'ble',
|
|
104
105
|
protocolType: 'V1',
|
|
105
106
|
} as never);
|
|
106
|
-
const acquire = jest.fn().mockResolvedValue({ uuid: 'ble-id', protocolType: '
|
|
107
|
+
const acquire = jest.fn().mockResolvedValue({ uuid: 'ble-id', protocolType: 'V1' });
|
|
107
108
|
device.deviceConnector = { acquire } as never;
|
|
108
109
|
|
|
109
110
|
await device.acquire();
|
|
110
111
|
|
|
111
|
-
expect(acquire).toHaveBeenCalledWith('ble-id', undefined, true,
|
|
112
|
+
expect(acquire).toHaveBeenCalledWith('ble-id', undefined, true, 'V1', undefined);
|
|
113
|
+
expect(device.getProtocol()).toBe('V1');
|
|
114
|
+
expect(device.originalDescriptor.protocolType).toBe('V1');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('bypasses a cached protocol only for explicit active detection', async () => {
|
|
118
|
+
jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native' as never);
|
|
119
|
+
const device = Device.fromDescriptor({
|
|
120
|
+
id: 'ble-id',
|
|
121
|
+
path: 'ble-id',
|
|
122
|
+
commType: 'ble',
|
|
123
|
+
protocolType: 'V1',
|
|
124
|
+
} as never);
|
|
125
|
+
const acquire = jest.fn().mockResolvedValue({ uuid: 'ble-id', protocolType: 'V2' });
|
|
126
|
+
device.deviceConnector = { acquire } as never;
|
|
127
|
+
|
|
128
|
+
await device.acquire(undefined, { forceProtocolDetection: true });
|
|
129
|
+
|
|
130
|
+
expect(acquire).toHaveBeenCalledWith('ble-id', undefined, true, undefined, undefined);
|
|
112
131
|
expect(device.getProtocol()).toBe('V2');
|
|
113
132
|
expect(device.originalDescriptor.protocolType).toBe('V2');
|
|
114
133
|
});
|
|
115
134
|
|
|
135
|
+
test('does not reuse a stale protocol when active detection returns no protocol', async () => {
|
|
136
|
+
jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native' as never);
|
|
137
|
+
const device = Device.fromDescriptor({
|
|
138
|
+
id: 'ble-id',
|
|
139
|
+
path: 'ble-id',
|
|
140
|
+
commType: 'ble',
|
|
141
|
+
protocolType: 'V1',
|
|
142
|
+
} as never);
|
|
143
|
+
const acquire = jest.fn().mockResolvedValue({ uuid: 'ble-id' });
|
|
144
|
+
const release = jest.fn().mockResolvedValue(undefined);
|
|
145
|
+
device.deviceConnector = { acquire, release } as never;
|
|
146
|
+
await expect(
|
|
147
|
+
device.acquire(undefined, {
|
|
148
|
+
forceProtocolDetection: true,
|
|
149
|
+
throwOnRunPromiseError: true,
|
|
150
|
+
})
|
|
151
|
+
).rejects.toMatchObject({ errorCode: HardwareErrorCode.RuntimeError });
|
|
152
|
+
expect(device.originalDescriptor.protocolType).toBe('V1');
|
|
153
|
+
expect(device.hasDeviceAcquire()).toBe(false);
|
|
154
|
+
expect(release).toHaveBeenCalledWith('ble-id', false);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test('actively reacquires an already connected device without initializing it', async () => {
|
|
158
|
+
jest.spyOn(DataManager, 'getSettings').mockReturnValue('webusb' as never);
|
|
159
|
+
const device = Device.fromDescriptor({
|
|
160
|
+
id: 'usb-device',
|
|
161
|
+
path: 'usb-device',
|
|
162
|
+
session: 'old-session',
|
|
163
|
+
protocolType: 'V1',
|
|
164
|
+
} as never);
|
|
165
|
+
device.mainId = 'old-session';
|
|
166
|
+
device.commands = { disposed: false } as never;
|
|
167
|
+
const release = jest.spyOn(device, 'release').mockImplementation(() => {
|
|
168
|
+
device.mainId = null;
|
|
169
|
+
device.originalDescriptor.session = null;
|
|
170
|
+
device.commands.disposed = true;
|
|
171
|
+
return Promise.resolve();
|
|
172
|
+
});
|
|
173
|
+
const acquire = jest.spyOn(device, 'acquire').mockImplementation(() => {
|
|
174
|
+
device.mainId = 'new-session';
|
|
175
|
+
device.originalDescriptor.session = 'new-session';
|
|
176
|
+
device.originalDescriptor.protocolType = 'V2';
|
|
177
|
+
device.commands.disposed = false;
|
|
178
|
+
return Promise.resolve();
|
|
179
|
+
});
|
|
180
|
+
const initialize = jest.spyOn(device, 'initialize').mockResolvedValue(undefined);
|
|
181
|
+
|
|
182
|
+
await device.run(() => Promise.resolve(undefined), {
|
|
183
|
+
forceProtocolDetection: true,
|
|
184
|
+
skipInitialize: true,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
expect(release).toHaveBeenCalled();
|
|
188
|
+
expect(acquire).toHaveBeenCalledWith(undefined, {
|
|
189
|
+
forceProtocolDetection: true,
|
|
190
|
+
});
|
|
191
|
+
expect(initialize).not.toHaveBeenCalled();
|
|
192
|
+
expect(device.getProtocol()).toBe('V2');
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test('keeps the freshly probed React Native BLE session until the call completes', async () => {
|
|
196
|
+
jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native' as never);
|
|
197
|
+
const device = Device.fromDescriptor({
|
|
198
|
+
id: 'ble-device',
|
|
199
|
+
path: 'ble-device',
|
|
200
|
+
commType: 'ble',
|
|
201
|
+
protocolType: 'V1',
|
|
202
|
+
} as never);
|
|
203
|
+
device.mainId = 'ble-device';
|
|
204
|
+
device.commands = { disposed: false } as never;
|
|
205
|
+
(device as unknown as { deviceAcquired: boolean }).deviceAcquired = true;
|
|
206
|
+
const release = jest.spyOn(device, 'release').mockResolvedValue(undefined);
|
|
207
|
+
const run = jest.fn().mockResolvedValue(undefined);
|
|
208
|
+
|
|
209
|
+
await device.run(run, {
|
|
210
|
+
forceProtocolDetection: true,
|
|
211
|
+
skipInitialize: true,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
expect(run).toHaveBeenCalledTimes(1);
|
|
215
|
+
expect(release).toHaveBeenCalledTimes(1);
|
|
216
|
+
});
|
|
217
|
+
|
|
116
218
|
test('keeps an explicit connectProtocol as a strict expected protocol', async () => {
|
|
117
219
|
jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native' as never);
|
|
118
220
|
const device = Device.fromDescriptor({
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
+
|
|
3
|
+
import DeviceUploadNft from '../src/api/protocol-v2/DeviceUploadNft';
|
|
4
|
+
|
|
5
|
+
jest.mock('../src/data/config', () => ({
|
|
6
|
+
getSDKVersion: jest.fn(() => '1.0.0'),
|
|
7
|
+
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
const createRgba = (width: number, height: number) => {
|
|
11
|
+
const data = new Uint8Array(width * height * 4);
|
|
12
|
+
for (let index = 3; index < data.length; index += 4) data[index] = 0xff;
|
|
13
|
+
return data;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const createMethod = ({
|
|
17
|
+
typedCall,
|
|
18
|
+
supportedMessages = [60805, 60808, 61500],
|
|
19
|
+
useFullBundle = false,
|
|
20
|
+
}: {
|
|
21
|
+
typedCall: jest.Mock;
|
|
22
|
+
supportedMessages?: number[];
|
|
23
|
+
useFullBundle?: boolean;
|
|
24
|
+
}) => {
|
|
25
|
+
const method = new DeviceUploadNft({
|
|
26
|
+
id: 1,
|
|
27
|
+
payload: {
|
|
28
|
+
method: 'deviceUploadNft',
|
|
29
|
+
image: { width: 540, height: 540, rgba: createRgba(540, 540) },
|
|
30
|
+
thumbnail: { width: 263, height: 263, rgba: createRgba(263, 263) },
|
|
31
|
+
title: 'CryptoPunk #3100',
|
|
32
|
+
subtitle: 'CryptoPunks',
|
|
33
|
+
timestampMs: 1_760_000_000_000,
|
|
34
|
+
chunkSize: 2048,
|
|
35
|
+
paceMs: 0,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
(method as any).device = {
|
|
39
|
+
commands: { typedCall },
|
|
40
|
+
ensureProtocolV2RuntimeContext: jest.fn(() =>
|
|
41
|
+
Promise.resolve({
|
|
42
|
+
version: 2,
|
|
43
|
+
build_fingerprint: 'application__1.0.0__test__DEV__DEBUG',
|
|
44
|
+
supported_messages: supportedMessages,
|
|
45
|
+
})
|
|
46
|
+
),
|
|
47
|
+
getCurrentFirmwareType: jest.fn(),
|
|
48
|
+
};
|
|
49
|
+
method.postMessage = jest.fn();
|
|
50
|
+
|
|
51
|
+
if (useFullBundle) {
|
|
52
|
+
method.init();
|
|
53
|
+
} else {
|
|
54
|
+
(method as any).params = { chunkSize: 2048, paceMs: 0, timeoutMs: 15_000 };
|
|
55
|
+
(method as any).bundle = {
|
|
56
|
+
basename: 'nft-deadbeef-1760000000000',
|
|
57
|
+
image: new Uint8Array([1]),
|
|
58
|
+
thumbnail: new Uint8Array([2]),
|
|
59
|
+
metadata: new TextEncoder().encode('{"title":"NFT","subtitle":""}'),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return method;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const fileWriteSuccess = (params: { file: { offset: number; data: Uint8Array } }) => ({
|
|
67
|
+
message: {
|
|
68
|
+
processed_byte: params.file.offset + params.file.data.byteLength,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const nftFileList = (count: number, basenameAt?: number) =>
|
|
73
|
+
Array.from({ length: count }, (_, index) => {
|
|
74
|
+
const basename =
|
|
75
|
+
index === basenameAt
|
|
76
|
+
? 'nft-deadbeef-1760000000000'
|
|
77
|
+
: `nft-${index.toString(16).padStart(8, '0')}-${1760000000001 + index}`;
|
|
78
|
+
return [`${basename}.bin`, `${basename}_m.bin`, `${basename}.json`];
|
|
79
|
+
})
|
|
80
|
+
.flat()
|
|
81
|
+
.join('\n');
|
|
82
|
+
|
|
83
|
+
describe('DeviceUploadNft', () => {
|
|
84
|
+
test('defaults to maximum-size chunks without artificial pacing', () => {
|
|
85
|
+
const method = new DeviceUploadNft({
|
|
86
|
+
id: 1,
|
|
87
|
+
payload: {
|
|
88
|
+
method: 'deviceUploadNft',
|
|
89
|
+
image: { width: 540, height: 540, rgba: createRgba(540, 540) },
|
|
90
|
+
thumbnail: { width: 263, height: 263, rgba: createRgba(263, 263) },
|
|
91
|
+
title: 'CryptoPunk #3100',
|
|
92
|
+
subtitle: 'CryptoPunks',
|
|
93
|
+
timestampMs: 1_760_000_000_000,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
method.init();
|
|
98
|
+
|
|
99
|
+
expect((method as any).params).toMatchObject({
|
|
100
|
+
chunkSize: 2048,
|
|
101
|
+
paceMs: 0,
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('uploads the triplet in order without creating the firmware-owned directory', async () => {
|
|
106
|
+
const typedCall = jest.fn((request: string, _response: string, params: any) => {
|
|
107
|
+
if (request === 'FilesystemDirList') {
|
|
108
|
+
return { message: { path: 'vol1:/nft', child_files: '' } };
|
|
109
|
+
}
|
|
110
|
+
if (request === 'FilesystemFileWrite') return fileWriteSuccess(params);
|
|
111
|
+
if (request === 'NftUpdate') return { message: { message: 'NFT updated' } };
|
|
112
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
113
|
+
});
|
|
114
|
+
const method = createMethod({ typedCall, useFullBundle: true });
|
|
115
|
+
|
|
116
|
+
const result = await method.run();
|
|
117
|
+
|
|
118
|
+
const requests = typedCall.mock.calls.map(call => call[0]);
|
|
119
|
+
expect(requests[0]).toBe('FilesystemDirList');
|
|
120
|
+
expect(typedCall).toHaveBeenNthCalledWith(
|
|
121
|
+
1,
|
|
122
|
+
'FilesystemDirList',
|
|
123
|
+
'FilesystemDir',
|
|
124
|
+
{ path: 'vol1:/nft', depth: 1 },
|
|
125
|
+
{ timeoutMs: 15_000 }
|
|
126
|
+
);
|
|
127
|
+
expect(requests).not.toContain('FilesystemDirMake');
|
|
128
|
+
expect(requests.at(-1)).toBe('NftUpdate');
|
|
129
|
+
const fileWrites = typedCall.mock.calls.filter(call => call[0] === 'FilesystemFileWrite');
|
|
130
|
+
const paths = fileWrites.map(call => call[2].file.path as string);
|
|
131
|
+
const imagePath = paths[0];
|
|
132
|
+
const thumbnailStart = paths.findIndex(path => path.endsWith('_m.bin'));
|
|
133
|
+
const metadataStart = paths.findIndex(path => path.endsWith('.json'));
|
|
134
|
+
expect(imagePath).toMatch(/^vol1:\/nft\/nft-[a-f0-9]{8}-1760000000000\.bin$/);
|
|
135
|
+
expect(thumbnailStart).toBeGreaterThan(0);
|
|
136
|
+
expect(metadataStart).toBeGreaterThan(thumbnailStart);
|
|
137
|
+
expect(new Set(paths.slice(0, thumbnailStart))).toEqual(new Set([imagePath]));
|
|
138
|
+
expect(new Set(paths.slice(thumbnailStart, metadataStart))).toEqual(
|
|
139
|
+
new Set([result.thumbnailPath])
|
|
140
|
+
);
|
|
141
|
+
expect(typedCall).toHaveBeenLastCalledWith(
|
|
142
|
+
'NftUpdate',
|
|
143
|
+
'Success',
|
|
144
|
+
{ file_name_no_ext: result.basename },
|
|
145
|
+
{ timeoutMs: 15_000 }
|
|
146
|
+
);
|
|
147
|
+
expect(result).toMatchObject({
|
|
148
|
+
nftUpdated: true,
|
|
149
|
+
message: 'NFT updated',
|
|
150
|
+
totalSize: 583_212 + 138_876 + 53,
|
|
151
|
+
});
|
|
152
|
+
expect(method.postMessage).toHaveBeenLastCalledWith({
|
|
153
|
+
event: 'UI_EVENT',
|
|
154
|
+
type: 'ui-device_progress',
|
|
155
|
+
payload: expect.objectContaining({
|
|
156
|
+
progress: 100,
|
|
157
|
+
transferredBytes: result.totalSize,
|
|
158
|
+
totalBytes: result.totalSize,
|
|
159
|
+
}),
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('retries one timed-out NftUpdate with the same basename', async () => {
|
|
164
|
+
const timeout = Object.assign(new Error('Protocol V2 response timeout'), {
|
|
165
|
+
code: 'response-timeout',
|
|
166
|
+
});
|
|
167
|
+
let updateCalls = 0;
|
|
168
|
+
const typedCall = jest.fn((request: string, _response: string, params: any) => {
|
|
169
|
+
if (request === 'FilesystemDirList') {
|
|
170
|
+
return { message: { path: 'vol1:/nft', child_files: '' } };
|
|
171
|
+
}
|
|
172
|
+
if (request === 'FilesystemFileWrite') return fileWriteSuccess(params);
|
|
173
|
+
if (request === 'NftUpdate') {
|
|
174
|
+
updateCalls += 1;
|
|
175
|
+
if (updateCalls === 1) throw timeout;
|
|
176
|
+
return { message: { message: 'NFT updated' } };
|
|
177
|
+
}
|
|
178
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
179
|
+
});
|
|
180
|
+
const method = createMethod({ typedCall });
|
|
181
|
+
|
|
182
|
+
await expect(method.run()).resolves.toMatchObject({ nftUpdated: true });
|
|
183
|
+
|
|
184
|
+
const updateRequests = typedCall.mock.calls.filter(call => call[0] === 'NftUpdate');
|
|
185
|
+
expect(updateRequests).toHaveLength(2);
|
|
186
|
+
expect(updateRequests[1]).toEqual(updateRequests[0]);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test('does not retry a non-timeout NftUpdate failure', async () => {
|
|
190
|
+
const typedCall = jest.fn((request: string, _response: string, params: any) => {
|
|
191
|
+
if (request === 'FilesystemDirList') {
|
|
192
|
+
return { message: { path: 'vol1:/nft', child_files: '' } };
|
|
193
|
+
}
|
|
194
|
+
if (request === 'FilesystemFileWrite') return fileWriteSuccess(params);
|
|
195
|
+
if (request === 'NftUpdate') throw new Error('Invalid NFT metadata');
|
|
196
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
197
|
+
});
|
|
198
|
+
const method = createMethod({ typedCall });
|
|
199
|
+
|
|
200
|
+
await expect(method.run()).rejects.toThrow('Invalid NFT metadata');
|
|
201
|
+
expect(typedCall.mock.calls.filter(call => call[0] === 'NftUpdate')).toHaveLength(1);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test('propagates a second NftUpdate timeout after one retry', async () => {
|
|
205
|
+
const timeout = Object.assign(new Error('Protocol V2 response timeout'), {
|
|
206
|
+
code: 'response-timeout',
|
|
207
|
+
});
|
|
208
|
+
const typedCall = jest.fn((request: string, _response: string, params: any) => {
|
|
209
|
+
if (request === 'FilesystemDirList') {
|
|
210
|
+
return { message: { path: 'vol1:/nft', child_files: '' } };
|
|
211
|
+
}
|
|
212
|
+
if (request === 'FilesystemFileWrite') return fileWriteSuccess(params);
|
|
213
|
+
if (request === 'NftUpdate') throw timeout;
|
|
214
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
215
|
+
});
|
|
216
|
+
const method = createMethod({ typedCall });
|
|
217
|
+
|
|
218
|
+
await expect(method.run()).rejects.toBe(timeout);
|
|
219
|
+
expect(typedCall.mock.calls.filter(call => call[0] === 'NftUpdate')).toHaveLength(2);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test('does not publish when a file write fails', async () => {
|
|
223
|
+
const typedCall = jest.fn((request: string) => {
|
|
224
|
+
if (request === 'FilesystemDirList') {
|
|
225
|
+
return { message: { path: 'vol1:/nft', child_files: '' } };
|
|
226
|
+
}
|
|
227
|
+
if (request === 'FilesystemFileWrite') throw new Error('Filesystem full');
|
|
228
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
229
|
+
});
|
|
230
|
+
const method = createMethod({ typedCall });
|
|
231
|
+
|
|
232
|
+
await expect(method.run()).rejects.toThrow('Filesystem full');
|
|
233
|
+
expect(typedCall.mock.calls.some(call => call[0] === 'NftUpdate')).toBe(false);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test('rejects unsupported firmware before writing files', async () => {
|
|
237
|
+
const typedCall = jest.fn();
|
|
238
|
+
const method = createMethod({ typedCall, supportedMessages: [60805] });
|
|
239
|
+
|
|
240
|
+
await expect(method.run()).rejects.toBeDefined();
|
|
241
|
+
expect(typedCall).not.toHaveBeenCalled();
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test('rejects a new NFT before writing when ten complete NFT bundles exist', async () => {
|
|
245
|
+
const typedCall = jest.fn((request: string) => {
|
|
246
|
+
if (request === 'FilesystemDirList') {
|
|
247
|
+
return { message: { path: 'vol1:/nft', child_files: nftFileList(10) } };
|
|
248
|
+
}
|
|
249
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
250
|
+
});
|
|
251
|
+
const method = createMethod({ typedCall });
|
|
252
|
+
|
|
253
|
+
await expect(method.run()).rejects.toMatchObject({
|
|
254
|
+
errorCode: HardwareErrorCode.NftStorageLimitReached,
|
|
255
|
+
params: { count: 10, limit: 10 },
|
|
256
|
+
});
|
|
257
|
+
expect(typedCall.mock.calls.map(call => call[0])).toEqual(['FilesystemDirList']);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test('allows an idempotent retry for a basename already counted at the limit', async () => {
|
|
261
|
+
const typedCall = jest.fn((request: string, _response: string, params: any) => {
|
|
262
|
+
if (request === 'FilesystemDirList') {
|
|
263
|
+
return { message: { path: 'vol1:/nft', child_files: nftFileList(10, 0) } };
|
|
264
|
+
}
|
|
265
|
+
if (request === 'FilesystemFileWrite') return fileWriteSuccess(params);
|
|
266
|
+
if (request === 'NftUpdate') return { message: {} };
|
|
267
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
268
|
+
});
|
|
269
|
+
const method = createMethod({ typedCall });
|
|
270
|
+
|
|
271
|
+
await expect(method.run()).resolves.toMatchObject({ nftUpdated: true });
|
|
272
|
+
expect(typedCall.mock.calls.some(call => call[0] === 'FilesystemFileWrite')).toBe(true);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test('does not count unrelated or incomplete files as stored NFTs', async () => {
|
|
276
|
+
const typedCall = jest.fn((request: string, _response: string, params: any) => {
|
|
277
|
+
if (request === 'FilesystemDirList') {
|
|
278
|
+
return {
|
|
279
|
+
message: {
|
|
280
|
+
path: 'vol1:/nft',
|
|
281
|
+
child_files: `${nftFileList(9)}\nnft-ffffffff-1760000009999.json\nnotes.txt`,
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
if (request === 'FilesystemFileWrite') return fileWriteSuccess(params);
|
|
286
|
+
if (request === 'NftUpdate') return { message: {} };
|
|
287
|
+
throw new Error(`Unexpected request: ${request}`);
|
|
288
|
+
});
|
|
289
|
+
const method = createMethod({ typedCall });
|
|
290
|
+
|
|
291
|
+
await expect(method.run()).resolves.toMatchObject({ nftUpdated: true });
|
|
292
|
+
});
|
|
293
|
+
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { EDeviceType, EFirmwareType,
|
|
1
|
+
import { EDeviceType, EFirmwareType, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
2
|
|
|
3
3
|
import FirmwareUpdateV2 from '../../src/api/FirmwareUpdateV2';
|
|
4
4
|
import { getBinary } from '../../src/api/firmware/getBinary';
|
|
5
|
-
import {
|
|
5
|
+
import { uploadFirmware } from '../../src/api/firmware/uploadFirmware';
|
|
6
6
|
import * as utils from '../../src/utils';
|
|
7
7
|
|
|
8
8
|
jest.mock('../../src/data/config', () => ({
|
|
@@ -18,8 +18,7 @@ jest.mock('../../src/api/firmware/getBinary', () => ({
|
|
|
18
18
|
|
|
19
19
|
jest.mock('../../src/api/firmware/uploadFirmware', () => ({
|
|
20
20
|
updateResources: jest.fn(),
|
|
21
|
-
|
|
22
|
-
uploadFirmwareFromSource: jest.fn(),
|
|
21
|
+
uploadFirmware: jest.fn(),
|
|
23
22
|
}));
|
|
24
23
|
|
|
25
24
|
jest.mock('../../src/device/DevicePool', () => ({
|
|
@@ -30,9 +29,7 @@ jest.mock('../../src/device/DevicePool', () => ({
|
|
|
30
29
|
}));
|
|
31
30
|
|
|
32
31
|
const mockGetBinary = getBinary as jest.MockedFunction<typeof getBinary>;
|
|
33
|
-
const
|
|
34
|
-
typeof uploadFirmwareFromSource
|
|
35
|
-
>;
|
|
32
|
+
const mockUploadFirmware = uploadFirmware as jest.MockedFunction<typeof uploadFirmware>;
|
|
36
33
|
|
|
37
34
|
type DeviceType = 'CLASSIC1S' | 'PURE';
|
|
38
35
|
|
|
@@ -178,26 +175,7 @@ describe('FirmwareUpdateV2 download-before-reboot safety', () => {
|
|
|
178
175
|
expect(mockGetBinary).toHaveBeenCalledTimes(1);
|
|
179
176
|
expectNoFirmwareMutationCalls(typedCall);
|
|
180
177
|
expect(acquire).not.toHaveBeenCalled();
|
|
181
|
-
expect(
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it('preserves structured preparation errors before any firmware mutation', async () => {
|
|
185
|
-
const preparationError = ERRORS.TypedError(
|
|
186
|
-
HardwareErrorCode.RuntimeError,
|
|
187
|
-
'Firmware must be prepared by the external firmware host',
|
|
188
|
-
{
|
|
189
|
-
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
190
|
-
artifactName: 'firmware',
|
|
191
|
-
}
|
|
192
|
-
);
|
|
193
|
-
mockGetBinary.mockRejectedValue(preparationError);
|
|
194
|
-
const { method, typedCall, acquire } = createMethod();
|
|
195
|
-
|
|
196
|
-
await expect(method.run()).rejects.toBe(preparationError);
|
|
197
|
-
|
|
198
|
-
expectNoFirmwareMutationCalls(typedCall);
|
|
199
|
-
expect(acquire).not.toHaveBeenCalled();
|
|
200
|
-
expect(mockUploadFirmwareFromSource).not.toHaveBeenCalled();
|
|
178
|
+
expect(mockUploadFirmware).not.toHaveBeenCalled();
|
|
201
179
|
});
|
|
202
180
|
|
|
203
181
|
it.each([
|
|
@@ -238,11 +216,9 @@ describe('FirmwareUpdateV2 download-before-reboot safety', () => {
|
|
|
238
216
|
binary: firmwareBinary,
|
|
239
217
|
} as Awaited<ReturnType<typeof getBinary>>);
|
|
240
218
|
});
|
|
241
|
-
|
|
219
|
+
mockUploadFirmware.mockImplementation(() => {
|
|
242
220
|
callOrder.push('upload');
|
|
243
|
-
return Promise.resolve(
|
|
244
|
-
expectedResponse as Awaited<ReturnType<typeof uploadFirmwareFromSource>>
|
|
245
|
-
);
|
|
221
|
+
return Promise.resolve(expectedResponse as Awaited<ReturnType<typeof uploadFirmware>>);
|
|
246
222
|
});
|
|
247
223
|
const { method, typedCall } = createMethod({
|
|
248
224
|
updateType,
|
|
@@ -272,15 +248,15 @@ describe('FirmwareUpdateV2 download-before-reboot safety', () => {
|
|
|
272
248
|
},
|
|
273
249
|
})
|
|
274
250
|
);
|
|
275
|
-
expect(
|
|
251
|
+
expect(mockUploadFirmware).toHaveBeenCalledWith(
|
|
276
252
|
updateType,
|
|
277
253
|
expect.any(Function),
|
|
278
254
|
expect.any(Function),
|
|
279
255
|
method.device,
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
256
|
+
{
|
|
257
|
+
payload: firmwareBinary,
|
|
258
|
+
rebootOnSuccess: true,
|
|
259
|
+
},
|
|
284
260
|
isUpdateBootloader
|
|
285
261
|
);
|
|
286
262
|
}
|
|
@@ -295,11 +271,11 @@ describe('FirmwareUpdateV2 download-before-reboot safety', () => {
|
|
|
295
271
|
binary: firmwareBinary,
|
|
296
272
|
} as Awaited<ReturnType<typeof getBinary>>);
|
|
297
273
|
});
|
|
298
|
-
|
|
274
|
+
mockUploadFirmware.mockImplementation(() => {
|
|
299
275
|
callOrder.push('upload');
|
|
300
276
|
return Promise.resolve({
|
|
301
277
|
success: true,
|
|
302
|
-
} as Awaited<ReturnType<typeof
|
|
278
|
+
} as Awaited<ReturnType<typeof uploadFirmware>>);
|
|
303
279
|
});
|
|
304
280
|
const { method, typedCall, acquire } = createMethod({
|
|
305
281
|
bootloaderMode: true,
|
|
@@ -324,9 +300,9 @@ describe('FirmwareUpdateV2 download-before-reboot safety', () => {
|
|
|
324
300
|
resolveDownload = resolve;
|
|
325
301
|
})
|
|
326
302
|
);
|
|
327
|
-
|
|
303
|
+
mockUploadFirmware.mockResolvedValue({
|
|
328
304
|
success: true,
|
|
329
|
-
} as Awaited<ReturnType<typeof
|
|
305
|
+
} as Awaited<ReturnType<typeof uploadFirmware>>);
|
|
330
306
|
const { method, typedCall } = createMethod();
|
|
331
307
|
|
|
332
308
|
const runPromise = method.run();
|
|
@@ -357,7 +333,7 @@ describe('FirmwareUpdateV2 download-before-reboot safety', () => {
|
|
|
357
333
|
|
|
358
334
|
expectNoFirmwareMutationCalls(typedCall);
|
|
359
335
|
expect(acquire).not.toHaveBeenCalled();
|
|
360
|
-
expect(
|
|
336
|
+
expect(mockUploadFirmware).not.toHaveBeenCalled();
|
|
361
337
|
});
|
|
362
338
|
|
|
363
339
|
it.each([
|
|
@@ -366,23 +342,23 @@ describe('FirmwareUpdateV2 download-before-reboot safety', () => {
|
|
|
366
342
|
])(
|
|
367
343
|
'keeps the non-empty %s binary overload and never downloads it again',
|
|
368
344
|
async (_kind, binary) => {
|
|
369
|
-
|
|
345
|
+
mockUploadFirmware.mockResolvedValue({
|
|
370
346
|
success: true,
|
|
371
|
-
} as Awaited<ReturnType<typeof
|
|
347
|
+
} as Awaited<ReturnType<typeof uploadFirmware>>);
|
|
372
348
|
const { method } = createMethod({ binary });
|
|
373
349
|
|
|
374
350
|
await method.run();
|
|
375
351
|
|
|
376
352
|
expect(mockGetBinary).not.toHaveBeenCalled();
|
|
377
|
-
expect(
|
|
353
|
+
expect(mockUploadFirmware).toHaveBeenCalledWith(
|
|
378
354
|
'firmware',
|
|
379
355
|
expect.any(Function),
|
|
380
356
|
expect.any(Function),
|
|
381
357
|
method.device,
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
358
|
+
{
|
|
359
|
+
payload: binary,
|
|
360
|
+
rebootOnSuccess: true,
|
|
361
|
+
},
|
|
386
362
|
undefined
|
|
387
363
|
);
|
|
388
364
|
}
|
|
@@ -393,21 +369,17 @@ describe('FirmwareUpdateV2 download-before-reboot safety', () => {
|
|
|
393
369
|
['DataView', new DataView(new Uint8Array([9, 3, 4, 9]).buffer, 1, 2), [3, 4]],
|
|
394
370
|
['custom Buffer', createCustomBuffer(4), [1, 2, 3, 4]],
|
|
395
371
|
])('normalizes a non-empty %s before rebooting', async (_kind, binary, expectedBytes) => {
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
uploadedBytes = Array.from(new Uint8Array(await source.readAt(0, source.size)));
|
|
400
|
-
return {
|
|
401
|
-
success: true,
|
|
402
|
-
} as Awaited<ReturnType<typeof uploadFirmwareFromSource>>;
|
|
403
|
-
}
|
|
404
|
-
);
|
|
372
|
+
mockUploadFirmware.mockResolvedValue({
|
|
373
|
+
success: true,
|
|
374
|
+
} as Awaited<ReturnType<typeof uploadFirmware>>);
|
|
405
375
|
const { method } = createMethod({ binary });
|
|
406
376
|
|
|
407
377
|
await method.run();
|
|
408
378
|
|
|
409
379
|
expect(mockGetBinary).not.toHaveBeenCalled();
|
|
410
|
-
|
|
380
|
+
const normalizedBinary = mockUploadFirmware.mock.calls[0][4].payload;
|
|
381
|
+
expect(normalizedBinary).toBeInstanceOf(ArrayBuffer);
|
|
382
|
+
expect(Array.from(new Uint8Array(normalizedBinary))).toEqual(expectedBytes);
|
|
411
383
|
});
|
|
412
384
|
|
|
413
385
|
it.each([
|
|
@@ -425,6 +397,6 @@ describe('FirmwareUpdateV2 download-before-reboot safety', () => {
|
|
|
425
397
|
expect(mockGetBinary).not.toHaveBeenCalled();
|
|
426
398
|
expectNoFirmwareMutationCalls(typedCall);
|
|
427
399
|
expect(acquire).not.toHaveBeenCalled();
|
|
428
|
-
expect(
|
|
400
|
+
expect(mockUploadFirmware).not.toHaveBeenCalled();
|
|
429
401
|
});
|
|
430
402
|
});
|