test(apps.helpers): unit tests

This commit is contained in:
Nicolas Meienberger 2022-08-11 17:05:54 +02:00
parent 95d62d5c03
commit ea1c14f110
3 changed files with 276 additions and 5 deletions

View file

@ -0,0 +1,273 @@
import { faker } from '@faker-js/faker';
import fs from 'fs-extra';
import { DataSource } from 'typeorm';
import config from '../../../config';
import { setupConnection, teardownConnection } from '../../../test/connection';
import { checkAppRequirements, checkEnvFile, generateEnvFile, getAppInfo, getAvailableApps, getEnvMap, getUpdateInfo, runAppScript } from '../apps.helpers';
import { AppInfo } from '../apps.types';
import { createApp } from './apps.factory';
jest.mock('fs-extra');
jest.mock('child_process');
jest.mock('internal-ip');
jest.mock('tcp-port-used', () => ({
check: (port: number) => {
if (port === 53) {
return true;
}
return false;
},
}));
let db: DataSource | null = null;
const TEST_SUITE = 'appshelpers';
beforeAll(async () => {
db = await setupConnection(TEST_SUITE);
});
afterAll(async () => {
await db?.destroy();
await teardownConnection(TEST_SUITE);
});
describe('checkAppRequirements', () => {
let app1: AppInfo;
beforeEach(async () => {
const app1create = await createApp({});
app1 = app1create.appInfo;
// @ts-ignore
fs.__createMockFiles(app1create.MockFiles);
});
it('should return true if there are no particular requirement', async () => {
const ivValid = await checkAppRequirements(app1.id);
expect(ivValid).toBe(true);
});
it('Should throw an error if app does not exist', async () => {
await expect(checkAppRequirements('not-existing-app')).rejects.toThrow('App not-existing-app not found');
});
it('Should return false if a required port is in use', async () => {
const { appInfo, MockFiles } = await createApp({ requiredPort: 53 });
// @ts-ignore
fs.__createMockFiles(MockFiles);
const ivValid = await checkAppRequirements(appInfo.id);
expect(ivValid).toBe(false);
});
});
describe('getEnvMap', () => {
let app1: AppInfo;
beforeEach(async () => {
const app1create = await createApp({ installed: true });
app1 = app1create.appInfo;
// @ts-ignore
fs.__createMockFiles(app1create.MockFiles);
});
it('should return a map of env vars', async () => {
const envMap = await getEnvMap(app1.id);
expect(envMap.get('TEST_FIELD')).toBe('test');
});
});
describe('checkEnvFile', () => {
let app1: AppInfo;
beforeEach(async () => {
const app1create = await createApp({ installed: true });
app1 = app1create.appInfo;
// @ts-ignore
fs.__createMockFiles(app1create.MockFiles);
});
it('Should not throw if all required fields are present', async () => {
await checkEnvFile(app1.id);
});
it('Should throw if a required field is missing', () => {
const newAppEnv = 'APP_PORT=test\n';
fs.writeFileSync(`${config.ROOT_FOLDER}/app-data/${app1.id}/app.env`, newAppEnv);
try {
checkEnvFile(app1.id);
expect(true).toBe(false);
} catch (e: any) {
expect(e).toBeDefined();
expect(e.message).toBe('New info needed. App config needs to be updated');
}
});
});
describe('runAppScript', () => {
let app1: AppInfo;
beforeEach(async () => {
const app1create = await createApp({ installed: true });
app1 = app1create.appInfo;
// @ts-ignore
fs.__createMockFiles(app1create.MockFiles);
});
it('Should run the app script', async () => {
const { MockFiles } = await createApp({ installed: true });
// @ts-ignore
fs.__createMockFiles(MockFiles);
await runAppScript(['install', app1.id]);
});
});
describe('generateEnvFile', () => {
let app1: AppInfo;
beforeEach(async () => {
const app1create = await createApp({ installed: true });
app1 = app1create.appInfo;
// @ts-ignore
fs.__createMockFiles(app1create.MockFiles);
});
it('Should generate an env file', async () => {
const fakevalue = faker.random.alphaNumeric(10);
generateEnvFile(app1.id, { TEST_FIELD: fakevalue });
const envmap = await getEnvMap(app1.id);
expect(envmap.get('TEST_FIELD')).toBe(fakevalue);
});
it('Should automatically generate value for random field', async () => {
const { appInfo, MockFiles } = await createApp({ installed: true, randomField: true });
// @ts-ignore
fs.__createMockFiles(MockFiles);
generateEnvFile(appInfo.id, { TEST_FIELD: 'test' });
const envmap = await getEnvMap(appInfo.id);
expect(envmap.get('RANDOM_FIELD')).toBeDefined();
expect(envmap.get('RANDOM_FIELD')).toHaveLength(32);
});
it('Should not re-generate random field if it already exists', async () => {
const { appInfo, MockFiles } = await createApp({ installed: true, randomField: true });
// @ts-ignore
fs.__createMockFiles(MockFiles);
const randomField = faker.random.alphaNumeric(32);
fs.writeFileSync(`${config.ROOT_FOLDER}/app-data/${appInfo.id}/app.env`, `RANDOM_FIELD=${randomField}`);
generateEnvFile(appInfo.id, { TEST_FIELD: 'test' });
const envmap = await getEnvMap(appInfo.id);
expect(envmap.get('RANDOM_FIELD')).toBe(randomField);
});
it('Should throw an error if required field is not provided', async () => {
try {
generateEnvFile(app1.id, {});
expect(true).toBe(false);
} catch (e: any) {
expect(e).toBeDefined();
expect(e.message).toBe('Variable TEST_FIELD is required');
}
});
it('Should throw an error if app does not exist', async () => {
try {
generateEnvFile('not-existing-app', { TEST_FIELD: 'test' });
expect(true).toBe(false);
} catch (e: any) {
expect(e).toBeDefined();
expect(e.message).toBe('App not-existing-app not found');
}
});
});
describe('getAvailableApps', () => {
beforeEach(async () => {
const app1create = await createApp({ installed: true });
const app2create = await createApp({});
// @ts-ignore
fs.__createMockFiles(Object.assign(app1create.MockFiles, app2create.MockFiles));
});
it('Should return all available apps', async () => {
const availableApps = await getAvailableApps();
expect(availableApps.length).toBe(2);
});
});
describe('getAppInfo', () => {
let app1: AppInfo;
beforeEach(async () => {
const app1create = await createApp({ installed: false });
app1 = app1create.appInfo;
// @ts-ignore
fs.__createMockFiles(app1create.MockFiles);
});
it('Should return app info', async () => {
const appInfo = await getAppInfo(app1.id);
expect(appInfo.id).toBe(app1.id);
});
it('Should take config.json locally if app is installed', async () => {
const { appInfo, MockFiles } = await createApp({ installed: true });
// @ts-ignore
fs.__createMockFiles(MockFiles);
fs.writeFileSync(`${config.ROOT_FOLDER}/repos/repo-id/apps/${app1.id}/config.json`, '{}');
const app = await getAppInfo(appInfo.id);
expect(app.id).toEqual(appInfo.id);
});
it('Should throw an error if app does not exist', async () => {
try {
await getAppInfo('not-existing-app');
expect(true).toBe(false);
} catch (e: any) {
expect(e).toBeDefined();
expect(e.message).toBe('Error loading app not-existing-app');
}
});
});
describe('getUpdateInfo', () => {
let app1: AppInfo;
beforeEach(async () => {
const app1create = await createApp({ installed: true });
app1 = app1create.appInfo;
// @ts-ignore
fs.__createMockFiles(app1create.MockFiles);
});
it('Should return update info', async () => {
const updateInfo = await getUpdateInfo(app1.id);
console.log(app1);
console.log(updateInfo);
expect(updateInfo?.latest).toBe(app1.tipi_version);
expect(updateInfo?.current).toBe(1);
});
it('Should return null if app is not installed', async () => {
const updateInfo = await getUpdateInfo(faker.random.word());
expect(updateInfo).toBeNull();
});
});

