From ceca048b8dbcc7a81601be65115001af62b75022 Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Thu, 19 May 2022 14:36:03 +0300 Subject: [PATCH] Introduce periodical check for updates Signed-off-by: Janne Savolainen --- ...ing-for-updates-automatically.test.ts.snap | 11 ++ ...checking-for-updates-automatically.test.ts | 117 ++++++++++++++++++ src/main/getDiForUnitTesting.ts | 4 + ...periodical-check-for-updates.injectable.ts | 33 +++++ .../start-checking-for-updates.injectable.ts | 29 +++++ .../stop-checking-for-updates.injectable.ts | 27 ++++ 6 files changed, 221 insertions(+) create mode 100644 src/behaviours/update-app/__snapshots__/checking-for-updates-automatically.test.ts.snap create mode 100644 src/behaviours/update-app/checking-for-updates-automatically.test.ts create mode 100644 src/main/update-app/periodical-check-for-updates/periodical-check-for-updates.injectable.ts create mode 100644 src/main/update-app/periodical-check-for-updates/start-checking-for-updates.injectable.ts create mode 100644 src/main/update-app/periodical-check-for-updates/stop-checking-for-updates.injectable.ts diff --git a/src/behaviours/update-app/__snapshots__/checking-for-updates-automatically.test.ts.snap b/src/behaviours/update-app/__snapshots__/checking-for-updates-automatically.test.ts.snap new file mode 100644 index 0000000000..b8012d142a --- /dev/null +++ b/src/behaviours/update-app/__snapshots__/checking-for-updates-automatically.test.ts.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`checking for updates automatically given updater is enabled and configuration exists, when started renders 1`] = ` + +
+
+
+ +`; diff --git a/src/behaviours/update-app/checking-for-updates-automatically.test.ts b/src/behaviours/update-app/checking-for-updates-automatically.test.ts new file mode 100644 index 0000000000..d28926b428 --- /dev/null +++ b/src/behaviours/update-app/checking-for-updates-automatically.test.ts @@ -0,0 +1,117 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; +import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; +import type { RenderResult } from "@testing-library/react"; +import electronUpdaterIsActiveInjectable from "../../main/electron-app/features/electron-updater-is-active.injectable"; +import publishIsConfiguredInjectable from "../../main/update-app/publish-is-configured.injectable"; +import type { AsyncFnMock } from "@async-fn/jest"; +import asyncFn from "@async-fn/jest"; +import checkForUpdatesInjectable from "../../main/update-app/check-for-updates/check-for-updates.injectable"; +import startCheckingForUpdatesInjectable + from "../../main/update-app/periodical-check-for-updates/start-checking-for-updates.injectable"; + +const ENOUGH_TIME = 1000 * 60 * 60 * 2; + +describe("checking for updates automatically", () => { + let applicationBuilder: ApplicationBuilder; + let checkForUpdatesMock: AsyncFnMock<() => Promise>; + + beforeEach(() => { + jest.useFakeTimers(); + + applicationBuilder = getApplicationBuilder(); + + applicationBuilder.beforeApplicationStart(({ mainDi }) => { + mainDi.unoverride(startCheckingForUpdatesInjectable); + + checkForUpdatesMock = asyncFn(); + + mainDi.override( + checkForUpdatesInjectable, + () => checkForUpdatesMock, + ); + }); + }); + + describe("given updater is enabled and configuration exists, when started", () => { + let rendered: RenderResult; + + beforeEach(async () => { + applicationBuilder.beforeApplicationStart(({ mainDi }) => { + mainDi.override(electronUpdaterIsActiveInjectable, () => true); + mainDi.override(publishIsConfiguredInjectable, () => true); + }); + + rendered = await applicationBuilder.render(); + }); + + it("renders", () => { + expect(rendered.baseElement).toMatchSnapshot(); + }); + + it("checks for updates", () => { + expect(checkForUpdatesMock).toHaveBeenCalled(); + }); + + it("when just not enough time passes, does not check for updates again automatically yet", () => { + checkForUpdatesMock.mockClear(); + + jest.advanceTimersByTime(ENOUGH_TIME - 1); + + expect(checkForUpdatesMock).not.toHaveBeenCalled(); + }); + + it("when just enough time passes, checks for updates again automatically", () => { + checkForUpdatesMock.mockClear(); + + jest.advanceTimersByTime(ENOUGH_TIME); + + expect(checkForUpdatesMock).toHaveBeenCalled(); + }); + }); + + describe("given updater is enabled but no configuration exist, when started", () => { + beforeEach(async () => { + applicationBuilder.beforeApplicationStart(({ mainDi }) => { + mainDi.override(electronUpdaterIsActiveInjectable, () => true); + mainDi.override(publishIsConfiguredInjectable, () => false); + }); + + await applicationBuilder.render(); + }); + + it("does not check for updates", () => { + expect(checkForUpdatesMock).not.toHaveBeenCalled(); + }); + + it("when enough time passes for checking updates again, still does not check for updates", () => { + jest.advanceTimersByTime(ENOUGH_TIME); + + expect(checkForUpdatesMock).not.toHaveBeenCalled(); + }); + }); + + describe("given updater is not enabled but and configuration exist, when started", () => { + beforeEach(async () => { + applicationBuilder.beforeApplicationStart(({ mainDi }) => { + mainDi.override(electronUpdaterIsActiveInjectable, () => false); + mainDi.override(publishIsConfiguredInjectable, () => true); + }); + + await applicationBuilder.render(); + }); + + it("does not check for updates", () => { + expect(checkForUpdatesMock).not.toHaveBeenCalled(); + }); + + it("when enough time passes for checking updates again, still does not check for updates", () => { + jest.advanceTimersByTime(ENOUGH_TIME); + + expect(checkForUpdatesMock).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/src/main/getDiForUnitTesting.ts b/src/main/getDiForUnitTesting.ts index e1ac3490ca..605b733ac2 100644 --- a/src/main/getDiForUnitTesting.ts +++ b/src/main/getDiForUnitTesting.ts @@ -84,6 +84,8 @@ import setUpdateOnQuitInjectable from "./electron-app/features/set-update-on-qui import downloadPlatformUpdateInjectable from "./update-app/download-platform-update/download-platform-update.injectable"; import startCatalogSyncInjectable from "./catalog-sync-to-renderer/start-catalog-sync.injectable"; import startKubeConfigSyncInjectable from "./start-main-application/runnables/kube-config-sync/start-kube-config-sync.injectable"; +import startCheckingForUpdatesInjectable + from "./update-app/periodical-check-for-updates/start-checking-for-updates.injectable"; export function getDiForUnitTesting(opts: GetDiForUnitTestingOptions = {}) { const { @@ -130,6 +132,8 @@ export function getDiForUnitTesting(opts: GetDiForUnitTestingOptions = {}) { di.override(applicationMenuInjectable, () => ({ start: () => {}, stop: () => {} })); + di.override(startCheckingForUpdatesInjectable, () => ({ run: () => {} })); + // TODO: Remove usages of globally exported appEventBus to get rid of this di.override(appEventBusInjectable, () => new EventEmitter<[AppEvent]>()); diff --git a/src/main/update-app/periodical-check-for-updates/periodical-check-for-updates.injectable.ts b/src/main/update-app/periodical-check-for-updates/periodical-check-for-updates.injectable.ts new file mode 100644 index 0000000000..c973330030 --- /dev/null +++ b/src/main/update-app/periodical-check-for-updates/periodical-check-for-updates.injectable.ts @@ -0,0 +1,33 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { getStartableStoppable } from "../../../common/utils/get-startable-stoppable"; +import checkForUpdatesInjectable from "../check-for-updates/check-for-updates.injectable"; + +const periodicalCheckForUpdatesInjectable = getInjectable({ + id: "periodical-check-for-updates", + + instantiate: (di) => { + const checkForUpdates = di.inject(checkForUpdatesInjectable); + + return getStartableStoppable("periodical-check-for-updates", () => { + const TWO_HOURS = 1000 * 60 * 60 * 2; + + // Note: intentional orphan promise to make checking for updates happen in the background + checkForUpdates(); + + const intervalId = setInterval(() => { + // Note: intentional orphan promise to make checking for updates happen in the background + checkForUpdates(); + }, TWO_HOURS); + + return () => { + clearInterval(intervalId); + }; + }); + }, +}); + +export default periodicalCheckForUpdatesInjectable; diff --git a/src/main/update-app/periodical-check-for-updates/start-checking-for-updates.injectable.ts b/src/main/update-app/periodical-check-for-updates/start-checking-for-updates.injectable.ts new file mode 100644 index 0000000000..9a9b9cf206 --- /dev/null +++ b/src/main/update-app/periodical-check-for-updates/start-checking-for-updates.injectable.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import periodicalCheckForUpdatesInjectable from "./periodical-check-for-updates.injectable"; +import { afterRootFrameIsReadyInjectionToken } from "../../start-main-application/runnable-tokens/after-root-frame-is-ready-injection-token"; +import updatingIsEnabledInjectable from "../updating-is-enabled.injectable"; + +const startCheckingForUpdatesInjectable = getInjectable({ + id: "start-checking-for-updates", + + instantiate: (di) => { + const periodicalCheckForUpdates = di.inject(periodicalCheckForUpdatesInjectable); + const updatingIsEnabled = di.inject(updatingIsEnabledInjectable); + + return { + run: async () => { + if (updatingIsEnabled) { + await periodicalCheckForUpdates.start(); + } + }, + }; + }, + + injectionToken: afterRootFrameIsReadyInjectionToken, +}); + +export default startCheckingForUpdatesInjectable; diff --git a/src/main/update-app/periodical-check-for-updates/stop-checking-for-updates.injectable.ts b/src/main/update-app/periodical-check-for-updates/stop-checking-for-updates.injectable.ts new file mode 100644 index 0000000000..13aefe4d96 --- /dev/null +++ b/src/main/update-app/periodical-check-for-updates/stop-checking-for-updates.injectable.ts @@ -0,0 +1,27 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { beforeQuitOfFrontEndInjectionToken } from "../../start-main-application/runnable-tokens/before-quit-of-front-end-injection-token"; +import periodicalCheckForUpdatesInjectable from "./periodical-check-for-updates.injectable"; + +const stopCheckingForUpdatesInjectable = getInjectable({ + id: "stop-checking-for-updates", + + instantiate: (di) => { + const periodicalCheckForUpdates = di.inject(periodicalCheckForUpdatesInjectable); + + return { + run: async () => { + if (periodicalCheckForUpdates.started) { + await periodicalCheckForUpdates.stop(); + } + }, + }; + }, + + injectionToken: beforeQuitOfFrontEndInjectionToken, +}); + +export default stopCheckingForUpdatesInjectable;