UNPKG

@ledgerhq/live-common

Version:
265 lines • 14.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const rxjs_1 = require("rxjs"); const errors_1 = require("@ledgerhq/errors"); const getDeviceInfo_1 = require("../tasks/getDeviceInfo"); const getLatestFirmware_1 = require("../tasks/getLatestFirmware"); const aDeviceInfo_1 = require("../../mock/fixtures/aDeviceInfo"); const aLatestFirmwareContext_1 = require("../../mock/fixtures/aLatestFirmwareContext"); const getLatestAvailableFirmware_1 = require("./getLatestAvailableFirmware"); const core_1 = require("../tasks/core"); // Needed for rxjs timeout jest.useFakeTimers(); // Needs to mock the timer from rxjs used in the task retry mechanism (`retry` with `timer`) jest.mock("rxjs", () => { const originalModule = jest.requireActual("rxjs"); return { ...originalModule, timer: jest.fn(() => { return (0, rxjs_1.of)(1); }), }; }); jest.mock("../tasks/getDeviceInfo"); // Going to test also the retry mechanism of the tasks associated to the action const mockedInternalGetDeviceInfoTask = jest.mocked(getDeviceInfo_1.internalGetDeviceInfoTask); const mockedGetDeviceInfoTask = jest.mocked(getDeviceInfo_1.getDeviceInfoTask); // `sharedLogicTaskWrapper` contains the retry logic, and we want to test it. // If we only mock `internalGetDeviceInfoTask`, `getDeviceInfoTask` will still be defined with the original // `internalGetDeviceInfoTask` and not the mocked version. So we need to redefine it. mockedGetDeviceInfoTask.mockImplementation((0, core_1.sharedLogicTaskWrapper)(getDeviceInfo_1.internalGetDeviceInfoTask)); jest.mock("../tasks/getLatestFirmware"); const mockedGetLatestFirmwareTask = jest.mocked(getLatestFirmware_1.getLatestFirmwareTask); describe("getLatestAvailableFirmwareAction", () => { let aDeviceInfo; let aLatestFirmwareContext; beforeEach(() => { aDeviceInfo = (0, aDeviceInfo_1.aDeviceInfoBuilder)(); aLatestFirmwareContext = (0, aLatestFirmwareContext_1.aLatestFirmwareContextBuilder)(); // mockedTimer.mockReturnValue(of(1)); }); afterEach(() => { mockedInternalGetDeviceInfoTask.mockClear(); mockedGetLatestFirmwareTask.mockClear(); // mockedTimer.mockClear(); jest.clearAllTimers(); }); describe("The device is in a correct state", () => { it("should return the latest available firmware for the device if there is one", done => { // Happy path mockedInternalGetDeviceInfoTask.mockReturnValue((0, rxjs_1.of)({ type: "data", deviceInfo: aDeviceInfo })); mockedGetLatestFirmwareTask.mockReturnValue((0, rxjs_1.of)({ type: "data", firmwareUpdateContext: aLatestFirmwareContext })); let step = 1; (0, getLatestAvailableFirmware_1.getLatestAvailableFirmwareAction)({ deviceId: "A_DEVICE_ID", }).subscribe({ next: ({ firmwareUpdateContext, deviceInfo, lockedDevice, status, }) => { try { switch (step) { case 1: expect(firmwareUpdateContext).toBeNull(); expect(deviceInfo).toBeNull(); expect(lockedDevice).toBe(false); expect(status).toBe("ongoing"); break; case 2: expect(firmwareUpdateContext).toBeNull(); expect(deviceInfo).toEqual(aDeviceInfo); expect(lockedDevice).toBe(false); expect(status).toBe("ongoing"); break; case 3: expect(firmwareUpdateContext).toEqual(aLatestFirmwareContext); expect(deviceInfo).toEqual(aDeviceInfo); expect(lockedDevice).toBe(false); expect(status).toBe("available-firmware"); done(); break; } } catch (expectError) { done(expectError); } step += 1; }, }); }); it("should return no latest available firmware information if there is none for the device", done => { // Happy path mockedInternalGetDeviceInfoTask.mockReturnValue((0, rxjs_1.of)({ type: "data", deviceInfo: aDeviceInfo })); mockedGetLatestFirmwareTask.mockReturnValue((0, rxjs_1.of)({ type: "taskError", error: "FirmwareUpToDate", })); let step = 1; (0, getLatestAvailableFirmware_1.getLatestAvailableFirmwareAction)({ deviceId: "A_DEVICE_ID", }).subscribe({ next: ({ firmwareUpdateContext, deviceInfo, lockedDevice, status, }) => { try { switch (step) { case 1: expect(firmwareUpdateContext).toBeNull(); expect(deviceInfo).toBeNull(); expect(lockedDevice).toBe(false); expect(status).toBe("ongoing"); break; case 2: expect(firmwareUpdateContext).toBeNull(); expect(deviceInfo).toEqual(aDeviceInfo); expect(lockedDevice).toBe(false); expect(status).toBe("ongoing"); break; case 3: expect(firmwareUpdateContext).toBeNull(); expect(deviceInfo).toEqual(aDeviceInfo); expect(lockedDevice).toBe(false); expect(status).toBe("no-available-firmware"); done(); break; } } catch (expectError) { done(expectError); } step += 1; }, }); }); }); describe("When the device is locked", () => { it("should notify the function consumer of the need to unlock the device, and once done, continue the get latest available firmware flow", done => { let count = 0; // Returns a LockedDeviceError first, then the device info pretending the device has been unlocked mockedInternalGetDeviceInfoTask.mockReturnValue(new rxjs_1.Observable(o => { if (count < 1) { count++; // Mocks the internal task, some shared error are thrown (like `LockedDeviceError`) // and caught by the `sharedLogicTaskWrapper` o.error(new errors_1.LockedDeviceError()); } else { o.next({ type: "data", deviceInfo: aDeviceInfo }); } })); mockedGetLatestFirmwareTask.mockReturnValue((0, rxjs_1.of)({ type: "data", firmwareUpdateContext: aLatestFirmwareContext })); let step = 1; (0, getLatestAvailableFirmware_1.getLatestAvailableFirmwareAction)({ deviceId: "A_DEVICE_ID", }).subscribe({ next: ({ firmwareUpdateContext, deviceInfo, lockedDevice, status, error, }) => { try { switch (step) { case 1: expect(firmwareUpdateContext).toBeNull(); expect(deviceInfo).toBeNull(); expect(lockedDevice).toBe(false); expect(status).toBe("ongoing"); expect(error).toBeNull(); break; case 2: expect(firmwareUpdateContext).toBeNull(); expect(deviceInfo).toBeNull(); expect(lockedDevice).toBe(true); // The actions is retrying its inner tasks on a locked device error expect(status).toBe("ongoing"); expect(error).not.toBeNull(); // No need to advance the timer, as the retry timer is mocked to return directly, without a timeout break; // A retry happened, this time with an unlocked device case 3: expect(firmwareUpdateContext).toBeNull(); expect(deviceInfo).toEqual(aDeviceInfo); expect(lockedDevice).toBe(false); expect(status).toBe("ongoing"); expect(error).toBeNull(); break; case 4: expect(firmwareUpdateContext).toEqual(aLatestFirmwareContext); expect(deviceInfo).toEqual(aDeviceInfo); expect(lockedDevice).toBe(false); expect(status).toBe("available-firmware"); expect(error).toBeNull(); done(); break; } } catch (expectError) { done(expectError); } step += 1; }, }); }); }); describe("When another APDU/action was ongoing, and a Transport race condition occurred", () => { it("should notify the function consumer of the need to unlock the device, and once done, continue the get latest available firmware flow", done => { let count = 0; // Returns a LockedDeviceError first, then the device info pretending the device has been unlocked mockedInternalGetDeviceInfoTask.mockReturnValue(new rxjs_1.Observable(o => { if (count < 1) { count++; // Mocks the internal task, some shared error are thrown (like `TransportRaceCondition`) // and caught by the `sharedLogicTaskWrapper` o.error(new errors_1.TransportRaceCondition()); } else { o.next({ type: "data", deviceInfo: aDeviceInfo }); } })); mockedGetLatestFirmwareTask.mockReturnValue((0, rxjs_1.of)({ type: "data", firmwareUpdateContext: aLatestFirmwareContext })); let step = 1; (0, getLatestAvailableFirmware_1.getLatestAvailableFirmwareAction)({ deviceId: "A_DEVICE_ID", }).subscribe({ next: ({ firmwareUpdateContext, deviceInfo, lockedDevice, status, error, }) => { try { switch (step) { case 1: expect(firmwareUpdateContext).toBeNull(); expect(deviceInfo).toBeNull(); expect(lockedDevice).toBe(false); expect(status).toBe("ongoing"); expect(error).toBeNull(); break; case 2: expect(firmwareUpdateContext).toBeNull(); expect(deviceInfo).toBeNull(); expect(lockedDevice).toBe(false); // The actions is retrying its inner tasks on a locked device error expect(status).toBe("ongoing"); expect(error).toEqual(expect.objectContaining({ type: "SharedError", name: "TransportRaceCondition", retrying: true, })); // No need to advance the timer, as the retry timer is mocked to return directly, without a timeout break; // A retry happened, this time with an unlocked device case 3: expect(firmwareUpdateContext).toBeNull(); expect(deviceInfo).toEqual(aDeviceInfo); expect(lockedDevice).toBe(false); expect(status).toBe("ongoing"); expect(error).toBeNull(); break; case 4: expect(firmwareUpdateContext).toEqual(aLatestFirmwareContext); expect(deviceInfo).toEqual(aDeviceInfo); expect(lockedDevice).toBe(false); expect(status).toBe("available-firmware"); expect(error).toBeNull(); done(); break; } } catch (expectError) { done(expectError); } step += 1; }, }); }); }); }); //# sourceMappingURL=getLatestAvailableFirmware.test.js.map