Skip to content

Commit

Permalink
Merge pull request #318 from dappnode/pablo/execute-both-tests
Browse files Browse the repository at this point in the history
Execute every end to end test even previous throws error
  • Loading branch information
pablomendezroyo authored Apr 24, 2023
2 parents ecf4781 + 9fee8d6 commit 9a40649
Show file tree
Hide file tree
Showing 2 changed files with 187 additions and 127 deletions.
206 changes: 79 additions & 127 deletions src/commands/githubActions/endToEndTest/executeTests.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import got from "got";
import { Compose } from "../../../files/compose/types.js";
import { getContainerName } from "../../../params.js";
import { Manifest } from "../../../types.js";
import { DappmanagerTestApi } from "./dappmanagerTestApi.js";
import Docker from "dockerode";
import chalk from "chalk";
import { getStakerConfigByNetwork } from "./params.js";
import { getIsStakerPkg } from "./utils.js";
Expand All @@ -22,6 +19,7 @@ import {
executionClientsMainnet,
executionClientsPrater
} from "./types.js";
import { executeTestCheckers } from "./testCheckers.js";

/**
* Execute the tests for the integration test workflow. These tests require
Expand Down Expand Up @@ -50,158 +48,114 @@ export async function executePackageInstallAndUpdateTest({
environmentByService?: Record<string, string>;
network?: Network;
}): Promise<void> {
const errors: Error[] = [];
const { name } = manifest;
const isStakerPkg = getIsStakerPkg(name);

// TEST: Install package from scratch
console.log(chalk.dim("\nTEST: Installing pkg from scratch"));
// Remove package, if not found it will throw an error but it's ok
await dappmanagerTestApi
.packageRemove({ dnpName: name })
.catch(() => console.log("Package already removed"));
await dappmanagerTestApi.packageInstall({
dnpName: name,
version: releaseMultiHash,
userSettings: { environment: environmentByService || {} }
});
// Set staker config if staker package
if (isStakerPkg && network)
await setStakerConfig(name, dappmanagerTestApi, network);
await executeTestCheckers({
dnpName: manifest.name,
await testPackageInstallFromScratch(
dappmanagerTestApi,
releaseMultiHash,
isStakerPkg,
name,
compose,
errorLogsTimeout,
healthCheckUrl
healthCheckUrl,
network,
environmentByService
).catch(e => {
errors.push(e);
});

// Skip update test if running in test environment, dappnodesdk package name is not published
if (process.env.ENVIRONMENT === "TEST") return;
if (process.env.ENVIRONMENT !== "TEST") {
// Update package to the given hash
console.log(chalk.dim("\nTEST: Install production pkg and update"));
await testPackageInstallAndUpdate(
dappmanagerTestApi,
releaseMultiHash,
isStakerPkg,
name,
compose,
errorLogsTimeout,
healthCheckUrl,
network,
environmentByService
).catch(e => {
errors.push(e);
});
}

// Update package to the given hash
console.log(chalk.dim("\nTEST: Install production pkg and update"));
// Throw aggregated error if any
if (errors.length > 0) throw AggregateError(errors);
}

async function testPackageInstallAndUpdate(
dappmanagerTestApi: DappmanagerTestApi,
releaseMultiHash: string,
isStakerPkg: boolean,
dnpName: string,
compose: Compose,
errorLogsTimeout: number,
healthCheckUrl?: string,
network?: Network,
environmentByService?: Record<string, string>
): Promise<void> {
// Remove package, if not found it will throw an error but it's ok
await dappmanagerTestApi.packageRemove({ dnpName: manifest.name });
await dappmanagerTestApi
.packageRemove({ dnpName })
.catch(() => console.log("Package already removed"));
await dappmanagerTestApi.packageInstall({
dnpName: manifest.name,
dnpName,
version: "latest", // Install production version
userSettings: { environment: environmentByService }
});
await dappmanagerTestApi.packageInstall({
dnpName: manifest.name,
dnpName,
version: releaseMultiHash, // Install test version
userSettings: { environment: environmentByService }
});
// Set staker config if staker package
if (isStakerPkg && network)
await setStakerConfig(name, dappmanagerTestApi, network);
await setStakerConfig(dnpName, dappmanagerTestApi, network);
await executeTestCheckers({
dnpName: manifest.name,
dnpName,
compose,
errorLogsTimeout,
healthCheckUrl
});
}

async function executeTestCheckers({
dnpName,
compose,
errorLogsTimeout,
healthCheckUrl,
network
}: {
dnpName: string;
compose: Compose;
errorLogsTimeout: number;
healthCheckUrl?: string;
network?: Network;
}): Promise<void> {
const docker = new Docker();
for (const service of Object.keys(compose.services)) {
const containerName = getContainerName({
dnpName,
serviceName: service,
isCore: false
});
await ensureContainerStatus(containerName, docker).then(() =>
console.log(chalk.green(` ✓ Container ${containerName} is running`))
);
await ensureNoErrorLogs(containerName, docker, errorLogsTimeout).then(() =>
console.log(chalk.green(` ✓ No error logs found in ${containerName}`))
);
}
if (healthCheckUrl)
await ensureHealthCheck(healthCheckUrl).then(() =>
console.log(chalk.green(` ✓ Healthcheck endpoint returned 200`))
);
if (network)
await attestanceProof(network).then(() =>
console.log(chalk.green(` ✓ Attestation proof`))
);
}

async function ensureContainerStatus(
containerName: string,
docker: Docker
): Promise<void> {
for (let i = 0; i < 10; i++) {
const container = await docker.getContainer(containerName).inspect();
if (container.State.Status !== "running") {
const errorMessage = `Container ${containerName} is not running. Status: ${container.State.Status}`;
console.error(chalk.red(` x ${errorMessage}`));
throw Error(errorMessage);
}
await new Promise(resolve => setTimeout(resolve, 1000));
}
}

async function ensureHealthCheck(healthCheckUrl: string): Promise<void> {
const res = await got(healthCheckUrl);
if (res.statusCode < 200 || res.statusCode > 299) {
const errorMessage = `HTTP code !== 2XX. Healthcheck endpoint returned ${res.statusCode}`;
console.error(chalk.red(`x ${errorMessage}`));
throw Error(errorMessage);
}
}

async function ensureNoErrorLogs(
containerName: string,
docker: Docker,
errorLogsTimeout: number
async function testPackageInstallFromScratch(
dappmanagerTestApi: DappmanagerTestApi,
releaseMultiHash: string,
isStakerPkg: boolean,
dnpName: string,
compose: Compose,
errorLogsTimeout: number,
healthCheckUrl?: string,
network?: Network,
environmentByService?: Record<string, string>
): Promise<void> {
// maximum number of seconds to wait for the container to be running smoothly and check its logs are 120 seconds
errorLogsTimeout > 120 && (errorLogsTimeout = 120);

// Wait the timeout to ensure that no error logs are produced
await new Promise(resolve => setTimeout(resolve, errorLogsTimeout * 1000));

const logs = (
await docker.getContainer(containerName).logs({
follow: false,
stdout: true,
stderr: true,
timestamps: true
})
).toString();

// collect all the error logs print them and throw an error if any
const errorRegex = /.*\s+error.*/gi;
const errorLogs = logs.match(errorRegex);

