feat(cli): add e2e test for backend

This commit is contained in:
Ivan Shmidt
2020-07-23 22:12:53 +02:00
parent 189e1bf547
commit 5f025442dc
8 changed files with 399 additions and 13 deletions
+96 -2
View File
@@ -16,6 +16,7 @@
const os = require('os');
const fs = require('fs-extra');
const fetch = require('node-fetch');
const killTree = require('tree-kill');
const { resolve: resolvePath, join: joinPath } = require('path');
const Browser = require('zombie');
@@ -28,6 +29,7 @@ const {
waitForExit,
print,
} = require('./helpers');
const pgtools = require('pgtools');
async function main() {
const rootDir = await fs.mkdtemp(resolvePath(os.tmpdir(), 'backstage-e2e-'));
@@ -36,8 +38,9 @@ async function main() {
print('Building dist workspace');
const workspaceDir = await buildDistWorkspace('workspace', rootDir);
const isPostgres = Boolean(process.env.POSTGRES_USER);
print('Creating a Backstage App');
const appDir = await createApp('test-app', workspaceDir, rootDir);
const appDir = await createApp('test-app', isPostgres, workspaceDir, rootDir);
print('Creating a Backstage Plugin');
const pluginName = await createPlugin('test-plugin', appDir);
@@ -45,6 +48,9 @@ async function main() {
print('Starting the app');
await testAppServe(pluginName, appDir);
print('Testing the backend startup');
await testBackendStart(appDir, isPostgres);
print('All tests successful, removing test dir');
await fs.remove(rootDir);
}
@@ -97,7 +103,7 @@ async function pinYarnVersion(dir) {
/**
* Creates a new app inside rootDir called test-app, using packages from the workspaceDir
*/
async function createApp(appName, workspaceDir, rootDir) {
async function createApp(appName, isPostgres, workspaceDir, rootDir) {
const child = spawnPiped(
[
'node',
@@ -119,6 +125,14 @@ async function createApp(appName, workspaceDir, rootDir) {
await waitFor(() => stdout.includes('Enter a name for the app'));
child.stdin.write(`${appName}\n`);
await waitFor(() => stdout.includes('Select database for the backend'));
if (!isPostgres) {
// Simulate down arrow press
child.stdin.write(`\u001B\u005B\u0042`);
}
child.stdin.write(`\n`);
print('Waiting for app create script to be done');
await waitForExit(child);
@@ -250,5 +264,85 @@ async function testAppServe(pluginName, appDir) {
}
}
/** Creates PG databases (drops if exists before) */
async function createDB(database) {
const config = {
host: process.env.POSTGRES_HOST,
port: process.env.POSTGRES_PORT,
user: process.env.POSTGRES_USER,
password: process.env.POSTGRES_PASSWORD,
};
try {
await pgtools.dropdb({ config }, database);
} catch (_) {
/* do nothing*/
}
return pgtools.createdb(config, database);
}
/**
* Start serving the newly created backend and make sure that all db migrations works correctly
*/
async function testBackendStart(appDir, isPostgres) {
if (isPostgres) {
print('Creating DBs');
await Promise.all(
[
'catalog',
'scaffolder',
'auth',
'identity',
'proxy',
'techdocs',
].map(name => createDB(`backstage_plugin_${name}`)),
);
print('Created DBs');
}
const child = spawnPiped(['yarn', 'workspace', 'backend', 'start'], {
cwd: appDir,
});
let stdout = '';
let stderr = '';
child.stdout.on('data', data => {
stdout = stdout + data.toString('utf8');
});
child.stderr.on('data', data => {
stderr = stderr + data.toString('utf8');
});
let successful = false;
try {
await waitFor(() => stdout.includes('Listening on ') || stderr !== '');
if (stderr !== '') {
// Skipping the whole block
throw new Error();
}
print('Try to fetch entities from the backend');
// Try fetch entities, should be ok
await fetch('http://localhost:7000/catalog/entities').then(res =>
res.json(),
);
print('Entities fetched successfully');
successful = true;
} finally {
print('Stopping the child process');
// Kill entire process group, otherwise we'll end up with hanging serve processes
killTree(child.pid);
}
try {
await waitForExit(child);
} catch (error) {
if (!successful) {
throw error;
}
print('Backend startup test finished successfully');
}
}
process.on('unhandledRejection', handleError);
main(process.argv.slice(2)).catch(handleError);
+6
View File
@@ -76,6 +76,12 @@ function handleError(err) {
}
}
/**
* Waits for fn() to be true
* Checks every 100ms
* .cancel() is available
* @returns {Promise} Promise of resolution
*/
function waitFor(fn) {
return new Promise(resolve => {
const handle = setInterval(() => {