|
@@ -0,0 +1,217 @@
|
|
|
|
+import { faker } from '@faker-js/faker';
|
|
|
|
+import axios from 'axios';
|
|
|
|
+import fs from 'fs-extra';
|
|
|
|
+import { DataSource } from 'typeorm';
|
|
|
|
+import TipiCache from '../../../config/TipiCache';
|
|
|
|
+import * as TipiConfig from '../../../core/config/TipiConfig';
|
|
|
|
+import { setConfig } from '../../../core/config/TipiConfig';
|
|
|
|
+import { setupConnection, teardownConnection } from '../../../test/connection';
|
|
|
|
+import { gcall } from '../../../test/gcall';
|
|
|
|
+import { restartMutation, updateMutation } from '../../../test/mutations';
|
|
|
|
+import { systemInfoQuery, versionQuery } from '../../../test/queries';
|
|
|
|
+import User from '../../auth/user.entity';
|
|
|
|
+import { createUser } from '../../auth/__tests__/user.factory';
|
|
|
|
+import { SystemInfoResponse } from '../system.types';
|
|
|
|
+
|
|
|
|
+jest.mock('fs-extra');
|
|
|
|
+jest.mock('axios');
|
|
|
|
+
|
|
|
|
+beforeEach(async () => {
|
|
|
|
+ jest.resetModules();
|
|
|
|
+ jest.resetAllMocks();
|
|
|
|
+ jest.restoreAllMocks();
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+let db: DataSource | null = null;
|
|
|
|
+const TEST_SUITE = 'systemresolver';
|
|
|
|
+beforeAll(async () => {
|
|
|
|
+ db = await setupConnection(TEST_SUITE);
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+afterAll(async () => {
|
|
|
|
+ await db?.destroy();
|
|
|
|
+ await teardownConnection(TEST_SUITE);
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+beforeEach(async () => {
|
|
|
|
+ jest.resetModules();
|
|
|
|
+ jest.resetAllMocks();
|
|
|
|
+ jest.restoreAllMocks();
|
|
|
|
+ await User.clear();
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+describe('Test: systemInfo', () => {
|
|
|
|
+ beforeEach(async () => {});
|
|
|
|
+
|
|
|
|
+ it('Should return correct system info from file', async () => {
|
|
|
|
+ const systemInfo = {
|
|
|
|
+ cpu: { load: 10 },
|
|
|
|
+ memory: { available: 100, total: 1000, used: 900 },
|
|
|
|
+ disk: { available: 100, total: 1000, used: 900 },
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const MockFiles = {
|
|
|
|
+ '/runtipi/state/system-info.json': JSON.stringify(systemInfo),
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // @ts-ignore
|
|
|
|
+ fs.__createMockFiles(MockFiles);
|
|
|
|
+
|
|
|
|
+ const { data } = await gcall<{ systemInfo: SystemInfoResponse }>({ source: systemInfoQuery });
|
|
|
|
+
|
|
|
|
+ expect(data?.systemInfo).toBeDefined();
|
|
|
|
+ expect(data?.systemInfo.cpu).toBeDefined();
|
|
|
|
+ expect(data?.systemInfo.cpu.load).toBe(systemInfo.cpu.load);
|
|
|
|
+ expect(data?.systemInfo.memory).toBeDefined();
|
|
|
|
+ expect(data?.systemInfo.memory.available).toBe(systemInfo.memory.available);
|
|
|
|
+ expect(data?.systemInfo.memory.total).toBe(systemInfo.memory.total);
|
|
|
|
+ expect(data?.systemInfo.memory.used).toBe(systemInfo.memory.used);
|
|
|
|
+ expect(data?.systemInfo.disk).toBeDefined();
|
|
|
|
+ expect(data?.systemInfo.disk.available).toBe(systemInfo.disk.available);
|
|
|
|
+ expect(data?.systemInfo.disk.total).toBe(systemInfo.disk.total);
|
|
|
|
+ expect(data?.systemInfo.disk.used).toBe(systemInfo.disk.used);
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ it('Should return 0 for missing values', async () => {
|
|
|
|
+ const systemInfo = {
|
|
|
|
+ cpu: {},
|
|
|
|
+ memory: {},
|
|
|
|
+ disk: {},
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const MockFiles = {
|
|
|
|
+ '/runtipi/state/system-info.json': JSON.stringify(systemInfo),
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // @ts-ignore
|
|
|
|
+ fs.__createMockFiles(MockFiles);
|
|
|
|
+
|
|
|
|
+ const { data } = await gcall<{ systemInfo: SystemInfoResponse }>({ source: systemInfoQuery });
|
|
|
|
+
|
|
|
|
+ expect(data?.systemInfo).toBeDefined();
|
|
|
|
+ expect(data?.systemInfo.cpu).toBeDefined();
|
|
|
|
+ expect(data?.systemInfo.cpu.load).toBe(0);
|
|
|
|
+ expect(data?.systemInfo.memory).toBeDefined();
|
|
|
|
+ expect(data?.systemInfo.memory.available).toBe(0);
|
|
|
|
+ expect(data?.systemInfo.memory.total).toBe(0);
|
|
|
|
+ expect(data?.systemInfo.memory.used).toBe(0);
|
|
|
|
+ expect(data?.systemInfo.disk).toBeDefined();
|
|
|
|
+ expect(data?.systemInfo.disk.available).toBe(0);
|
|
|
|
+ expect(data?.systemInfo.disk.total).toBe(0);
|
|
|
|
+ expect(data?.systemInfo.disk.used).toBe(0);
|
|
|
|
+ });
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+describe('Test: getVersion', () => {
|
|
|
|
+ const current = `${faker.random.numeric(1)}.${faker.random.numeric(1)}.${faker.random.numeric()}`;
|
|
|
|
+ const latest = `${faker.random.numeric(1)}.${faker.random.numeric(1)}.${faker.random.numeric()}`;
|
|
|
|
+ beforeEach(async () => {
|
|
|
|
+ jest.spyOn(axios, 'get').mockResolvedValue({
|
|
|
|
+ data: { name: `v${latest}` },
|
|
|
|
+ });
|
|
|
|
+ setConfig('version', current);
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ it('Should return correct version', async () => {
|
|
|
|
+ const { data } = await gcall<{ version: { current: string; latest?: string } }>({
|
|
|
|
+ source: versionQuery,
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ expect(data?.version).toBeDefined();
|
|
|
|
+ expect(data?.version.current).toBeDefined();
|
|
|
|
+ expect(data?.version.latest).toBeDefined();
|
|
|
|
+ expect(data?.version.current).toBe(current);
|
|
|
|
+ expect(data?.version.latest).toBe(latest);
|
|
|
|
+ });
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+describe('Test: restart', () => {
|
|
|
|
+ beforeEach(async () => {
|
|
|
|
+ setConfig('status', 'RUNNING');
|
|
|
|
+ setConfig('version', '1.0.0');
|
|
|
|
+ TipiCache.set('latestVersion', '1.0.1');
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ it('Should return true', async () => {
|
|
|
|
+ const user = await createUser();
|
|
|
|
+ const { data } = await gcall<{ restart: boolean }>({ source: restartMutation, userId: user.id });
|
|
|
|
+
|
|
|
|
+ expect(data?.restart).toBeDefined();
|
|
|
|
+ expect(data?.restart).toBe(true);
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ it("Should return an error if user doesn't exist", async () => {
|
|
|
|
+ const { data, errors } = await gcall<{ restart: boolean }>({
|
|
|
|
+ source: restartMutation,
|
|
|
|
+ userId: 1,
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ expect(errors?.[0].message).toBe('Access denied! You need to be authorized to perform this action!');
|
|
|
|
+ expect(data?.restart).toBeUndefined();
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ it('Should throw an error if no userId is not provided', async () => {
|
|
|
|
+ const { data, errors } = await gcall<{ restart: boolean }>({ source: restartMutation });
|
|
|
|
+
|
|
|
|
+ expect(errors?.[0].message).toBe('Access denied! You need to be authorized to perform this action!');
|
|
|
|
+ expect(data?.restart).toBeUndefined();
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ it('Should set app status to restarting', async () => {
|
|
|
|
+ const spy = jest.spyOn(TipiConfig, 'setConfig');
|
|
|
|
+
|
|
|
|
+ const user = await createUser();
|
|
|
|
+ await gcall<{ restart: boolean }>({ source: restartMutation, userId: user.id });
|
|
|
|
+
|
|
|
|
+ expect(spy).toHaveBeenCalledTimes(2);
|
|
|
|
+
|
|
|
|
+ expect(spy).toHaveBeenNthCalledWith(1, 'status', 'RESTARTING');
|
|
|
|
+ expect(spy).toHaveBeenNthCalledWith(2, 'status', 'RUNNING');
|
|
|
|
+
|
|
|
|
+ spy.mockRestore();
|
|
|
|
+ });
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+describe('Test: update', () => {
|
|
|
|
+ beforeEach(async () => {
|
|
|
|
+ setConfig('status', 'RUNNING');
|
|
|
|
+ setConfig('version', '1.0.0');
|
|
|
|
+ TipiCache.set('latestVersion', '1.0.1');
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ it('Should return true', async () => {
|
|
|
|
+ const user = await createUser();
|
|
|
|
+ const { data } = await gcall<{ update: boolean }>({ source: updateMutation, userId: user.id });
|
|
|
|
+
|
|
|
|
+ expect(data?.update).toBeDefined();
|
|
|
|
+ expect(data?.update).toBe(true);
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ it("Should return an error if user doesn't exist", async () => {
|
|
|
|
+ const { data, errors } = await gcall<{ update: boolean }>({ source: updateMutation, userId: 1 });
|
|
|
|
+
|
|
|
|
+ expect(errors?.[0].message).toBe('Access denied! You need to be authorized to perform this action!');
|
|
|
|
+ expect(data?.update).toBeUndefined();
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ it('Should throw an error if no userId is not provided', async () => {
|
|
|
|
+ const { data, errors } = await gcall<{ update: boolean }>({ source: updateMutation });
|
|
|
|
+
|
|
|
|
+ expect(errors?.[0].message).toBe('Access denied! You need to be authorized to perform this action!');
|
|
|
|
+ expect(data?.update).toBeUndefined();
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ it('Should set app status to updating', async () => {
|
|
|
|
+ const spy = jest.spyOn(TipiConfig, 'setConfig');
|
|
|
|
+
|
|
|
|
+ const user = await createUser();
|
|
|
|
+ await gcall<{ update: boolean }>({ source: updateMutation, userId: user.id });
|
|
|
|
+
|
|
|
|
+ expect(spy).toHaveBeenCalledTimes(2);
|
|
|
|
+
|
|
|
|
+ expect(spy).toHaveBeenNthCalledWith(1, 'status', 'UPDATING');
|
|
|
|
+ expect(spy).toHaveBeenNthCalledWith(2, 'status', 'RUNNING');
|
|
|
|
+
|
|
|
|
+ spy.mockRestore();
|
|
|
|
+ });
|
|
|
|
+});
|