View file

@ -1,7 +1,7 @@
import AppsService from '../apps.service';
import fs from 'fs-extra';
import config from '../../../config';
import childProcess, { ChildProcess } from 'child_process';
import childProcess from 'child_process';
import { AppInfo, AppStatusEnum } from '../apps.types';
import App from '../app.entity';
import { createApp } from './apps.factory';

View file

@ -10,7 +10,7 @@ import App from './app.entity';
export const checkAppRequirements = async (appName: string) => {
let valid = true;
const configFile: AppInfo | null = readJsonFile(`/apps/${appName}/config.json`);
const configFile: AppInfo | null = readJsonFile(`/repos/${config.APPS_REPO_ID}/apps/${appName}/config.json`);
if (!configFile) {
throw new Error(`App ${appName} not found`);
@ -134,9 +134,7 @@ export const getAppInfo = (id: string): AppInfo => {
const configFile: AppInfo = readJsonFile(`/apps/${id}/config.json`);
configFile.description = readFile(`/apps/${id}/metadata/description.md`).toString();
return configFile;
}
if (fileExists(`/repos/${repoId}`)) {
} else if (fileExists(`/repos/${repoId}`)) {
const configFile: AppInfo = readJsonFile(`/repos/${repoId}/apps/${id}/config.json`);
configFile.description = readFile(`/repos/${repoId}/apps/${id}/metadata/description.md`);