runtipi/packages/system-api/src/modules/system/__tests__/system.resolver.test.ts

242 lines
7.9 KiB
TypeScript

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';
import EventDispatcher from '../../../core/config/EventDispatcher';
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 () => {
// Arrange
EventDispatcher.prototype.dispatchEventAsync = jest.fn().mockResolvedValueOnce({ success: true });
// Act
const user = await createUser();
const { data } = await gcall<{ restart: boolean }>({ source: restartMutation, userId: user.id });
// Assert
expect(data?.restart).toBeDefined();
expect(data?.restart).toBe(true);
});
it("Should return an error if user doesn't exist", async () => {
// Arrange
const { data, errors } = await gcall<{ restart: boolean }>({
source: restartMutation,
userId: 1,
});
// Assert
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 () => {
// Arrange
const { data, errors } = await gcall<{ restart: boolean }>({ source: restartMutation });
// Assert
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 () => {
// Arrange
EventDispatcher.prototype.dispatchEventAsync = jest.fn().mockResolvedValueOnce({ success: true });
const spy = jest.spyOn(TipiConfig, 'setConfig');
const user = await createUser();
// Act
await gcall<{ restart: boolean }>({ source: restartMutation, userId: user.id });
// Assert
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 () => {
// Arrange
EventDispatcher.prototype.dispatchEventAsync = jest.fn().mockResolvedValueOnce({ success: true });
const user = await createUser();
// Act
const { data } = await gcall<{ update: boolean }>({ source: updateMutation, userId: user.id });
// Assert
expect(data?.update).toBeDefined();
expect(data?.update).toBe(true);
});
it("Should return an error if user doesn't exist", async () => {
// Act
const { data, errors } = await gcall<{ update: boolean }>({ source: updateMutation, userId: 1 });
// Assert
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 () => {
// Act
const { data, errors } = await gcall<{ update: boolean }>({ source: updateMutation });
// Assert
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 () => {
// Arrange
EventDispatcher.prototype.dispatchEventAsync = jest.fn().mockResolvedValueOnce({ success: true });
const spy = jest.spyOn(TipiConfig, 'setConfig');
const user = await createUser();
// Act
await gcall<{ update: boolean }>({ source: updateMutation, userId: user.id });
// Assert
expect(spy).toHaveBeenCalledTimes(2);
expect(spy).toHaveBeenNthCalledWith(1, 'status', 'UPDATING');
expect(spy).toHaveBeenNthCalledWith(2, 'status', 'RUNNING');
spy.mockRestore();
});
});