Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ci: setup e2e-tests in github workflow #976

Merged
merged 2 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions .github/workflows/e2e_tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: End-to-end tests

on:
push:
branches:
- '**'
paths:
- Cargo.toml
- Cargo.lock
- 'contracts/**'
- 'crates/**'
- 'e2e-tests/**'
- '.github/workflows/e2e_tests.yml'

jobs:
test:
name: Test
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup rust toolchain
run: rustup toolchain install stable --profile minimal

- name: Setup rust cache
uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true

- name: Build apps
run: |
./apps/kv-store/build.sh

- name: Build contracts
run: |
./contracts/context-config/build.sh
./contracts/proxy-lib/build.sh

- name: Build binaries
run: |
cargo build -p meroctl -p merod -p e2e-tests

- name: Run e2e tests
run: |
export NO_COLOR=1
echo "Running e2e tests, check job summary for details"
echo "# E2E tests 🏗️" >> $GITHUB_STEP_SUMMARY
./target/debug/e2e-tests \
--input-dir ./e2e-tests/config \
--output-dir ./e2e-tests/corpus \
--merod-binary ./target/debug/merod \
--meroctl-binary ./target/debug/meroctl \
--output-format markdown >> $GITHUB_STEP_SUMMARY

- name: Run e2e tests
if: success() || failure()
run: |
LOGS_DIR=./e2e-tests/corpus/logs
if [ ! -d "$LOGS_DIR" ]; then
echo "Directory $LOGS_DIR does not exist."
exit 1
fi

echo "# Node logs 📋" >> $GITHUB_STEP_SUMMARY

