@onekeyfe/hd-core 1.2.0-alpha.73 → 1.2.0-alpha.75

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 (120) hide show
  1. package/__tests__/connectSettings.test.ts +5 -45
  2. package/__tests__/devicePoolBootloaderPath.test.ts +64 -0
  3. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +31 -59
  4. package/__tests__/protocol-v2.test.ts +100 -405
  5. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  6. package/dist/api/FirmwareUpdate.d.ts.map +1 -1
  7. package/dist/api/FirmwareUpdateV2.d.ts +0 -10
  8. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  9. package/dist/api/FirmwareUpdateV3.d.ts +3 -7
  10. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  11. package/dist/api/FirmwareUpdateV4.d.ts +5 -25
  12. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  13. package/dist/api/device/DeviceUpdateBootloader.d.ts +1 -5
  14. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  15. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +0 -8
  16. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  17. package/dist/api/firmware/getBinary.d.ts +0 -7
  18. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  19. package/dist/api/firmware/updateBootloader.d.ts +0 -2
  20. package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
  21. package/dist/api/firmware/uploadFirmware.d.ts +1 -9
  22. package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
  23. package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
  24. package/dist/core/index.d.ts.map +1 -1
  25. package/dist/data-manager/DataManager.d.ts +0 -1
  26. package/dist/data-manager/DataManager.d.ts.map +1 -1
  27. package/dist/data-manager/connectSettings.d.ts.map +1 -1
  28. package/dist/device/Device.d.ts +0 -1
  29. package/dist/device/Device.d.ts.map +1 -1
  30. package/dist/device/DeviceConnector.d.ts +1 -1
  31. package/dist/device/DeviceConnector.d.ts.map +1 -1
  32. package/dist/device/DevicePool.d.ts +1 -0
  33. package/dist/device/DevicePool.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 +1076 -3080
  37. package/dist/inject.d.ts.map +1 -1
  38. package/dist/lowLevelInject.d.ts.map +1 -1
  39. package/dist/topLevelInject.d.ts.map +1 -1
  40. package/dist/types/api/checkAllFirmwareRelease.d.ts +0 -4
  41. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  42. package/dist/types/api/deviceUpdateBootloader.d.ts +0 -6
  43. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  44. package/dist/types/api/export.d.ts +1 -4
  45. package/dist/types/api/export.d.ts.map +1 -1
  46. package/dist/types/api/firmwareUpdate.d.ts +0 -68
  47. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  48. package/dist/types/api/index.d.ts +1 -9
  49. package/dist/types/api/index.d.ts.map +1 -1
  50. package/dist/types/settings.d.ts +0 -13
  51. package/dist/types/settings.d.ts.map +1 -1
  52. package/dist/utils/deviceFeaturesCompat.d.ts +0 -1
  53. package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
  54. package/package.json +4 -4
  55. package/src/api/CheckAllFirmwareRelease.ts +6 -57
  56. package/src/api/FirmwareUpdate.ts +1 -13
  57. package/src/api/FirmwareUpdateV2.ts +121 -296
  58. package/src/api/FirmwareUpdateV3.ts +57 -263
  59. package/src/api/FirmwareUpdateV4.ts +347 -875
  60. package/src/api/device/DeviceUpdateBootloader.ts +6 -125
  61. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +1 -72
  62. package/src/api/firmware/updateBootloader.ts +4 -19
  63. package/src/api/firmware/uploadFirmware.ts +22 -140
  64. package/src/core/index.ts +16 -0
  65. package/src/data-manager/DataManager.ts +54 -62
  66. package/src/data-manager/connectSettings.ts +0 -11
  67. package/src/device/Device.ts +3 -7
  68. package/src/device/DeviceConnector.ts +2 -2
  69. package/src/device/DevicePool.ts +15 -1
  70. package/src/index.ts +0 -5
  71. package/src/inject.ts +2 -22
  72. package/src/lowLevelInject.ts +1 -5
  73. package/src/topLevelInject.ts +1 -5
  74. package/src/types/api/checkAllFirmwareRelease.ts +0 -4
  75. package/src/types/api/deviceUpdateBootloader.ts +0 -6
  76. package/src/types/api/export.ts +0 -18
  77. package/src/types/api/firmwareUpdate.ts +0 -75
  78. package/src/types/api/index.ts +0 -14
  79. package/src/types/settings.ts +0 -13
  80. package/src/utils/deviceFeaturesCompat.ts +0 -6
  81. package/__tests__/device-initialize-timeout.test.ts +0 -56
  82. package/__tests__/firmware-update/check-all-firmware-release.test.ts +0 -175
  83. package/__tests__/firmware-update/device-update-bootloader.test.ts +0 -87
  84. package/__tests__/firmware-update/firmware-artifact-source.test.ts +0 -119
  85. package/__tests__/firmware-update/firmware-host-binding.test.ts +0 -70
  86. package/__tests__/firmware-update/firmware-preparation-error.test.ts +0 -27
  87. package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +0 -200
  88. package/__tests__/firmware-update/firmware-update-capabilities.test.ts +0 -13
  89. package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +0 -394
  90. package/__tests__/firmware-update/firmware-update-plan.test.ts +0 -593
  91. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +0 -231
  92. package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +0 -63
  93. package/__tests__/firmware-update/firmware-upload-source.test.ts +0 -70
  94. package/dist/api/firmware/FirmwareArtifactSource.d.ts +0 -25
  95. package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +0 -1
  96. package/dist/api/firmware/FirmwareHostBinding.d.ts +0 -6
  97. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +0 -1
  98. package/dist/api/firmware/FirmwarePreparationError.d.ts +0 -3
  99. package/dist/api/firmware/FirmwarePreparationError.d.ts.map +0 -1
  100. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +0 -3
  101. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +0 -1
  102. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +0 -25
  103. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +0 -1
  104. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +0 -32
  105. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +0 -1
  106. package/dist/types/api/firmwareUpdateCapabilities.d.ts +0 -9
  107. package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +0 -1
  108. package/dist/types/api/firmwareUpdatePlan.d.ts +0 -28
  109. package/dist/types/api/firmwareUpdatePlan.d.ts.map +0 -1
  110. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +0 -42
  111. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +0 -1
  112. package/src/api/firmware/FirmwareArtifactSource.ts +0 -278
  113. package/src/api/firmware/FirmwareHostBinding.ts +0 -100
  114. package/src/api/firmware/FirmwarePreparationError.ts +0 -12
  115. package/src/api/firmware/FirmwareUpdateCapabilities.ts +0 -9
  116. package/src/api/firmware/FirmwareUpdatePlan.ts +0 -772
  117. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +0 -449
  118. package/src/types/api/firmwareUpdateCapabilities.ts +0 -9
  119. package/src/types/api/firmwareUpdatePlan.ts +0 -38
  120. package/src/types/api/firmwareUpdatePreparedPlan.ts +0 -53
