@onekeyfe/hd-core 1.2.0-alpha.68 → 1.2.0-alpha.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +28 -0
  2. package/__tests__/connectSettings.test.ts +5 -47
  3. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +31 -59
  4. package/__tests__/protocol-v2-firmware-targets.test.ts +33 -1
  5. package/__tests__/protocol-v2-resources.test.ts +9 -6
  6. package/__tests__/protocol-v2.test.ts +174 -383
  7. package/__tests__/protocolV2FileWrite.test.ts +48 -7
  8. package/dist/api/CheckAllFirmwareRelease.d.ts +2 -1
  9. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  10. package/dist/api/FirmwareUpdate.d.ts.map +1 -1
  11. package/dist/api/FirmwareUpdateV2.d.ts +0 -10
  12. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  13. package/dist/api/FirmwareUpdateV3.d.ts +3 -7
  14. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  15. package/dist/api/FirmwareUpdateV4.d.ts +6 -25
  16. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  17. package/dist/api/device/DeviceUpdateBootloader.d.ts +1 -5
  18. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  19. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +0 -8
  20. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  21. package/dist/api/firmware/getBinary.d.ts +0 -7
  22. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  23. package/dist/api/firmware/updateBootloader.d.ts +0 -2
  24. package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
  25. package/dist/api/firmware/uploadFirmware.d.ts +1 -9
  26. package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
  27. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
  28. package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
  29. package/dist/data-manager/DataManager.d.ts +0 -1
  30. package/dist/data-manager/DataManager.d.ts.map +1 -1
  31. package/dist/data-manager/connectSettings.d.ts.map +1 -1
  32. package/dist/device/Device.d.ts +0 -1
  33. package/dist/device/Device.d.ts.map +1 -1
  34. package/dist/index.d.ts +2 -180
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +1041 -3037
  37. package/dist/inject.d.ts.map +1 -1
  38. package/dist/lowLevelInject.d.ts.map +1 -1
  39. package/dist/protocols/protocol-v2/resources.d.ts +1 -1
  40. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
  41. package/dist/topLevelInject.d.ts.map +1 -1
  42. package/dist/types/api/checkAllFirmwareRelease.d.ts +0 -4
  43. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  44. package/dist/types/api/deviceUpdateBootloader.d.ts +0 -6
  45. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  46. package/dist/types/api/export.d.ts +1 -4
  47. package/dist/types/api/export.d.ts.map +1 -1
  48. package/dist/types/api/firmwareUpdate.d.ts +0 -68
  49. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  50. package/dist/types/api/index.d.ts +1 -9
  51. package/dist/types/api/index.d.ts.map +1 -1
  52. package/dist/types/settings.d.ts +1 -14
  53. package/dist/types/settings.d.ts.map +1 -1
  54. package/dist/utils/deviceFeaturesCompat.d.ts +0 -1
  55. package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
  56. package/package.json +4 -4
  57. package/src/api/CheckAllFirmwareRelease.ts +25 -61
  58. package/src/api/FirmwareUpdate.ts +1 -13
  59. package/src/api/FirmwareUpdateV2.ts +121 -296
  60. package/src/api/FirmwareUpdateV3.ts +57 -263
  61. package/src/api/FirmwareUpdateV4.ts +287 -865
  62. package/src/api/device/DeviceUpdateBootloader.ts +6 -125
  63. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +1 -72
  64. package/src/api/firmware/updateBootloader.ts +4 -19
  65. package/src/api/firmware/uploadFirmware.ts +22 -140
  66. package/src/api/helpers/protocolV2FileWrite.ts +54 -4
  67. package/src/data-manager/DataManager.ts +54 -59
  68. package/src/data-manager/connectSettings.ts +0 -11
  69. package/src/device/Device.ts +1 -6
  70. package/src/index.ts +0 -5
  71. package/src/inject.ts +2 -22
  72. package/src/lowLevelInject.ts +1 -5
  73. package/src/protocols/protocol-v2/resources.ts +5 -1
  74. package/src/topLevelInject.ts +1 -5
  75. package/src/types/api/checkAllFirmwareRelease.ts +0 -4
  76. package/src/types/api/deviceUpdateBootloader.ts +0 -6
  77. package/src/types/api/export.ts +0 -18
  78. package/src/types/api/firmwareUpdate.ts +0 -75
  79. package/src/types/api/index.ts +0 -14
  80. package/src/types/settings.ts +2 -14
  81. package/src/utils/deviceFeaturesCompat.ts +0 -6
  82. package/__tests__/device-initialize-timeout.test.ts +0 -56
  83. package/__tests__/firmware-update/check-all-firmware-release.test.ts +0 -175
  84. package/__tests__/firmware-update/device-update-bootloader.test.ts +0 -87
  85. package/__tests__/firmware-update/firmware-artifact-source.test.ts +0 -119
  86. package/__tests__/firmware-update/firmware-host-binding.test.ts +0 -70
  87. package/__tests__/firmware-update/firmware-preparation-error.test.ts +0 -27
  88. package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +0 -200
  89. package/__tests__/firmware-update/firmware-update-capabilities.test.ts +0 -13
  90. package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +0 -394
  91. package/__tests__/firmware-update/firmware-update-plan.test.ts +0 -593
  92. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +0 -231
  93. package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +0 -63
  94. package/__tests__/firmware-update/firmware-upload-source.test.ts +0 -70
  95. package/dist/api/firmware/FirmwareArtifactSource.d.ts +0 -25
  96. package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +0 -1
  97. package/dist/api/firmware/FirmwareHostBinding.d.ts +0 -6
  98. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +0 -1
  99. package/dist/api/firmware/FirmwarePreparationError.d.ts +0 -3
  100. package/dist/api/firmware/FirmwarePreparationError.d.ts.map +0 -1
  101. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +0 -3
  102. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +0 -1
  103. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +0 -25
  104. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +0 -1
  105. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +0 -32
  106. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +0 -1
  107. package/dist/types/api/firmwareUpdateCapabilities.d.ts +0 -9
  108. package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +0 -1
  109. package/dist/types/api/firmwareUpdatePlan.d.ts +0 -28
  110. package/dist/types/api/firmwareUpdatePlan.d.ts.map +0 -1
  111. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +0 -42
  112. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +0 -1
  113. package/src/api/firmware/FirmwareArtifactSource.ts +0 -278
  114. package/src/api/firmware/FirmwareHostBinding.ts +0 -100
  115. package/src/api/firmware/FirmwarePreparationError.ts +0 -12
  116. package/src/api/firmware/FirmwareUpdateCapabilities.ts +0 -9
  117. package/src/api/firmware/FirmwareUpdatePlan.ts +0 -772
  118. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +0 -449
  119. package/src/types/api/firmwareUpdateCapabilities.ts +0 -9
  120. package/src/types/api/firmwareUpdatePlan.ts +0 -38
  121. package/src/types/api/firmwareUpdatePreparedPlan.ts +0 -53
