@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.
- package/__tests__/check-all-firmware-release-protocol-v2.test.ts +411 -86
- package/__tests__/check-firmware-release-protocol-v2.test.ts +199 -0
- package/__tests__/firmware-memory-host.test.ts +126 -0
- package/__tests__/firmware-update/device-update-bootloader.test.ts +14 -2
- package/__tests__/firmware-update/firmware-host-binding.test.ts +9 -0
- package/__tests__/firmware-update/firmware-prepared-host-digest.test.ts +573 -0
- package/__tests__/firmware-update/firmware-prepared-resource-archive.test.ts +150 -0
- package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +65 -2
- package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +80 -0
- package/__tests__/firmware-update/firmware-update-plan.test.ts +293 -71
- package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +53 -1
- package/__tests__/firmware-update/firmware-update-prepared-resource-executors.test.ts +326 -0
- package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +2 -2
- package/__tests__/protocol-v2-firmware-targets.test.ts +16 -0
- package/__tests__/protocol-v2-resources.test.ts +148 -232
- package/__tests__/protocol-v2.test.ts +1326 -510
- package/dist/api/CheckAllFirmwareRelease.d.ts +2 -3
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/CheckBLEFirmwareRelease.d.ts +3 -2
- package/dist/api/CheckBLEFirmwareRelease.d.ts.map +1 -1
- package/dist/api/CheckBootloaderRelease.d.ts +2 -1
- package/dist/api/CheckBootloaderRelease.d.ts.map +1 -1
- package/dist/api/CheckFirmwareRelease.d.ts +3 -2
- package/dist/api/CheckFirmwareRelease.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV2.d.ts +1 -0
- package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +11 -9
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/UploadPortfolio.d.ts.map +1 -1
- package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareHostBinding.d.ts +1 -1
- package/dist/api/firmware/FirmwareHostBinding.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareMemoryHost.d.ts +22 -0
- package/dist/api/firmware/FirmwareMemoryHost.d.ts.map +1 -0
- package/dist/api/firmware/FirmwarePreparedResourceArchive.d.ts +11 -0
- package/dist/api/firmware/FirmwarePreparedResourceArchive.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts +48 -1
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +7 -1
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -1
- package/dist/api/firmware/getBinary.d.ts +0 -1
- package/dist/api/firmware/getBinary.d.ts.map +1 -1
- package/dist/api/firmware/protocolV2Release.d.ts +33 -0
- package/dist/api/firmware/protocolV2Release.d.ts.map +1 -0
- package/dist/data-manager/DataManager.d.ts +4 -3
- package/dist/data-manager/DataManager.d.ts.map +1 -1
- package/dist/index.d.ts +108 -114
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1825 -1006
- package/dist/protocols/protocol-v2/resources.d.ts +10 -28
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
- package/dist/types/api/checkAllFirmwareRelease.d.ts +20 -8
- package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
- package/dist/types/api/checkBLEFirmwareRelease.d.ts +2 -13
- package/dist/types/api/checkBLEFirmwareRelease.d.ts.map +1 -1
- package/dist/types/api/checkBootloaderRelease.d.ts +2 -6
- package/dist/types/api/checkBootloaderRelease.d.ts.map +1 -1
- package/dist/types/api/checkFirmwareRelease.d.ts +2 -13
- package/dist/types/api/checkFirmwareRelease.d.ts.map +1 -1
- package/dist/types/api/checkFirmwareTypeAvailable.d.ts +2 -2
- package/dist/types/api/checkFirmwareTypeAvailable.d.ts.map +1 -1
- package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
- package/dist/types/api/export.d.ts +1 -1
- package/dist/types/api/export.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdate.d.ts +4 -12
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdatePlan.d.ts +2 -2
- package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -1
- package/dist/types/settings.d.ts +35 -36
- package/dist/types/settings.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/CheckAllFirmwareRelease.ts +113 -80
- package/src/api/CheckBLEFirmwareRelease.ts +37 -5
- package/src/api/CheckBootloaderRelease.ts +35 -3
- package/src/api/CheckFirmwareRelease.ts +35 -7
- package/src/api/FirmwareUpdateV2.ts +231 -138
- package/src/api/FirmwareUpdateV3.ts +101 -56
- package/src/api/FirmwareUpdateV4.ts +936 -408
- package/src/api/UploadPortfolio.ts +18 -0
- package/src/api/device/DeviceUpdateBootloader.ts +37 -11
- package/src/api/firmware/FirmwareHostBinding.ts +26 -3
- package/src/api/firmware/FirmwareMemoryHost.ts +143 -0
- package/src/api/firmware/FirmwarePreparedResourceArchive.ts +207 -0
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +122 -73
- package/src/api/firmware/FirmwareUpdatePlan.ts +295 -122
- package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +49 -12
- package/src/api/firmware/protocolV2Release.ts +168 -0
- package/src/data-manager/DataManager.ts +35 -19
- package/src/index.ts +10 -0
- package/src/protocols/protocol-v2/resources.ts +157 -341
- package/src/types/api/checkAllFirmwareRelease.ts +27 -12
- package/src/types/api/checkBLEFirmwareRelease.ts +4 -13
- package/src/types/api/checkBootloaderRelease.ts +2 -6
- package/src/types/api/checkFirmwareRelease.ts +2 -13
- package/src/types/api/checkFirmwareTypeAvailable.ts +2 -2
- package/src/types/api/deviceUpdateBootloader.ts +1 -0
- package/src/types/api/export.ts +6 -1
- package/src/types/api/firmwareUpdate.ts +12 -17
- package/src/types/api/firmwareUpdatePlan.ts +2 -2
- package/src/types/settings.ts +35 -61
- package/src/utils/deviceFeaturesUtils.ts +2 -2
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
|
|
2
|
+
|
|
3
|
+
import FirmwareUpdateV2 from '../../src/api/FirmwareUpdateV2';
|
|
4
|
+
import FirmwareUpdateV3 from '../../src/api/FirmwareUpdateV3';
|
|
5
|
+
import FirmwareUpdateV4 from '../../src/api/FirmwareUpdateV4';
|
|
6
|
+
import DeviceUpdateBootloader from '../../src/api/device/DeviceUpdateBootloader';
|
|
7
|
+
import {
|
|
8
|
+
registerFirmwareUpdateHostBinding,
|
|
9
|
+
unregisterFirmwareUpdateHostBinding,
|
|
10
|
+
} from '../../src/api/firmware/FirmwareHostBinding';
|
|
11
|
+
import { prepareFirmwareUpdatePlan } from '../../src/api/firmware/FirmwareUpdatePreparedPlan';
|
|
12
|
+
import { buildFirmwareUpdatePlan } from '../../src/api/firmware/FirmwareUpdatePlan';
|
|
13
|
+
|
|
14
|
+
import type { FirmwareArtifactReference } from '../../src/types/api/firmwareUpdate';
|
|
15
|
+
import type { Features } from '../../src/types';
|
|
16
|
+
|
|
17
|
+
jest.mock('../../src/data/config', () => ({
|
|
18
|
+
getSDKVersion: jest.fn(() => '1.0.0'),
|
|
19
|
+
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
const noUpdate = {
|
|
23
|
+
status: 'valid' as const,
|
|
24
|
+
release: undefined,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const createPreparedPlan = ({
|
|
28
|
+
executor,
|
|
29
|
+
target,
|
|
30
|
+
}: {
|
|
31
|
+
executor: 'v2' | 'v3';
|
|
32
|
+
target: 'firmware' | 'bootloader';
|
|
33
|
+
}) => {
|
|
34
|
+
const sha256 = target === 'firmware' ? 'a'.repeat(64) : 'b'.repeat(64);
|
|
35
|
+
const artifact: FirmwareArtifactReference = {
|
|
36
|
+
artifactRef: `fw:${sha256}`,
|
|
37
|
+
size: 4,
|
|
38
|
+
sha256,
|
|
39
|
+
};
|
|
40
|
+
const isV3 = executor === 'v3';
|
|
41
|
+
const plan = buildFirmwareUpdatePlan({
|
|
42
|
+
features: {
|
|
43
|
+
deviceType: isV3 ? EDeviceType.Pro : EDeviceType.Classic1s,
|
|
44
|
+
serialNo: isV3 ? 'pro-device-id' : 'classic-device-id',
|
|
45
|
+
firmwareVersion: '1.0.0',
|
|
46
|
+
bootloaderVersion: isV3 ? '2.8.0' : '1.0.0',
|
|
47
|
+
} as Features,
|
|
48
|
+
firmwareType: EFirmwareType.Universal,
|
|
49
|
+
platform: 'desktop',
|
|
50
|
+
firmware:
|
|
51
|
+
target === 'firmware'
|
|
52
|
+
? {
|
|
53
|
+
status: 'outdated',
|
|
54
|
+
release: {
|
|
55
|
+
url: 'https://firmware.onekey.so/firmware.bin',
|
|
56
|
+
expectedSize: artifact.size,
|
|
57
|
+
fingerprint: artifact.sha256,
|
|
58
|
+
version: [3, 0, 0],
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
: noUpdate,
|
|
62
|
+
ble: noUpdate,
|
|
63
|
+
bootloader:
|
|
64
|
+
target === 'bootloader'
|
|
65
|
+
? {
|
|
66
|
+
status: 'outdated',
|
|
67
|
+
shouldUpdate: true,
|
|
68
|
+
release: {
|
|
69
|
+
bootloaderResource: 'https://firmware.onekey.so/bootloader.bin',
|
|
70
|
+
bootloaderExpectedSize: artifact.size,
|
|
71
|
+
bootloaderFingerprint: artifact.sha256,
|
|
72
|
+
bootloaderVersion: [2, 0, 0],
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
: noUpdate,
|
|
76
|
+
});
|
|
77
|
+
expect(plan.executor).toBe(executor);
|
|
78
|
+
const preparedPlan = prepareFirmwareUpdatePlan({
|
|
79
|
+
plan,
|
|
80
|
+
leaseRef: `fwlease:${executor}:${target}`,
|
|
81
|
+
artifacts: [{ artifactId: target, artifact }],
|
|
82
|
+
});
|
|
83
|
+
return { artifact, preparedPlan };
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const registerMismatchedHost = () => {
|
|
87
|
+
const artifactReader = {
|
|
88
|
+
open: jest.fn(),
|
|
89
|
+
read: jest.fn(),
|
|
90
|
+
close: jest.fn(),
|
|
91
|
+
};
|
|
92
|
+
const hostBindingGeneration = registerFirmwareUpdateHostBinding({
|
|
93
|
+
preparedPlanDigest: 'f'.repeat(64),
|
|
94
|
+
artifactReader,
|
|
95
|
+
});
|
|
96
|
+
return { artifactReader, hostBindingGeneration };
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const createPreparedV4Plan = () => {
|
|
100
|
+
const sha256 = 'c'.repeat(64);
|
|
101
|
+
const artifact: FirmwareArtifactReference = {
|
|
102
|
+
artifactRef: `fw:${sha256}`,
|
|
103
|
+
size: 4,
|
|
104
|
+
sha256,
|
|
105
|
+
};
|
|
106
|
+
const plan = buildFirmwareUpdatePlan({
|
|
107
|
+
features: {
|
|
108
|
+
deviceType: EDeviceType.Pro2,
|
|
109
|
+
serialNo: 'pro2-device-id',
|
|
110
|
+
firmwareVersion: '1.0.0',
|
|
111
|
+
bootloaderVersion: '1.0.0',
|
|
112
|
+
} as Features,
|
|
113
|
+
firmwareType: EFirmwareType.Universal,
|
|
114
|
+
platform: 'desktop',
|
|
115
|
+
firmware: {
|
|
116
|
+
status: 'outdated',
|
|
117
|
+
release: {
|
|
118
|
+
components: {
|
|
119
|
+
applicationP1: {
|
|
120
|
+
target: 'APPLICATION_P1',
|
|
121
|
+
url: 'https://firmware.onekey.so/pro2/application-p1.bin',
|
|
122
|
+
expectedSize: artifact.size,
|
|
123
|
+
fingerprint: artifact.sha256,
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
ble: noUpdate,
|
|
129
|
+
bootloader: noUpdate,
|
|
130
|
+
});
|
|
131
|
+
expect(plan.executor).toBe('v4');
|
|
132
|
+
const preparedPlan = prepareFirmwareUpdatePlan({
|
|
133
|
+
plan,
|
|
134
|
+
leaseRef: 'fwlease:v4:app_v1',
|
|
135
|
+
artifacts: [{ artifactId: 'component:app_v1', artifact }],
|
|
136
|
+
});
|
|
137
|
+
return { artifact, preparedPlan };
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
describe('prepared firmware host digest binding', () => {
|
|
141
|
+
afterEach(() => {
|
|
142
|
+
unregisterFirmwareUpdateHostBinding();
|
|
143
|
+
jest.restoreAllMocks();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('FirmwareUpdateV2 rejects a host registered for another prepared plan', () => {
|
|
147
|
+
const { artifact, preparedPlan } = createPreparedPlan({
|
|
148
|
+
executor: 'v2',
|
|
149
|
+
target: 'firmware',
|
|
150
|
+
});
|
|
151
|
+
const { artifactReader, hostBindingGeneration } = registerMismatchedHost();
|
|
152
|
+
const method = new FirmwareUpdateV2({
|
|
153
|
+
id: 1,
|
|
154
|
+
payload: {
|
|
155
|
+
method: 'firmwareUpdateV2',
|
|
156
|
+
platform: 'desktop',
|
|
157
|
+
updateType: 'firmware',
|
|
158
|
+
artifact,
|
|
159
|
+
preparedPlan,
|
|
160
|
+
hostBindingGeneration,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
expect(() => method.init()).toThrow('does not match the prepared plan');
|
|
165
|
+
expect(artifactReader.open).not.toHaveBeenCalled();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test('FirmwareUpdateV2 rejects a direct reader when the prepared host generation is missing', () => {
|
|
169
|
+
const { artifact, preparedPlan } = createPreparedPlan({
|
|
170
|
+
executor: 'v2',
|
|
171
|
+
target: 'firmware',
|
|
172
|
+
});
|
|
173
|
+
const directArtifactReader = {
|
|
174
|
+
open: jest.fn(),
|
|
175
|
+
read: jest.fn(),
|
|
176
|
+
close: jest.fn(),
|
|
177
|
+
};
|
|
178
|
+
const method = new FirmwareUpdateV2({
|
|
179
|
+
id: 1,
|
|
180
|
+
payload: {
|
|
181
|
+
method: 'firmwareUpdateV2',
|
|
182
|
+
platform: 'desktop',
|
|
183
|
+
updateType: 'firmware',
|
|
184
|
+
artifact,
|
|
185
|
+
preparedPlan,
|
|
186
|
+
artifactReader: directArtifactReader,
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
expect(() => method.init()).toThrow('host binding generation NaN is stale');
|
|
191
|
+
expect(directArtifactReader.open).not.toHaveBeenCalled();
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test('FirmwareUpdateV2 does not treat a binary call carrying a prepared plan as legacy', () => {
|
|
195
|
+
const { preparedPlan } = createPreparedPlan({
|
|
196
|
+
executor: 'v2',
|
|
197
|
+
target: 'firmware',
|
|
198
|
+
});
|
|
199
|
+
const method = new FirmwareUpdateV2({
|
|
200
|
+
id: 1,
|
|
201
|
+
payload: {
|
|
202
|
+
method: 'firmwareUpdateV2',
|
|
203
|
+
platform: 'desktop',
|
|
204
|
+
updateType: 'firmware',
|
|
205
|
+
binary: new ArrayBuffer(4),
|
|
206
|
+
preparedPlan,
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
expect(() => method.init()).toThrow(
|
|
211
|
+
'Prepared firmware plans cannot be combined with legacy firmware inputs'
|
|
212
|
+
);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test('FirmwareUpdateV2 derives its component artifact from the digest-bound plan', () => {
|
|
216
|
+
const { artifact, preparedPlan } = createPreparedPlan({
|
|
217
|
+
executor: 'v2',
|
|
218
|
+
target: 'firmware',
|
|
219
|
+
});
|
|
220
|
+
const artifactReader = {
|
|
221
|
+
open: jest.fn(),
|
|
222
|
+
read: jest.fn(),
|
|
223
|
+
close: jest.fn(),
|
|
224
|
+
};
|
|
225
|
+
const hostBindingGeneration = registerFirmwareUpdateHostBinding({
|
|
226
|
+
preparedPlanDigest: preparedPlan.preparedPlanDigest,
|
|
227
|
+
artifactReader,
|
|
228
|
+
});
|
|
229
|
+
const method = new FirmwareUpdateV2({
|
|
230
|
+
id: 1,
|
|
231
|
+
payload: {
|
|
232
|
+
method: 'firmwareUpdateV2',
|
|
233
|
+
platform: 'desktop',
|
|
234
|
+
updateType: 'firmware',
|
|
235
|
+
preparedPlan,
|
|
236
|
+
hostBindingGeneration,
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
expect(() => method.init()).not.toThrow();
|
|
241
|
+
expect((method as unknown as { params: { artifact: unknown } }).params.artifact).toEqual(
|
|
242
|
+
artifact
|
|
243
|
+
);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
test('FirmwareUpdateV3 rejects a host registered for another prepared plan', () => {
|
|
247
|
+
const { artifact, preparedPlan } = createPreparedPlan({
|
|
248
|
+
executor: 'v3',
|
|
249
|
+
target: 'firmware',
|
|
250
|
+
});
|
|
251
|
+
const { artifactReader, hostBindingGeneration } = registerMismatchedHost();
|
|
252
|
+
const method = new FirmwareUpdateV3({
|
|
253
|
+
id: 1,
|
|
254
|
+
payload: {
|
|
255
|
+
method: 'firmwareUpdateV3',
|
|
256
|
+
platform: 'desktop',
|
|
257
|
+
artifacts: { firmware: artifact },
|
|
258
|
+
preparedPlan,
|
|
259
|
+
hostBindingGeneration,
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
expect(() => method.init()).toThrow('does not match the prepared plan');
|
|
264
|
+
expect(artifactReader.open).not.toHaveBeenCalled();
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test('FirmwareUpdateV3 rejects a direct reader when the prepared host generation is missing', () => {
|
|
268
|
+
const { artifact, preparedPlan } = createPreparedPlan({
|
|
269
|
+
executor: 'v3',
|
|
270
|
+
target: 'firmware',
|
|
271
|
+
});
|
|
272
|
+
const directArtifactReader = {
|
|
273
|
+
open: jest.fn(),
|
|
274
|
+
read: jest.fn(),
|
|
275
|
+
close: jest.fn(),
|
|
276
|
+
};
|
|
277
|
+
const method = new FirmwareUpdateV3({
|
|
278
|
+
id: 1,
|
|
279
|
+
payload: {
|
|
280
|
+
method: 'firmwareUpdateV3',
|
|
281
|
+
platform: 'desktop',
|
|
282
|
+
artifacts: { firmware: artifact },
|
|
283
|
+
preparedPlan,
|
|
284
|
+
artifactReader: directArtifactReader,
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
expect(() => method.init()).toThrow('host binding generation NaN is stale');
|
|
289
|
+
expect(directArtifactReader.open).not.toHaveBeenCalled();
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test('FirmwareUpdateV3 does not execute legacy inputs outside the prepared plan', () => {
|
|
293
|
+
const { artifact, preparedPlan } = createPreparedPlan({
|
|
294
|
+
executor: 'v3',
|
|
295
|
+
target: 'firmware',
|
|
296
|
+
});
|
|
297
|
+
const method = new FirmwareUpdateV3({
|
|
298
|
+
id: 1,
|
|
299
|
+
payload: {
|
|
300
|
+
method: 'firmwareUpdateV3',
|
|
301
|
+
platform: 'desktop',
|
|
302
|
+
artifacts: { firmware: artifact },
|
|
303
|
+
preparedPlan,
|
|
304
|
+
bleBinary: new ArrayBuffer(4),
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
expect(() => method.init()).toThrow(
|
|
309
|
+
'Prepared firmware plans cannot be combined with legacy firmware inputs'
|
|
310
|
+
);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
test('FirmwareUpdateV3 derives its component artifacts from the digest-bound plan', () => {
|
|
314
|
+
const { artifact, preparedPlan } = createPreparedPlan({
|
|
315
|
+
executor: 'v3',
|
|
316
|
+
target: 'firmware',
|
|
317
|
+
});
|
|
318
|
+
const artifactReader = {
|
|
319
|
+
open: jest.fn(),
|
|
320
|
+
read: jest.fn(),
|
|
321
|
+
close: jest.fn(),
|
|
322
|
+
};
|
|
323
|
+
const hostBindingGeneration = registerFirmwareUpdateHostBinding({
|
|
324
|
+
preparedPlanDigest: preparedPlan.preparedPlanDigest,
|
|
325
|
+
artifactReader,
|
|
326
|
+
});
|
|
327
|
+
const method = new FirmwareUpdateV3({
|
|
328
|
+
id: 1,
|
|
329
|
+
payload: {
|
|
330
|
+
method: 'firmwareUpdateV3',
|
|
331
|
+
platform: 'desktop',
|
|
332
|
+
preparedPlan,
|
|
333
|
+
hostBindingGeneration,
|
|
334
|
+
},
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
expect(() => method.init()).not.toThrow();
|
|
338
|
+
expect(
|
|
339
|
+
(method as unknown as { params: { artifacts: { firmware: unknown } } }).params.artifacts
|
|
340
|
+
.firmware
|
|
341
|
+
).toEqual(artifact);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
test('DeviceUpdateBootloader rejects a host registered for another prepared plan', async () => {
|
|
345
|
+
const { artifact, preparedPlan } = createPreparedPlan({
|
|
346
|
+
executor: 'v2',
|
|
347
|
+
target: 'bootloader',
|
|
348
|
+
});
|
|
349
|
+
const { artifactReader, hostBindingGeneration } = registerMismatchedHost();
|
|
350
|
+
const method = new DeviceUpdateBootloader({
|
|
351
|
+
id: 1,
|
|
352
|
+
payload: {
|
|
353
|
+
method: 'deviceUpdateBootloader',
|
|
354
|
+
artifact,
|
|
355
|
+
preparedPlan,
|
|
356
|
+
hostBindingGeneration,
|
|
357
|
+
},
|
|
358
|
+
});
|
|
359
|
+
method.device = {
|
|
360
|
+
features: {
|
|
361
|
+
deviceType: EDeviceType.Classic1s,
|
|
362
|
+
serialNo: 'classic-device-id',
|
|
363
|
+
},
|
|
364
|
+
} as any;
|
|
365
|
+
|
|
366
|
+
await expect(method.run()).rejects.toThrow('does not match the prepared plan');
|
|
367
|
+
expect(artifactReader.open).not.toHaveBeenCalled();
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
test('DeviceUpdateBootloader rejects a direct reader when the prepared host generation is missing', async () => {
|
|
371
|
+
const { artifact, preparedPlan } = createPreparedPlan({
|
|
372
|
+
executor: 'v2',
|
|
373
|
+
target: 'bootloader',
|
|
374
|
+
});
|
|
375
|
+
const directArtifactReader = {
|
|
376
|
+
open: jest.fn(),
|
|
377
|
+
read: jest.fn(),
|
|
378
|
+
close: jest.fn(),
|
|
379
|
+
};
|
|
380
|
+
const method = new DeviceUpdateBootloader({
|
|
381
|
+
id: 1,
|
|
382
|
+
payload: {
|
|
383
|
+
method: 'deviceUpdateBootloader',
|
|
384
|
+
artifact,
|
|
385
|
+
preparedPlan,
|
|
386
|
+
artifactReader: directArtifactReader,
|
|
387
|
+
},
|
|
388
|
+
});
|
|
389
|
+
method.device = {
|
|
390
|
+
features: {
|
|
391
|
+
deviceType: EDeviceType.Classic1s,
|
|
392
|
+
serialNo: 'classic-device-id',
|
|
393
|
+
},
|
|
394
|
+
} as any;
|
|
395
|
+
|
|
396
|
+
await expect(method.run()).rejects.toThrow('host binding generation NaN is stale');
|
|
397
|
+
expect(directArtifactReader.open).not.toHaveBeenCalled();
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
test('DeviceUpdateBootloader does not treat a binary call carrying a prepared plan as legacy', async () => {
|
|
401
|
+
const { preparedPlan } = createPreparedPlan({
|
|
402
|
+
executor: 'v2',
|
|
403
|
+
target: 'bootloader',
|
|
404
|
+
});
|
|
405
|
+
const method = new DeviceUpdateBootloader({
|
|
406
|
+
id: 1,
|
|
407
|
+
payload: {
|
|
408
|
+
method: 'deviceUpdateBootloader',
|
|
409
|
+
binary: new ArrayBuffer(4),
|
|
410
|
+
preparedPlan,
|
|
411
|
+
},
|
|
412
|
+
});
|
|
413
|
+
method.device = { features: undefined } as any;
|
|
414
|
+
|
|
415
|
+
await expect(method.run()).rejects.toThrow(
|
|
416
|
+
'Prepared bootloader plans cannot be combined with a legacy binary'
|
|
417
|
+
);
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
test('DeviceUpdateBootloader derives its artifact from the digest-bound plan', async () => {
|
|
421
|
+
const { artifact, preparedPlan } = createPreparedPlan({
|
|
422
|
+
executor: 'v2',
|
|
423
|
+
target: 'bootloader',
|
|
424
|
+
});
|
|
425
|
+
const artifactReader = {
|
|
426
|
+
open: jest.fn().mockResolvedValue({ readerId: 'bootloader-reader', size: artifact.size }),
|
|
427
|
+
read: jest.fn(),
|
|
428
|
+
close: jest.fn().mockResolvedValue(undefined),
|
|
429
|
+
};
|
|
430
|
+
const hostBindingGeneration = registerFirmwareUpdateHostBinding({
|
|
431
|
+
preparedPlanDigest: preparedPlan.preparedPlanDigest,
|
|
432
|
+
artifactReader,
|
|
433
|
+
});
|
|
434
|
+
const method = new DeviceUpdateBootloader({
|
|
435
|
+
id: 1,
|
|
436
|
+
payload: {
|
|
437
|
+
method: 'deviceUpdateBootloader',
|
|
438
|
+
preparedPlan,
|
|
439
|
+
hostBindingGeneration,
|
|
440
|
+
},
|
|
441
|
+
});
|
|
442
|
+
method.device = {
|
|
443
|
+
features: {
|
|
444
|
+
deviceType: EDeviceType.Classic1s,
|
|
445
|
+
serialNo: 'classic-device-id',
|
|
446
|
+
},
|
|
447
|
+
getCurrentDeviceType: () => EDeviceType.Touch,
|
|
448
|
+
} as any;
|
|
449
|
+
const updateTouchBootloader = jest
|
|
450
|
+
.spyOn(method, 'updateTouchBootloader')
|
|
451
|
+
.mockResolvedValue(true);
|
|
452
|
+
|
|
453
|
+
await expect(method.run()).resolves.toBe(true);
|
|
454
|
+
expect(artifactReader.open).toHaveBeenCalledWith({ artifactRef: artifact.artifactRef });
|
|
455
|
+
expect(updateTouchBootloader).toHaveBeenCalledWith(
|
|
456
|
+
expect.objectContaining({ firmwareType: preparedPlan.firmwareType })
|
|
457
|
+
);
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
test('FirmwareUpdateV4 rejects a prepared plan without a host binding generation', () => {
|
|
461
|
+
const { preparedPlan } = createPreparedV4Plan();
|
|
462
|
+
const directArtifactReader = {
|
|
463
|
+
open: jest.fn(),
|
|
464
|
+
read: jest.fn(),
|
|
465
|
+
close: jest.fn(),
|
|
466
|
+
};
|
|
467
|
+
const method = new FirmwareUpdateV4({
|
|
468
|
+
id: 1,
|
|
469
|
+
payload: {
|
|
470
|
+
method: 'firmwareUpdateV4',
|
|
471
|
+
platform: 'desktop',
|
|
472
|
+
preparedPlan,
|
|
473
|
+
artifactReader: directArtifactReader,
|
|
474
|
+
},
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
expect(() => method.init()).toThrow('host binding generation NaN is stale');
|
|
478
|
+
expect(directArtifactReader.open).not.toHaveBeenCalled();
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
test('FirmwareUpdateV4 rejects a host registered for another prepared plan', () => {
|
|
482
|
+
const { preparedPlan } = createPreparedV4Plan();
|
|
483
|
+
const { artifactReader, hostBindingGeneration } = registerMismatchedHost();
|
|
484
|
+
const method = new FirmwareUpdateV4({
|
|
485
|
+
id: 1,
|
|
486
|
+
payload: {
|
|
487
|
+
method: 'firmwareUpdateV4',
|
|
488
|
+
platform: 'desktop',
|
|
489
|
+
preparedPlan,
|
|
490
|
+
hostBindingGeneration,
|
|
491
|
+
},
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
expect(() => method.init()).toThrow('does not match the prepared plan');
|
|
495
|
+
expect(artifactReader.open).not.toHaveBeenCalled();
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
test('FirmwareUpdateV4 obtains the prepared reader only from the digest-bound host', async () => {
|
|
499
|
+
const { artifact, preparedPlan } = createPreparedV4Plan();
|
|
500
|
+
const directArtifactReader = {
|
|
501
|
+
open: jest.fn(),
|
|
502
|
+
read: jest.fn(),
|
|
503
|
+
close: jest.fn(),
|
|
504
|
+
};
|
|
505
|
+
const boundArtifactReader = {
|
|
506
|
+
open: jest.fn().mockResolvedValue({ readerId: 'bound-reader', size: artifact.size }),
|
|
507
|
+
read: jest.fn(),
|
|
508
|
+
close: jest.fn(),
|
|
509
|
+
};
|
|
510
|
+
const hostBindingGeneration = registerFirmwareUpdateHostBinding({
|
|
511
|
+
preparedPlanDigest: preparedPlan.preparedPlanDigest,
|
|
512
|
+
artifactReader: boundArtifactReader,
|
|
513
|
+
});
|
|
514
|
+
const method = new FirmwareUpdateV4({
|
|
515
|
+
id: 1,
|
|
516
|
+
payload: {
|
|
517
|
+
method: 'firmwareUpdateV4',
|
|
518
|
+
platform: 'desktop',
|
|
519
|
+
preparedPlan,
|
|
520
|
+
hostBindingGeneration,
|
|
521
|
+
artifactReader: directArtifactReader,
|
|
522
|
+
},
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
method.init();
|
|
526
|
+
const executionReader = (
|
|
527
|
+
method as unknown as {
|
|
528
|
+
params: { artifactReader: typeof boundArtifactReader };
|
|
529
|
+
}
|
|
530
|
+
).params.artifactReader;
|
|
531
|
+
await executionReader.open({ artifactRef: artifact.artifactRef });
|
|
532
|
+
|
|
533
|
+
expect(boundArtifactReader.open).toHaveBeenCalledWith({ artifactRef: artifact.artifactRef });
|
|
534
|
+
expect(directArtifactReader.open).not.toHaveBeenCalled();
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
test('keeps non-prepared direct binary calls on the legacy paths', async () => {
|
|
538
|
+
const v2 = new FirmwareUpdateV2({
|
|
539
|
+
id: 1,
|
|
540
|
+
payload: {
|
|
541
|
+
method: 'firmwareUpdateV2',
|
|
542
|
+
platform: 'desktop',
|
|
543
|
+
updateType: 'firmware',
|
|
544
|
+
binary: new ArrayBuffer(4),
|
|
545
|
+
},
|
|
546
|
+
});
|
|
547
|
+
const v3 = new FirmwareUpdateV3({
|
|
548
|
+
id: 2,
|
|
549
|
+
payload: {
|
|
550
|
+
method: 'firmwareUpdateV3',
|
|
551
|
+
platform: 'desktop',
|
|
552
|
+
firmwareBinary: new ArrayBuffer(4),
|
|
553
|
+
},
|
|
554
|
+
});
|
|
555
|
+
const bootloader = new DeviceUpdateBootloader({
|
|
556
|
+
id: 3,
|
|
557
|
+
payload: {
|
|
558
|
+
method: 'deviceUpdateBootloader',
|
|
559
|
+
binary: new ArrayBuffer(4),
|
|
560
|
+
},
|
|
561
|
+
});
|
|
562
|
+
bootloader.device = {
|
|
563
|
+
features: {},
|
|
564
|
+
getCurrentDeviceType: () => EDeviceType.Touch,
|
|
565
|
+
getCurrentFirmwareType: () => EFirmwareType.Universal,
|
|
566
|
+
} as any;
|
|
567
|
+
jest.spyOn(bootloader, 'updateTouchBootloader').mockResolvedValue(true);
|
|
568
|
+
|
|
569
|
+
expect(() => v2.init()).not.toThrow();
|
|
570
|
+
expect(() => v3.init()).not.toThrow();
|
|
571
|
+
await expect(bootloader.run()).resolves.toBe(true);
|
|
572
|
+
});
|
|
573
|
+
});
|