app-store/apps/__tests__/apps.test.ts
2023-04-03 23:25:19 +02:00

196 lines
5.0 KiB
TypeScript

import fs from 'fs';
import jsyaml from 'js-yaml';
interface AppConfig {
id: string;
port: number;
categories: string[];
requirements?: {
ports?: number[];
};
name: string;
description: string;
version?: string;
tipi_version: number;
short_desc: string;
author: string;
source: string;
available: boolean;
}
const networkExceptions = ['pihole', 'tailscale', 'homeassistant', 'plex', 'zerotier', 'gladys'];
const getAppConfigs = (): AppConfig[] => {
const apps: AppConfig[] = [];
const appsDir = fs.readdirSync('./apps');
appsDir.forEach((app: string) => {
const path = `./apps/${app}/config.json`;
if (fs.existsSync(path)) {
const configFile = fs.readFileSync(path).toString();
try {
const config: AppConfig = JSON.parse(configFile);
if (config.available) {
apps.push(config);
}
} catch (e) {
console.error('Error parsing config file', app);
}
}
});
return apps;
};
describe('App configs', () => {
it('Get app config should return at least one app', () => {
const apps = getAppConfigs();
expect(apps.length).toBeGreaterThan(0);
});
it('Each app should have an id', () => {
const apps = getAppConfigs();
apps.forEach((app) => {
expect(app.id).toBeDefined();
});
});
it('Each app should have a md description', () => {
const apps = getAppConfigs();
apps.forEach((app) => {
const path = `./apps/${app.id}/metadata/description.md`;
if (fs.existsSync(path)) {
const description = fs.readFileSync(path).toString();
expect(description).toBeDefined();
} else {
console.error(`Missing description for app ${app.id}`);
expect(true).toBe(false);
}
});
});
it('Each app should have categories defined as an array', () => {
const apps = getAppConfigs();
apps.forEach((app) => {
expect(app.categories).toBeDefined();
expect(app.categories).toBeInstanceOf(Array);
});
});
it('Each app should have a name', () => {
const apps = getAppConfigs();
apps.forEach((app) => {
expect(app.name).toBeDefined();
});
});
it('Each app should have a description', () => {
const apps = getAppConfigs();
apps.forEach((app) => {
expect(app.description).toBeDefined();
});
});
it('Each app should have a port', () => {
const apps = getAppConfigs();
apps.forEach((app) => {
expect(app.port).toBeDefined();
expect(app.port).toBeGreaterThan(999);
expect(app.port).toBeLessThan(65535);
});
});
it('Each app should have a different port', () => {
const appConfigs = getAppConfigs();
const ports = appConfigs.map((app) => app.port);
expect(new Set(ports).size).toBe(appConfigs.length);
});
it('Each app should have a unique id', () => {
const appConfigs = getAppConfigs();
const ids = appConfigs.map((app) => app.id);
expect(new Set(ids).size).toBe(appConfigs.length);
});
it('Each app should have a version', () => {
const apps = getAppConfigs();
apps.forEach((app) => {
expect(app.tipi_version).toBeDefined();
expect(app.tipi_version).toBeGreaterThan(0);
});
});
it('Each app should have a docker-compose file beside it', () => {
const apps = getAppConfigs();
apps.forEach((app) => {
expect(fs.existsSync(`./apps/${app.id}/docker-compose.yml`)).toBe(true);
});
});
it('Each app should have a metadata folder beside it', () => {
const apps = getAppConfigs();
apps.forEach((app) => {
expect(fs.existsSync(`./apps/${app.id}/metadata`)).toBe(true);
});
});
it('Each app should have a file named logo.jpg in the metadata folder', () => {
const apps = getAppConfigs();
apps.forEach((app) => {
expect(fs.existsSync(`./apps/${app.id}/metadata/logo.jpg`)).toBe(true);
});
});
it('Each app should have a container name equals to its id', () => {
const apps = getAppConfigs();
apps.forEach((app) => {
const dockerComposeFile = fs.readFileSync(`./apps/${app.id}/docker-compose.yml`).toString();
const dockerCompose: any = jsyaml.load(dockerComposeFile);
if (!dockerCompose.services[app.id]) {
console.error(app.id);
}
expect(dockerCompose.services[app.id]).toBeDefined();
expect(dockerCompose.services[app.id].container_name).toBe(app.id);
});
});
it('Each app should have network tipi_main_network', () => {
const apps = getAppConfigs();
apps.forEach((app) => {
if (!networkExceptions.includes(app.id)) {
const dockerComposeFile = fs.readFileSync(`./apps/${app.id}/docker-compose.yml`).toString();
const dockerCompose: any = jsyaml.load(dockerComposeFile);
expect(dockerCompose.services[app.id]).toBeDefined();
if (!dockerCompose.services[app.id].networks) {
console.error(app.id);
}
expect(dockerCompose.services[app.id].networks).toBeDefined();
expect(dockerCompose.services[app.id].networks).toStrictEqual(['tipi_main_network']);
}
});
});
});