@onekeyfe/hd-core 1.2.0-alpha.97 → 1.2.0-alpha.99

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 (103) hide show
  1. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +411 -86
  2. package/__tests__/check-firmware-release-protocol-v2.test.ts +199 -0
  3. package/__tests__/firmware-memory-host.test.ts +126 -0
  4. package/__tests__/firmware-update/device-update-bootloader.test.ts +14 -2
  5. package/__tests__/firmware-update/firmware-host-binding.test.ts +9 -0
  6. package/__tests__/firmware-update/firmware-prepared-host-digest.test.ts +573 -0
  7. package/__tests__/firmware-update/firmware-prepared-resource-archive.test.ts +150 -0
  8. package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +65 -2
  9. package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +80 -0
  10. package/__tests__/firmware-update/firmware-update-plan.test.ts +293 -71
  11. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +53 -1
  12. package/__tests__/firmware-update/firmware-update-prepared-resource-executors.test.ts +326 -0
  13. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +2 -2
  14. package/__tests__/protocol-v2-firmware-targets.test.ts +16 -0
  15. package/__tests__/protocol-v2-resources.test.ts +148 -232
  16. package/__tests__/protocol-v2.test.ts +1326 -510
  17. package/dist/api/CheckAllFirmwareRelease.d.ts +2 -3
  18. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  19. package/dist/api/CheckBLEFirmwareRelease.d.ts +3 -2
  20. package/dist/api/CheckBLEFirmwareRelease.d.ts.map +1 -1
  21. package/dist/api/CheckBootloaderRelease.d.ts +2 -1
  22. package/dist/api/CheckBootloaderRelease.d.ts.map +1 -1
  23. package/dist/api/CheckFirmwareRelease.d.ts +3 -2
  24. package/dist/api/CheckFirmwareRelease.d.ts.map +1 -1
  25. package/dist/api/FirmwareUpdateV2.d.ts +1 -0
  26. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  27. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  28. package/dist/api/FirmwareUpdateV4.d.ts +11 -9
  29. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  30. package/dist/api/UploadPortfolio.d.ts.map +1 -1
  31. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  32. package/dist/api/firmware/FirmwareHostBinding.d.ts +1 -1
  33. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +1 -1
  34. package/dist/api/firmware/FirmwareMemoryHost.d.ts +22 -0
  35. package/dist/api/firmware/FirmwareMemoryHost.d.ts.map +1 -0
  36. package/dist/api/firmware/FirmwarePreparedResourceArchive.d.ts +11 -0
  37. package/dist/api/firmware/FirmwarePreparedResourceArchive.d.ts.map +1 -0
  38. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  39. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +48 -1
  40. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -1
  41. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +7 -1
  42. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -1
  43. package/dist/api/firmware/getBinary.d.ts +0 -1
  44. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  45. package/dist/api/firmware/protocolV2Release.d.ts +33 -0
  46. package/dist/api/firmware/protocolV2Release.d.ts.map +1 -0
  47. package/dist/data-manager/DataManager.d.ts +4 -3
  48. package/dist/data-manager/DataManager.d.ts.map +1 -1
  49. package/dist/index.d.ts +108 -114
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +1825 -1006
  52. package/dist/protocols/protocol-v2/resources.d.ts +10 -28
  53. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
  54. package/dist/types/api/checkAllFirmwareRelease.d.ts +20 -8
  55. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  56. package/dist/types/api/checkBLEFirmwareRelease.d.ts +2 -13
  57. package/dist/types/api/checkBLEFirmwareRelease.d.ts.map +1 -1
  58. package/dist/types/api/checkBootloaderRelease.d.ts +2 -6
  59. package/dist/types/api/checkBootloaderRelease.d.ts.map +1 -1
  60. package/dist/types/api/checkFirmwareRelease.d.ts +2 -13
  61. package/dist/types/api/checkFirmwareRelease.d.ts.map +1 -1
  62. package/dist/types/api/checkFirmwareTypeAvailable.d.ts +2 -2
  63. package/dist/types/api/checkFirmwareTypeAvailable.d.ts.map +1 -1
  64. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  65. package/dist/types/api/export.d.ts +1 -1
  66. package/dist/types/api/export.d.ts.map +1 -1
  67. package/dist/types/api/firmwareUpdate.d.ts +4 -12
  68. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  69. package/dist/types/api/firmwareUpdatePlan.d.ts +2 -2
  70. package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -1
  71. package/dist/types/settings.d.ts +35 -36
  72. package/dist/types/settings.d.ts.map +1 -1
  73. package/package.json +4 -4
  74. package/src/api/CheckAllFirmwareRelease.ts +113 -80
  75. package/src/api/CheckBLEFirmwareRelease.ts +37 -5
  76. package/src/api/CheckBootloaderRelease.ts +35 -3
  77. package/src/api/CheckFirmwareRelease.ts +35 -7
  78. package/src/api/FirmwareUpdateV2.ts +231 -138
  79. package/src/api/FirmwareUpdateV3.ts +101 -56
  80. package/src/api/FirmwareUpdateV4.ts +936 -408
  81. package/src/api/UploadPortfolio.ts +18 -0
  82. package/src/api/device/DeviceUpdateBootloader.ts +37 -11
  83. package/src/api/firmware/FirmwareHostBinding.ts +26 -3
  84. package/src/api/firmware/FirmwareMemoryHost.ts +143 -0
  85. package/src/api/firmware/FirmwarePreparedResourceArchive.ts +207 -0
  86. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +122 -73
  87. package/src/api/firmware/FirmwareUpdatePlan.ts +295 -122
  88. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +49 -12
  89. package/src/api/firmware/protocolV2Release.ts +168 -0
  90. package/src/data-manager/DataManager.ts +35 -19
  91. package/src/index.ts +10 -0
  92. package/src/protocols/protocol-v2/resources.ts +157 -341
  93. package/src/types/api/checkAllFirmwareRelease.ts +27 -12
  94. package/src/types/api/checkBLEFirmwareRelease.ts +4 -13
  95. package/src/types/api/checkBootloaderRelease.ts +2 -6
  96. package/src/types/api/checkFirmwareRelease.ts +2 -13
  97. package/src/types/api/checkFirmwareTypeAvailable.ts +2 -2
  98. package/src/types/api/deviceUpdateBootloader.ts +1 -0
  99. package/src/types/api/export.ts +6 -1
  100. package/src/types/api/firmwareUpdate.ts +12 -17
  101. package/src/types/api/firmwareUpdatePlan.ts +2 -2
  102. package/src/types/settings.ts +35 -61
  103. package/src/utils/deviceFeaturesUtils.ts +2 -2
