diff --git a/packages/dashboard/src/pages/settings.tsx b/packages/dashboard/src/pages/settings.tsx index 1986b32c..4807d46b 100644 --- a/packages/dashboard/src/pages/settings.tsx +++ b/packages/dashboard/src/pages/settings.tsx @@ -4,6 +4,9 @@ import Layout from '../components/Layout'; import { useLogoutMutation, useRestartMutation, useUpdateMutation, useVersionQuery } from '../generated/graphql'; import { useRef, useState } from 'react'; +// Necessary to avoid flickering when initiating an update or restart +const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + const Settings: NextPage = () => { const toast = useToast(); const restartDisclosure = useDisclosure(); @@ -51,11 +54,9 @@ const Settings: NextPage = () => { const handleRestart = async () => { setLoading(true); try { - setTimeout(() => { - logout(); - }, 2000); - - await restart(); + restart(); + await wait(2000); + logout(); } catch (error) { handleError(error); } finally { @@ -66,11 +67,9 @@ const Settings: NextPage = () => { const handleUpdate = async () => { setLoading(true); try { - setTimeout(() => { - logout(); - }, 2000); - - await update(); + update(); + await wait(2000); + logout(); } catch (error) { handleError(error); } finally { diff --git a/packages/system-api/src/modules/system/__tests__/system.service.test.ts b/packages/system-api/src/modules/system/__tests__/system.service.test.ts index 252e81aa..7f93a1ae 100644 --- a/packages/system-api/src/modules/system/__tests__/system.service.test.ts +++ b/packages/system-api/src/modules/system/__tests__/system.service.test.ts @@ -1,10 +1,12 @@ import fs from 'fs-extra'; import semver from 'semver'; +import childProcess from 'child_process'; import axios from 'axios'; import SystemService from '../system.service'; import { faker } from '@faker-js/faker'; import TipiCache from '../../../config/TipiCache'; import { setConfig } from '../../../core/config/TipiConfig'; +import logger from '../../../config/logger/logger'; jest.mock('fs-extra'); jest.mock('child_process'); @@ -110,6 +112,22 @@ describe('Test: restart', () => { expect(restart).toBeTruthy(); }); + + it('Should log error if fails', async () => { + // @ts-ignore + const spy = jest.spyOn(childProcess, 'execFile').mockImplementation((_path, _args, _, cb) => { + // @ts-ignore + if (cb) cb('error', null, null); + }); + const log = jest.spyOn(logger, 'error'); + + const restart = await SystemService.restart(); + + expect(restart).toBeTruthy(); + expect(log).toHaveBeenCalledWith('Error restarting: error'); + + spy.mockRestore(); + }); }); describe('Test: update', () => { @@ -121,4 +139,57 @@ describe('Test: update', () => { expect(update).toBeTruthy(); }); + + it('Should throw an error if latest version is not set', async () => { + TipiCache.del('latestVersion'); + const spy = jest.spyOn(axios, 'get').mockResolvedValue({ + data: { name: null }, + }); + + setConfig('version', '0.0.1'); + + await expect(SystemService.update()).rejects.toThrow('Could not get latest version'); + + spy.mockRestore(); + }); + + it('Should throw if current version is higher than latest', async () => { + setConfig('version', '0.0.2'); + TipiCache.set('latestVersion', '0.0.1'); + + await expect(SystemService.update()).rejects.toThrow('Current version is newer than latest version'); + }); + + it('Should throw if current version is equal to latest', async () => { + setConfig('version', '0.0.1'); + TipiCache.set('latestVersion', '0.0.1'); + + await expect(SystemService.update()).rejects.toThrow('Current version is already up to date'); + }); + + it('Should throw an error if there is a major version difference', async () => { + setConfig('version', '0.0.1'); + TipiCache.set('latestVersion', '1.0.0'); + + await expect(SystemService.update()).rejects.toThrow('The major version has changed. Please update manually'); + }); + + it('Should log error if fails', async () => { + // @ts-ignore + const spy = jest.spyOn(childProcess, 'execFile').mockImplementation((_path, _args, _, cb) => { + // @ts-ignore + if (cb) cb('error', null, null); + }); + const log = jest.spyOn(logger, 'error'); + + setConfig('version', '0.0.1'); + TipiCache.set('latestVersion', '0.0.2'); + + const update = await SystemService.update(); + + expect(update).toBeTruthy(); + expect(log).toHaveBeenCalledWith('Error updating: error'); + + spy.mockRestore(); + }); }); diff --git a/packages/system-api/src/modules/system/system.service.ts b/packages/system-api/src/modules/system/system.service.ts index 800bdea6..e0979514 100644 --- a/packages/system-api/src/modules/system/system.service.ts +++ b/packages/system-api/src/modules/system/system.service.ts @@ -22,8 +22,6 @@ const systemInfoSchema = z.object({ }), }); -const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); - const systemInfo = (): z.infer => { const info = systemInfoSchema.safeParse(readJsonFile('/state/system-info.json')); @@ -59,8 +57,6 @@ const getVersion = async (): Promise<{ current: string; latest?: string }> => { const restart = async (): Promise => { setConfig('status', 'RESTARTING'); - await wait(2000); - runScript('/scripts/system.sh', ['restart'], (err: string) => { setConfig('status', 'RUNNING'); if (err) { @@ -68,15 +64,14 @@ const restart = async (): Promise => { } }); + setConfig('status', 'RUNNING'); + return true; }; const update = async (): Promise => { const { current, latest } = await getVersion(); - console.log(current, latest); - await wait(2000); - if (!latest) { throw new Error('Could not get latest version'); }