From 1fef40279c6f727b6923bc37e8928f5f7cf43d8c Mon Sep 17 00:00:00 2001 From: Eric Swanson <64809312+ericswanson-dfinity@users.noreply.github.com> Date: Thu, 28 Sep 2023 06:44:33 -0700 Subject: [PATCH] test: run e2e/integration tests vs replica rather than ic-ref (#474) * test: run e2e tests vs replica rather than ic-ref * expected error message * expected errors * error-messages * errors * error results * . * creation fee * replica burns some cycles * more cycles * wow * . * start clean * . * format --- .github/workflows/ic-ref.yml | 30 +++--- ref-tests/tests/ic-ref.rs | 161 ++++++++++++++++++++++----------- ref-tests/tests/integration.rs | 7 +- 3 files changed, 127 insertions(+), 71 deletions(-) diff --git a/.github/workflows/ic-ref.yml b/.github/workflows/ic-ref.yml index 5e0d8556..2afb36ea 100644 --- a/.github/workflows/ic-ref.yml +++ b/.github/workflows/ic-ref.yml @@ -28,6 +28,11 @@ jobs: with: path: main + - name: Install dfx + uses: dfinity/setup-dfx@main + with: + dfx-version: "0.15.0" + - name: Cargo cache uses: actions/cache@v2 with: @@ -48,13 +53,8 @@ jobs: wget https://github.com/dfinity/cycles-wallet/releases/download/${{ matrix.wallet-tag }}/wallet.wasm mv wallet.wasm $HOME/wallet.wasm - - name: Download ic-ref and universal-canister + - name: Download universal-canister run: | - wget https://download.dfinity.systems/ic-ref/ic-ref-0.0.1-${{ matrix.ic-hs-ref }}-x86_64-linux.tar.gz - tar -xvf ic-ref-0.0.1-${{ matrix.ic-hs-ref }}-x86_64-linux.tar.gz ic-ref - mkdir -p $HOME/bin - mv ic-ref $HOME/bin/ic-ref - wget https://download.dfinity.systems/ic-ref/ic-ref-test-0.0.1-${{ matrix.ic-hs-ref }}-x86_64-linux.tar.gz tar -xvf ic-ref-test-0.0.1-${{ matrix.ic-hs-ref }}-x86_64-linux.tar.gz test-data/universal-canister.wasm mv test-data/universal-canister.wasm $HOME/canister.wasm @@ -62,14 +62,14 @@ jobs: - name: Run Integration Tests run: | set -ex - $HOME/bin/ic-ref --pick-port --write-port-to $HOME/ic_ref_port & + dfx start --background --clean sleep 1 - export IC_REF_PORT=$(cat $HOME/ic_ref_port) + export IC_REF_PORT=$(dfx info replica-port) export IC_UNIVERSAL_CANISTER_PATH=$HOME/canister.wasm export IC_WALLET_CANISTER_PATH=$HOME/wallet.wasm cd main cargo test --all-features -- --ignored - killall ic-ref + dfx stop env: RUST_BACKTRACE: 1 @@ -89,14 +89,14 @@ jobs: # create key: pkcs11-tool -k --module $HSM_PKCS11_LIBRARY_PATH --login --slot-index $HSM_SLOT_INDEX -d $HSM_KEY_ID --key-type EC:prime256v1 --pin $HSM_PIN - $HOME/bin/ic-ref --pick-port --write-port-to $HOME/ic_ref_port & + dfx start --background --clean sleep 1 - export IC_REF_PORT=$(cat $HOME/ic_ref_port) + export IC_REF_PORT=$(dfx info replica-port) export IC_UNIVERSAL_CANISTER_PATH=$HOME/canister.wasm export IC_WALLET_CANISTER_PATH=$HOME/wallet.wasm cd main/ref-tests cargo test --all-features -- --ignored --nocapture --test-threads=1 - killall ic-ref + dfx stop env: RUST_BACKTRACE: 1 HSM_PKCS11_LIBRARY_PATH: /usr/lib/softhsm/libsofthsm2.so @@ -108,12 +108,12 @@ jobs: - name: Run Doc Tests run: | set -ex - $HOME/bin/ic-ref --pick-port --write-port-to $HOME/ic_ref_port & + dfx start --background --clean sleep 1 - export IC_REF_PORT=$(cat $HOME/ic_ref_port) + export IC_REF_PORT=$(dfx info replica-port) cd main cargo test --all-features --doc -- --ignored - killall ic-ref + dfx stop env: RUST_BACKTRACE: 1 diff --git a/ref-tests/tests/ic-ref.rs b/ref-tests/tests/ic-ref.rs index d8da9380..963c63c8 100644 --- a/ref-tests/tests/ic-ref.rs +++ b/ref-tests/tests/ic-ref.rs @@ -62,10 +62,16 @@ mod management_canister { }; use sha2::{Digest, Sha256}; use std::collections::HashSet; + use std::convert::TryInto; mod create_canister { use super::with_agent; - use ic_agent::{export::Principal, AgentError}; + use ic_agent::{ + agent::{RejectCode, RejectResponse}, + export::Principal, + AgentError, + }; + use ic_utils::interfaces::ManagementCanister; use ref_tests::get_effective_canister_id; use std::str::FromStr; @@ -103,13 +109,14 @@ mod management_canister { .call_and_wait() .await; - let payload_content = - "canister does not exist: 75hes-oqbaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q" - .to_string(); - assert!(matches!(result, - Err(AgentError::HttpError(payload)) - if String::from_utf8(payload.content.clone()).expect("Expected utf8") == payload_content)); + Err(AgentError::ReplicaError(RejectResponse { + reject_code: RejectCode::DestinationInvalid, + reject_message, + error_code: Some(ref error_code) + })) if reject_message == "Canister 75hes-oqbaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q not found" && + error_code == "IC0301")); + Ok(()) }) } @@ -164,7 +171,7 @@ mod management_canister { .with_mode(InstallMode::Reinstall) .call_and_wait() .await; - assert!(matches!(result, Err(AgentError::HttpError(..)))); + assert!(matches!(result, Err(AgentError::ReplicaError(..)))); // Upgrade should succeed. ic00.install_code(&canister_id, &canister_wasm) @@ -178,7 +185,7 @@ mod management_canister { .with_mode(InstallMode::Upgrade) .call_and_wait() .await; - assert!(matches!(result, Err(AgentError::HttpError(..)))); + assert!(matches!(result, Err(AgentError::ReplicaError(..)))); // Change controller. ic00.update_settings(&canister_id) @@ -192,9 +199,14 @@ mod management_canister { .with_controller(other_agent_principal) .call_and_wait() .await; - assert!(matches!(result, Err(AgentError::HttpError(payload)) - if String::from_utf8(payload.content.clone()).expect("Expected utf8") - == *"Wrong sender")); + assert!( + matches!(result, Err(AgentError::ReplicaError(RejectResponse{ + reject_code: RejectCode::CanisterError, + reject_message, + error_code: Some(ref error_code), + })) if reject_message == format!("Only controllers of canister {} can call ic00 method update_settings", canister_id) && + error_code == "IC0512") + ); // Reinstall as new controller other_ic00 @@ -411,8 +423,8 @@ mod management_canister { matches!(result, Err(AgentError::ReplicaError(RejectResponse{ reject_code: RejectCode::CanisterError, reject_message, - .. - })) if reject_message == "canister is not running") + error_code: None, + })) if reject_message == format!("Canister {} is stopped", canister_id)) ); // Can't call query on a stopped canister @@ -421,8 +433,9 @@ mod management_canister { matches!(result, Err(AgentError::ReplicaError(RejectResponse{ reject_code: RejectCode::CanisterError, reject_message, - .. - })) if reject_message == "canister is stopped") + error_code: Some(ref error_code), + })) if reject_message == format!("IC0508: Canister {} is stopped and therefore does not have a CallContextManager", canister_id) && + error_code == "IC0508") ); // Upgrade should succeed @@ -444,8 +457,8 @@ mod management_canister { matches!(result, Err(AgentError::ReplicaError(RejectResponse{ reject_code: RejectCode::DestinationInvalid, reject_message, - .. - })) if reject_message == "method does not exist: update") + error_code: None, + })) if reject_message == format!("Canister {} has no update method 'update'", canister_id)) ); // Can call query @@ -454,17 +467,14 @@ mod management_canister { matches!(result, Err(AgentError::ReplicaError(RejectResponse{ reject_code: RejectCode::DestinationInvalid, reject_message, - .. - })) if reject_message == "query method does not exist") + error_code: Some(ref error_code), + })) if reject_message == format!("IC0302: Canister {} has no query method 'query'", canister_id) && + error_code == "IC0302") ); // Another start is a noop ic00.start_canister(&canister_id).call_and_wait().await?; - // Delete a running canister should fail. - let result = ic00.delete_canister(&canister_id).call_and_wait().await; - assert!(matches!(result, Err(AgentError::ReplicaError { .. }))); - // Stop should succeed. ic00.stop_canister(&canister_id).call_and_wait().await?; @@ -473,9 +483,14 @@ mod management_canister { // Cannot call update let result = agent.update(&canister_id, "update").call_and_wait().await; - assert!(matches!(result, Err(AgentError::HttpError(payload)) - if String::from_utf8(payload.content.clone()).expect("Expected utf8") - == format!("canister no longer exists: {}", canister_id.to_text()))); + assert!( + matches!(result, Err(AgentError::ReplicaError(RejectResponse{ + reject_code: RejectCode::DestinationInvalid, + reject_message, + error_code: Some(ref error_code), + })) if reject_message == format!("Canister {} not found", canister_id) && + error_code == "IC0301") + ); // Cannot call query let result = agent.query(&canister_id, "query").with_arg([]).call().await; @@ -483,17 +498,21 @@ mod management_canister { matches!(result, Err(AgentError::ReplicaError(RejectResponse{ reject_code: RejectCode::DestinationInvalid, reject_message, - .. - })) if reject_message - == format!("canister no longer exists: {}", canister_id.to_text())) + error_code: Some(ref error_code) + })) if reject_message == format!("IC0301: Canister {} not found", canister_id) && + error_code == "IC0301") ); // Cannot query canister status let result = ic00.canister_status(&canister_id).call_and_wait().await; assert!(match result { - Err(AgentError::HttpError(payload)) - if String::from_utf8(payload.content.clone()).expect("Expected utf8") - == format!("canister no longer exists: {}", canister_id.to_text()) => + Err(AgentError::ReplicaError(RejectResponse{ + reject_code: RejectCode::DestinationInvalid, + reject_message, + error_code: Some(ref error_code) + })) + if reject_message == format!("Canister {} not found", canister_id) && + error_code == "IC0301" => true, Ok((_status_call_result,)) => false, _ => false, @@ -501,9 +520,14 @@ mod management_canister { // Delete a deleted canister should fail. let result = ic00.delete_canister(&canister_id).call_and_wait().await; - assert!(matches!(result, Err(AgentError::HttpError(payload)) - if String::from_utf8(payload.content.clone()).expect("Expected utf8") - == format!("canister no longer exists: {}", canister_id.to_text()))); + assert!( + matches!(result, Err(AgentError::ReplicaError(RejectResponse{ + reject_code: RejectCode::DestinationInvalid, + reject_message, + error_code: Some(ref error_code) + })) if reject_message == format!("Canister {} not found", canister_id) && + error_code == "IC0301") + ); Ok(()) }) } @@ -538,33 +562,51 @@ mod management_canister { .start_canister(&canister_id) .call_and_wait() .await; - assert!(matches!(result, Err(AgentError::HttpError(payload)) - if String::from_utf8(payload.content.clone()).expect("Expected utf8") - == *"Wrong sender")); + assert!(matches!(result, + Err(AgentError::ReplicaError(RejectResponse { + reject_code: RejectCode::CanisterError, + reject_message, + error_code: Some(ref error_code) + })) if reject_message == format!("Only controllers of canister {} can call ic00 method start_canister", canister_id) && + error_code == "IC0512")); // Stop as a wrong controller should fail. let result = other_ic00.stop_canister(&canister_id).call_and_wait().await; - assert!(matches!(result, Err(AgentError::HttpError(payload)) - if String::from_utf8(payload.content.clone()).expect("Expected utf8") - == *"Wrong sender")); + assert!( + matches!(result, + Err(AgentError::ReplicaError(RejectResponse { + reject_code: RejectCode::CanisterError, + reject_message, + error_code: Some(ref error_code) + })) if reject_message == format!("Only controllers of canister {} can call ic00 method stop_canister", canister_id) && + error_code == "IC0512") + ); // Get canister status as a wrong controller should fail. let result = other_ic00 .canister_status(&canister_id) .call_and_wait() .await; - assert!(matches!(result, Err(AgentError::HttpError(payload)) - if String::from_utf8(payload.content.clone()).expect("Expected utf8") - == *"Wrong sender")); + assert!(matches!(result, + Err(AgentError::ReplicaError(RejectResponse { + reject_code: RejectCode::CanisterError, + reject_message, + error_code: Some(ref error_code) + })) if reject_message == format!("Only controllers of canister {} can call ic00 method canister_status", canister_id) && + error_code == "IC0512")); // Delete as a wrong controller should fail. let result = other_ic00 .delete_canister(&canister_id) .call_and_wait() .await; - assert!(matches!(result, Err(AgentError::HttpError(payload)) - if String::from_utf8(payload.content.clone()).expect("Expected utf8") - == *"Wrong sender")); + assert!(matches!(result, + Err(AgentError::ReplicaError(RejectResponse { + reject_code: RejectCode::CanisterError, + reject_message, + error_code: Some(ref error_code) + })) if reject_message == format!("Only controllers of canister {} can call ic00 method delete_canister", canister_id) && + error_code == "IC0512")); Ok(()) }) @@ -596,8 +638,14 @@ mod management_canister { let args = Argument::from_candid((create_args,)); + let creation_fee = 8000000000; let (create_result,): (CreateResult,) = wallet - .call(Principal::management_canister(), "create_canister", args, 0) + .call( + Principal::management_canister(), + "create_canister", + args, + creation_fee, + ) .call_and_wait() .await?; let canister_id = create_result.canister_id; @@ -614,7 +662,7 @@ mod management_canister { .call_and_wait() .await?; - assert_eq!(result.cycles, 0_u64); + assert!(result.cycles > 0_u64 && result.cycles < creation_fee); let ic00 = ManagementCanister::create(&agent); // cycle balance is default_canister_balance when creating with @@ -626,7 +674,10 @@ mod management_canister { .call_and_wait() .await?; let result = ic00.canister_status(&canister_id_1).call_and_wait().await?; - assert_eq!(result.0.cycles, default_canister_balance); + // assume some cycles are already burned + let cycles: i128 = result.0.cycles.0.try_into().unwrap(); + let burned = default_canister_balance as i128 - cycles; + assert!(burned > 0 && burned < 100_000_000); // cycle balance should be amount specified to // provisional_create_canister_with_cycles call @@ -638,7 +689,9 @@ mod management_canister { .call_and_wait() .await?; let result = ic00.canister_status(&canister_id_2).call_and_wait().await?; - assert_eq!(result.0.cycles, amount); + let cycles: i128 = result.0.cycles.0.try_into().unwrap(); + let burned = amount as i128 - cycles; + assert!(burned > 0 && burned < 100_000_000); Ok(()) }) @@ -928,8 +981,8 @@ mod extras { Err(AgentError::ReplicaError(RejectResponse { reject_code: RejectCode::DestinationInvalid, reject_message, - .. - })) if reject_message == "The specified_id of the created canister is already in use.")); + error_code: None, + })) if reject_message == "Canister iimsn-6yaaa-aaaaa-afiaa-cai is already installed")); Ok(()) }) diff --git a/ref-tests/tests/integration.rs b/ref-tests/tests/integration.rs index 7b4ac15e..1bf9b015 100644 --- a/ref-tests/tests/integration.rs +++ b/ref-tests/tests/integration.rs @@ -104,7 +104,10 @@ fn canister_reject_call() { result, Err(AgentError::ReplicaError(RejectResponse { reject_code: RejectCode::DestinationInvalid, - reject_message: "method does not exist: wallet_send".to_string(), + reject_message: format!( + "Canister {} has no update method 'wallet_send'", + alice.canister_id() + ), error_code: None })) ); @@ -148,7 +151,7 @@ fn wallet_canister_create_and_install() { let wallet = WalletCanister::create(&agent, wallet_id).await?; let create_result = wallet - .wallet_create_canister(1_000_000, None, None, None, None) + .wallet_create_canister(100_000_000_000, None, None, None, None) .await?; #[derive(CandidType)]