@@ -35,7 +35,10 @@ import ProtocolInfoRequest from '../src/api/protocol-v2/ProtocolInfoRequest';
35
35
  import EVMSignTypedData from '../src/api/evm/EVMSignTypedData';
36
36
  import EVMSignMessageEIP712 from '../src/api/evm/EVMSignMessageEIP712';
37
37
  import FirmwareUpdateV3 from '../src/api/FirmwareUpdateV3';
38
- import FirmwareUpdateV4, { assertProtocolV2ReconnectIdentity } from '../src/api/FirmwareUpdateV4';
38
+ import FirmwareUpdateV4, {
39
+ assertProtocolV2FirmwareTargetsSupported,
40
+ assertProtocolV2ReconnectIdentity,
41
+ } from '../src/api/FirmwareUpdateV4';
39
42
  import GetPassphraseState from '../src/api/GetPassphraseState';
40
43
  import GetOnekeyFeatures from '../src/api/GetOnekeyFeatures';
41
44
  import { batchGetPublickeys } from '../src/api/helpers/batchGetPublickeys';
@@ -98,7 +101,6 @@ import {
98
101
  getMethodVersionRange,
99
102
  } from '../src/utils';
100
103
  import { getDeviceFirmwareVersion } from '../src/utils/deviceVersionUtils';
101
- import { openFirmwareByteSource } from '../src/api/firmware/FirmwareArtifactSource';
102
104
  import {
103
105
  getPassphraseState,
104
106
  getPassphraseStateWithRefreshDeviceInfo,
@@ -255,7 +257,10 @@ describe('UploadPortfolio', () => {
255
257
  'FilesystemFileWrite',
256
258
  'FilesystemFile',
257
259
  expect.any(Object),
258
- { timeoutMs: undefined }
260
+ expect.objectContaining({
261
+ timeoutMs: undefined,
262
+ onWriteCompleted: expect.any(Function),
263
+ })
259
264
  );
260
265
  expect(typedCall).toHaveBeenNthCalledWith(2, 'PortfolioUpdate', 'Success', {});
261
266
  });
@@ -298,7 +303,10 @@ describe('UploadPortfolio', () => {
298
303
  append: false,
299
304
  ui_percentage: 100,
300
305
  },
301
- { timeoutMs: undefined }
306
+ expect.objectContaining({
307
+ timeoutMs: undefined,
308
+ onWriteCompleted: expect.any(Function),
309
+ })
302
310
  );
303
311
  expect(typedCall).toHaveBeenNthCalledWith(2, 'PortfolioUpdate', 'Success', {});
304
312
  expect(method.postMessage).not.toHaveBeenCalled();
@@ -3793,6 +3801,7 @@ describe('API compatibility handling', () => {
3793
3801
  });
3794
3802
 
