Skip to content

Commit

Permalink
fix(e2e-tests): fix merod port issue, pass PR number to update PR com…
Browse files Browse the repository at this point in the history
…ment step
  • Loading branch information
fbozic committed Jan 10, 2025
1 parent 7d5010d commit 7e8a0ca
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 70 deletions.
129 changes: 105 additions & 24 deletions .github/workflows/e2e_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,47 @@ jobs:
runs-on: ubuntu-latest

steps:
- name: Prepare e2e-tests config
id: prepare_e2e_tests_config
run: |
# Generate 4 unique random numbers
random_numbers=()
while [ ${#random_numbers[@]} -lt 3 ]; do
num=$((RANDOM%37001 + 3000))
if [[ ! " ${random_numbers[@]} " =~ " ${num} " ]]; then
random_numbers+=($num)
fi
done
# Export random numbers to environment variables
SWARM_PORT="${random_numbers[0]}"
SERVER_PORT="${random_numbers[1]}"
ICP_PORT="${random_numbers[2]}"
echo "SWARM_PORT=$SWARM_PORT" >> $GITHUB_OUTPUT
echo "SERVER_PORT=$SERVER_PORT" >> $GITHUB_OUTPUT
echo "ICP_PORT=$ICP_PORT" >> $GITHUB_OUTPUT
# # Update JSON file with jq
# jq --arg swarmPort "$SWARM_PORT" \
# --arg serverPort "$SERVER_PORT" \
# --arg icpPort "${random_numbers[2]}" \
# '.network.startSwarmPort = ($swarmPort | tonumber) |
# .network.startServerPort = ($serverPort | tonumber) |
# .protocolSandboxes[1].config.rpcUrl = "http://127.0.0.1:\($icpPort)"
# ' e2e-tests/config/config.json > updated_config.json
# mv updated_config.json e2e-tests/config/config.json
- name: Show ports
env:
SWARM_PORT: ${{ steps.prepare_e2e_tests_config.outputs.SWARM_PORT }}
SERVER_PORT: ${{ steps.prepare_e2e_tests_config.outputs.SERVER_PORT }}
ICP_PORT: ${{ steps.prepare_e2e_tests_config.outputs.ICP_PORT }}
run: |
echo "SWARM_PORT=$SWARM_PORT"
echo "SERVER_PORT=$SERVER_PORT"
echo "ICP_PORT=$ICP_PORT"
- name: Checkout code
uses: actions/checkout@v4

Expand All @@ -28,10 +69,37 @@ jobs:
uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
cache-all-crates: true # due to candid-extractor

- name: Install dfx
uses: dfinity/setup-dfx@main

- name: Get PR number test
if: success() || failure()
env:
GH_TOKEN: ${{ github.token }}
GH_REF: ${{ github.ref }}
shell: bash
run: |
A="${GH_REF#refs/heads/}"
echo "A=$A"
gh pr list \
--repo ${{ github.repository }} \
--state open \
--head "$A" \
--base master \
--json number \
-q '.[0].number'
echo "PR_NUMBER=$(gh pr list \
--repo ${{ github.repository }} \
--state open \
--head "${GH_REF#refs/heads/}" \
--base master \
--json number \
-q '.[0].number')"
- name: Build apps
run: |
./apps/kv-store/build.sh
Expand All @@ -46,6 +114,8 @@ jobs:
cargo build -p meroctl -p merod -p e2e-tests
- name: Deploy ICP local devnet
env:
ICP_PORT: ${{ steps.prepare_e2e_tests_config.outputs.ICP_PORT }}
run: |
cargo install candid-extractor
cd ./contracts/icp/context-config
Expand All @@ -60,39 +130,50 @@ jobs:
export SWARM_HOST=$(ifconfig | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | head -n 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
- name: Update pull request comment
- name: Get PR number
id: pr_number
if: success() || failure()
env:
GH_TOKEN: ${{ github.token }}
GH_REF: ${{ github.ref }}
shell: bash
run: |
A="${GH_REF#refs/heads/}"
echo "A=$A"
gh pr list \
--repo ${{ github.repository }} \
--state open \
--head "$A" \
--base master \
--json number \
-q '.[0].number'
echo "PR_NUMBER=$(gh pr list \
--repo ${{ github.repository }} \
--state open \
--head "${GH_REF#refs/heads/}" \
--base master \
--json number \
-q '.[0].number')" >> $GITHUB_OUTPUT
- name: Update pull request comment
if: (success() || failure()) && steps.pr_number.outputs.PR_NUMBER != ''
uses: thollander/actions-comment-pull-request@v3
with:
file-path: ./e2e-tests/corpus/report.md
pr-number: ${{ steps.pr_number.outputs.PR_NUMBER }}

- name: Show node logs
- name: Upload artifacts
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
uses: actions/upload-artifact@v4
with:
name: e2e-tests-corpus
path: e2e-tests/corpus/
retention-days: 2
2 changes: 1 addition & 1 deletion contracts/icp/context-config/deploy_devnet.sh
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ RECIPIENT_PRINCIPAL=$(dfx identity get-principal)
dfx identity use default

# Start dfx with clean state
dfx start --clean --background
dfx start --clean --background --host 127.0.0.1:${ICP_PORT:-4943}

# Create initial identity if needed
dfx identity new --storage-mode=plaintext minting || true
Expand Down
72 changes: 43 additions & 29 deletions e2e-tests/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,18 @@ impl<'a> TestContext<'a> {
pub struct Driver {
environment: TestEnvironment,
config: Config,
meroctl: Meroctl,
merods: HashMap<String, Merod>,
}

pub struct Mero {
ctl: Meroctl,
ds: HashMap<String, Merod>,
}

impl Driver {
pub fn new(environment: TestEnvironment, config: Config) -> Self {
let meroctl = Meroctl::new(&environment);
Self {
environment,
config,
meroctl,
merods: HashMap::new(),
}
}

Expand All @@ -96,9 +96,11 @@ impl Driver {
self.environment
.output_writer
.write_header(&format!("Running protocol {}", sandbox.name()), 1);
self.boot_merods(sandbox).await?;
report = self.run_scenarios(report, sandbox.name()).await?;
self.stop_merods().await;

let mero = self.setup_mero(sandbox).await?;
report = self.run_scenarios(&mero, report, sandbox.name()).await?;
self.stop_merods(&mero.ds).await;

self.environment
.output_writer
.write_header(&format!("Finished protocol {}", sandbox.name()), 1);
Expand All @@ -122,14 +124,16 @@ impl Driver {
report.result()
}

async fn boot_merods(&mut self, sandbox: &ProtocolSandboxEnvironment) -> EyreResult<()> {
async fn setup_mero(&mut self, sandbox: &ProtocolSandboxEnvironment) -> EyreResult<Mero> {
self.environment
.output_writer
.write_header("Starting merod nodes", 2);

let mut merods = HashMap::new();

for i in 0..self.config.network.node_count {
let node_name = format!("node{}", i + 1);
if !self.merods.contains_key(&node_name) {
if !merods.contains_key(&node_name) {
let mut args = vec![format!(
"discovery.rendezvous.namespace=\"calimero/e2e-tests/{}\"",
self.environment.test_id
Expand All @@ -140,7 +144,13 @@ impl Driver {
let mut config_args = vec![];
config_args.extend(args.iter().map(|arg| &**arg));

let merod = Merod::new(node_name.clone(), &self.environment);
let merod = Merod::new(
node_name.clone(),
self.environment.nodes_dir.join(sandbox.name()),
self.environment.logs_dir.join(sandbox.name()),
self.environment.merod_binary.clone(),
self.environment.output_writer,
);

let swarm_host = match env::var(&self.config.network.swarm_host_env) {
Ok(host) => host,
Expand All @@ -158,28 +168,34 @@ impl Driver {

merod.run().await?;

drop(self.merods.insert(node_name, merod));
drop(merods.insert(node_name, merod));
}
}

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

Ok(())
Ok(Mero {
ctl: Meroctl::new(
self.environment.nodes_dir.join(sandbox.name()),
self.environment.meroctl_binary.clone(),
self.environment.output_writer,
),
ds: merods,
})
}

async fn stop_merods(&mut self) {
for (_, merod) in self.merods.iter() {
async fn stop_merods(&mut self, merods: &HashMap<String, Merod>) {
for (_, merod) in merods.iter() {
if let Err(err) = merod.stop().await {
eprintln!("Error stopping merod: {:?}", err);
}
}

self.merods.clear();
}

async fn run_scenarios(
&self,
mero: &Mero,
mut report: TestRunReport,
protocol_name: String,
) -> EyreResult<TestRunReport> {
Expand All @@ -193,6 +209,7 @@ impl Driver {
if test_file_path.exists() {
let scenario_report = self
.run_scenario(
mero,
path.file_name()
.ok_or_eyre("failed")?
.to_str()
Expand All @@ -217,6 +234,7 @@ impl Driver {

async fn run_scenario(
&self,
mero: &Mero,
scenarion_name: &str,
file_path: PathBuf,
) -> EyreResult<TestScenarioReport> {
Expand All @@ -233,7 +251,7 @@ impl Driver {
.output_writer
.write_string(format!("Steps count: {}", scenario.steps.len()));

let (inviter, invitees) = match self.pick_inviter_node() {
let (inviter, invitees) = match self.pick_inviter_node(&mero.ds) {
Some((inviter, invitees)) => (inviter, invitees),
None => bail!("Not enough nodes to run the test"),
};
Expand All @@ -245,12 +263,8 @@ impl Driver {
.output_writer
.write_string(format!("Picked invitees: {:?}", invitees));

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

let mut report = TestScenarioReport::new(scenarion_name.to_owned());

Expand All @@ -271,8 +285,8 @@ impl Driver {
Ok(report)
}

fn pick_inviter_node(&self) -> Option<(String, Vec<String>)> {
let mut node_names: Vec<String> = self.merods.keys().cloned().collect();
fn pick_inviter_node(&self, merods: &HashMap<String, Merod>) -> Option<(String, Vec<String>)> {
let mut node_names: Vec<String> = merods.keys().cloned().collect();
if node_names.len() < 1 {
None
} else {
Expand Down Expand Up @@ -357,11 +371,11 @@ impl TestRunReport {
.steps
.iter()
.find(|step| &step.step_name == step_name)
.map_or(":interrobang:", |step| {
.map_or("N/A", |step| {
if step.result.is_ok() {
":white_check_mark:"
"Success"
} else {
":x:"
"Failure"
}
});
markdown.push_str(&format!(" {} |", result));
Expand Down
13 changes: 6 additions & 7 deletions e2e-tests/src/meroctl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,19 @@ 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,
home_dir: Utf8PathBuf,
binary: Utf8PathBuf,
output_writer: OutputWriter,
}

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

Expand Down Expand Up @@ -142,7 +141,7 @@ impl Meroctl {
async fn run_cmd(&self, node_name: &str, args: &[&str]) -> EyreResult<serde_json::Value> {
let mut root_args = vec![
"--home",
self.nodes_dir.as_str(),
self.home_dir.as_str(),
"--node-name",
node_name,
"--output-format",
Expand Down
Loading

0 comments on commit 7e8a0ca

Please sign in to comment.