if (errorLogs) {
const errorMessage = `Error logs found in ${containerName}`;
console.error(chalk.red(` x ${errorMessage}`));
errorLogs.forEach(errorLog => console.error(chalk.red(` ${errorLog}`)));
throw Error(errorMessage);
}
}

/**
* Test that the validators are attesting after a peri
*/
async function attestanceProof(network: Network): Promise<void> {
if (network === "mainnet") return;
// TODO
return;
// Remove package, if not found it will throw an error but it's ok
await dappmanagerTestApi
.packageRemove({ dnpName })
.catch(() => console.log("Package already removed"));
await dappmanagerTestApi.packageInstall({
dnpName,
version: releaseMultiHash,
userSettings: { environment: environmentByService || {} }
});
// Set staker config if staker package
if (isStakerPkg && network)
await setStakerConfig(dnpName, dappmanagerTestApi, network);
await executeTestCheckers({
dnpName,
compose,
errorLogsTimeout,
healthCheckUrl
});
}

async function setStakerConfig(
Expand All @@ -216,7 +170,6 @@ async function setStakerConfig(
stakerConfig.executionClient.dnpName = dnpName as ExecutionClientMainnet;
else if (consensusClientsMainnet.includes(dnpName as any))
stakerConfig.consensusClient.dnpName = dnpName as ConsensusClientMainnet;

break;
case "gnosis":
if (executionClientsGnosis.includes(dnpName as any))
Expand All @@ -232,7 +185,6 @@ async function setStakerConfig(
stakerConfig.consensusClient.dnpName = dnpName as ConsensusClientPrater;
await dappmanagerTestApi.stakerConfigSet(stakerConfig);
break;

default:
throw Error("unknown network");
}
Expand Down
108 changes: 108 additions & 0 deletions src/commands/githubActions/endToEndTest/testCheckers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import chalk from "chalk";
import Docker from "dockerode";
import got from "got";
import { Network } from "./types.js";
import { getContainerName } from "../../../params.js";
import { Compose } from "../../../types.js";

export async function executeTestCheckers({
dnpName,
compose,
errorLogsTimeout,
healthCheckUrl,
network
}: {
dnpName: string;
compose: Compose;
errorLogsTimeout: number;
healthCheckUrl?: string;
network?: Network;
}): Promise<void> {
const docker = new Docker();
for (const service of Object.keys(compose.services)) {
const containerName = getContainerName({
dnpName,
serviceName: service,
isCore: false
});
await ensureContainerStatus(containerName, docker).then(() =>
console.log(chalk.green(` ✓ Container ${containerName} is running`))
);
await ensureNoErrorLogs(containerName, docker, errorLogsTimeout).then(() =>
console.log(chalk.green(` ✓ No error logs found in ${containerName}`))
);
}
if (healthCheckUrl)
await ensureHealthCheck(healthCheckUrl).then(() =>
console.log(chalk.green(` ✓ Healthcheck endpoint returned 200`))
);
if (network)
await attestanceProof(network).then(() =>
console.log(chalk.green(` ✓ Attestation proof`))
);
}

async function ensureContainerStatus(
containerName: string,
docker: Docker
): Promise<void> {
for (let i = 0; i < 10; i++) {
const container = await docker.getContainer(containerName).inspect();
if (container.State.Status !== "running") {
const errorMessage = `Container ${containerName} is not running. Status: ${container.State.Status}`;
console.error(chalk.red(` x ${errorMessage}`));
throw Error(errorMessage);
}
await new Promise(resolve => setTimeout(resolve, 1000));
}
}

async function ensureHealthCheck(healthCheckUrl: string): Promise<void> {
const res = await got(healthCheckUrl);
if (res.statusCode < 200 || res.statusCode > 299) {
const errorMessage = `HTTP code !== 2XX. Healthcheck endpoint returned ${res.statusCode}`;
console.error(chalk.red(`x ${errorMessage}`));
throw Error(errorMessage);
}
}

async function ensureNoErrorLogs(
containerName: string,
docker: Docker,
errorLogsTimeout: number
): Promise<void> {
// maximum number of seconds to wait for the container to be running smoothly and check its logs are 120 seconds
errorLogsTimeout > 120 && (errorLogsTimeout = 120);

// Wait the timeout to ensure that no error logs are produced
await new Promise(resolve => setTimeout(resolve, errorLogsTimeout * 1000));

const logs = (
await docker.getContainer(containerName).logs({
follow: false,
stdout: true,
stderr: true,
timestamps: true
})
).toString();

// collect all the error logs print them and throw an error if any
const errorRegex = /.*\s+error.*/gi;
const errorLogs = logs.match(errorRegex);

if (errorLogs) {
const errorMessage = `Error logs found in ${containerName}`;
console.error(chalk.red(` x ${errorMessage}`));
errorLogs.forEach(errorLog => console.error(chalk.red(` ${errorLog}`)));
throw Error(errorMessage);
}
}

/**
* Test that the validators are attesting after a peri
*/
async function attestanceProof(network: Network): Promise<void> {
if (network === "mainnet") return;
// TODO
return;
}

0 comments on commit 9a40649

Please sign in to comment.