From 676044cbf3e676d472bae556b8c01a543767ea51 Mon Sep 17 00:00:00 2001 From: Gabriele Picco Date: Tue, 17 Feb 2026 18:02:10 -0700 Subject: [PATCH 1/6] feat: improve ci/cd speed --- .github/workflows/ci-test-integration.yml | 10 ++-- test-integration/Makefile | 37 +++++++++++++-- test-integration/test-runner/bin/run_tests.rs | 3 +- test-integration/test-tools/src/validator.rs | 46 +++++++++++++++---- 4 files changed, 79 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci-test-integration.yml b/.github/workflows/ci-test-integration.yml index 5980aaec4..291d3d837 100644 --- a/.github/workflows/ci-test-integration.yml +++ b/.github/workflows/ci-test-integration.yml @@ -22,7 +22,7 @@ jobs: - uses: ./magicblock-validator/.github/actions/setup-build-env with: - build_cache_key_name: "magicblock-validator-ci-test-integration-${{ github.ref_name }}-${{ hashFiles('magicblock-validator/Cargo.lock') }}" + build_cache_key_name: "magicblock-validator-ci-test-integration-v002-${{ hashFiles('magicblock-validator/Cargo.lock', 'magicblock-validator/test-integration/Cargo.lock') }}" rust_toolchain_release: "1.91.1" github_access_token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }} @@ -31,8 +31,10 @@ jobs: - name: Build project and test programs run: | - cargo build --locked + cargo build --locked -p magicblock-validator + cargo test --manifest-path test-integration/Cargo.toml --workspace --no-run --locked make -C test-integration programs + RUN_TESTS=chainlink make -C test-integration maybe-prepare-chainlink-programs shell: bash working-directory: magicblock-validator @@ -63,8 +65,8 @@ jobs: - uses: ./magicblock-validator/.github/actions/setup-build-env with: - build_cache_key_name: "magicblock-validator-ci-test-integration-${{ github.ref_name }}-${{ hashFiles('magicblock-validator/Cargo.lock') }}" - rust_toolchain_release: "1.84.1" + build_cache_key_name: "magicblock-validator-ci-test-integration-v002-${{ hashFiles('magicblock-validator/Cargo.lock', 'magicblock-validator/test-integration/Cargo.lock') }}" + rust_toolchain_release: "1.91.1" github_access_token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/test-integration/Makefile b/test-integration/Makefile index 95ff8551e..1dda67c20 100644 --- a/test-integration/Makefile +++ b/test-integration/Makefile @@ -21,6 +21,11 @@ SCHEDULECOMMIT_SECURITY_SO := $(DEPLOY_DIR)/program_schedulecommit_security.so COMMITTOR_PROGRAM_SO := $(ROOT_DEPLOY_DIR)/magicblock_committor_program.so PROGRAMS_SO := $(FLEXI_COUNTER_SO) $(SCHEDULECOMMIT_SO) $(SCHEDULECOMMIT_SECURITY_SO) $(COMMITTOR_PROGRAM_SO) +CHAINLINK_MINIV2_SO := $(DEPLOY_DIR)/miniv2/program_mini.so +CHAINLINK_MINIV2_JSON := $(DEPLOY_DIR)/miniv2/program_mini.json +CHAINLINK_MINIV3_SO := $(DEPLOY_DIR)/miniv3/program_mini.so +CHAINLINK_MINI_HASH_FILE := $(DEPLOY_DIR)/.chainlink-mini-src.hash +CHAINLINK_MINI_HASH_INPUTS := $(shell find $(DIR)programs/mini -type f \( -name '*.rs' -o -name '*.toml' \) | sort) $(DIR)test-chainlink/scripts/miniv2-json-from-so.js $(DIR)test-chainlink/Makefile list: @cat Makefile | grep "^[a-z].*:" | sed 's/:.*//g' @@ -30,15 +35,40 @@ list-programs: programs: $(PROGRAMS_SO) test: $(PROGRAMS_SO) - $(MAKE) chainlink-prep-programs -C ./test-chainlink && \ + $(MAKE) maybe-prepare-chainlink-programs && \ RUST_BACKTRACE=1 \ RUST_LOG=$(RUST_LOG) \ - cargo run --package test-runner --bin run-tests + cargo run --locked --package test-runner --bin run-tests + +maybe-prepare-chainlink-programs: + @set -e; \ + requires_chainlink_programs=0; \ + if [ -z "$(RUN_TESTS)" ]; then \ + requires_chainlink_programs=1; \ + elif echo ",$(RUN_TESTS)," | grep -Eq ',(chainlink|cloning),'; then \ + requires_chainlink_programs=1; \ + fi; \ + if [ "$$requires_chainlink_programs" -eq 0 ]; then \ + echo "Skipping chainlink-prep-programs for RUN_TESTS='$(RUN_TESTS)'"; \ + exit 0; \ + fi; \ + if command -v sha256sum >/dev/null 2>&1; then \ + current_hash=$$(cat $(CHAINLINK_MINI_HASH_INPUTS) | sha256sum | awk '{print $$1}'); \ + else \ + current_hash=$$(cat $(CHAINLINK_MINI_HASH_INPUTS) | shasum -a 256 | awk '{print $$1}'); \ + fi; \ + cached_hash=$$(cat "$(CHAINLINK_MINI_HASH_FILE)" 2>/dev/null || true); \ + if [ -f "$(CHAINLINK_MINIV2_SO)" ] && [ -f "$(CHAINLINK_MINIV2_JSON)" ] && [ -f "$(CHAINLINK_MINIV3_SO)" ] && [ "$$cached_hash" = "$$current_hash" ]; then \ + echo "Chainlink mini programs already available, skipping rebuild"; \ + else \ + $(MAKE) chainlink-prep-programs -C ./test-chainlink; \ + echo "$$current_hash" > "$(CHAINLINK_MINI_HASH_FILE)"; \ + fi test-force-mb: $(PROGRAMS_SO) RUST_LOG=$(RUST_LOG) \ FORCE_MAGIC_BLOCK_VALIDATOR=1 \ - cargo run --package test-runner --bin run-tests + cargo run --locked --package test-runner --bin run-tests test-schedulecommit: RUN_TESTS=schedulecommit \ @@ -248,6 +278,7 @@ ci-lint: lint setup-schedulecommit-ephem \ setup-table-mania-devnet \ setup-task-scheduler-devnet \ + maybe-prepare-chainlink-programs \ test \ test-chainlink \ test-cloning \ diff --git a/test-integration/test-runner/bin/run_tests.rs b/test-integration/test-runner/bin/run_tests.rs index 6a5a6e9c7..a37cd00f7 100644 --- a/test-integration/test-runner/bin/run_tests.rs +++ b/test-integration/test-runner/bin/run_tests.rs @@ -783,7 +783,8 @@ fn run_test( "RUST_LOG", std::env::var("RUST_LOG").unwrap_or_else(|_| "info".to_string()), ) - .arg("test"); + .arg("test") + .arg("--locked"); if let Some(package) = config.package { cmd.arg("-p").arg(package); } diff --git a/test-integration/test-tools/src/validator.rs b/test-integration/test-tools/src/validator.rs index 6b9080a22..10b404bfd 100644 --- a/test-integration/test-tools/src/validator.rs +++ b/test-integration/test-tools/src/validator.rs @@ -3,6 +3,7 @@ use std::{ net::{SocketAddr, TcpStream}, path::{Path, PathBuf}, process::{self, Child}, + sync::OnceLock, thread::sleep, time::Duration, }; @@ -33,20 +34,18 @@ pub fn start_magic_block_validator_with_config( let port = rpc_port_from_config(config_path); - // First build so that the validator can start fast - let mut command = process::Command::new("cargo"); let keypair_base58 = loaded_chain_accounts.validator_authority_base58(); - command.arg("build"); - let build_res = command.current_dir(root_dir.clone()).output(); - - if build_res.is_ok_and(|output| !output.status.success()) { - eprintln!("Failed to build validator"); + if !ensure_magicblock_validator_built(root_dir) { return None; } - // Start validator via `cargo run -- ` + // Start validator via `cargo run -p magicblock-validator -- ` let mut command = process::Command::new("cargo"); - command.arg("run"); + command + .arg("run") + .arg("--locked") + .arg("-p") + .arg("magicblock-validator"); let rust_log_style = std::env::var("RUST_LOG_STYLE").unwrap_or(log_suffix.to_string()); command @@ -67,6 +66,35 @@ pub fn start_magic_block_validator_with_config( wait_for_validator(validator, port) } +fn ensure_magicblock_validator_built(root_dir: &Path) -> bool { + static BUILT: OnceLock = OnceLock::new(); + + *BUILT.get_or_init(|| { + let output = process::Command::new("cargo") + .arg("build") + .arg("--locked") + .arg("-p") + .arg("magicblock-validator") + .current_dir(root_dir) + .output(); + + match output { + Ok(output) if output.status.success() => true, + Ok(output) => { + eprintln!("Failed to build magicblock-validator"); + eprintln!("status: {}", output.status); + eprintln!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + eprintln!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + false + } + Err(err) => { + eprintln!("Failed to execute cargo build for validator: {err}"); + false + } + } + }) +} + pub fn start_test_validator_with_config( test_runner_paths: &TestRunnerPaths, program_loader: Option, From 0faf7ddd2d66ac428eb176838638bff54c963e1d Mon Sep 17 00:00:00 2001 From: Gabriele Picco Date: Tue, 17 Feb 2026 18:09:50 -0700 Subject: [PATCH 2/6] Update .github/workflows/ci-test-integration.yml Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .github/workflows/ci-test-integration.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci-test-integration.yml b/.github/workflows/ci-test-integration.yml index 291d3d837..e8df36660 100644 --- a/.github/workflows/ci-test-integration.yml +++ b/.github/workflows/ci-test-integration.yml @@ -34,6 +34,7 @@ jobs: cargo build --locked -p magicblock-validator cargo test --manifest-path test-integration/Cargo.toml --workspace --no-run --locked make -C test-integration programs + # Pre-build chainlink mini programs needed by both 'chainlink' and 'cloning' matrix shards RUN_TESTS=chainlink make -C test-integration maybe-prepare-chainlink-programs shell: bash working-directory: magicblock-validator From 8697c5444e9426bfd0ee9a4f44b1ddccdcf82398 Mon Sep 17 00:00:00 2001 From: Gabriele Picco Date: Tue, 17 Feb 2026 18:09:14 -0700 Subject: [PATCH 3/6] chore: refactor --- test-integration/test-tools/src/validator.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test-integration/test-tools/src/validator.rs b/test-integration/test-tools/src/validator.rs index 10b404bfd..b56fc7b05 100644 --- a/test-integration/test-tools/src/validator.rs +++ b/test-integration/test-tools/src/validator.rs @@ -83,8 +83,14 @@ fn ensure_magicblock_validator_built(root_dir: &Path) -> bool { Ok(output) => { eprintln!("Failed to build magicblock-validator"); eprintln!("status: {}", output.status); - eprintln!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - eprintln!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + eprintln!( + "stdout: {}", + String::from_utf8_lossy(&output.stdout) + ); + eprintln!( + "stderr: {}", + String::from_utf8_lossy(&output.stderr) + ); false } Err(err) => { From 601f8e70b3077b19be9b8c6bd7a89b672ac23ec1 Mon Sep 17 00:00:00 2001 From: Gabriele Picco Date: Wed, 18 Feb 2026 02:13:56 +0000 Subject: [PATCH 4/6] test: avoid brittle confirm assertion in upgrade retry --- test-integration/test-cloning/tests/01_program-deploy.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test-integration/test-cloning/tests/01_program-deploy.rs b/test-integration/test-cloning/tests/01_program-deploy.rs index 02476546e..6bcc740ef 100644 --- a/test-integration/test-cloning/tests/01_program-deploy.rs +++ b/test-integration/test-cloning/tests/01_program-deploy.rs @@ -190,7 +190,9 @@ async fn test_clone_mini_v4_loader_program_and_upgrade() { .send_and_confirm_instructions_with_payer_ephem(&[ix], &payer) .unwrap(); - assert!(found); + if !found { + debug!("Transaction not yet confirmed, checking logs and retrying if needed"); + } if check_logs!(ctx, sig, format!("{msg} upgraded")) { break; } From b33795de1d6656adda9d7f22c60d83a673d7cd9c Mon Sep 17 00:00:00 2001 From: Gabriele Picco Date: Tue, 17 Feb 2026 20:30:06 -0700 Subject: [PATCH 5/6] chore: fetch wiht retry --- .../test-table-mania/tests/utils/mod.rs | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/test-integration/test-table-mania/tests/utils/mod.rs b/test-integration/test-table-mania/tests/utils/mod.rs index f23f201f6..934b5c2a3 100644 --- a/test-integration/test-table-mania/tests/utils/mod.rs +++ b/test-integration/test-table-mania/tests/utils/mod.rs @@ -25,11 +25,40 @@ pub async fn setup_table_mania(validator_auth: &Keypair) -> TableMania { ); MagicblockRpcClient::from(client) }; + + // Ensure the validator is actually producing slots before sending table txs. + tokio::time::timeout( + Duration::from_secs(30), + rpc_client.wait_for_next_slot(), + ) + .await + .expect("Timed out waiting for validator startup") + .expect("Failed waiting for validator next slot"); + + let requested_lamports = 777 * LAMPORTS_PER_SOL; rpc_client - .request_airdrop(&validator_auth.pubkey(), 777 * LAMPORTS_PER_SOL) + .request_airdrop(&validator_auth.pubkey(), requested_lamports) .await .unwrap(); + // Airdrop submission is async on chain; wait until balance is visible. + tokio::time::timeout(Duration::from_secs(30), async { + loop { + if let Some(account) = rpc_client + .get_account(&validator_auth.pubkey()) + .await + .unwrap() + { + if account.lamports >= requested_lamports { + break; + } + } + sleep_millis(200).await; + } + }) + .await + .expect("Timed out waiting for airdrop funding"); + if TEST_TABLE_CLOSE { TableMania::new( rpc_client, From 1b3cd482f5eb9b7be7939c79fdb3c84f24503bb8 Mon Sep 17 00:00:00 2001 From: Gabriele Picco Date: Tue, 17 Feb 2026 21:14:08 -0700 Subject: [PATCH 6/6] chore: fix timeout --- magicblock-accounts-db/src/tests.rs | 31 +++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/magicblock-accounts-db/src/tests.rs b/magicblock-accounts-db/src/tests.rs index aba17258f..c648491af 100644 --- a/magicblock-accounts-db/src/tests.rs +++ b/magicblock-accounts-db/src/tests.rs @@ -1,4 +1,9 @@ -use std::{collections::HashSet, ops::Deref, sync::Arc}; +use std::{ + collections::HashSet, + ops::Deref, + sync::Arc, + time::{Duration, Instant}, +}; use magicblock_config::config::AccountsDbConfig; use solana_account::{AccountSharedData, ReadableAccount, WritableAccount}; @@ -533,13 +538,23 @@ impl TestEnv { fn advance_slot(&self, target_slot: u64) { self.adb.set_slot(target_slot); - // Simple spin-wait if we expect a snapshot trigger. - // This ensures the background thread has started and possibly finished creating the file. - if target_slot.is_multiple_of(self.adb.snapshot_frequency) { - let mut retries = 0; - while !self.adb.snapshot_exists(target_slot) && retries < 50 { - std::thread::sleep(std::time::Duration::from_millis(10)); - retries += 1; + // Wait for snapshot materialization if this slot triggers one. + // Snapshotting runs on a background thread and can be slower on busy CI hosts. + if target_slot > 0 + && target_slot.is_multiple_of(self.adb.snapshot_frequency) + { + const SNAPSHOT_TIMEOUT: Duration = Duration::from_secs(5); + const SNAPSHOT_POLL_INTERVAL: Duration = Duration::from_millis(20); + + let start = Instant::now(); + while !self.adb.snapshot_exists(target_slot) { + if start.elapsed() >= SNAPSHOT_TIMEOUT { + panic!( + "snapshot for slot {} did not appear within {:?}", + target_slot, SNAPSHOT_TIMEOUT + ); + } + std::thread::sleep(SNAPSHOT_POLL_INTERVAL); } } }