for FOLDER in "$LOGS_DIR"/*; do
if [ -d "$FOLDER" ]; then

RUN_LOG="$FOLDER/run.log"
if [ -f "$RUN_LOG" ]; then
echo "## Node logs: $(basename $FOLDER) 📋" >> $GITHUB_STEP_SUMMARY
cat "$RUN_LOG" >> $GITHUB_STEP_SUMMARY
else
echo "## No run.log found in $FOLDER ⚠️" >> $GITHUB_STEP_SUMMARY
fi
fi
done
18 changes: 16 additions & 2 deletions e2e-tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,26 @@ Binary crate which runs e2e tests for the merod node.

## Usage

Build the merod and meroctl binaries and run the e2e tests with the following
commands:
First build apps, contracts, and mero binaries. After that run the e2e tests.

Example of running the e2e tests:

```bash
./apps/kv-store/build.sh

./contracts/context-config/build.sh
./contracts/proxy-lib/build.sh

cargo build -p merod
cargo build -p meroctl

export NO_COLOR=1 # Disable color output for merod logs
cargo run -p e2e-tests -- --input-dir ./e2e-tests/config --output-dir ./e2e-tests/corpus --merod-binary ./target/debug/merod --meroctl-binary ./target/debug/meroctl
```

Useful env vars for debugging:

- `RUST_LOG=debug` - enable debug logs
- `RUST_LOG=near_jsonrpc_client=debug` - or more specific logs
- `NEAR_ENABLE_SANDBOX_LOG=1` - enable near sandbox logs
- `NO_COLOR=1` - disable color output
66 changes: 48 additions & 18 deletions e2e-tests/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use tokio::time::sleep;
use crate::config::Config;
use crate::meroctl::Meroctl;
use crate::merod::Merod;
use crate::output::OutputWriter;
use crate::steps::{TestScenario, TestStep};
use crate::TestEnvironment;

Expand All @@ -24,21 +25,28 @@ pub struct TestContext<'a> {
pub context_id: Option<String>,
pub inviter_public_key: Option<String>,
pub invitees_public_keys: HashMap<String, String>,
pub output_writer: OutputWriter,
}

pub trait Test {
async fn run_assert(&self, ctx: &mut TestContext<'_>) -> EyreResult<()>;
}

impl<'a> TestContext<'a> {
pub fn new(inviter: String, invitees: Vec<String>, meroctl: &'a Meroctl) -> Self {
pub fn new(
inviter: String,
invitees: Vec<String>,
meroctl: &'a Meroctl,
output_writer: OutputWriter,
) -> Self {
Self {
inviter,
invitees,
meroctl,
context_id: None,
inviter_public_key: None,
invitees_public_keys: HashMap::new(),
output_writer,
}
}
}
Expand Down Expand Up @@ -81,6 +89,13 @@ impl Driver {

self.stop_merods().await;

if let Err(e) = &result {
self.environment
.output_writer
.write_str("Error occurred during test run:");
self.environment.output_writer.write_string(e.to_string());
}

result
}

Expand Down Expand Up @@ -113,7 +128,9 @@ impl Driver {
}

async fn boot_merods(&mut self) -> EyreResult<()> {
println!("========================= Starting nodes ===========================");
self.environment
.output_writer
.write_header("Starting merod nodes", 2);

for i in 0..self.config.network.node_count {
let node_name = format!("node{}", i + 1);
Expand Down Expand Up @@ -178,9 +195,7 @@ impl Driver {
}

// TODO: Implement health check?
sleep(Duration::from_secs(20)).await;

println!("====================================================================");
sleep(Duration::from_secs(10)).await;

Ok(())
}
Expand Down Expand Up @@ -213,35 +228,50 @@ impl Driver {
}

async fn run_scenario(&self, file_path: PathBuf) -> EyreResult<()> {
println!("================= Setting up scenario and context ==================");
self.environment
.output_writer
.write_header("Running scenario", 2);

let scenario: TestScenario = from_slice(&read(&file_path).await?)?;

println!(
"Loaded test scenario from file: {:?}\n{:?}",
file_path, scenario
);
self.environment
.output_writer
.write_string(format!("Source file: {:?}", file_path));
self.environment
.output_writer
.write_string(format!("Steps count: {}", scenario.steps.len()));

let (inviter, invitees) = match self.pick_inviter_node() {
Some((inviter, invitees)) => (inviter, invitees),
None => bail!("Not enough nodes to run the test"),
};

println!("Picked inviter: {}", inviter);
println!("Picked invitees: {:?}", invitees);
self.environment
.output_writer
.write_string(format!("Picked inviter: {}", inviter));
self.environment
.output_writer
.write_string(format!("Picked invitees: {:?}", invitees));

let mut ctx = TestContext::new(inviter, invitees, &self.meroctl);

println!("====================================================================");
let mut ctx = TestContext::new(
inviter,
invitees,
&self.meroctl,
self.environment.output_writer,
);

for step in scenario.steps.iter() {
println!("======================== Starting step =============================");
println!("Step: {:?}", step);
self.environment
.output_writer
.write_header("Running test step", 3);
self.environment.output_writer.write_str("Step spec:");
self.environment.output_writer.write_json(&step)?;

match step {
TestStep::ContextCreate(step) => step.run_assert(&mut ctx).await?,
TestStep::ContextInviteJoin(step) => step.run_assert(&mut ctx).await?,
TestStep::JsonRpcCall(step) => step.run_assert(&mut ctx).await?,
};
println!("====================================================================");
}

Ok(())
Expand Down
9 changes: 9 additions & 0 deletions e2e-tests/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ use config::Config;
use const_format::concatcp;
use driver::Driver;
use eyre::Result as EyreResult;
use output::{OutputFormat, OutputWriter};
use rand::Rng;
use tokio::fs::{create_dir_all, read_to_string, remove_dir_all};

mod config;
mod driver;
mod meroctl;
mod merod;
mod output;
mod steps;

pub const EXAMPLES: &str = r"
Expand Down Expand Up @@ -51,6 +53,11 @@ pub struct Args {
#[arg(long, value_name = "PATH")]
#[arg(env = "MEROCTL_BINARY", hide_env_values = true)]
pub meroctl_binary: Utf8PathBuf,

/// Format of the E2E test output.
#[arg(long, value_name = "OUTPUT_FORMAT", default_value_t, value_enum)]
#[arg(env = "E2E_OUTPUT_FORMAT", hide_env_values = true)]
pub output_format: OutputFormat,
}

#[derive(Debug)]
Expand All @@ -62,6 +69,7 @@ pub struct TestEnvironment {
pub output_dir: Utf8PathBuf,
pub nodes_dir: Utf8PathBuf,
pub logs_dir: Utf8PathBuf,
pub output_writer: OutputWriter,
}

impl Into<TestEnvironment> for Args {
Expand All @@ -76,6 +84,7 @@ impl Into<TestEnvironment> for Args {
output_dir: self.output_dir.clone(),
nodes_dir: self.output_dir.join("nodes"),
logs_dir: self.output_dir.join("logs"),
output_writer: OutputWriter::new(self.output_format),
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion e2e-tests/src/meroctl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@ use camino::Utf8PathBuf;
use eyre::{bail, eyre, OptionExt, Result as EyreResult};
use tokio::process::Command;

use crate::output::OutputWriter;
use crate::TestEnvironment;

pub struct Meroctl {
nodes_dir: Utf8PathBuf,
binary: Utf8PathBuf,
output_writer: OutputWriter,
}

impl Meroctl {
pub fn new(environment: &TestEnvironment) -> Self {
Self {
nodes_dir: environment.nodes_dir.clone(),
binary: environment.meroctl_binary.clone(),
output_writer: environment.output_writer,
}
}

Expand Down Expand Up @@ -148,7 +151,8 @@ impl Meroctl {

root_args.extend(args);

println!("Command: '{:}' {:?}", &self.binary, root_args);
self.output_writer
.write_string(format!("Command: '{:}' {:?}", &self.binary, root_args));

let output = Command::new(&self.binary)
.args(root_args)
Expand Down
6 changes: 5 additions & 1 deletion e2e-tests/src/merod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use tokio::fs::{create_dir_all, File};
use tokio::io::copy;
use tokio::process::{Child, Command};

use crate::output::OutputWriter;
use crate::TestEnvironment;

pub struct Merod {
Expand All @@ -15,6 +16,7 @@ pub struct Merod {
nodes_dir: Utf8PathBuf,
log_dir: Utf8PathBuf,
binary: Utf8PathBuf,
output_writer: OutputWriter,
}

impl Merod {
Expand All @@ -25,6 +27,7 @@ impl Merod {
log_dir: environment.logs_dir.join(&name),
binary: environment.merod_binary.clone(),
name,
output_writer: environment.output_writer,
}
}

Expand Down Expand Up @@ -92,7 +95,8 @@ impl Merod {
let log_file = self.log_dir.join(format!("{}.log", log_suffix));
let mut log_file = File::create(&log_file).await?;

println!("Command: '{:}' {:?}", &self.binary, root_args);
self.output_writer
.write_string(format!("Command: '{:}' {:?}", &self.binary, root_args));

let mut child = Command::new(&self.binary)
.args(root_args)
Expand Down
Loading