@@ -0,0 +1,199 @@
1
+ import { EFirmwareType } from '@onekeyfe/hd-shared';
2
+
3
+ import CheckAllFirmwareRelease from '../src/api/CheckAllFirmwareRelease';
4
+ import CheckBLEFirmwareRelease from '../src/api/CheckBLEFirmwareRelease';
5
+ import CheckBootloaderRelease from '../src/api/CheckBootloaderRelease';
6
+ import CheckFirmwareRelease from '../src/api/CheckFirmwareRelease';
7
+ import { DataManager } from '../src/data-manager';
8
+
9
+ import type { IFirmwareReleaseInfo } from '../src/types';
10
+
11
+ jest.mock('../src/data/config', () => ({
12
+ DEFAULT_DOMAIN: 'https://example.com/',
13
+ getSDKVersion: () => '0.0.0-test',
14
+ }));
15
+
16
+ const release: IFirmwareReleaseInfo = {
17
+ required: false,
18
+ version: [2, 0, 0],
19
+ url: 'https://example.com/application-p1.okpkg',
20
+ fingerprint: 'release-fingerprint',
21
+ changelog: {
22
+ 'zh-CN': '更新',
23
+ 'en-US': 'Update',
24
+ },
25
+ bootloaderChangelog: {
26
+ 'zh-CN': '旧版 Bootloader 更新',
27
+ 'en-US': 'Legacy bootloader update',
28
+ },
29
+ installOrder: ['bootloader', 'applicationP1', 'coprocessor'],
30
+ components: {
31
+ bootloader: {
32
+ target: 'BOOTLOADER',
33
+ url: 'https://example.com/bootloader.okpkg',
34
+ version: [2, 0, 0],
35
+ },
36
+ applicationP1: {
37
+ target: 'APPLICATION_P1',
38
+ url: 'https://example.com/application-p1.okpkg',
39
+ version: [2, 0, 0],
40
+ },
41
+ coprocessor: {
42
+ target: 'COPROCESSOR',
43
+ url: 'https://example.com/coprocessor.okpkg',
44
+ version: [2, 0, 0],
45
+ },
46
+ },
47
+ };
48
+
49
+ const createDevice = (deviceType: 'pro2' | 'neo') => ({
50
+ isProtocolV2: () => true,
51
+ features: {
52
+ deviceType,
53
+ firmwareVersion: '1.0.0',
54
+ },
55
+ getDeviceState: jest.fn().mockResolvedValue({
56
+ identity: {
57
+ deviceType,
58
+ firmwareType: EFirmwareType.Universal,
59
+ },
60
+ status: { mode: 'normal' },
61
+ versions: {
62
+ firmware: '1.0.0',
63
+ applicationP1: '1.0.0',
64
+ bootloader: '1.0.0',
65
+ board: '1.0.0',
66
+ ble: '1.0.0',
67
+ },
68
+ }),
69
+ });
70
+
71
+ describe.each(['pro2', 'neo'] as const)('Protocol V2 release checks for %s', deviceType => {
72
+ afterEach(() => {
73
+ jest.restoreAllMocks();
74
+ });
75
+
76
+ test('supports firmware, BLE, bootloader, and aggregate release checks', async () => {
77
+ jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(release);
78
+ jest.spyOn(DataManager, 'getProtocolV2ResourceSource').mockReturnValue({
79
+ archiveUrl: `https://example.com/${deviceType}/pro2-resource.zip`,
80
+ archiveSha256: 'a'.repeat(64),
81
+ archiveSize: 16_815_479,
82
+ });
83
+
84
+ const firmwareMethod = new CheckFirmwareRelease({
85
+ id: 1,
86
+ payload: { method: 'checkFirmwareRelease' },
87
+ });
88
+ firmwareMethod.init();
89
+ firmwareMethod.device = createDevice(deviceType) as unknown as CheckFirmwareRelease['device'];
90
+
91
+ const bleMethod = new CheckBLEFirmwareRelease({
92
+ id: 2,
93
+ payload: { method: 'checkBLEFirmwareRelease' },
94
+ });
95
+ bleMethod.init();
96
+ bleMethod.device = createDevice(deviceType) as unknown as CheckBLEFirmwareRelease['device'];
97
+
98
+ const bootloaderMethod = new CheckBootloaderRelease({
99
+ id: 3,
100
+ payload: { method: 'checkBootloaderRelease' },
101
+ });
102
+ bootloaderMethod.init();
103
+ bootloaderMethod.device = createDevice(
104
+ deviceType
105
+ ) as unknown as CheckBootloaderRelease['device'];
106
+
107
+ const allMethod = new CheckAllFirmwareRelease({
108
+ id: 4,
109
+ payload: { method: 'checkAllFirmwareRelease' },
110
+ });
111
+ allMethod.init();
112
+ allMethod.device = createDevice(deviceType) as unknown as CheckAllFirmwareRelease['device'];
113
+
114
+ await expect(firmwareMethod.run()).resolves.toMatchObject({
115
+ status: 'outdated',
116
+ shouldUpdate: true,
117
+ release,
118
+ });
119
+ await expect(bleMethod.run()).resolves.toMatchObject({
120
+ status: 'outdated',
121
+ shouldUpdate: true,
122
+ release: {
123
+ protocol: 'V2',
124
+ configKey: 'coprocessor',
125
+ componentTarget: 'COPROCESSOR',
126
+ target: 'COPROCESSOR',
127
+ url: 'https://example.com/coprocessor.okpkg',
128
+ version: [2, 0, 0],
129
+ },
130
+ });
131
+ await expect(bootloaderMethod.run()).resolves.toMatchObject({
132
+ status: 'outdated',
133
+ shouldUpdate: true,
134
+ changelog: [release.changelog],
135
+ release: {
136
+ protocol: 'V2',
137
+ configKey: 'bootloader',
138
+ componentTarget: 'BOOTLOADER',
139
+ target: 'BOOTLOADER',
140
+ url: 'https://example.com/bootloader.okpkg',
141
+ version: [2, 0, 0],
142
+ changelog: release.changelog,
143
+ },
144
+ });
145
+ await expect(allMethod.run()).resolves.toMatchObject({
146
+ protocol: 'V2',
147
+ deviceType,
148
+ status: 'outdated',
149
+ hasUpgrade: true,
150
+ targetsToUpdate: ['boot', 'app_v1', 'coprocessor', 'resource'],
151
+ firmware: { status: 'outdated', shouldUpdate: true },
152
+ ble: {
153
+ status: 'outdated',
154
+ shouldUpdate: true,
155
+ release: { componentTarget: 'COPROCESSOR' },
156
+ },
157
+ bootloader: {
158
+ status: 'outdated',
159
+ shouldUpdate: true,
160
+ release: { componentTarget: 'BOOTLOADER' },
161
+ },
162
+ });
163
+
164
+ expect(firmwareMethod.getSupportedProtocols()).toEqual(['V1', 'V2']);
165
+ expect(bleMethod.getSupportedProtocols()).toEqual(['V1', 'V2']);
166
+ expect(bootloaderMethod.getSupportedProtocols()).toEqual(['V1', 'V2']);
167
+ expect(allMethod.getSupportedProtocols()).toEqual(['V1', 'V2']);
168
+ });
169
+ });
170
+
171
+ test('returns null from optional release probes when device features are unavailable', async () => {
172
+ const deviceWithoutFeatures = {
173
+ isProtocolV2: jest.fn(() => true),
174
+ features: undefined,
175
+ getDeviceState: jest.fn(),
176
+ };
177
+ const firmwareMethod = new CheckFirmwareRelease({
178
+ id: 1,
179
+ payload: { method: 'checkFirmwareRelease' },
180
+ });
181
+ const bleMethod = new CheckBLEFirmwareRelease({
182
+ id: 2,
183
+ payload: { method: 'checkBLEFirmwareRelease' },
184
+ });
185
+ const bootloaderMethod = new CheckBootloaderRelease({
186
+ id: 3,
187
+ payload: { method: 'checkBootloaderRelease' },
188
+ });
189
+
190
+ firmwareMethod.device = deviceWithoutFeatures as unknown as CheckFirmwareRelease['device'];
191
+ bleMethod.device = deviceWithoutFeatures as unknown as CheckBLEFirmwareRelease['device'];
192
+ bootloaderMethod.device = deviceWithoutFeatures as unknown as CheckBootloaderRelease['device'];
193
+
194
+ await expect(firmwareMethod.run()).resolves.toBeNull();
195
+ await expect(bleMethod.run()).resolves.toBeNull();
196
+ await expect(bootloaderMethod.run()).resolves.toBeNull();
197
+ expect(deviceWithoutFeatures.isProtocolV2).not.toHaveBeenCalled();
198
+ expect(deviceWithoutFeatures.getDeviceState).not.toHaveBeenCalled();
199
+ });
@@ -0,0 +1,126 @@
1
+ import { prepareFirmwareUpdateV4MemoryHost } from '../src/api/firmware/FirmwareMemoryHost';
2
+
3
+ import type { CoreApi } from '../src/types/api';
4
+ import type { FirmwareUpdatePlan } from '../src/types/api/firmwareUpdatePlan';
5
+
6
+ describe('prepareFirmwareUpdateV4MemoryHost', () => {
7
+ test('registers artifact-backed component and resource readers', async () => {
8
+ const componentBinary = new Uint8Array([1, 2, 3, 4]).buffer;
9
+ const archiveBinary = new Uint8Array([5, 6]).buffer;
10
+ const manifestBinary = new TextEncoder().encode('{"schema":1}').buffer;
11
+ let artifactReader: Parameters<
12
+ CoreApi['registerFirmwareUpdateHostBinding']
13
+ >[0]['artifactReader'];
14
+ const unregisterFirmwareUpdateHostBinding = jest.fn();
15
+ const sdk = {
16
+ prepareFirmwareUpdatePlan: jest.fn(({ plan, artifacts }) => ({
17
+ schemaVersion: 2,
18
+ preparedPlanDigest: 'b'.repeat(64),
19
+ planDigest: plan.planDigest,
20
+ networkPolicy: 'forbid',
21
+ executor: plan.executor,
22
+ deviceIdentity: plan.deviceIdentity,
23
+ deviceModel: plan.deviceModel,
24
+ firmwareType: plan.firmwareType,
25
+ platform: plan.platform,
26
+ leaseRef: 'test-lease',
27
+ targetsToUpdate: plan.targetsToUpdate,
28
+ artifacts: plan.artifacts.map(planArtifact => {
29
+ const input = artifacts.find(item => item.artifactId === planArtifact.artifactId);
30
+ return {
31
+ ...planArtifact,
32
+ artifact: input?.artifact,
33
+ materializedEntries: input?.materializedEntries,
34
+ };
35
+ }),
36
+ })),
37
+ registerFirmwareUpdateHostBinding: jest.fn(binding => {
38
+ artifactReader = binding.artifactReader;
39
+ return 7;
40
+ }),
41
+ unregisterFirmwareUpdateHostBinding,
42
+ } as unknown as Pick<
43
+ CoreApi,
44
+ | 'prepareFirmwareUpdatePlan'
45
+ | 'registerFirmwareUpdateHostBinding'
46
+ | 'unregisterFirmwareUpdateHostBinding'
47
+ >;
48
+ const plan = {
49
+ schemaVersion: 2,
50
+ planDigest: 'a'.repeat(64),
51
+ executor: 'v4',
52
+ deviceIdentity: 'device-id',
53
+ deviceModel: 'pro2',
54
+ firmwareType: 0,
55
+ platform: 'web',
56
+ targetsToUpdate: ['boot', 'resource'],
57
+ artifacts: [
58
+ {
59
+ artifactId: 'component:boot',
60
+ role: 'component',
61
+ target: 'boot',
62
+ url: 'https://example.com/boot.okpkg',
63
+ container: 'raw',
64
+ },
65
+ {
66
+ artifactId: 'resource:archive',
67
+ role: 'resourceBundle',
68
+ target: 'resource',
69
+ url: 'https://example.com/resource.zip',
70
+ container: 'zip',
71
+ logicalName: 'resource-archive',
72
+ },
73
+ ],
74
+ } as FirmwareUpdatePlan;
75
+
76
+ const host = prepareFirmwareUpdateV4MemoryHost({
77
+ sdk,
78
+ plan,
79
+ artifacts: [
80
+ { artifactId: 'component:boot', binary: componentBinary },
81
+ {
82
+ artifactId: 'resource:archive',
83
+ binary: archiveBinary,
84
+ materializedEntries: [{ entryName: 'manifest.json', binary: manifestBinary }],
85
+ },
86
+ ],
87
+ });
88
+
89
+ expect(host.hostBindingGeneration).toBe(7);
90
+ expect(sdk.registerFirmwareUpdateHostBinding).toHaveBeenCalledWith(
91
+ expect.objectContaining({ preparedPlanDigest: host.preparedPlan.preparedPlanDigest })
92
+ );
93
+ const componentArtifact = host.preparedPlan.artifacts.find(
94
+ artifact => artifact.target === 'boot'
95
+ )?.artifact;
96
+ const resourceEntryArtifact = host.preparedPlan.artifacts.find(
97
+ artifact => artifact.target === 'resource'
98
+ )?.materializedEntries?.[0]?.artifact;
99
+ expect(componentArtifact?.size).toBe(componentBinary.byteLength);
100
+ expect(resourceEntryArtifact?.size).toBe(manifestBinary.byteLength);
101
+ if (!artifactReader || !componentArtifact || !resourceEntryArtifact) {
102
+ throw new Error('Firmware memory host test setup is incomplete');
103
+ }
104
+ new Uint8Array(componentBinary).fill(9);
105
+ const opened = await artifactReader.open({
106
+ artifactRef: componentArtifact.artifactRef,
107
+ });
108
+ const chunk = await artifactReader.read({
109
+ readerId: opened.readerId,
110
+ offset: 1,
111
+ length: 2,
112
+ });
113
+ expect(Array.from(new Uint8Array(chunk.data))).toEqual([2, 3]);
114
+ await artifactReader.close({ readerId: opened.readerId });
115
+
116
+ // Materialized ZIP entries are receipts only. Execution opens the approved
117
+ // archive and re-derives these bytes, so the memory host must not retain a
118
+ // second readable copy of every expanded resource file.
119
+ expect(() => artifactReader.open({ artifactRef: resourceEntryArtifact.artifactRef })).toThrow(
120
+ 'Firmware memory artifact is unavailable'
121
+ );
122
+
123
+ host.release();
124
+ expect(unregisterFirmwareUpdateHostBinding).toHaveBeenCalledWith(7);
125
+ });
126
+ });
@@ -9,6 +9,11 @@ import {
9
9
  jest.mock('../../src/api/firmware/FirmwareUpdatePreparedPlan', () => ({
10
10
  assertFirmwareUpdatePreparedPlanBinding: jest.fn(),
11
11
  assertFirmwareUpdatePreparedPlanDeviceIdentity: jest.fn(),
12
+ clearFirmwareUpdatePreparedPlanDeviceIdentityPin: jest.fn(),
13
+ getFirmwareUpdatePreparedRawArtifact: jest.fn(
14
+ ({ preparedPlan }: { preparedPlan: { artifacts: unknown[] } }) => preparedPlan.artifacts[0]
15
+ ),
16
+ validateFirmwareUpdatePreparedPlan: jest.fn((preparedPlan: unknown) => preparedPlan),
12
17
  }));
13
18
 
14
19
  jest.mock('../../src/data-manager', () => ({
@@ -36,14 +41,21 @@ const createMethod = () => {
36
41
  read: jest.fn(),
37
42
  close,
38
43
  };
39
- const hostBindingGeneration = registerFirmwareUpdateHostBinding({ artifactReader });
44
+ const hostBindingGeneration = registerFirmwareUpdateHostBinding({
45
+ artifactReader,
46
+ preparedPlanDigest: 'a'.repeat(64),
47
+ });
40
48
  const method = new DeviceUpdateBootloader({
41
49
  id: 1,
42
50
  payload: {
43
51
  method: 'deviceUpdateBootloader',
44
52
  artifact,
45
53
  hostBindingGeneration,
46
- preparedPlan: {},
54
+ preparedPlan: {
55
+ preparedPlanDigest: 'a'.repeat(64),
56
+ firmwareType: EFirmwareType.Universal,
57
+ artifacts: [{ artifact }],
58
+ },
47
59
  },
48
60
  });
49
61
  (method as any).device = {
@@ -6,6 +6,7 @@ import {
6
6
  } from '../../src/api/firmware/FirmwareHostBinding';
7
7
 
8
8
  const createBinding = () => ({
9
+ preparedPlanDigest: 'a'.repeat(64),
9
10
  artifactReader: {
10
11
  open: jest.fn(() => Promise.resolve({ readerId: 'reader-1', size: 4 })),
11
12
  read: jest.fn(() =>
@@ -67,4 +68,12 @@ describe('firmware host binding generation', () => {
67
68
  readerId: 'stale-reader',
68
69
  });
69
70
  });
71
+
72
+ test('rejects a prepared plan from another host generation before opening artifacts', () => {
73
+ const generation = registerFirmwareUpdateHostBinding(createBinding());
74
+
75
+ expect(() => resolveFirmwareUpdateHostBinding(generation, 'b'.repeat(64))).toThrow(
76
+ 'does not match the prepared plan'
77
+ );
78
+ });
70
79
  });