3795
3803
  describe('Protocol V2 firmware update targets', () => {
3804
+ const OKPP_HEADER_SIZE = 0x52a0;
3796
3805
  let forceReloadDataSpy: jest.SpyInstance;
3797
3806
 
3798
3807
  beforeEach(() => {
@@ -3803,6 +3812,38 @@ describe('Protocol V2 firmware update targets', () => {
3803
3812
  jest.restoreAllMocks();
3804
3813
  });
3805
3814
 
3815
+ const buildOkppHeader = ({
3816
+ type = 'RESC',
3817
+ version = [1, 0, 0],
3818
+ payloadHash = 'ab'.repeat(64),
3819
+ headerHash = 'cd'.repeat(64),
3820
+ }: {
3821
+ type?: string;
3822
+ version?: [number, number, number];
3823
+ payloadHash?: string;
3824
+ headerHash?: string;
3825
+ } = {}) => {
3826
+ const header = new Uint8Array(OKPP_HEADER_SIZE);
3827
+ const view = new DataView(header.buffer);
3828
+ 'OKPP'.split('').forEach((char, index) => {
3829
+ header[index] = char.charCodeAt(0);
3830
+ });
3831
+ view.setUint32(0x04, 1, true);
3832
+ type.split('').forEach((char, index) => {
3833
+ header[0x08 + index] = char.charCodeAt(0);
3834
+ });
3835
+ view.setUint32(0x0c, OKPP_HEADER_SIZE, true);
3836
+ view.setUint32(0x10, version[0] * 0x10000 + version[1] * 0x100 + version[2], true);
3837
+ const writeHex = (offset: number, hex: string) => {
3838
+ for (let i = 0; i < hex.length / 2; i++) {
3839
+ header[offset + i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
3840
+ }
3841
+ };
3842
+ writeHex(0x200, payloadHash);
3843
+ writeHex(0x240, headerHash);
3844
+ return header;
3845
+ };
3846
+
3806
3847
  test('keeps Protocol V2 firmware updates off the firmwareUpdateV3 path', () => {
3807
3848
  const method = new FirmwareUpdateV3({
3808
3849
  id: 1,
@@ -3910,7 +3951,7 @@ describe('Protocol V2 firmware update targets', () => {
3910
3951
  });
3911
3952
  });
3912
3953
 
3913
- test('delegates Protocol V2 mode transition to the phase executor', async () => {
3954
+ test('enters Protocol V2 bootloader before upload', async () => {
3914
3955
  const method = new FirmwareUpdateV4({
3915
3956
  id: 1,
3916
3957
  payload: {
@@ -3945,7 +3986,7 @@ describe('Protocol V2 firmware update targets', () => {
3945
3986
 
3946
3987
  await method.run();
3947
3988
 
3948
- expect((method as any).enterProtocolV2BootloaderMode).not.toHaveBeenCalled();
3989
+ expect((method as any).enterProtocolV2BootloaderMode).toHaveBeenCalledTimes(1);
3949
3990
  expect((method as any).executeProtocolV2Update).toHaveBeenCalledWith({
3950
3991
  fwBinaryMap: [
3951
3992
  {
@@ -3956,6 +3997,7 @@ describe('Protocol V2 firmware update targets', () => {
3956
3997
  ],
3957
3998
  bootloaderBinary: null,
3958
3999
  });
4000
+ expect(method.postTipMessage).toHaveBeenCalledWith('SwitchFirmwareReconnectDevice');
3959
4001
  });
3960
4002
 
3961
4003
  test('enters Protocol V2 bootloader before reading and downloading remote resources', async () => {
@@ -3989,6 +4031,10 @@ describe('Protocol V2 firmware update targets', () => {
3989
4031
  });
3990
4032
 
3991
4033
  const order: string[] = [];
4034
+ (method as any).prepareProtocolV2BootResources = jest.fn().mockImplementation(() => {
4035
+ order.push('prepare-startup-resources');
4036
+ return Promise.resolve([]);
4037
+ });
3992
4038
  (method as any).enterProtocolV2BootloaderMode = jest.fn().mockImplementation(() => {
3993
4039
  order.push('enter-bootloader');
3994
4040
  return Promise.resolve(true);
@@ -4017,7 +4063,12 @@ describe('Protocol V2 firmware update targets', () => {
4017
4063
 
4018
4064
  await method.run();
4019
4065
 
4020
- expect(order).toEqual(['enter-bootloader', 'prepare-resources', 'execute-update']);
4066
+ expect(order).toEqual([
4067
+ 'prepare-startup-resources',
4068
+ 'enter-bootloader',
4069
+ 'prepare-resources',
4070
+ 'execute-update',
4071
+ ]);
4021
4072
  });
4022
4073
 
4023
4074
  test('reboots Protocol V2 normal-mode device to bootloader before transfer', async () => {
@@ -4261,6 +4312,41 @@ describe('Protocol V2 firmware update targets', () => {
4261
4312
  expect(typedCall.mock.calls.map(call => call[0])).toEqual(['DeviceInfoGet']);
4262
4313
  });
4263
4314
 
4315
+ test('polls again when Protocol V2 bootloader serial is temporarily unavailable', async () => {
4316
+ const method = new FirmwareUpdateV4({
4317
+ id: 1,
4318
+ payload: {
4319
+ method: 'firmwareUpdateV4',
4320
+ },
4321
+ });
4322
+ const typedCall = jest
4323
+ .fn()
4324
+ .mockResolvedValueOnce({
4325
+ type: 'DeviceInfo',
4326
+ message: { protocol_version: 1, hw: {} },
4327
+ })
4328
+ .mockResolvedValueOnce({
4329
+ type: 'DeviceInfo',
4330
+ message: protocolV2BootloaderDeviceInfo,
4331
+ });
4332
+ const reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
4333
+ (method as any).device = stubDevice({
4334
+ originalDescriptor: { id: 'ble-id', protocolType: 'V2' },
4335
+ getCommands: () => ({ typedCall }),
4336
+ probeProtocolV2RuntimeState: jest.fn().mockResolvedValue({
4337
+ mode: 'bootloader',
4338
+ bootloaderMode: true,
4339
+ }),
4340
+ });
4341
+ (method as any).reconnectProtocolV2Device = reconnectProtocolV2Device;
4342
+ (method as any).protocolV2ExpectedSerialNumber = 'PR9999999999';
4343
+
4344
+ await (method as any).waitForProtocolV2BootloaderMode(60 * 1000, 0);
4345
+
4346
+ expect(reconnectProtocolV2Device).toHaveBeenCalledTimes(2);
4347
+ expect(typedCall).toHaveBeenCalledTimes(2);
4348
+ });
4349
+
4264
4350
  test('does not run generic initialize during Protocol V2 USB firmware reconnect', async () => {
4265
4351
  const method = new FirmwareUpdateV4({
4266
4352
  id: 1,
@@ -4688,9 +4774,9 @@ describe('Protocol V2 firmware update targets', () => {
4688
4774
  const typedCall = jest
4689
4775
  .fn()
4690
4776
  .mockRejectedValue(
4691
- Object.assign(
4692
- new Error("Failed to execute 'open' on 'USBDevice': The device was disconnected"),
4693
- { name: 'NotFoundError' }
4777
+ new DOMException(
4778
+ "Failed to execute 'open' on 'USBDevice': The device was disconnected",
4779
+ 'NotFoundError'
4694
4780
  )
4695
4781
  );
4696
4782
 
@@ -4911,181 +4997,6 @@ describe('Protocol V2 firmware update targets', () => {
4911
4997
  expect((method as any).probeProtocolV2NormalMode).toHaveBeenCalledWith(deviceInfo);
4912
4998
  });
4913
4999
 
4914
- test('rejects a finished target whose payload version conflicts with the plan', () => {
4915
- const method = new FirmwareUpdateV4({
4916
- id: 1,
4917
- payload: {
4918
- method: 'firmwareUpdateV4',
4919
- platform: 'desktop',
4920
- expectedTargetVersions: {
4921
- app_v1: '2.0.0',
4922
- },
4923
- },
4924
- });
4925
- method.init();
4926
- method.postProgressMessage = jest.fn();
4927
-
4928
- expect(() =>
4929
- (method as any).assertProtocolV2TargetStatus(
4930
- [{ target_id: 4, status: 2, payload_version: 0x010000 }],
4931
- new Set([4])
4932
- )
4933
- ).toThrow('expected 131072');
4934
- });
4935
-
4936
- test('fails install polling immediately with the original firmware verification error', async () => {
4937
- const method = new FirmwareUpdateV4({
4938
- id: 1,
4939
- payload: {
4940
- method: 'firmwareUpdateV4',
4941
- platform: 'desktop',
4942
- expectedTargetVersions: {
4943
- app_v1: '2.0.0',
4944
- },
4945
- },
4946
- });
4947
- method.init();
4948
- const typedCall = jest.fn().mockResolvedValue({
4949
- type: 'DeviceFirmwareUpdateStatus',
4950
- message: {
4951
- records: [{ target_id: 4, status: 2, payload_version: 0x010000 }],
4952
- },
4953
- });
4954
- const dateNowSpy = jest
4955
- .spyOn(Date, 'now')
4956
- .mockReturnValueOnce(0)
4957
- .mockReturnValueOnce(0)
4958
- .mockReturnValue(300_001);
4959
- const setTimeoutSpy = jest.spyOn(global, 'setTimeout').mockImplementation(((
4960
- callback: () => void
4961
- ) => {
4962
- callback();
4963
- return 0 as any;
4964
- }) as typeof setTimeout);
4965
-
4966
- (method as any).device = stubDevice({
4967
- getCommands: () => ({ typedCall }),
4968
- });
4969
- (method as any).reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
4970
- (method as any).verifyProtocolV2ReconnectIdentity = jest.fn().mockResolvedValue(undefined);
4971
-
4972
- try {
4973
- await expect(
4974
- (method as any).waitForProtocolV2FirmwareUpdateComplete([
4975
- { target_id: 4, path: 'vol0:/application_p1.bin' },
4976
- ])
4977
- ).rejects.toMatchObject({
4978
- errorCode: HardwareErrorCode.FirmwareVerificationFailed,
4979
- message: expect.stringContaining('reached payload version 65536, expected 131072'),
4980
- });
4981
- } finally {
4982
- dateNowSpy.mockRestore();
4983
- setTimeoutSpy.mockRestore();
4984
- }
4985
-
4986
- expect(typedCall).toHaveBeenCalledTimes(1);
4987
- });
4988
-
4989
- test('fails install polling immediately with the original status conflict error', async () => {
4990
- const method = new FirmwareUpdateV4({
4991
- id: 1,
4992
- payload: {
4993
- method: 'firmwareUpdateV4',
4994
- },
4995
- });
4996
- const typedCall = jest.fn().mockResolvedValue({
4997
- type: 'DeviceFirmwareUpdateStatus',
4998
- message: {
4999
- records: [{ target_id: 4, status: 2, path: 'vol0:/unexpected.bin' }],
5000
- },
5001
- });
5002
- const dateNowSpy = jest
5003
- .spyOn(Date, 'now')
5004
- .mockReturnValueOnce(0)
5005
- .mockReturnValueOnce(0)
5006
- .mockReturnValue(300_001);
5007
- const setTimeoutSpy = jest.spyOn(global, 'setTimeout').mockImplementation(((
5008
- callback: () => void
5009
- ) => {
5010
- callback();
5011
- return 0 as any;
5012
- }) as typeof setTimeout);
5013
-
5014
- (method as any).device = stubDevice({
5015
- getCommands: () => ({ typedCall }),
5016
- });
5017
- (method as any).reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
5018
- (method as any).verifyProtocolV2ReconnectIdentity = jest.fn().mockResolvedValue(undefined);
5019
-
5020
- try {
5021
- await expect(
5022
- (method as any).waitForProtocolV2FirmwareUpdateComplete([
5023
- { target_id: 4, path: 'vol0:/application_p1.bin' },
5024
- ])
5025
- ).rejects.toMatchObject({
5026
- errorCode: HardwareErrorCode.RuntimeError,
5027
- message: 'Protocol V2 install status conflicts with target 4',
5028
- params: {
5029
- firmwareUpdateCode: 'FirmwareInstallStatusConflict',
5030
- },
5031
- });
5032
- } finally {
5033
- dateNowSpy.mockRestore();
5034
- setTimeoutSpy.mockRestore();
5035
- }
5036
-
5037
- expect(typedCall).toHaveBeenCalledTimes(1);
5038
- });
5039
-
5040
- test('does not fail a completed multi-app install when final app versions are unobservable', () => {
5041
- const method = new FirmwareUpdateV4({
5042
- id: 1,
5043
- payload: {
5044
- method: 'firmwareUpdateV4',
5045
- platform: 'desktop',
5046
- targetsToUpdate: ['app_v1', 'app_v2'],
5047
- expectedTargetVersions: {
5048
- app_v1: '1.0.0',
5049
- app_v2: '2.0.0',
5050
- },
5051
- },
5052
- });
5053
- method.init();
5054
- (method as any).protocolV2LatestFinalFeatures = {
5055
- major_version: 3,
5056
- minor_version: 0,
5057
- patch_version: 0,
5058
- };
5059
- (method as any).protocolV2FinalStatusVerified = true;
5060
-
5061
- expect(() => (method as any).assertExpectedProtocolV2Versions()).not.toThrow();
5062
- });
5063
-
5064
- test('rejects unobservable final versions after the status endpoint fallback', () => {
5065
- const method = new FirmwareUpdateV4({
5066
- id: 1,
5067
- payload: {
5068
- method: 'firmwareUpdateV4',
5069
- platform: 'desktop',
5070
- targetsToUpdate: ['app_v1', 'app_v2'],
5071
- expectedTargetVersions: {
5072
- app_v1: '1.0.0',
5073
- app_v2: '2.0.0',
5074
- },
5075
- },
5076
- });
5077
- method.init();
5078
- (method as any).protocolV2LatestFinalFeatures = {
5079
- major_version: 3,
5080
- minor_version: 0,
5081
- patch_version: 0,
5082
- };
5083
-
5084
- expect(() => (method as any).assertExpectedProtocolV2Versions()).toThrow(
5085
- 'has no observable final version after status fallback'
5086
- );
5087
- });
5088
-
5089
5000
  test('polls target status after update ACK and finishes when all targets complete', async () => {
5090
5001
  const method = new FirmwareUpdateV4({
5091
5002
  id: 1,
@@ -5307,7 +5218,7 @@ describe('Protocol V2 firmware update targets', () => {
5307
5218
  ).toBe(true);
5308
5219
 
5309
5220
  (method.postProgressMessage as jest.Mock).mockClear();
5310
- expect(() =>
5221
+ expect(
5311
5222
  (method as any).assertProtocolV2TargetStatus(
5312
5223
  [
5313
5224
  { target_id: 4, status: 'FW_MGMT_UPDATER_TASK_STATUS_FINISHED' },
@@ -5315,8 +5226,8 @@ describe('Protocol V2 firmware update targets', () => {
5315
5226
  ],
5316
5227
  new Set([4, 10])
5317
5228
  )
5318
- ).toThrow('Protocol V2 install status conflicts with target 4');
5319
- expect(method.postProgressMessage).not.toHaveBeenCalled();
5229
+ ).toBe(false);
5230
+ expect(method.postProgressMessage).toHaveBeenCalledWith(50, 'installingFirmware');
5320
5231
  expect(method.postProgressMessage).not.toHaveBeenCalledWith(100, 'installingFirmware');
5321
5232
 
5322
5233
  expect(
@@ -5420,14 +5331,10 @@ describe('Protocol V2 firmware update targets', () => {
5420
5331
  const writtenPaths: string[] = [];
5421
5332
  method.postTipMessage = jest.fn();
5422
5333
  method.postProgressMessage = jest.fn();
5423
- (method as any).protocolV2SourceUpdateProcess = jest.fn().mockImplementation(params => {
5334
+ (method as any).protocolV2CommonUpdateProcess = jest.fn().mockImplementation(params => {
5424
5335
  writtenPaths.push(params.filePath);
5425
- return Number(params.processedSize ?? 0) + Number(params.source.size);
5336
+ return Number(params.processedSize ?? 0) + Number(params.payload.byteLength);
5426
5337
  });
5427
- (method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(undefined);
5428
- (method as any).exitProtocolV2BootloaderToNormal = jest.fn().mockResolvedValue(undefined);
5429
- (method as any).waitForProtocolV2FinalFeatures = jest.fn().mockResolvedValue({});
5430
- (method as any).completeProtocolV2FinalVerification = jest.fn().mockResolvedValue({});
5431
5338
  (method as any).verifyProtocolV2StagedFile = jest.fn().mockResolvedValue(undefined);
5432
5339
  (method as any).protocolV2StartFirmwareUpdate = jest.fn().mockResolvedValue(undefined);
5433
5340
  (method as any).waitForProtocolV2FirmwareUpdateComplete = jest
@@ -5467,12 +5374,10 @@ describe('Protocol V2 firmware update targets', () => {
5467
5374
  'vol0:/coprocessor.bin',
5468
5375
  1
5469
5376
  );
5470
- expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenCalledTimes(2);
5471
- expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenNthCalledWith(1, {
5472
- targets: [{ target_id: 3, path: 'vol0:/bootloader.bin' }],
5473
- });
5474
- expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenNthCalledWith(2, {
5377
+ expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenCalledTimes(1);
5378
+ expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenCalledWith({
5475
5379
  targets: [
5380
+ { target_id: 3, path: 'vol0:/bootloader.bin' },
5476
5381
  { target_id: 6, path: 'vol0:/coprocessor.bin' },
5477
5382
  { target_id: 7, path: 'vol0:/se01.bin' },
5478
5383
  { target_id: 4, path: 'vol0:/application_p1.bin' },
@@ -5482,7 +5387,7 @@ describe('Protocol V2 firmware update targets', () => {
5482
5387
  expect((method as any).waitForProtocolV2FirmwareUpdateComplete).toHaveBeenCalled();
5483
5388
  });
5484
5389
 
5485
- test('announces separate resource and component transfer phases', async () => {
5390
+ test('announces one transfer when syncing resource bundles and staging firmware', async () => {
5486
5391
  const method = new FirmwareUpdateV4({
5487
5392
  id: 1,
5488
5393
  payload: {
@@ -5492,14 +5397,11 @@ describe('Protocol V2 firmware update targets', () => {
5492
5397
 
5493
5398
  method.postTipMessage = jest.fn();
5494
5399
  method.postProgressMessage = jest.fn();
5495
- (method as any).protocolV2SourceUpdateProcess = jest
5400
+ (method as any).protocolV2CommonUpdateProcess = jest
5496
5401
  .fn()
5497
5402
  .mockImplementation(params =>
5498
- Promise.resolve(Number(params.processedSize ?? 0) + Number(params.source.size))
5403
+ Promise.resolve(Number(params.processedSize ?? 0) + Number(params.payload.byteLength))
5499
5404
  );
5500
- (method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(undefined);
5501
- (method as any).exitProtocolV2BootloaderToNormal = jest.fn().mockResolvedValue(undefined);
5502
- (method as any).completeProtocolV2FinalVerification = jest.fn().mockResolvedValue({});
5503
5405
  (method as any).verifyProtocolV2StagedFile = jest.fn().mockResolvedValue(undefined);
5504
5406
  (method as any).protocolV2StartFirmwareUpdate = jest.fn().mockResolvedValue(undefined);
5505
5407
  (method as any).waitForProtocolV2FirmwareUpdateComplete = jest
@@ -5519,22 +5421,21 @@ describe('Protocol V2 firmware update targets', () => {
5519
5421
  {
5520
5422
  fileName: 'application_p1.bin',
5521
5423
  binary: new Uint8Array([3]).buffer,
5522
- targetId: 4,
5424
+ targetId: 3,
5523
5425
  },
5524
5426
  ],
5525
5427
  });
5526
5428
 
5527
- expect(method.postTipMessage).toHaveBeenCalledTimes(3);
5429
+ expect(method.postTipMessage).toHaveBeenCalledTimes(2);
5528
5430
  expect(method.postTipMessage).toHaveBeenNthCalledWith(1, 'StartTransferData');
5529
- expect(method.postTipMessage).toHaveBeenNthCalledWith(2, 'StartTransferData');
5530
- expect(method.postTipMessage).toHaveBeenNthCalledWith(3, 'ConfirmOnDevice');
5531
- expect((method as any).protocolV2SourceUpdateProcess).toHaveBeenNthCalledWith(
5431
+ expect(method.postTipMessage).toHaveBeenNthCalledWith(2, 'ConfirmOnDevice');
5432
+ expect((method as any).protocolV2CommonUpdateProcess).toHaveBeenNthCalledWith(
5532
5433
  1,
5533
- expect.objectContaining({ processedSize: 0, totalSize: 2 })
5434
+ expect.objectContaining({ processedSize: 0, totalSize: 3 })
5534
5435
  );
5535
- expect((method as any).protocolV2SourceUpdateProcess).toHaveBeenNthCalledWith(
5436
+ expect((method as any).protocolV2CommonUpdateProcess).toHaveBeenNthCalledWith(
5536
5437
  2,
5537
- expect.objectContaining({ processedSize: 0, totalSize: 1 })
5438
+ expect.objectContaining({ processedSize: 2, totalSize: 3 })
5538
5439
  );
5539
5440
  });
5540
5441
 
@@ -5548,8 +5449,7 @@ describe('Protocol V2 firmware update targets', () => {
5548
5449
 
5549
5450
  method.postTipMessage = jest.fn();
5550
5451
  method.postProgressMessage = jest.fn();
5551
- (method as any).protocolV2SourceUpdateProcess = jest.fn().mockResolvedValue(3);
5552
- (method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(undefined);
5452
+ (method as any).protocolV2CommonUpdateProcess = jest.fn().mockResolvedValue(3);
5553
5453
  (method as any).device = stubDevice({
5554
5454
  getCommands: () => ({
5555
5455
  typedCall: jest.fn().mockResolvedValue({
@@ -5595,14 +5495,11 @@ describe('Protocol V2 firmware update targets', () => {
5595
5495
 
5596
5496
  method.postTipMessage = jest.fn();
5597
5497
  method.postProgressMessage = jest.fn();
5598
- (method as any).protocolV2SourceUpdateProcess = jest
5498
+ (method as any).protocolV2CommonUpdateProcess = jest
5599
5499
  .fn()
5600
5500
  .mockImplementation(params =>
5601
- Promise.resolve(Number(params.processedSize ?? 0) + Number(params.source.size))
5501
+ Promise.resolve(Number(params.processedSize ?? 0) + Number(params.payload.byteLength))
5602
5502
  );
5603
- (method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(undefined);
5604
- (method as any).exitProtocolV2BootloaderToNormal = jest.fn().mockResolvedValue(undefined);
5605
- (method as any).completeProtocolV2FinalVerification = jest.fn().mockResolvedValue({});
5606
5503
  (method as any).verifyProtocolV2StagedFile = jest.fn().mockResolvedValue(undefined);
5607
5504
  (method as any).protocolV2StartFirmwareUpdate = jest.fn().mockResolvedValue(undefined);
5608
5505
  (method as any).waitForProtocolV2FirmwareUpdateComplete = jest
@@ -5925,58 +5822,6 @@ describe('Protocol V2 firmware update targets', () => {
5925
5822
  });
5926
5823
  });
5927
5824
 
5928
- test('rejects prepared RESC bundle paths before opening an artifact source', async () => {
5929
- const method = new FirmwareUpdateV4({
5930
- id: 1,
5931
- payload: {
5932
- method: 'firmwareUpdateV4',
5933
- },
5934
- });
5935
- const openPreparedSource = jest.fn();
5936
- (method as any).params = {
5937
- targetsToUpdate: ['resource'],
5938
- resourceBundleArtifacts: [
5939
- {
5940
- name: 'images',
5941
- artifact: {
5942
- artifactRef: `fw:${'a'.repeat(64)}`,
5943
- size: 1,
5944
- sha256: 'a'.repeat(64),
5945
- },
5946
- },
5947
- ],
5948
- };
5949
- (method as any).openProtocolV2PreparedSource = openPreparedSource;
5950
- const getFirmwareLatestReleaseSpy = jest
5951
- .spyOn(DataManager, 'getFirmwareLatestRelease')
5952
- .mockReturnValue({
5953
- required: false,
5954
- version: [1, 0, 0],
5955
- url: '',
5956
- resourceBundles: [
5957
- {
5958
- name: 'images',
5959
- url: 'https://example.com/images.okpkg',
5960
- devicePath: 'vol0:/resource/../images.okpkg',
5961
- },
5962
- ],
5963
- fingerprint: '',
5964
- changelog: {
5965
- 'zh-CN': '',
5966
- 'en-US': '',
5967
- },
5968
- });
5969
-
5970
- await expect(
5971
- (method as any).prepareProtocolV2ResourceSources('universal', {
5972
- deviceType: 'pro2',
5973
- })
5974
- ).rejects.toThrow('resourceBundles[].devicePath');
5975
- expect(openPreparedSource).not.toHaveBeenCalled();
5976
-
5977
- getFirmwareLatestReleaseSpy.mockRestore();
5978
- });
5979
-
5980
5825
  test('stops a remote update before reboot when the latest config cannot be refreshed', async () => {
5981
5826
  const method = new FirmwareUpdateV4({
5982
5827
  id: 1,
@@ -6132,10 +5977,7 @@ describe('Protocol V2 firmware update targets', () => {
6132
5977
 
6133
5978
  method.postTipMessage = jest.fn();
6134
5979
  method.postProgressMessage = jest.fn();
6135
- (method as any).protocolV2SourceUpdateProcess = jest.fn().mockResolvedValue(3);
6136
- (method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(undefined);
6137
- (method as any).completeProtocolV2FinalVerification = jest.fn().mockResolvedValue({});
6138
- (method as any).verifyProtocolV2StagedFile = jest.fn().mockResolvedValue(undefined);
5980
+ (method as any).protocolV2CommonUpdateProcess = jest.fn().mockResolvedValue(3);
6139
5981
  (method as any).protocolV2StartFirmwareUpdate = jest.fn();
6140
5982
  (method as any).waitForProtocolV2FirmwareUpdateComplete = jest.fn();
6141
5983
 
@@ -6151,7 +5993,7 @@ describe('Protocol V2 firmware update targets', () => {
6151
5993
  fwBinaryMap: [],
6152
5994
  });
6153
5995
 
6154
- expect((method as any).protocolV2SourceUpdateProcess).toHaveBeenCalledTimes(1);
5996
+ expect((method as any).protocolV2CommonUpdateProcess).toHaveBeenCalledTimes(1);
6155
5997
  expect((method as any).protocolV2StartFirmwareUpdate).not.toHaveBeenCalled();
6156
5998
  expect((method as any).waitForProtocolV2FirmwareUpdateComplete).not.toHaveBeenCalled();
6157
5999
  });
@@ -6184,19 +6026,12 @@ describe('Protocol V2 firmware update targets', () => {
6184
6026
  });
6185
6027
  method.postMessage = jest.fn();
6186
6028
 
6187
- const source = await openFirmwareByteSource({
6188
- binary: new Uint8Array(4097).buffer,
6029
+ await (method as any).protocolV2CommonUpdateProcess({
6030
+ payload: new Uint8Array(4097).buffer,
6031
+ filePath: 'vol1:firmware.bin',
6032
+ processedSize: 0,
6033
+ totalSize: 4097,
6189
6034
  });
6190
- try {
6191
- await (method as any).protocolV2SourceUpdateProcess({
6192
- source,
6193
- filePath: 'vol1:firmware.bin',
6194
- processedSize: 0,
6195
- totalSize: 4097,
6196
- });
6197
- } finally {
6198
- await source?.close();
6199
- }
6200
6035
 
6201
6036
  const writePayloads = typedCall.mock.calls.map(call => call[2]);
6202
6037
  expect(writePayloads.map(payload => payload.file.offset)).toEqual([0, 4000]);
@@ -6264,16 +6099,12 @@ describe('Protocol V2 firmware update targets', () => {
6264
6099
  method.postProgressMessage = jest.fn();
6265
6100
 
6266
6101
  try {
6267
- const source = await openFirmwareByteSource({
6268
- binary: new Uint8Array(8001).buffer,
6269
- });
6270
- await (method as any).protocolV2SourceUpdateProcess({
6271
- source,
6102
+ await (method as any).protocolV2CommonUpdateProcess({
6103
+ payload: new Uint8Array(8001).buffer,
6272
6104
  filePath: 'vol0:/firmware.bin',
6273
6105
  processedSize: 0,
6274
6106
  totalSize: 8001,
6275
6107
  });
6276
- await source?.close();
6277
6108
  } finally {
6278
6109
  setTimeoutSpy.mockRestore();
6279
6110
  }
@@ -6308,21 +6139,16 @@ describe('Protocol V2 firmware update targets', () => {
6308
6139
  });
6309
6140
  method.postProgressMessage = jest.fn();
6310
6141
  method.postTipMessage = jest.fn();
6311
- (method as any).recoverProtocolV2FileTransfer = jest.fn().mockResolvedValue(undefined);
6312
6142
 
6313
- const source = await openFirmwareByteSource({
6314
- binary: new Uint8Array(4097).buffer,
6315
- });
6316
6143
  await expect(
6317
- (method as any).protocolV2SourceUpdateProcess({
6318
- source,
6144
+ (method as any).protocolV2WriteWholeFile({
6145
+ payload: new Uint8Array(4097).buffer,
6319
6146
  filePath: 'vol0:/firmware.bin',
6320
6147
  processedSize: 0,
6321
6148
  totalSize: 4097,
6322
6149
  })
6323
6150
  ).rejects.toMatchObject({ errorCode: HardwareErrorCode.EmmcFileWriteFirmwareError });
6324
- await source?.close();
6325
- expect(typedCall).toHaveBeenCalledTimes(6);
6151
+ expect(typedCall).toHaveBeenCalledTimes(2);
6326
6152
  });
6327
6153
 
6328
6154
  test('caps native BLE firmware upload chunks below the WebUSB limit', async () => {
@@ -6355,27 +6181,26 @@ describe('Protocol V2 firmware update targets', () => {
6355
6181
  });
6356
6182
  method.postProgressMessage = jest.fn();
6357
6183
 
6358
- const source = await openFirmwareByteSource({
6359
- binary: new Uint8Array(1801).buffer,
6184
+ await (method as any).protocolV2CommonUpdateProcess({
6185
+ payload: new Uint8Array(1801).buffer,
6186
+ filePath: 'vol1:ble-firmware.bin',
6187
+ processedSize: 0,
6188
+ totalSize: 1801,
6360
6189
  });
6361
- try {
6362
- await (method as any).protocolV2SourceUpdateProcess({
6363
- source,
6364
- filePath: 'vol1:ble-firmware.bin',
6365
- processedSize: 0,
6366
- totalSize: 1801,
6367
- });
6368
- } finally {
6369
- await source?.close();
6370
- }
6371
6190
 
6372
6191
  const writePayloads = typedCall.mock.calls.map(call => call[2]);
6373
6192
  expect(writePayloads.map(payload => payload.file.offset)).toEqual([0, 1800]);
6374
6193
  expect(writePayloads.map(payload => payload.file.data.byteLength)).toEqual([1800, 1]);
6375
6194
  expect(writePayloads.map(payload => payload.ui_percentage)).toEqual([0, 100]);
6376
6195
  expect(typedCall.mock.calls.map(call => call[3])).toEqual([
6377
- { writeWithResponse: true },
6378
- { writeWithResponse: true },
6196
+ expect.objectContaining({
6197
+ writeWithResponse: false,
6198
+ onWriteCompleted: expect.any(Function),
6199
+ }),
6200
+ expect.objectContaining({
6201
+ writeWithResponse: false,
6202
+ onWriteCompleted: expect.any(Function),
6203
+ }),
6379
6204
  ]);
6380
6205
  });
6381
6206
 
@@ -6488,9 +6313,9 @@ describe('Protocol V2 firmware update method', () => {
6488
6313
  const typedCall = jest
6489
6314
  .fn()
6490
6315
  .mockRejectedValue(
6491
- Object.assign(
6492
- new Error("Failed to execute 'open' on 'USBDevice': The device was disconnected"),
6493
- { name: 'NotFoundError' }
6316
+ new DOMException(
6317
+ "Failed to execute 'open' on 'USBDevice': The device was disconnected",
6318
+ 'NotFoundError'
6494
6319
  )
6495
6320
  );
6496
6321
 
@@ -6713,21 +6538,6 @@ describe('Protocol V2 firmware reconnect identity', () => {
6713
6538
  });
6714
6539
  };
6715
6540
 
6716
- test('keeps the host plan identity as the V4 reconnect anchor', () => {
6717
- const method = new FirmwareUpdateV4({
6718
- id: 1,
6719
- payload: {
6720
- method: 'firmwareUpdateV4',
6721
- platform: 'native',
6722
- expectedDeviceId: 'persisted-device-id',
6723
- },
6724
- });
6725
-
6726
- method.init();
6727
-
6728
- expect((method as any).params.expectedDeviceId).toBe('persisted-device-id');
6729
- });
6730
-
6731
6541
  test('rejects a different physical serial before firmware transfer resumes', () => {
6732
6542
  expect(() => assertProtocolV2ReconnectIdentity('expected-serial', 'other-serial')).toThrow(
6733
6543
  'identity mismatch'
@@ -6737,15 +6547,15 @@ describe('Protocol V2 firmware reconnect identity', () => {
6737
6547
  ).not.toThrow();
6738
6548
  });
6739
6549
 
6740
- test('fails closed when the physical serial is unavailable', () => {
6741
- expect(() => assertProtocolV2ReconnectIdentity('expected-serial', undefined)).toThrow(
6742
- expect.objectContaining({ errorCode: HardwareErrorCode.DeviceNotFound })
6743
- );
6744
- expect(() => assertProtocolV2ReconnectIdentity(undefined, 'actual-serial')).toThrow(
6745
- expect.objectContaining({ errorCode: HardwareErrorCode.DeviceNotFound })
6746
- );
6550
+ test('requires the same descriptor path when either physical serial is unavailable', () => {
6551
+ expect(() =>
6552
+ assertProtocolV2ReconnectIdentity('expected-serial', undefined, 'usb-path', 'usb-path')
6553
+ ).not.toThrow();
6554
+ expect(() =>
6555
+ assertProtocolV2ReconnectIdentity(undefined, 'actual-serial', 'usb-path', 'other-path')
6556
+ ).toThrow('identity unavailable');
6747
6557
  expect(() => assertProtocolV2ReconnectIdentity(undefined, undefined)).toThrow(
6748
- expect.objectContaining({ errorCode: HardwareErrorCode.DeviceNotFound })
6558
+ 'identity unavailable'
6749
6559
  );
6750
6560
  });
6751
6561
 
@@ -6764,6 +6574,7 @@ describe('Protocol V2 firmware reconnect identity', () => {
6764
6574
  },
6765
6575
  });
6766
6576
  (method as any).device = stubDevice({
6577
+ originalDescriptor: { id: 'usb-id', path: 'usb-path', protocolType: 'V2' },
6767
6578
  features: { deviceId: 'wallet-derived-id' },
6768
6579
  getCommands: () => ({ typedCall }),
6769
6580
  });
@@ -6779,7 +6590,7 @@ describe('Protocol V2 firmware reconnect identity', () => {
6779
6590
  );
6780
6591
  });
6781
6592
 
6782
- test('rejects firmware update startup when DeviceInfo has no physical serial', async () => {
6593
+ test('allows firmware update startup when DeviceInfo has no physical serial', async () => {
6783
6594
  const method = new FirmwareUpdateV4({
6784
6595
  id: 1,
6785
6596
  payload: { method: 'firmwareUpdateV4' },
@@ -6789,42 +6600,13 @@ describe('Protocol V2 firmware reconnect identity', () => {
6789
6600
  message: { protocol_version: 1, hw: {} },
6790
6601
  });
6791
6602
  (method as any).device = stubDevice({
6603
+ originalDescriptor: { id: 'usb-id', path: 'usb-path', protocolType: 'V2' },
6792
6604
  getCommands: () => ({ typedCall }),
6793
6605
  });
6794
6606
 
6795
- await expect((method as any).captureProtocolV2PhysicalIdentity()).rejects.toMatchObject({
6796
- errorCode: HardwareErrorCode.DeviceNotFound,
6797
- });
6798
- });
6799
-
6800
- test('uses the BLE read limit when reading an existing firmware bundle header', async () => {
6801
- const method = new FirmwareUpdateV4({
6802
- id: 1,
6803
- payload: {
6804
- method: 'firmwareUpdateV4',
6805
- platform: 'native',
6806
- chunkSize: 1800,
6807
- },
6808
- });
6809
- method.init();
6810
- const readChunkLengths: number[] = [];
6811
- const typedCall = jest.fn((requestType: string, _responseType: string, request: any) => {
6812
- if (requestType === 'FilesystemPathInfoQuery') {
6813
- return Promise.resolve({ message: { exist: true, directory: false, size: 0x52a0 } });
6814
- }
6815
- if (requestType === 'FilesystemFileRead') {
6816
- readChunkLengths.push(request.chunk_len);
6817
- return Promise.resolve({ message: { data: new Uint8Array(request.chunk_len) } });
6818
- }
6819
- throw new Error(`Unexpected request: ${requestType}`);
6820
- });
6821
- (method as any).device = stubDevice({
6822
- getCommands: () => ({ typedCall }),
6823
- });
6824
-
6825
- await expect((method as any).readProtocolV2DeviceFileHeader('bundle.okpp')).resolves.toBeNull();
6826
- expect(readChunkLengths.length).toBeGreaterThan(1);
6827
- expect(Math.max(...readChunkLengths)).toBe(900);
6607
+ await expect((method as any).captureProtocolV2PhysicalIdentity()).resolves.toBeUndefined();
6608
+ expect((method as any).protocolV2ExpectedSerialNumber).toBeUndefined();
6609
+ expect((method as any).protocolV2ExpectedPath).toBeDefined();
6828
6610
  });
6829
6611
 
6830
6612
  test('uses filesystem reads instead of ResourceInventoryGet for resource comparison', async () => {
@@ -6941,7 +6723,7 @@ describe('Protocol V2 firmware reconnect identity', () => {
6941
6723
  resourcesSpy.mockRestore();
6942
6724
  });
6943
6725
 
6944
- test('does not resolve boot resources unless the optional target is selected', async () => {
6726
+ test('does not resolve boot resources when no resource target is selected', async () => {
6945
6727
  const method = new FirmwareUpdateV4({
6946
6728
  id: 1,
6947
6729
  payload: { method: 'firmwareUpdateV4', platform: 'web' },
@@ -6956,7 +6738,7 @@ describe('Protocol V2 firmware reconnect identity', () => {
6956
6738
  expect(downloadSpy).not.toHaveBeenCalled();
6957
6739
  });
6958
6740
 
6959
- test('downloads and maps selected boot resources as direct RES files', async () => {
6741
+ test('includes startup resources in the complete resource target', async () => {
6960
6742
  const bytes = new Uint8Array([1, 2, 3, 4]);
6961
6743
  const binary = bytes.buffer as ArrayBuffer;
6962
6744
  const fileHash = Array.from(sha256(bytes), byte => byte.toString(16).padStart(2, '0')).join('');
@@ -6978,7 +6760,7 @@ describe('Protocol V2 firmware reconnect identity', () => {
6978
6760
  payload: {
6979
6761
  method: 'firmwareUpdateV4',
6980
6762
  platform: 'web',
6981
- targetsToUpdate: ['boot_resources'],
6763
+ targetsToUpdate: ['resource'],
6982
6764
  },
6983
6765
  });
6984
6766
  method.init();
@@ -7921,7 +7703,10 @@ describe('Protocol V2 file write method', () => {
7921
7703
  append: false,
7922
7704
  ui_percentage: 100,
7923
7705
  },
7924
- { timeoutMs: undefined }
7706
+ expect.objectContaining({
7707
+ timeoutMs: undefined,
7708
+ onWriteCompleted: expect.any(Function),
7709
+ })
7925
7710
  );
7926
7711
  expect(method.postMessage).toHaveBeenCalledWith({
7927
7712
  event: 'UI_EVENT',
@@ -7970,7 +7755,10 @@ describe('Protocol V2 file write method', () => {
7970
7755
  append: false,
7971
7756
  ui_percentage: 0,
7972
7757
  },
7973
- { timeoutMs: undefined }
7758
+ expect.objectContaining({
7759
+ timeoutMs: undefined,
7760
+ onWriteCompleted: expect.any(Function),
7761
+ })
7974
7762
  );
7975
7763
  expect(typedCall).toHaveBeenNthCalledWith(
7976
7764
  2,
@@ -7987,7 +7775,10 @@ describe('Protocol V2 file write method', () => {
7987
7775
  append: false,
7988
7776
  ui_percentage: 100,
7989
7777
  },
7990
- { timeoutMs: undefined }
7778
+ expect.objectContaining({
7779
+ timeoutMs: undefined,
7780
+ onWriteCompleted: expect.any(Function),
7781
+ })
7991
7782
  );
7992
7783
  expect(result).toMatchObject({
7993
7784
  path: 'vol1:test.bin',