@@ -101,7 +101,6 @@ import {
101
101
  getMethodVersionRange,
102
102
  } from '../src/utils';
103
103
  import { getDeviceFirmwareVersion } from '../src/utils/deviceVersionUtils';
104
- import { openFirmwareByteSource } from '../src/api/firmware/FirmwareArtifactSource';
105
104
  import {
106
105
  getPassphraseState,
107
106
  getPassphraseStateWithRefreshDeviceInfo,
@@ -3802,6 +3801,7 @@ describe('API compatibility handling', () => {
3802
3801
  });
3803
3802
 
3804
3803
  describe('Protocol V2 firmware update targets', () => {
3804
+ const OKPP_HEADER_SIZE = 0x52a0;
3805
3805
  let forceReloadDataSpy: jest.SpyInstance;
3806
3806
 
3807
3807
  beforeEach(() => {
@@ -3812,6 +3812,38 @@ describe('Protocol V2 firmware update targets', () => {
3812
3812
  jest.restoreAllMocks();
3813
3813
  });
3814
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
+
3815
3847
  test('keeps Protocol V2 firmware updates off the firmwareUpdateV3 path', () => {
3816
3848
  const method = new FirmwareUpdateV3({
3817
3849
  id: 1,
@@ -3919,7 +3951,7 @@ describe('Protocol V2 firmware update targets', () => {
3919
3951
  });
3920
3952
  });
3921
3953
 
3922
- test('delegates Protocol V2 mode transition to the phase executor', async () => {
3954
+ test('enters Protocol V2 bootloader before upload', async () => {
3923
3955
  const method = new FirmwareUpdateV4({
3924
3956
  id: 1,
3925
3957
  payload: {
@@ -3954,7 +3986,7 @@ describe('Protocol V2 firmware update targets', () => {
3954
3986
 
3955
3987
  await method.run();
3956
3988
 
3957
- expect((method as any).enterProtocolV2BootloaderMode).not.toHaveBeenCalled();
3989
+ expect((method as any).enterProtocolV2BootloaderMode).toHaveBeenCalledTimes(1);
3958
3990
  expect((method as any).executeProtocolV2Update).toHaveBeenCalledWith({
3959
3991
  fwBinaryMap: [
3960
3992
  {
@@ -3965,6 +3997,7 @@ describe('Protocol V2 firmware update targets', () => {
3965
3997
  ],
3966
3998
  bootloaderBinary: null,
3967
3999
  });
4000
+ expect(method.postTipMessage).toHaveBeenCalledWith('SwitchFirmwareReconnectDevice');
3968
4001
  });
3969
4002
 
3970
4003
  test('enters Protocol V2 bootloader before reading and downloading remote resources', async () => {
@@ -4741,9 +4774,9 @@ describe('Protocol V2 firmware update targets', () => {
4741
4774
  const typedCall = jest
4742
4775
  .fn()
4743
4776
  .mockRejectedValue(
4744
- Object.assign(
4745
- new Error("Failed to execute 'open' on 'USBDevice': The device was disconnected"),
4746
- { name: 'NotFoundError' }
4777
+ new DOMException(
4778
+ "Failed to execute 'open' on 'USBDevice': The device was disconnected",
4779
+ 'NotFoundError'
4747
4780
  )
4748
4781
  );
4749
4782
 
@@ -4964,181 +4997,6 @@ describe('Protocol V2 firmware update targets', () => {
4964
4997
  expect((method as any).probeProtocolV2NormalMode).toHaveBeenCalledWith(deviceInfo);
4965
4998
  });
4966
4999
 
4967
- test('rejects a finished target whose payload version conflicts with the plan', () => {
4968
- const method = new FirmwareUpdateV4({
4969
- id: 1,
4970
- payload: {
4971
- method: 'firmwareUpdateV4',
4972
- platform: 'desktop',
4973
- expectedTargetVersions: {
4974
- app_v1: '2.0.0',
4975
- },
4976
- },
4977
- });
4978
- method.init();
4979
- method.postProgressMessage = jest.fn();
4980
-
4981
- expect(() =>
4982
- (method as any).assertProtocolV2TargetStatus(
4983
- [{ target_id: 4, status: 2, payload_version: 0x010000 }],
4984
- new Set([4])
4985
- )
4986
- ).toThrow('expected 131072');
4987
- });
4988
-
4989
- test('fails install polling immediately with the original firmware verification error', async () => {
4990
- const method = new FirmwareUpdateV4({
4991
- id: 1,
4992
- payload: {
4993
- method: 'firmwareUpdateV4',
4994
- platform: 'desktop',
4995
- expectedTargetVersions: {
4996
- app_v1: '2.0.0',
4997
- },
4998
- },
4999
- });
5000
- method.init();
5001
- const typedCall = jest.fn().mockResolvedValue({
5002
- type: 'DeviceFirmwareUpdateStatus',
5003
- message: {
5004
- records: [{ target_id: 4, status: 2, payload_version: 0x010000 }],
5005
- },
5006
- });
5007
- const dateNowSpy = jest
5008
- .spyOn(Date, 'now')
5009
- .mockReturnValueOnce(0)
5010
- .mockReturnValueOnce(0)
5011
- .mockReturnValue(300_001);
5012
- const setTimeoutSpy = jest.spyOn(global, 'setTimeout').mockImplementation(((
5013
- callback: () => void
5014
- ) => {
5015
- callback();
5016
- return 0 as any;
5017
- }) as typeof setTimeout);
5018
-
5019
- (method as any).device = stubDevice({
5020
- getCommands: () => ({ typedCall }),
5021
- });
5022
- (method as any).reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
5023
- (method as any).verifyProtocolV2ReconnectIdentity = jest.fn().mockResolvedValue(undefined);
5024
-
5025
- try {
5026
- await expect(
5027
- (method as any).waitForProtocolV2FirmwareUpdateComplete([
5028
- { target_id: 4, path: 'vol0:/application_p1.bin' },
5029
- ])
5030
- ).rejects.toMatchObject({
5031
- errorCode: HardwareErrorCode.FirmwareVerificationFailed,
5032
- message: expect.stringContaining('reached payload version 65536, expected 131072'),
5033
- });
5034
- } finally {
5035
- dateNowSpy.mockRestore();
5036
- setTimeoutSpy.mockRestore();
5037
- }
5038
-
5039
- expect(typedCall).toHaveBeenCalledTimes(1);
5040
- });
5041
-
5042
- test('fails install polling immediately with the original status conflict error', async () => {
5043
- const method = new FirmwareUpdateV4({
5044
- id: 1,
5045
- payload: {
5046
- method: 'firmwareUpdateV4',
5047
- },
5048
- });
5049
- const typedCall = jest.fn().mockResolvedValue({
5050
- type: 'DeviceFirmwareUpdateStatus',
5051
- message: {
5052
- records: [{ target_id: 4, status: 2, path: 'vol0:/unexpected.bin' }],
5053
- },
5054
- });
5055
- const dateNowSpy = jest
5056
- .spyOn(Date, 'now')
5057
- .mockReturnValueOnce(0)
5058
- .mockReturnValueOnce(0)
5059
- .mockReturnValue(300_001);
5060
- const setTimeoutSpy = jest.spyOn(global, 'setTimeout').mockImplementation(((
5061
- callback: () => void
5062
- ) => {
5063
- callback();
5064
- return 0 as any;
5065
- }) as typeof setTimeout);
5066
-
5067
- (method as any).device = stubDevice({
5068
- getCommands: () => ({ typedCall }),
5069
- });
5070
- (method as any).reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
5071
- (method as any).verifyProtocolV2ReconnectIdentity = jest.fn().mockResolvedValue(undefined);
5072
-
5073
- try {
5074
- await expect(
5075
- (method as any).waitForProtocolV2FirmwareUpdateComplete([
5076
- { target_id: 4, path: 'vol0:/application_p1.bin' },
5077
- ])
5078
- ).rejects.toMatchObject({
5079
- errorCode: HardwareErrorCode.RuntimeError,
5080
- message: 'Protocol V2 install status conflicts with target 4',
5081
- params: {
5082
- firmwareUpdateCode: 'FirmwareInstallStatusConflict',
5083
- },
5084
- });
5085
- } finally {
5086
- dateNowSpy.mockRestore();
5087
- setTimeoutSpy.mockRestore();
5088
- }
5089
-
5090
- expect(typedCall).toHaveBeenCalledTimes(1);
5091
- });
5092
-
5093
- test('does not fail a completed multi-app install when final app versions are unobservable', () => {
5094
- const method = new FirmwareUpdateV4({
5095
- id: 1,
5096
- payload: {
5097
- method: 'firmwareUpdateV4',
5098
- platform: 'desktop',
5099
- targetsToUpdate: ['app_v1', 'app_v2'],
5100
- expectedTargetVersions: {
5101
- app_v1: '1.0.0',
5102
- app_v2: '2.0.0',
5103
- },
5104
- },
5105
- });
5106
- method.init();
5107
- (method as any).protocolV2LatestFinalFeatures = {
5108
- major_version: 3,
5109
- minor_version: 0,
5110
- patch_version: 0,
5111
- };
5112
- (method as any).protocolV2FinalStatusVerified = true;
5113
-
5114
- expect(() => (method as any).assertExpectedProtocolV2Versions()).not.toThrow();
5115
- });
5116
-
5117
- test('rejects unobservable final versions after the status endpoint fallback', () => {
5118
- const method = new FirmwareUpdateV4({
5119
- id: 1,
5120
- payload: {
5121
- method: 'firmwareUpdateV4',
5122
- platform: 'desktop',
5123
- targetsToUpdate: ['app_v1', 'app_v2'],
5124
- expectedTargetVersions: {
5125
- app_v1: '1.0.0',
5126
- app_v2: '2.0.0',
5127
- },
5128
- },
5129
- });
5130
- method.init();
5131
- (method as any).protocolV2LatestFinalFeatures = {
5132
- major_version: 3,
5133
- minor_version: 0,
5134
- patch_version: 0,
5135
- };
5136
-
5137
- expect(() => (method as any).assertExpectedProtocolV2Versions()).toThrow(
5138
- 'has no observable final version after status fallback'
5139
- );
5140
- });
5141
-
5142
5000
  test('polls target status after update ACK and finishes when all targets complete', async () => {
5143
5001
  const method = new FirmwareUpdateV4({
5144
5002
  id: 1,
@@ -5360,7 +5218,7 @@ describe('Protocol V2 firmware update targets', () => {
5360
5218
  ).toBe(true);
5361
5219
 
5362
5220
  (method.postProgressMessage as jest.Mock).mockClear();
5363
- expect(() =>
5221
+ expect(
5364
5222
  (method as any).assertProtocolV2TargetStatus(
5365
5223
  [
5366
5224
  { target_id: 4, status: 'FW_MGMT_UPDATER_TASK_STATUS_FINISHED' },
@@ -5368,8 +5226,8 @@ describe('Protocol V2 firmware update targets', () => {
5368
5226
  ],
5369
5227
  new Set([4, 10])
5370
5228
  )
5371
- ).toThrow('Protocol V2 install status conflicts with target 4');
5372
- expect(method.postProgressMessage).not.toHaveBeenCalled();
5229
+ ).toBe(false);
5230
+ expect(method.postProgressMessage).toHaveBeenCalledWith(50, 'installingFirmware');
5373
5231
  expect(method.postProgressMessage).not.toHaveBeenCalledWith(100, 'installingFirmware');
5374
5232
 
5375
5233
  expect(
@@ -5473,14 +5331,10 @@ describe('Protocol V2 firmware update targets', () => {
5473
5331
  const writtenPaths: string[] = [];
5474
5332
  method.postTipMessage = jest.fn();
5475
5333
  method.postProgressMessage = jest.fn();
5476
- (method as any).protocolV2SourceUpdateProcess = jest.fn().mockImplementation(params => {
5334
+ (method as any).protocolV2CommonUpdateProcess = jest.fn().mockImplementation(params => {
5477
5335
  writtenPaths.push(params.filePath);
5478
- return Number(params.processedSize ?? 0) + Number(params.source.size);
5336
+ return Number(params.processedSize ?? 0) + Number(params.payload.byteLength);
5479
5337
  });
5480
- (method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(undefined);
5481
- (method as any).exitProtocolV2BootloaderToNormal = jest.fn().mockResolvedValue(undefined);
5482
- (method as any).waitForProtocolV2FinalFeatures = jest.fn().mockResolvedValue({});
5483
- (method as any).completeProtocolV2FinalVerification = jest.fn().mockResolvedValue({});
5484
5338
  (method as any).verifyProtocolV2StagedFile = jest.fn().mockResolvedValue(undefined);
5485
5339
  (method as any).protocolV2StartFirmwareUpdate = jest.fn().mockResolvedValue(undefined);
5486
5340
  (method as any).waitForProtocolV2FirmwareUpdateComplete = jest
@@ -5520,12 +5374,10 @@ describe('Protocol V2 firmware update targets', () => {
5520
5374
  'vol0:/coprocessor.bin',
5521
5375
  1
5522
5376
  );
5523
- expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenCalledTimes(2);
5524
- expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenNthCalledWith(1, {
5525
- targets: [{ target_id: 3, path: 'vol0:/bootloader.bin' }],
5526
- });
5527
- expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenNthCalledWith(2, {
5377
+ expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenCalledTimes(1);
5378
+ expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenCalledWith({
5528
5379
  targets: [
5380
+ { target_id: 3, path: 'vol0:/bootloader.bin' },
5529
5381
  { target_id: 6, path: 'vol0:/coprocessor.bin' },
5530
5382
  { target_id: 7, path: 'vol0:/se01.bin' },
5531
5383
  { target_id: 4, path: 'vol0:/application_p1.bin' },
@@ -5535,7 +5387,7 @@ describe('Protocol V2 firmware update targets', () => {
5535
5387
  expect((method as any).waitForProtocolV2FirmwareUpdateComplete).toHaveBeenCalled();
5536
5388
  });
5537
5389
 
5538
- test('announces separate resource and component transfer phases', async () => {
5390
+ test('announces one transfer when syncing resource bundles and staging firmware', async () => {
5539
5391
  const method = new FirmwareUpdateV4({
5540
5392
  id: 1,
5541
5393
  payload: {
@@ -5545,14 +5397,11 @@ describe('Protocol V2 firmware update targets', () => {
5545
5397
 
5546
5398
  method.postTipMessage = jest.fn();
5547
5399
  method.postProgressMessage = jest.fn();
5548
- (method as any).protocolV2SourceUpdateProcess = jest
5400
+ (method as any).protocolV2CommonUpdateProcess = jest
5549
5401
  .fn()
5550
5402
  .mockImplementation(params =>
5551
- Promise.resolve(Number(params.processedSize ?? 0) + Number(params.source.size))
5403
+ Promise.resolve(Number(params.processedSize ?? 0) + Number(params.payload.byteLength))
5552
5404
  );
5553
- (method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(undefined);
5554
- (method as any).exitProtocolV2BootloaderToNormal = jest.fn().mockResolvedValue(undefined);
5555
- (method as any).completeProtocolV2FinalVerification = jest.fn().mockResolvedValue({});
5556
5405
  (method as any).verifyProtocolV2StagedFile = jest.fn().mockResolvedValue(undefined);
5557
5406
  (method as any).protocolV2StartFirmwareUpdate = jest.fn().mockResolvedValue(undefined);
5558
5407
  (method as any).waitForProtocolV2FirmwareUpdateComplete = jest
@@ -5572,22 +5421,21 @@ describe('Protocol V2 firmware update targets', () => {
5572
5421
  {
5573
5422
  fileName: 'application_p1.bin',
5574
5423
  binary: new Uint8Array([3]).buffer,
5575
- targetId: 4,
5424
+ targetId: 3,
5576
5425
  },
5577
5426
  ],
5578
5427
  });
5579
5428
 
5580
- expect(method.postTipMessage).toHaveBeenCalledTimes(3);
5429
+ expect(method.postTipMessage).toHaveBeenCalledTimes(2);
5581
5430
  expect(method.postTipMessage).toHaveBeenNthCalledWith(1, 'StartTransferData');
5582
- expect(method.postTipMessage).toHaveBeenNthCalledWith(2, 'StartTransferData');
5583
- expect(method.postTipMessage).toHaveBeenNthCalledWith(3, 'ConfirmOnDevice');
5584
- expect((method as any).protocolV2SourceUpdateProcess).toHaveBeenNthCalledWith(
5431
+ expect(method.postTipMessage).toHaveBeenNthCalledWith(2, 'ConfirmOnDevice');
5432
+ expect((method as any).protocolV2CommonUpdateProcess).toHaveBeenNthCalledWith(
5585
5433
  1,
5586
- expect.objectContaining({ processedSize: 0, totalSize: 2 })
5434
+ expect.objectContaining({ processedSize: 0, totalSize: 3 })
5587
5435
  );
5588
- expect((method as any).protocolV2SourceUpdateProcess).toHaveBeenNthCalledWith(
5436
+ expect((method as any).protocolV2CommonUpdateProcess).toHaveBeenNthCalledWith(
5589
5437
  2,
5590
- expect.objectContaining({ processedSize: 0, totalSize: 1 })
5438
+ expect.objectContaining({ processedSize: 2, totalSize: 3 })
5591
5439
  );
5592
5440
  });
5593
5441
 
@@ -5601,8 +5449,7 @@ describe('Protocol V2 firmware update targets', () => {
5601
5449
 
5602
5450
  method.postTipMessage = jest.fn();
5603
5451
  method.postProgressMessage = jest.fn();
5604
- (method as any).protocolV2SourceUpdateProcess = jest.fn().mockResolvedValue(3);
5605
- (method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(undefined);
5452
+ (method as any).protocolV2CommonUpdateProcess = jest.fn().mockResolvedValue(3);
5606
5453
  (method as any).device = stubDevice({
5607
5454
  getCommands: () => ({
5608
5455
  typedCall: jest.fn().mockResolvedValue({
@@ -5648,14 +5495,11 @@ describe('Protocol V2 firmware update targets', () => {
5648
5495
 
5649
5496
  method.postTipMessage = jest.fn();
5650
5497
  method.postProgressMessage = jest.fn();
5651
- (method as any).protocolV2SourceUpdateProcess = jest
5498
+ (method as any).protocolV2CommonUpdateProcess = jest
5652
5499
  .fn()
5653
5500
  .mockImplementation(params =>
5654
- Promise.resolve(Number(params.processedSize ?? 0) + Number(params.source.size))
5501
+ Promise.resolve(Number(params.processedSize ?? 0) + Number(params.payload.byteLength))
5655
5502
  );
5656
- (method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(undefined);
5657
- (method as any).exitProtocolV2BootloaderToNormal = jest.fn().mockResolvedValue(undefined);
5658
- (method as any).completeProtocolV2FinalVerification = jest.fn().mockResolvedValue({});
5659
5503
  (method as any).verifyProtocolV2StagedFile = jest.fn().mockResolvedValue(undefined);
5660
5504
  (method as any).protocolV2StartFirmwareUpdate = jest.fn().mockResolvedValue(undefined);
5661
5505
  (method as any).waitForProtocolV2FirmwareUpdateComplete = jest
@@ -5978,58 +5822,6 @@ describe('Protocol V2 firmware update targets', () => {
5978
5822
  });
5979
5823
  });
5980
5824
 
5981
- test('rejects prepared RESC bundle paths before opening an artifact source', async () => {
5982
- const method = new FirmwareUpdateV4({
5983
- id: 1,
5984
- payload: {
5985
- method: 'firmwareUpdateV4',
5986
- },
5987
- });
5988
- const openPreparedSource = jest.fn();
5989
- (method as any).params = {
5990
- targetsToUpdate: ['resource'],
5991
- resourceBundleArtifacts: [
5992
- {
5993
- name: 'images',
5994
- artifact: {
5995
- artifactRef: `fw:${'a'.repeat(64)}`,
5996
- size: 1,
5997
- sha256: 'a'.repeat(64),
5998
- },
5999
- },
6000
- ],
6001
- };
6002
- (method as any).openProtocolV2PreparedSource = openPreparedSource;
6003
- const getFirmwareLatestReleaseSpy = jest
6004
- .spyOn(DataManager, 'getFirmwareLatestRelease')
6005
- .mockReturnValue({
6006
- required: false,
6007
- version: [1, 0, 0],
6008
- url: '',
6009
- resourceBundles: [
6010
- {
6011
- name: 'images',
6012
- url: 'https://example.com/images.okpkg',
6013
- devicePath: 'vol0:/resource/../images.okpkg',
6014
- },
6015
- ],
6016
- fingerprint: '',
6017
- changelog: {
6018
- 'zh-CN': '',
6019
- 'en-US': '',
6020
- },
6021
- });
6022
-
6023
- await expect(
6024
- (method as any).prepareProtocolV2ResourceSources('universal', {
6025
- deviceType: 'pro2',
6026
- })
6027
- ).rejects.toThrow('resourceBundles[].devicePath');
6028
- expect(openPreparedSource).not.toHaveBeenCalled();
6029
-
6030
- getFirmwareLatestReleaseSpy.mockRestore();
6031
- });
6032
-
6033
5825
  test('stops a remote update before reboot when the latest config cannot be refreshed', async () => {
6034
5826
  const method = new FirmwareUpdateV4({
6035
5827
  id: 1,
@@ -6192,10 +5984,7 @@ describe('Protocol V2 firmware update targets', () => {
6192
5984
 
6193
5985
  method.postTipMessage = jest.fn();
6194
5986
  method.postProgressMessage = jest.fn();
6195
- (method as any).protocolV2SourceUpdateProcess = jest.fn().mockResolvedValue(3);
6196
- (method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(undefined);
6197
- (method as any).completeProtocolV2FinalVerification = jest.fn().mockResolvedValue({});
6198
- (method as any).verifyProtocolV2StagedFile = jest.fn().mockResolvedValue(undefined);
5987
+ (method as any).protocolV2CommonUpdateProcess = jest.fn().mockResolvedValue(3);
6199
5988
  (method as any).protocolV2StartFirmwareUpdate = jest.fn();
6200
5989
  (method as any).waitForProtocolV2FirmwareUpdateComplete = jest.fn();
6201
5990
 
@@ -6211,7 +6000,7 @@ describe('Protocol V2 firmware update targets', () => {
6211
6000
  fwBinaryMap: [],
6212
6001
  });
6213
6002
 
6214
- expect((method as any).protocolV2SourceUpdateProcess).toHaveBeenCalledTimes(1);
6003
+ expect((method as any).protocolV2CommonUpdateProcess).toHaveBeenCalledTimes(1);
6215
6004
  expect((method as any).protocolV2StartFirmwareUpdate).not.toHaveBeenCalled();
6216
6005
  expect((method as any).waitForProtocolV2FirmwareUpdateComplete).not.toHaveBeenCalled();
6217
6006
  });
@@ -6244,19 +6033,12 @@ describe('Protocol V2 firmware update targets', () => {
6244
6033
  });
6245
6034
  method.postMessage = jest.fn();
6246
6035
 
6247
- const source = await openFirmwareByteSource({
6248
- binary: new Uint8Array(4097).buffer,
6036
+ await (method as any).protocolV2CommonUpdateProcess({
6037
+ payload: new Uint8Array(4097).buffer,
6038
+ filePath: 'vol1:firmware.bin',
6039
+ processedSize: 0,
6040
+ totalSize: 4097,
6249
6041
  });
6250
- try {
6251
- await (method as any).protocolV2SourceUpdateProcess({
6252
- source,
6253
- filePath: 'vol1:firmware.bin',
6254
- processedSize: 0,
6255
- totalSize: 4097,
6256
- });
6257
- } finally {
6258
- await source?.close();
6259
- }
6260
6042
 
6261
6043
  const writePayloads = typedCall.mock.calls.map(call => call[2]);
6262
6044
  expect(writePayloads.map(payload => payload.file.offset)).toEqual([0, 4000]);
@@ -6277,13 +6059,7 @@ describe('Protocol V2 firmware update targets', () => {
6277
6059
  });
6278
6060
  });
6279
6061
 
6280
- // TODO(#850/#855): PR #855 added resume-on-retry and per-chunk retry on the
6281
- // writeProtocolV2File path. PR #850 replaced that path with FirmwareByteSource
6282
- // streaming (protocolV2SourceUpdateProcess), which restarts a failed transfer from
6283
- // offset 0 and retries the whole file instead of a single chunk. Re-enable after the
6284
- // resume/per-chunk-retry behavior is re-implemented on writeFirmwareByteSource, which
6285
- // needs a start-offset parameter in FirmwareArtifactSource.ts.
6286
- test.skip('resumes from the last confirmed offset after an ambiguous chunk write failure', async () => {
6062
+ test('resumes from the last confirmed offset after an ambiguous chunk write failure', async () => {
6287
6063
  const method = new FirmwareUpdateV4({
6288
6064
  id: 1,
6289
6065
  payload: {
@@ -6341,16 +6117,12 @@ describe('Protocol V2 firmware update targets', () => {
6341
6117
  method.postProgressMessage = jest.fn();
6342
6118
 
6343
6119
  try {
6344
- const source = await openFirmwareByteSource({
6345
- binary: new Uint8Array(8001).buffer,
6346
- });
6347
- await (method as any).protocolV2SourceUpdateProcess({
6348
- source,
6120
+ await (method as any).protocolV2CommonUpdateProcess({
6121
+ payload: new Uint8Array(8001).buffer,
6349
6122
  filePath: 'vol0:/firmware.bin',
6350
6123
  processedSize: 0,
6351
6124
  totalSize: 8001,
6352
6125
  });
6353
- await source?.close();
6354
6126
  } finally {
6355
6127
  setTimeoutSpy.mockRestore();
6356
6128
  }
@@ -6365,13 +6137,7 @@ describe('Protocol V2 firmware update targets', () => {
6365
6137
  expect(initialize).toHaveBeenCalledTimes(1);
6366
6138
  });
6367
6139
 
6368
- // TODO(#850/#855): PR #855 added resume-on-retry and per-chunk retry on the
6369
- // writeProtocolV2File path. PR #850 replaced that path with FirmwareByteSource
6370
- // streaming (protocolV2SourceUpdateProcess), which restarts a failed transfer from
6371
- // offset 0 and retries the whole file instead of a single chunk. Re-enable after the
6372
- // resume/per-chunk-retry behavior is re-implemented on writeFirmwareByteSource, which
6373
- // needs a start-offset parameter in FirmwareArtifactSource.ts.
6374
- test.skip('restarts from offset zero when remote staging is behind the confirmed offset', async () => {
6140
+ test('restarts from offset zero when remote staging is behind the confirmed offset', async () => {
6375
6141
  const method = new FirmwareUpdateV4({
6376
6142
  id: 1,
6377
6143
  payload: { method: 'firmwareUpdateV4' },
@@ -6415,13 +6181,7 @@ describe('Protocol V2 firmware update targets', () => {
6415
6181
  expect((method as any).recoverProtocolV2FileTransfer).toHaveBeenCalledTimes(1);
6416
6182
  });
6417
6183
 
6418
- // TODO(#850/#855): PR #855 added resume-on-retry and per-chunk retry on the
6419
- // writeProtocolV2File path. PR #850 replaced that path with FirmwareByteSource
6420
- // streaming (protocolV2SourceUpdateProcess), which restarts a failed transfer from
6421
- // offset 0 and retries the whole file instead of a single chunk. Re-enable after the
6422
- // resume/per-chunk-retry behavior is re-implemented on writeFirmwareByteSource, which
6423
- // needs a start-offset parameter in FirmwareArtifactSource.ts.
6424
- test.skip('retries an idempotent firmware chunk before restarting the whole file', async () => {
6184
+ test('retries an idempotent firmware chunk before restarting the whole file', async () => {
6425
6185
  const method = new FirmwareUpdateV4({
6426
6186
  id: 1,
6427
6187
  payload: { method: 'firmwareUpdateV4' },
@@ -6478,30 +6238,19 @@ describe('Protocol V2 firmware update targets', () => {
6478
6238
  });
6479
6239
  method.postProgressMessage = jest.fn();
6480
6240
  method.postTipMessage = jest.fn();
6481
- (method as any).recoverProtocolV2FileTransfer = jest.fn().mockResolvedValue(undefined);
6482
6241
 
6483
- const source = await openFirmwareByteSource({
6484
- binary: new Uint8Array(4097).buffer,
6485
- });
6486
6242
  await expect(
6487
- (method as any).protocolV2SourceUpdateProcess({
6488
- source,
6243
+ (method as any).protocolV2WriteWholeFile({
6244
+ payload: new Uint8Array(4097).buffer,
6489
6245
  filePath: 'vol0:/firmware.bin',
6490
6246
  processedSize: 0,
6491
6247
  totalSize: 4097,
6492
6248
  })
6493
6249
  ).rejects.toMatchObject({ errorCode: HardwareErrorCode.EmmcFileWriteFirmwareError });
6494
- await source?.close();
6495
- expect(typedCall).toHaveBeenCalledTimes(6);
6250
+ expect(typedCall).toHaveBeenCalledTimes(2);
6496
6251
  });
6497
6252
 
6498
- // TODO(#850/#855): PR #855 added resume-on-retry and per-chunk retry on the
6499
- // writeProtocolV2File path. PR #850 replaced that path with FirmwareByteSource
6500
- // streaming (protocolV2SourceUpdateProcess), which restarts a failed transfer from
6501
- // offset 0 and retries the whole file instead of a single chunk. Re-enable after the
6502
- // resume/per-chunk-retry behavior is re-implemented on writeFirmwareByteSource, which
6503
- // needs a start-offset parameter in FirmwareArtifactSource.ts.
6504
- test.skip('coalesces public progress events without dropping confirmed-byte callbacks', async () => {
6253
+ test('coalesces public progress events without dropping confirmed-byte callbacks', async () => {
6505
6254
  const method = new FirmwareUpdateV4({
6506
6255
  id: 1,
6507
6256
  payload: { method: 'firmwareUpdateV4' },
@@ -6567,19 +6316,12 @@ describe('Protocol V2 firmware update targets', () => {
6567
6316
  });
6568
6317
  method.postProgressMessage = jest.fn();
6569
6318
 
6570
- const source = await openFirmwareByteSource({
6571
- binary: new Uint8Array(1801).buffer,
6319
+ await (method as any).protocolV2CommonUpdateProcess({
6320
+ payload: new Uint8Array(1801).buffer,
6321
+ filePath: 'vol1:ble-firmware.bin',
6322
+ processedSize: 0,
6323
+ totalSize: 1801,
6572
6324
  });
6573
- try {
6574
- await (method as any).protocolV2SourceUpdateProcess({
6575
- source,
6576
- filePath: 'vol1:ble-firmware.bin',
6577
- processedSize: 0,
6578
- totalSize: 1801,
6579
- });
6580
- } finally {
6581
- await source?.close();
6582
- }
6583
6325
 
6584
6326
  const writePayloads = typedCall.mock.calls.map(call => call[2]);
6585
6327
  expect(writePayloads.map(payload => payload.file.offset)).toEqual([0, 1800]);
@@ -6627,16 +6369,12 @@ describe('Protocol V2 firmware update targets', () => {
6627
6369
  });
6628
6370
  method.postProgressMessage = jest.fn();
6629
6371
 
6630
- const source = await openFirmwareByteSource({
6631
- binary: new Uint8Array(1961).buffer,
6632
- });
6633
- await (method as any).protocolV2SourceUpdateProcess({
6634
- source,
6372
+ await (method as any).protocolV2CommonUpdateProcess({
6373
+ payload: new Uint8Array(1961).buffer,
6635
6374
  filePath: 'vol0:/application_p1.bin',
6636
6375
  processedSize: 0,
6637
6376
  totalSize: 1961,
6638
6377
  });
6639
- await source?.close();
6640
6378
 
6641
6379
  const writePayloads = typedCall.mock.calls.map(call => call[2]);
6642
6380
  expect(writePayloads.map(payload => payload.file.offset)).toEqual([0, 1960]);
@@ -6752,9 +6490,9 @@ describe('Protocol V2 firmware update method', () => {
6752
6490
  const typedCall = jest
6753
6491
  .fn()
6754
6492
  .mockRejectedValue(
6755
- Object.assign(
6756
- new Error("Failed to execute 'open' on 'USBDevice': The device was disconnected"),
6757
- { name: 'NotFoundError' }
6493
+ new DOMException(
6494
+ "Failed to execute 'open' on 'USBDevice': The device was disconnected",
6495
+ 'NotFoundError'
6758
6496
  )
6759
6497
  );
6760
6498
 
@@ -6977,21 +6715,6 @@ describe('Protocol V2 firmware reconnect identity', () => {
6977
6715
  });
6978
6716
  };
6979
6717
 
6980
- test('keeps the host plan identity as the V4 reconnect anchor', () => {
6981
- const method = new FirmwareUpdateV4({
6982
- id: 1,
6983
- payload: {
6984
- method: 'firmwareUpdateV4',
6985
- platform: 'native',
6986
- expectedDeviceId: 'persisted-device-id',
6987
- },
6988
- });
6989
-
6990
- method.init();
6991
-
6992
- expect((method as any).params.expectedDeviceId).toBe('persisted-device-id');
6993
- });
6994
-
6995
6718
  test('rejects a different physical serial before firmware transfer resumes', () => {
6996
6719
  expect(() => assertProtocolV2ReconnectIdentity('expected-serial', 'other-serial')).toThrow(
6997
6720
  'identity mismatch'
@@ -7001,15 +6724,15 @@ describe('Protocol V2 firmware reconnect identity', () => {
7001
6724
  ).not.toThrow();
7002
6725
  });
7003
6726
 
7004
- test('fails closed when the physical serial is unavailable', () => {
7005
- expect(() => assertProtocolV2ReconnectIdentity('expected-serial', undefined)).toThrow(
7006
- expect.objectContaining({ errorCode: HardwareErrorCode.DeviceNotFound })
7007
- );
7008
- expect(() => assertProtocolV2ReconnectIdentity(undefined, 'actual-serial')).toThrow(
7009
- expect.objectContaining({ errorCode: HardwareErrorCode.DeviceNotFound })
7010
- );
6727
+ test('requires the same descriptor path when either physical serial is unavailable', () => {
6728
+ expect(() =>
6729
+ assertProtocolV2ReconnectIdentity('expected-serial', undefined, 'usb-path', 'usb-path')
6730
+ ).not.toThrow();
6731
+ expect(() =>
6732
+ assertProtocolV2ReconnectIdentity(undefined, 'actual-serial', 'usb-path', 'other-path')
6733
+ ).toThrow('identity unavailable');
7011
6734
  expect(() => assertProtocolV2ReconnectIdentity(undefined, undefined)).toThrow(
7012
- expect.objectContaining({ errorCode: HardwareErrorCode.DeviceNotFound })
6735
+ 'identity unavailable'
7013
6736
  );
7014
6737
  });
7015
6738
 
@@ -7028,6 +6751,7 @@ describe('Protocol V2 firmware reconnect identity', () => {
7028
6751
  },
7029
6752
  });
7030
6753
  (method as any).device = stubDevice({
6754
+ originalDescriptor: { id: 'usb-id', path: 'usb-path', protocolType: 'V2' },
7031
6755
  features: { deviceId: 'wallet-derived-id' },
7032
6756
  getCommands: () => ({ typedCall }),
7033
6757
  });
@@ -7043,7 +6767,7 @@ describe('Protocol V2 firmware reconnect identity', () => {
7043
6767
  );
7044
6768
  });
7045
6769
 
7046
- test('rejects firmware update startup when DeviceInfo has no physical serial', async () => {
6770
+ test('allows firmware update startup when DeviceInfo has no physical serial', async () => {
7047
6771
  const method = new FirmwareUpdateV4({
7048
6772
  id: 1,
7049
6773
  payload: { method: 'firmwareUpdateV4' },
@@ -7053,42 +6777,13 @@ describe('Protocol V2 firmware reconnect identity', () => {
7053
6777
  message: { protocol_version: 1, hw: {} },
7054
6778
  });
7055
6779
  (method as any).device = stubDevice({
6780
+ originalDescriptor: { id: 'usb-id', path: 'usb-path', protocolType: 'V2' },
7056
6781
  getCommands: () => ({ typedCall }),
7057
6782
  });
7058
6783
 
7059
- await expect((method as any).captureProtocolV2PhysicalIdentity()).rejects.toMatchObject({
7060
- errorCode: HardwareErrorCode.DeviceNotFound,
7061
- });
7062
- });
7063
-
7064
- test('uses the BLE read limit when reading an existing firmware bundle header', async () => {
7065
- const method = new FirmwareUpdateV4({
7066
- id: 1,
7067
- payload: {
7068
- method: 'firmwareUpdateV4',
7069
- platform: 'native',
7070
- chunkSize: 1800,
7071
- },
7072
- });
7073
- method.init();
7074
- const readChunkLengths: number[] = [];
7075
- const typedCall = jest.fn((requestType: string, _responseType: string, request: any) => {
7076
- if (requestType === 'FilesystemPathInfoQuery') {
7077
- return Promise.resolve({ message: { exist: true, directory: false, size: 0x52a0 } });
7078
- }
7079
- if (requestType === 'FilesystemFileRead') {
7080
- readChunkLengths.push(request.chunk_len);
7081
- return Promise.resolve({ message: { data: new Uint8Array(request.chunk_len) } });
7082
- }
7083
- throw new Error(`Unexpected request: ${requestType}`);
7084
- });
7085
- (method as any).device = stubDevice({
7086
- getCommands: () => ({ typedCall }),
7087
- });
7088
-
7089
- await expect((method as any).readProtocolV2DeviceFileHeader('bundle.okpp')).resolves.toBeNull();
7090
- expect(readChunkLengths.length).toBeGreaterThan(1);
7091
- expect(Math.max(...readChunkLengths)).toBe(900);
6784
+ await expect((method as any).captureProtocolV2PhysicalIdentity()).resolves.toBeUndefined();
6785
+ expect((method as any).protocolV2ExpectedSerialNumber).toBeUndefined();
6786
+ expect((method as any).protocolV2ExpectedPath).toBeDefined();
7092
6787
  });
7093
6788
 
7094
6789
  test('uses filesystem reads instead of ResourceInventoryGet for resource comparison', async () => {