@onekeyfe/hd-core 1.2.0-alpha.21 → 1.2.0-alpha.23
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__/DeviceCommands.test.ts +85 -0
- package/__tests__/connectSettings.test.ts +47 -5
- package/__tests__/device-identity.test.ts +24 -2
- package/__tests__/device-settings.test.ts +2 -2
- package/__tests__/device-wallet-session-store.test.ts +152 -7
- package/__tests__/firmware-update/check-all-firmware-release.test.ts +86 -0
- package/__tests__/firmware-update/firmware-artifact-source.test.ts +119 -0
- package/__tests__/firmware-update/firmware-host-binding.test.ts +70 -0
- package/__tests__/firmware-update/firmware-update-capabilities.test.ts +13 -0
- package/__tests__/firmware-update/firmware-update-plan.test.ts +326 -0
- package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +213 -0
- package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +39 -30
- package/__tests__/firmware-update/firmware-upload-source.test.ts +70 -0
- package/__tests__/logBlockEvent.test.ts +17 -0
- package/__tests__/open-wallet-session.test.ts +311 -12
- package/__tests__/pro2Wallpaper.test.ts +7 -18
- package/__tests__/protocol-v2-unlock-policy.test.ts +45 -0
- package/__tests__/protocol-v2.test.ts +685 -177
- package/__tests__/public-device-state-api.test.ts +69 -62
- package/__tests__/public-pro2-api-boundary.test.ts +8 -34
- package/dist/api/BaseMethod.d.ts +2 -1
- package/dist/api/BaseMethod.d.ts.map +1 -1
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/ClearSessionCache.d.ts.map +1 -1
- package/dist/api/FirmwareUpdate.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV2.d.ts +11 -0
- package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV3.d.ts +7 -3
- package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +29 -10
- 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/conflux/ConfluxGetAddress.d.ts +1 -0
- package/dist/api/conflux/ConfluxGetAddress.d.ts.map +1 -1
- package/dist/api/conflux/ConfluxSignMessage.d.ts +1 -0
- package/dist/api/conflux/ConfluxSignMessage.d.ts.map +1 -1
- package/dist/api/conflux/ConfluxSignMessageCIP23.d.ts +1 -0
- package/dist/api/conflux/ConfluxSignMessageCIP23.d.ts.map +1 -1
- package/dist/api/conflux/ConfluxSignTransaction.d.ts +1 -0
- package/dist/api/conflux/ConfluxSignTransaction.d.ts.map +1 -1
- package/dist/api/device/DeviceUnlock.d.ts +1 -1
- package/dist/api/device/DeviceUnlock.d.ts.map +1 -1
- package/dist/api/device/DeviceUpdateBootloader.d.ts +5 -1
- package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
- package/dist/api/device/DeviceWipe.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareArtifactSource.d.ts +25 -0
- package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareHostBinding.d.ts +6 -0
- package/dist/api/firmware/FirmwareHostBinding.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +7 -0
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +3 -0
- package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts +23 -0
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +30 -0
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -0
- package/dist/api/firmware/getBinary.d.ts +7 -0
- package/dist/api/firmware/getBinary.d.ts.map +1 -1
- package/dist/api/firmware/updateBootloader.d.ts +2 -0
- package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
- package/dist/api/firmware/uploadFirmware.d.ts +9 -1
- package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
- package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
- package/dist/api/index.d.ts +0 -17
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/nexa/NexaSignTransaction.d.ts +1 -0
- package/dist/api/nexa/NexaSignTransaction.d.ts.map +1 -1
- package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/data-manager/DataManager.d.ts +1 -0
- 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 +3 -2
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/device/DeviceStateMapper.d.ts.map +1 -1
- package/dist/device/DeviceStateProjector.d.ts.map +1 -1
- package/dist/events/device.d.ts +1 -0
- package/dist/events/device.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/events/ui-request.d.ts +1 -0
- package/dist/events/ui-request.d.ts.map +1 -1
- package/dist/index.d.ts +273 -230
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3060 -1676
- package/dist/inject.d.ts.map +1 -1
- package/dist/lowLevelInject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/unlockPolicy.d.ts +4 -0
- package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -0
- package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -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 +3 -0
- package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
- package/dist/types/api/deviceUnlock.d.ts +2 -2
- package/dist/types/api/deviceUnlock.d.ts.map +1 -1
- package/dist/types/api/deviceUpdateBootloader.d.ts +6 -0
- package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
- package/dist/types/api/export.d.ts +4 -1
- package/dist/types/api/export.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdate.d.ts +69 -0
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdateCapabilities.d.ts +9 -0
- package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +1 -0
- package/dist/types/api/firmwareUpdatePlan.d.ts +27 -0
- package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -0
- package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +42 -0
- package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +1 -0
- package/dist/types/api/getPassphraseState.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +10 -19
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/openWalletSession.d.ts +1 -0
- package/dist/types/api/openWalletSession.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +3 -76
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/api/sessionCache.d.ts +4 -1
- package/dist/types/api/sessionCache.d.ts.map +1 -1
- package/dist/types/settings.d.ts +13 -0
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/deviceInfoUtils.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/BaseMethod.ts +8 -3
- package/src/api/CheckAllFirmwareRelease.ts +23 -3
- package/src/api/ClearSessionCache.ts +4 -0
- package/src/api/FirmwareUpdate.ts +8 -1
- package/src/api/FirmwareUpdateV2.ts +292 -120
- package/src/api/FirmwareUpdateV3.ts +256 -55
- package/src/api/FirmwareUpdateV4.ts +821 -331
- package/src/api/GetPassphraseState.ts +11 -5
- package/src/api/OpenWalletSession.ts +74 -25
- package/src/api/UploadPortfolio.ts +1 -1
- package/src/api/conflux/ConfluxGetAddress.ts +2 -0
- package/src/api/conflux/ConfluxSignMessage.ts +2 -0
- package/src/api/conflux/ConfluxSignMessageCIP23.ts +2 -0
- package/src/api/conflux/ConfluxSignTransaction.ts +2 -0
- package/src/api/device/DeviceChangePin.ts +1 -1
- package/src/api/device/DeviceUnlock.ts +1 -2
- package/src/api/device/DeviceUpdateBootloader.ts +122 -6
- package/src/api/device/DeviceWipe.ts +3 -1
- package/src/api/firmware/FirmwareArtifactSource.ts +278 -0
- package/src/api/firmware/FirmwareHostBinding.ts +100 -0
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +50 -0
- package/src/api/firmware/FirmwareUpdateCapabilities.ts +9 -0
- package/src/api/firmware/FirmwareUpdatePlan.ts +621 -0
- package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +392 -0
- package/src/api/firmware/updateBootloader.ts +19 -4
- package/src/api/firmware/uploadFirmware.ts +140 -22
- package/src/api/helpers/protocolV2FileWrite.ts +6 -0
- package/src/api/index.ts +0 -18
- package/src/api/nexa/NexaSignTransaction.ts +2 -0
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +1 -1
- package/src/core/index.ts +1 -0
- package/src/data/messages/messages-protocol-v2.json +35 -1
- package/src/data-manager/DataManager.ts +33 -33
- package/src/data-manager/connectSettings.ts +11 -0
- package/src/device/Device.ts +80 -67
- package/src/device/DeviceCommands.ts +76 -159
- package/src/device/DeviceStateMapper.ts +18 -1
- package/src/device/DeviceStateProjector.ts +7 -1
- package/src/events/device.ts +1 -0
- package/src/events/logBlockEvent.ts +1 -14
- package/src/events/ui-request.ts +1 -0
- package/src/index.ts +5 -0
- package/src/inject.ts +22 -25
- package/src/lowLevelInject.ts +5 -1
- package/src/protocols/protocol-v2/settingsUnlockPolicy.ts +2 -3
- package/src/protocols/protocol-v2/unlockPolicy.ts +105 -0
- package/src/protocols/protocol-v2/unlockRetry.ts +1 -3
- package/src/protocols/protocol-v2/walletSession.ts +86 -9
- package/src/topLevelInject.ts +5 -1
- package/src/types/api/checkAllFirmwareRelease.ts +3 -0
- package/src/types/api/deviceUnlock.ts +2 -5
- package/src/types/api/deviceUpdateBootloader.ts +6 -0
- package/src/types/api/export.ts +18 -0
- package/src/types/api/firmwareUpdate.ts +76 -0
- package/src/types/api/firmwareUpdateCapabilities.ts +9 -0
- package/src/types/api/firmwareUpdatePlan.ts +36 -0
- package/src/types/api/firmwareUpdatePreparedPlan.ts +53 -0
- package/src/types/api/getPassphraseState.ts +4 -3
- package/src/types/api/index.ts +14 -34
- package/src/types/api/openWalletSession.ts +2 -0
- package/src/types/api/protocolV2.ts +3 -156
- package/src/types/api/sessionCache.ts +9 -4
- package/src/types/settings.ts +13 -0
- package/src/utils/deviceFeaturesUtils.ts +5 -1
- package/src/utils/deviceInfoUtils.ts +3 -16
|
@@ -41,6 +41,68 @@ describe('DeviceCommands failure mapping', () => {
|
|
|
41
41
|
expect(JSON.stringify(log.messages)).not.toContain('secret-wallet-address');
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
+
it('maps an invalid DeviceSessionGet resume to WalletSessionInvalid', async () => {
|
|
45
|
+
const commands = createCommands();
|
|
46
|
+
|
|
47
|
+
await expect(
|
|
48
|
+
commands._filterCommonTypes(
|
|
49
|
+
{
|
|
50
|
+
type: 'Failure',
|
|
51
|
+
message: {
|
|
52
|
+
code: 'Failure_ProcessError',
|
|
53
|
+
subcode: 2,
|
|
54
|
+
message: 'Invalid session',
|
|
55
|
+
},
|
|
56
|
+
} as any,
|
|
57
|
+
'DeviceSessionGet'
|
|
58
|
+
)
|
|
59
|
+
).rejects.toMatchObject({
|
|
60
|
+
errorCode: HardwareErrorCode.WalletSessionInvalid,
|
|
61
|
+
params: {
|
|
62
|
+
failureCode: 'Failure_ProcessError',
|
|
63
|
+
subcode: 2,
|
|
64
|
+
firmwareMessage: 'Invalid session',
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('maps wallet-session user cancellation to ActionCancelled', async () => {
|
|
70
|
+
const commands = createCommands();
|
|
71
|
+
|
|
72
|
+
await expect(
|
|
73
|
+
commands._filterCommonTypes(
|
|
74
|
+
{
|
|
75
|
+
type: 'Failure',
|
|
76
|
+
message: {
|
|
77
|
+
code: 'Failure_ProcessError',
|
|
78
|
+
subcode: 1,
|
|
79
|
+
message: 'Cancelled on device',
|
|
80
|
+
},
|
|
81
|
+
} as any,
|
|
82
|
+
'DeviceSessionAskPassphrase'
|
|
83
|
+
)
|
|
84
|
+
).rejects.toMatchObject({
|
|
85
|
+
errorCode: HardwareErrorCode.ActionCancelled,
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it.each([
|
|
90
|
+
[3, HardwareErrorCode.DeviceCheckUnlockTypeError],
|
|
91
|
+
[4, HardwareErrorCode.DeviceNotOpenedPassphrase],
|
|
92
|
+
])('maps wallet-session subcode %s to its canonical wallet error', async (subcode, code) => {
|
|
93
|
+
const commands = createCommands();
|
|
94
|
+
|
|
95
|
+
await expect(
|
|
96
|
+
commands._filterCommonTypes(
|
|
97
|
+
{
|
|
98
|
+
type: 'Failure',
|
|
99
|
+
message: { code: 'Failure_ProcessError', subcode, message: 'Session selection failed' },
|
|
100
|
+
} as any,
|
|
101
|
+
'DeviceSessionAskPin'
|
|
102
|
+
)
|
|
103
|
+
).rejects.toMatchObject({ errorCode: code });
|
|
104
|
+
});
|
|
105
|
+
|
|
44
106
|
it('rejects the Pro2 bootloader DeviceStatusGet unsupported response', async () => {
|
|
45
107
|
const commands = createCommands();
|
|
46
108
|
|
|
@@ -382,3 +444,26 @@ describe('DeviceCommands Protocol V2 interactive response compatibility', () =>
|
|
|
382
444
|
});
|
|
383
445
|
});
|
|
384
446
|
});
|
|
447
|
+
|
|
448
|
+
describe('DeviceCommands cancellation', () => {
|
|
449
|
+
it('stops waiting after the bounded cancellation timeout', async () => {
|
|
450
|
+
jest.useFakeTimers({ doNotFake: ['performance'] });
|
|
451
|
+
try {
|
|
452
|
+
const commands = createCommands();
|
|
453
|
+
commands.disposed = false;
|
|
454
|
+
commands.callPromise = new Promise(() => {});
|
|
455
|
+
const dispose = jest.fn().mockResolvedValue(undefined);
|
|
456
|
+
commands.dispose = dispose;
|
|
457
|
+
|
|
458
|
+
const cancellation = commands.cancel();
|
|
459
|
+
jest.advanceTimersByTime(10_000);
|
|
460
|
+
await Promise.resolve();
|
|
461
|
+
|
|
462
|
+
await expect(cancellation).resolves.toBeUndefined();
|
|
463
|
+
expect(dispose).toHaveBeenCalledWith(true);
|
|
464
|
+
expect(commands.callPromise).toBeUndefined();
|
|
465
|
+
} finally {
|
|
466
|
+
jest.useRealTimers();
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
});
|
|
@@ -1,19 +1,61 @@
|
|
|
1
|
+
import DataManager from '../src/data-manager/DataManager';
|
|
1
2
|
import { parseConnectSettings } from '../src/data-manager/connectSettings';
|
|
2
3
|
|
|
4
|
+
import type { RemoteConfigResponse } from '../src/types';
|
|
5
|
+
|
|
6
|
+
jest.mock('axios', () => ({
|
|
7
|
+
get: jest.fn(),
|
|
8
|
+
}));
|
|
9
|
+
|
|
3
10
|
jest.mock('../src/data/config', () => ({
|
|
4
11
|
getSDKVersion: jest.fn(() => '1.0.0'),
|
|
5
12
|
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
|
|
6
13
|
}));
|
|
7
14
|
|
|
15
|
+
const mockAxiosGet: jest.Mock = jest.requireMock('axios').get;
|
|
16
|
+
|
|
8
17
|
describe('parseConnectSettings', () => {
|
|
9
|
-
|
|
10
|
-
|
|
18
|
+
const preloadedConfig = {
|
|
19
|
+
bridge: {},
|
|
20
|
+
classic: { firmware: [], ble: [] },
|
|
21
|
+
classic1s: { firmware: [], ble: [] },
|
|
22
|
+
classicpure: { firmware: [], ble: [] },
|
|
23
|
+
mini: { firmware: [], ble: [] },
|
|
24
|
+
touch: { firmware: [], ble: [] },
|
|
25
|
+
pro: { firmware: [], ble: [] },
|
|
26
|
+
pro2: { firmware: [], ble: [] },
|
|
27
|
+
} as unknown as RemoteConfigResponse;
|
|
28
|
+
|
|
29
|
+
it('keeps a serializable preloaded config in parsed settings', () => {
|
|
30
|
+
const settings = parseConnectSettings({
|
|
31
|
+
firmwareManifestMode: 'sdk-managed',
|
|
32
|
+
preloadedConfig,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
expect(settings.firmwareManifestMode).toBe('sdk-managed');
|
|
36
|
+
expect(settings.preloadedConfig).toBe(preloadedConfig);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('uses the preloaded config before the SDK network client', async () => {
|
|
40
|
+
const settings = parseConnectSettings({
|
|
41
|
+
firmwareManifestMode: 'sdk-managed',
|
|
42
|
+
preloadedConfig,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
await DataManager.load(settings);
|
|
46
|
+
|
|
47
|
+
expect(mockAxiosGet).not.toHaveBeenCalled();
|
|
48
|
+
expect(DataManager.deviceMap.classic).toEqual({ firmware: [], ble: [] });
|
|
49
|
+
});
|
|
11
50
|
|
|
51
|
+
it('fails closed without a preloaded config in external-only mode', async () => {
|
|
12
52
|
const settings = parseConnectSettings({
|
|
13
|
-
|
|
14
|
-
configFetcher,
|
|
53
|
+
firmwareManifestMode: 'external-only',
|
|
15
54
|
});
|
|
16
55
|
|
|
17
|
-
expect(settings
|
|
56
|
+
await expect(DataManager.load(settings)).rejects.toThrow(
|
|
57
|
+
'External firmware config snapshot is required'
|
|
58
|
+
);
|
|
59
|
+
expect(mockAxiosGet).not.toHaveBeenCalled();
|
|
18
60
|
});
|
|
19
61
|
});
|
|
@@ -1,13 +1,35 @@
|
|
|
1
1
|
import { checkLiveDeviceId } from '../src/device/deviceIdentity';
|
|
2
|
+
import ConfluxGetAddress from '../src/api/conflux/ConfluxGetAddress';
|
|
3
|
+
import ConfluxSignMessage from '../src/api/conflux/ConfluxSignMessage';
|
|
4
|
+
import ConfluxSignMessageCIP23 from '../src/api/conflux/ConfluxSignMessageCIP23';
|
|
5
|
+
import ConfluxSignTransaction from '../src/api/conflux/ConfluxSignTransaction';
|
|
6
|
+
import NexaSignTransaction from '../src/api/nexa/NexaSignTransaction';
|
|
7
|
+
|
|
8
|
+
jest.mock('../src/data/config', () => ({
|
|
9
|
+
getSDKVersion: jest.fn(() => '1.0.0'),
|
|
10
|
+
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
|
|
11
|
+
}));
|
|
2
12
|
|
|
3
13
|
describe('live device identity', () => {
|
|
14
|
+
test.each([
|
|
15
|
+
['confluxGetAddress', ConfluxGetAddress],
|
|
16
|
+
['confluxSignMessage', ConfluxSignMessage],
|
|
17
|
+
['confluxSignMessageCIP23', ConfluxSignMessageCIP23],
|
|
18
|
+
['confluxSignTransaction', ConfluxSignTransaction],
|
|
19
|
+
['nexaSignTransaction', NexaSignTransaction],
|
|
20
|
+
])('%s requires a live device id check', (methodName, Method) => {
|
|
21
|
+
const method = new Method({ payload: { method: methodName } });
|
|
22
|
+
|
|
23
|
+
expect(method.checkDeviceId).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
|
|
4
26
|
test('refreshes Protocol V2 status before comparing the expected device id', async () => {
|
|
5
27
|
let currentDeviceId = 'OLD_DEVICE_ID';
|
|
6
28
|
const device = {
|
|
7
29
|
isProtocolV2: () => true,
|
|
8
|
-
getDeviceState: jest.fn().mockImplementation(
|
|
30
|
+
getDeviceState: jest.fn().mockImplementation(() => {
|
|
9
31
|
currentDeviceId = 'NEW_DEVICE_ID';
|
|
10
|
-
return { identity: { deviceId: currentDeviceId } };
|
|
32
|
+
return Promise.resolve({ identity: { deviceId: currentDeviceId } });
|
|
11
33
|
}),
|
|
12
34
|
checkDeviceId: jest.fn((expected: string) => currentDeviceId === expected),
|
|
13
35
|
};
|
|
@@ -37,7 +37,6 @@ function createDevice({ protocol }: { protocol: 'V1' | 'V2' }) {
|
|
|
37
37
|
|
|
38
38
|
describe('DeviceSettings protocol routing', () => {
|
|
39
39
|
it.each([
|
|
40
|
-
['label', { label: 'Shared Label' }],
|
|
41
40
|
['language', { language: 'ja-JP' }],
|
|
42
41
|
['brightness', { brightness: 80 }],
|
|
43
42
|
['haptic feedback', { hapticFeedback: true }],
|
|
@@ -56,6 +55,7 @@ describe('DeviceSettings protocol routing', () => {
|
|
|
56
55
|
});
|
|
57
56
|
|
|
58
57
|
it.each([
|
|
58
|
+
['label', { label: 'Shared Label' }],
|
|
59
59
|
['auto lock', { autoLockDelayMs: 60_000 }],
|
|
60
60
|
['auto shutdown', { autoShutdownDelayMs: 120_000 }],
|
|
61
61
|
['mixed settings', { brightness: 80, autoLockDelayMs: 60_000 }],
|
|
@@ -70,7 +70,7 @@ describe('DeviceSettings protocol routing', () => {
|
|
|
70
70
|
|
|
71
71
|
method.init();
|
|
72
72
|
|
|
73
|
-
expect(method.unlockPolicy).toBe('
|
|
73
|
+
expect(method.unlockPolicy).toBe('unlock-before-run');
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
it('uses ApplySettings and commits the confirmed patch for Protocol V1', async () => {
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
+
|
|
1
3
|
import ClearSessionCache from '../src/api/ClearSessionCache';
|
|
4
|
+
import DeviceWipe from '../src/api/device/DeviceWipe';
|
|
2
5
|
import {
|
|
3
6
|
DeviceWalletSessionStore,
|
|
4
7
|
deviceWalletSessionStore,
|
|
5
8
|
} from '../src/device/DeviceWalletSessionStore';
|
|
6
9
|
import { Device } from '../src/device/Device';
|
|
7
10
|
import { createCoreApi } from '../src/inject';
|
|
11
|
+
import TransportManager from '../src/data-manager/TransportManager';
|
|
8
12
|
|
|
9
13
|
jest.mock('../src/data/config', () => ({
|
|
10
14
|
getSDKVersion: jest.fn(() => '1.0.0'),
|
|
@@ -93,6 +97,46 @@ describe('DeviceWalletSessionStore', () => {
|
|
|
93
97
|
});
|
|
94
98
|
});
|
|
95
99
|
|
|
100
|
+
describe('Device wipe lifecycle invalidation', () => {
|
|
101
|
+
beforeEach(() => {
|
|
102
|
+
deviceWalletSessionStore.clear();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('clears the old Protocol V2 identity and wallet sessions after wipe succeeds', async () => {
|
|
106
|
+
const device = Device.fromDescriptor({
|
|
107
|
+
id: 'pro2-descriptor',
|
|
108
|
+
path: 'pro2-path',
|
|
109
|
+
protocolType: 'V2',
|
|
110
|
+
} as never);
|
|
111
|
+
device.features = {
|
|
112
|
+
protocol: 'V2',
|
|
113
|
+
deviceId: 'old-device-id',
|
|
114
|
+
unlocked: true,
|
|
115
|
+
passphraseProtection: true,
|
|
116
|
+
} as never;
|
|
117
|
+
device.passphraseState = 'hidden-a';
|
|
118
|
+
deviceWalletSessionStore.set('old-device-id', 'hidden-a', 'old-session');
|
|
119
|
+
deviceWalletSessionStore.set('pro2-path', 'hidden-a', 'temporary-session');
|
|
120
|
+
device.markPreInitialized({ passphraseState: 'hidden-a' });
|
|
121
|
+
device.commands = {
|
|
122
|
+
typedCall: jest.fn().mockResolvedValue({ message: { message: 'accepted' } }),
|
|
123
|
+
} as never;
|
|
124
|
+
|
|
125
|
+
const method = new DeviceWipe({ id: 1, payload: { method: 'deviceWipe' } });
|
|
126
|
+
method.init();
|
|
127
|
+
(method as any).device = device;
|
|
128
|
+
|
|
129
|
+
await expect(method.run()).resolves.toEqual({ message: 'accepted' });
|
|
130
|
+
|
|
131
|
+
expect(device.features).toBeUndefined();
|
|
132
|
+
expect(device.passphraseState).toBeUndefined();
|
|
133
|
+
expect(device.needReloadDevice).toBe(true);
|
|
134
|
+
expect(device.isPreInitializedValid(60_000)).toBe(false);
|
|
135
|
+
expect(deviceWalletSessionStore.get('old-device-id', 'hidden-a')).toBeUndefined();
|
|
136
|
+
expect(deviceWalletSessionStore.get('pro2-path', 'hidden-a')).toBeUndefined();
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
96
140
|
describe('ClearSessionCache', () => {
|
|
97
141
|
beforeEach(() => {
|
|
98
142
|
deviceWalletSessionStore.clear();
|
|
@@ -116,7 +160,19 @@ describe('ClearSessionCache', () => {
|
|
|
116
160
|
expect(deviceWalletSessionStore.get('device-2', 'hidden-a')).toBe('session-b');
|
|
117
161
|
});
|
|
118
162
|
|
|
119
|
-
test('
|
|
163
|
+
test('rejects passphraseState without a deviceId instead of clearing every device', () => {
|
|
164
|
+
deviceWalletSessionStore.set('device-1', 'hidden-a', 'session-a');
|
|
165
|
+
deviceWalletSessionStore.set('device-2', 'hidden-a', 'session-b');
|
|
166
|
+
const method = new ClearSessionCache({
|
|
167
|
+
payload: { method: 'clearSessionCache', passphraseState: 'hidden-a' },
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
expect(() => method.init()).toThrow('Parameter [deviceId] is required with [passphraseState].');
|
|
171
|
+
expect(deviceWalletSessionStore.get('device-1', 'hidden-a')).toBe('session-a');
|
|
172
|
+
expect(deviceWalletSessionStore.get('device-2', 'hidden-a')).toBe('session-b');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test('keeps wallet sessions internal instead of projecting them through Features', async () => {
|
|
120
176
|
const device = Device.fromDescriptor({ id: 'one', path: 'one' } as never);
|
|
121
177
|
device.features = {
|
|
122
178
|
protocol: 'V1',
|
|
@@ -133,11 +189,14 @@ describe('ClearSessionCache', () => {
|
|
|
133
189
|
} as never;
|
|
134
190
|
|
|
135
191
|
expect(device.state).not.toHaveProperty('session');
|
|
192
|
+
expect(device.getInternalState()).toBe('session-a');
|
|
136
193
|
expect(device.features).toMatchObject({
|
|
137
|
-
sessionId:
|
|
138
|
-
session_id:
|
|
139
|
-
passphraseState: 'hidden-a',
|
|
194
|
+
sessionId: null,
|
|
195
|
+
session_id: null,
|
|
140
196
|
});
|
|
197
|
+
expect(device.features).not.toHaveProperty('passphraseState');
|
|
198
|
+
expect(device.features?.raw?.protocolV1Features).not.toHaveProperty('sessionId');
|
|
199
|
+
expect(device.features?.raw?.protocolV1Features).not.toHaveProperty('session_id');
|
|
141
200
|
|
|
142
201
|
const method = new ClearSessionCache({
|
|
143
202
|
payload: {
|
|
@@ -151,10 +210,20 @@ describe('ClearSessionCache', () => {
|
|
|
151
210
|
|
|
152
211
|
expect(device.features?.sessionId).toBeNull();
|
|
153
212
|
expect(device.features?.session_id).toBeNull();
|
|
154
|
-
expect(device.state?.raw?.protocolV1Features).
|
|
213
|
+
expect(device.state?.raw?.protocolV1Features).not.toHaveProperty('sessionId');
|
|
214
|
+
expect(device.state?.raw?.protocolV1Features).not.toHaveProperty('session_id');
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test('uses only the transport path as the temporary Protocol V2 cache key', () => {
|
|
218
|
+
const device = Device.fromDescriptor({ id: 'descriptor-id' } as never);
|
|
219
|
+
device.features = {
|
|
220
|
+
protocol: 'V2',
|
|
221
|
+
passphraseState: 'hidden-a',
|
|
155
222
|
sessionId: 'session-a',
|
|
156
|
-
|
|
157
|
-
|
|
223
|
+
} as never;
|
|
224
|
+
|
|
225
|
+
expect(device.getInternalState()).toBeUndefined();
|
|
226
|
+
expect(deviceWalletSessionStore.get('descriptor-id', 'hidden-a')).toBeUndefined();
|
|
158
227
|
});
|
|
159
228
|
|
|
160
229
|
test('clears one wallet without using a device', async () => {
|
|
@@ -211,3 +280,79 @@ describe('ClearSessionCache', () => {
|
|
|
211
280
|
});
|
|
212
281
|
});
|
|
213
282
|
});
|
|
283
|
+
|
|
284
|
+
describe('Protocol V1 wallet identity initialization', () => {
|
|
285
|
+
beforeEach(() => {
|
|
286
|
+
deviceWalletSessionStore.clear();
|
|
287
|
+
jest.restoreAllMocks();
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
test('verifies the live device id before sending a cached wallet session', async () => {
|
|
291
|
+
const device = Device.fromDescriptor({ id: 'connect-b', path: 'connect-b' } as never);
|
|
292
|
+
device.features = {
|
|
293
|
+
protocol: 'V1',
|
|
294
|
+
deviceId: 'cached-device-a',
|
|
295
|
+
unlocked: true,
|
|
296
|
+
passphraseProtection: true,
|
|
297
|
+
} as never;
|
|
298
|
+
deviceWalletSessionStore.set('cached-device-a', 'hidden-a', 'session-a');
|
|
299
|
+
const typedCall = jest.fn().mockResolvedValue({
|
|
300
|
+
type: 'Features',
|
|
301
|
+
message: { device_id: 'live-device-b' },
|
|
302
|
+
});
|
|
303
|
+
device.commands = { typedCall } as never;
|
|
304
|
+
jest.spyOn(TransportManager, 'reconfigure').mockResolvedValue(undefined);
|
|
305
|
+
|
|
306
|
+
await expect(
|
|
307
|
+
device.initialize({ deviceId: 'cached-device-a', passphraseState: 'hidden-a' })
|
|
308
|
+
).rejects.toMatchObject({ errorCode: HardwareErrorCode.DeviceCheckDeviceIdError });
|
|
309
|
+
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
310
|
+
expect(typedCall).toHaveBeenCalledWith(
|
|
311
|
+
'Initialize',
|
|
312
|
+
'Features',
|
|
313
|
+
expect.not.objectContaining({ session_id: 'session-a' }),
|
|
314
|
+
expect.any(Object)
|
|
315
|
+
);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
test('resumes a cached V1 wallet only after the live device id matches', async () => {
|
|
319
|
+
const device = Device.fromDescriptor({ id: 'connect-a', path: 'connect-a' } as never);
|
|
320
|
+
device.features = {
|
|
321
|
+
protocol: 'V1',
|
|
322
|
+
deviceId: 'device-a',
|
|
323
|
+
unlocked: true,
|
|
324
|
+
passphraseProtection: true,
|
|
325
|
+
} as never;
|
|
326
|
+
deviceWalletSessionStore.set('device-a', 'hidden-a', 'session-a');
|
|
327
|
+
const typedCall = jest
|
|
328
|
+
.fn()
|
|
329
|
+
.mockResolvedValueOnce({ type: 'Features', message: { device_id: 'device-a' } })
|
|
330
|
+
.mockResolvedValueOnce({
|
|
331
|
+
type: 'Features',
|
|
332
|
+
message: { device_id: 'device-a', session_id: 'session-a' },
|
|
333
|
+
});
|
|
334
|
+
device.commands = { typedCall } as never;
|
|
335
|
+
jest.spyOn(TransportManager, 'reconfigure').mockResolvedValue(undefined);
|
|
336
|
+
|
|
337
|
+
await device.initialize({ deviceId: 'device-a', passphraseState: 'hidden-a' });
|
|
338
|
+
|
|
339
|
+
expect(typedCall).toHaveBeenCalledTimes(2);
|
|
340
|
+
expect(typedCall).toHaveBeenNthCalledWith(
|
|
341
|
+
1,
|
|
342
|
+
'Initialize',
|
|
343
|
+
'Features',
|
|
344
|
+
expect.not.objectContaining({ session_id: 'session-a' }),
|
|
345
|
+
expect.any(Object)
|
|
346
|
+
);
|
|
347
|
+
expect(typedCall).toHaveBeenNthCalledWith(
|
|
348
|
+
2,
|
|
349
|
+
'Initialize',
|
|
350
|
+
'Features',
|
|
351
|
+
expect.objectContaining({
|
|
352
|
+
session_id: 'session-a',
|
|
353
|
+
passphrase_state: 'hidden-a',
|
|
354
|
+
}),
|
|
355
|
+
expect.any(Object)
|
|
356
|
+
);
|
|
357
|
+
});
|
|
358
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
|
|
2
|
+
|
|
3
|
+
import CheckAllFirmwareRelease from '../../src/api/CheckAllFirmwareRelease';
|
|
4
|
+
import { buildFirmwareUpdatePlan } from '../../src/api/firmware/FirmwareUpdatePlan';
|
|
5
|
+
import {
|
|
6
|
+
getBleFirmwareReleaseInfo,
|
|
7
|
+
getBootloaderReleaseInfo,
|
|
8
|
+
getFirmwareReleaseInfo,
|
|
9
|
+
} from '../../src/api/firmware/releaseHelper';
|
|
10
|
+
|
|
11
|
+
jest.mock('../../src/data/config', () => ({
|
|
12
|
+
getSDKVersion: jest.fn(() => '1.0.0'),
|
|
13
|
+
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
jest.mock('../../src/api/firmware/FirmwareUpdatePlan', () => ({
|
|
17
|
+
buildFirmwareUpdatePlan: jest.fn(),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
jest.mock('../../src/api/firmware/releaseHelper', () => ({
|
|
21
|
+
getBleFirmwareReleaseInfo: jest.fn(),
|
|
22
|
+
getBootloaderReleaseInfo: jest.fn(),
|
|
23
|
+
getFirmwareReleaseInfo: jest.fn(),
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
const mockBuildFirmwareUpdatePlan = buildFirmwareUpdatePlan as jest.MockedFunction<
|
|
27
|
+
typeof buildFirmwareUpdatePlan
|
|
28
|
+
>;
|
|
29
|
+
const mockGetBleFirmwareReleaseInfo = getBleFirmwareReleaseInfo as jest.MockedFunction<
|
|
30
|
+
typeof getBleFirmwareReleaseInfo
|
|
31
|
+
>;
|
|
32
|
+
const mockGetBootloaderReleaseInfo = getBootloaderReleaseInfo as jest.MockedFunction<
|
|
33
|
+
typeof getBootloaderReleaseInfo
|
|
34
|
+
>;
|
|
35
|
+
const mockGetFirmwareReleaseInfo = getFirmwareReleaseInfo as jest.MockedFunction<
|
|
36
|
+
typeof getFirmwareReleaseInfo
|
|
37
|
+
>;
|
|
38
|
+
|
|
39
|
+
describe('CheckAllFirmwareRelease', () => {
|
|
40
|
+
test('keeps release information available when an optional Plan cannot be built', async () => {
|
|
41
|
+
const firmware = {
|
|
42
|
+
status: 'outdated' as const,
|
|
43
|
+
release: {
|
|
44
|
+
url: 'https://firmware.onekey.so/classic/firmware.bin',
|
|
45
|
+
version: [3, 0, 0],
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
const noUpdate = {
|
|
49
|
+
status: 'valid' as const,
|
|
50
|
+
release: undefined,
|
|
51
|
+
};
|
|
52
|
+
mockGetFirmwareReleaseInfo.mockReturnValue(firmware);
|
|
53
|
+
mockGetBootloaderReleaseInfo.mockReturnValue(noUpdate);
|
|
54
|
+
mockGetBleFirmwareReleaseInfo.mockReturnValue(noUpdate);
|
|
55
|
+
mockBuildFirmwareUpdatePlan.mockImplementation(() => {
|
|
56
|
+
throw new Error('FirmwarePlanInvalid');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const method = new CheckAllFirmwareRelease({
|
|
60
|
+
id: 1,
|
|
61
|
+
payload: {
|
|
62
|
+
method: 'checkAllFirmwareRelease',
|
|
63
|
+
platform: 'native',
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
method.init();
|
|
67
|
+
method.device = {
|
|
68
|
+
features: {
|
|
69
|
+
deviceType: EDeviceType.Classic1s,
|
|
70
|
+
serialNo: '',
|
|
71
|
+
firmwareType: EFirmwareType.Universal,
|
|
72
|
+
firmwareVersion: '1.0.0',
|
|
73
|
+
bootloaderVersion: '1.0.0',
|
|
74
|
+
},
|
|
75
|
+
} as typeof method.device;
|
|
76
|
+
|
|
77
|
+
await expect(method.run()).resolves.toEqual(
|
|
78
|
+
expect.objectContaining({
|
|
79
|
+
firmware,
|
|
80
|
+
bootloader: noUpdate,
|
|
81
|
+
ble: noUpdate,
|
|
82
|
+
firmwareUpdatePlan: undefined,
|
|
83
|
+
})
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MAX_FIRMWARE_ARTIFACT_READ_BYTES,
|
|
3
|
+
openFirmwareByteSource,
|
|
4
|
+
writeFirmwareByteSource,
|
|
5
|
+
} from '../../src/api/firmware/FirmwareArtifactSource';
|
|
6
|
+
|
|
7
|
+
import type { FirmwareArtifactReader } from '../../src/types/api/firmwareUpdate';
|
|
8
|
+
|
|
9
|
+
const artifact = {
|
|
10
|
+
artifactRef: `fw:${'a'.repeat(64)}`,
|
|
11
|
+
size: 6,
|
|
12
|
+
sha256: 'b'.repeat(64),
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const createReader = () => {
|
|
16
|
+
const open = jest.fn(() => Promise.resolve({ readerId: 'reader-1', size: artifact.size }));
|
|
17
|
+
const read = jest.fn(({ offset, length }: { readerId: string; offset: number; length: number }) =>
|
|
18
|
+
Promise.resolve({
|
|
19
|
+
data: Uint8Array.from([1, 2, 3, 4, 5, 6]).slice(offset, offset + length).buffer,
|
|
20
|
+
bytesRead: length,
|
|
21
|
+
eof: offset + length === artifact.size,
|
|
22
|
+
})
|
|
23
|
+
);
|
|
24
|
+
const close = jest.fn(() => Promise.resolve());
|
|
25
|
+
const reader: FirmwareArtifactReader = { open, read, close };
|
|
26
|
+
return { reader, open, read, close };
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
describe('FirmwareArtifactSource', () => {
|
|
30
|
+
it('reads an opaque artifact through bounded sequential reads', async () => {
|
|
31
|
+
const { reader, close } = createReader();
|
|
32
|
+
const source = await openFirmwareByteSource({ artifact, reader });
|
|
33
|
+
if (!source) {
|
|
34
|
+
throw new Error('Expected a firmware byte source');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const writes: Array<{ offset: number; data: number[] }> = [];
|
|
38
|
+
await writeFirmwareByteSource({
|
|
39
|
+
source,
|
|
40
|
+
chunkSize: 4,
|
|
41
|
+
write: ({ data, sourceOffset, length }) => {
|
|
42
|
+
writes.push({
|
|
43
|
+
offset: sourceOffset,
|
|
44
|
+
data: Array.from(new Uint8Array(data)),
|
|
45
|
+
});
|
|
46
|
+
return Promise.resolve(length);
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
await source.close();
|
|
50
|
+
|
|
51
|
+
expect(writes).toEqual([
|
|
52
|
+
{ offset: 0, data: [1, 2, 3, 4] },
|
|
53
|
+
{ offset: 4, data: [5, 6] },
|
|
54
|
+
]);
|
|
55
|
+
expect(close).toHaveBeenCalledWith({ readerId: 'reader-1' });
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('rejects a reader whose opened size differs from the receipt', async () => {
|
|
59
|
+
const { reader, open, close } = createReader();
|
|
60
|
+
open.mockResolvedValue({
|
|
61
|
+
readerId: 'reader-1',
|
|
62
|
+
size: artifact.size + 1,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
await expect(openFirmwareByteSource({ artifact, reader })).rejects.toMatchObject({
|
|
66
|
+
params: {
|
|
67
|
+
firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch',
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
expect(close).toHaveBeenCalledWith({ readerId: 'reader-1' });
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('rejects paths and over-limit reads before invoking the host', async () => {
|
|
74
|
+
const { reader, read } = createReader();
|
|
75
|
+
await expect(
|
|
76
|
+
openFirmwareByteSource({
|
|
77
|
+
artifact: { ...artifact, artifactRef: '/tmp/firmware.bin' },
|
|
78
|
+
reader,
|
|
79
|
+
})
|
|
80
|
+
).rejects.toMatchObject({
|
|
81
|
+
params: {
|
|
82
|
+
firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch',
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const source = await openFirmwareByteSource({ artifact, reader });
|
|
87
|
+
if (!source) {
|
|
88
|
+
throw new Error('Expected a firmware byte source');
|
|
89
|
+
}
|
|
90
|
+
await expect(
|
|
91
|
+
writeFirmwareByteSource({
|
|
92
|
+
source,
|
|
93
|
+
chunkSize: MAX_FIRMWARE_ARTIFACT_READ_BYTES + 1,
|
|
94
|
+
write: ({ length }) => Promise.resolve(length),
|
|
95
|
+
})
|
|
96
|
+
).rejects.toMatchObject({
|
|
97
|
+
params: {
|
|
98
|
+
firmwareUpdateCode: 'FirmwareArtifactReadTooLarge',
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
expect(read).not.toHaveBeenCalled();
|
|
102
|
+
await source.close();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('closes stale readers when open returns invalid metadata', async () => {
|
|
106
|
+
const { reader, open, close } = createReader();
|
|
107
|
+
open.mockResolvedValue({
|
|
108
|
+
readerId: '',
|
|
109
|
+
size: artifact.size,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
await expect(openFirmwareByteSource({ artifact, reader })).rejects.toMatchObject({
|
|
113
|
+
params: {
|
|
114
|
+
firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch',
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
expect(close).not.toHaveBeenCalled();
|
|
118
|
+
});
|
|
119
|
+
});
|