From b3f1e9e7cf44f38ae222db631a202e0c7befdf3a Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Tue, 26 Mar 2024 21:51:34 +0200 Subject: [PATCH] result handlers for errors, v2 --- .../panic-message-features/sc-config.toml | 1 + .../src/panic_features.rs | 5 + .../tests/pmf_blackbox_test.rs | 93 ++++++++++++++++++ .../panic-message-features/tests/pmf_proxy.rs | 95 +++++++++++++++++++ .../panic-message-features/wasm/src/lib.rs | 5 +- .../base/src/types/interaction/tx_env.rs | 3 + .../base/src/types/interaction/tx_env_sc.rs | 2 + .../interaction/tx_rh_list/tx_rh_list_exec.rs | 43 ++++++--- .../generate_proxy/proxy_template_gen.rs | 9 +- .../scenario/src/facade/result_handlers.rs | 4 + .../facade/result_handlers/expect_message.rs | 32 +++++++ .../facade/result_handlers/expect_status.rs | 27 ++++++ .../facade/result_handlers/returns_message.rs | 11 ++- .../facade/result_handlers/returns_status.rs | 18 +++- .../src/facade/world_tx/scenario_env.rs | 8 +- .../facade/world_tx/scenario_env_deploy.rs | 2 + .../src/facade/world_tx/scenario_env_exec.rs | 6 +- .../src/facade/world_tx/scenario_env_query.rs | 9 +- .../scenario/model/transaction/tx_expect.rs | 6 ++ .../snippets/src/itx/interactor_env_deploy.rs | 9 +- .../snippets/src/itx/interactor_env_exec.rs | 12 ++- .../snippets/src/itx/interactor_env_query.rs | 12 ++- 22 files changed, 371 insertions(+), 41 deletions(-) create mode 100644 contracts/feature-tests/panic-message-features/tests/pmf_blackbox_test.rs create mode 100644 contracts/feature-tests/panic-message-features/tests/pmf_proxy.rs create mode 100644 framework/scenario/src/facade/result_handlers/expect_message.rs create mode 100644 framework/scenario/src/facade/result_handlers/expect_status.rs diff --git a/contracts/feature-tests/panic-message-features/sc-config.toml b/contracts/feature-tests/panic-message-features/sc-config.toml index fc05c90bba..7b60b5fb8c 100644 --- a/contracts/feature-tests/panic-message-features/sc-config.toml +++ b/contracts/feature-tests/panic-message-features/sc-config.toml @@ -1,4 +1,5 @@ [settings] +proxy-paths = ["tests/pmf_proxy.rs"] main = "main" [contracts.main] diff --git a/contracts/feature-tests/panic-message-features/src/panic_features.rs b/contracts/feature-tests/panic-message-features/src/panic_features.rs index 4b4ed32258..a2726d2fea 100644 --- a/contracts/feature-tests/panic-message-features/src/panic_features.rs +++ b/contracts/feature-tests/panic-message-features/src/panic_features.rs @@ -24,4 +24,9 @@ pub trait PanicMessageFeatures { #[event("before-panic")] fn before_panic(&self); + + #[view] + fn sc_panic(&self) { + sc_panic!("sc_panic! test"); + } } diff --git a/contracts/feature-tests/panic-message-features/tests/pmf_blackbox_test.rs b/contracts/feature-tests/panic-message-features/tests/pmf_blackbox_test.rs new file mode 100644 index 0000000000..ff14571fd9 --- /dev/null +++ b/contracts/feature-tests/panic-message-features/tests/pmf_blackbox_test.rs @@ -0,0 +1,93 @@ +mod pmf_proxy; +use multiversx_sc::types::{AddressExpr, ScExpr}; +use multiversx_sc_scenario::{scenario_model::*, *}; + +const OWNER: AddressExpr = AddressExpr("owner"); +const SC_PMF: ScExpr = ScExpr("pmf"); +const CODE_EXPR: &str = "mxsc:output/panic-message-features.mxsc.json"; + +fn world() -> ScenarioWorld { + let mut blockchain = ScenarioWorld::new(); + blockchain.set_current_dir_from_workspace("contracts/examples/adder"); + + blockchain.register_contract(CODE_EXPR, panic_message_features::ContractBuilder); + blockchain +} + +fn setup() -> ScenarioWorld { + let mut world = world(); + let code = world.code_expression(CODE_EXPR); + + world.set_state_step( + SetStateStep::new() + .put_account(OWNER, Account::new().nonce(1)) + .put_account(SC_PMF, Account::new().code(code)), + ); + + world +} + +// TODO: move to basic-features a testing framework tester +#[test] +fn tx_returns_error_test() { + let mut world = setup(); + + let (status, message) = world + .tx() + .from(OWNER) + .to(SC_PMF) + .typed(pmf_proxy::PanicMessageFeaturesProxy) + .sc_panic() + .returns(ReturnsStatus) + .returns(ReturnsMessage) + .run(); + + assert_eq!(status, 4); + assert_eq!(message, "sc_panic! test"); +} + +#[test] +fn query_returns_error_test() { + let mut world = setup(); + + let (status, message) = world + .query() + .to(SC_PMF) + .typed(pmf_proxy::PanicMessageFeaturesProxy) + .sc_panic() + .returns(ReturnsStatus) + .returns(ReturnsMessage) + .run(); + + assert_eq!(status, 4); + assert_eq!(message, "sc_panic! test"); +} + +#[test] +fn tx_expect_error_test() { + let mut world = setup(); + + world + .tx() + .from(OWNER) + .to(SC_PMF) + .typed(pmf_proxy::PanicMessageFeaturesProxy) + .sc_panic() + .returns(ExpectStatus(4)) + .returns(ExpectMessage("sc_panic! test")) + .run(); +} + +#[test] +fn query_expect_error_test() { + let mut world = setup(); + + world + .query() + .to(SC_PMF) + .typed(pmf_proxy::PanicMessageFeaturesProxy) + .sc_panic() + .returns(ExpectStatus(4)) + .returns(ExpectMessage("sc_panic! test")) + .run(); +} diff --git a/contracts/feature-tests/panic-message-features/tests/pmf_proxy.rs b/contracts/feature-tests/panic-message-features/tests/pmf_proxy.rs new file mode 100644 index 0000000000..8226a1ec5f --- /dev/null +++ b/contracts/feature-tests/panic-message-features/tests/pmf_proxy.rs @@ -0,0 +1,95 @@ +// Code generated by the multiversx-sc proxy generator. DO NOT EDIT. + +//////////////////////////////////////////////////// +////////////////// AUTO-GENERATED ////////////////// +//////////////////////////////////////////////////// + +#![allow(dead_code)] +#![allow(clippy::all)] + +use multiversx_sc::proxy_imports::*; + +pub struct PanicMessageFeaturesProxy; + +impl TxProxyTrait for PanicMessageFeaturesProxy +where + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + type TxProxyMethods = PanicMessageFeaturesProxyMethods; + + fn proxy_methods(self, tx: Tx) -> Self::TxProxyMethods { + PanicMessageFeaturesProxyMethods { wrapped_tx: tx } + } +} + +pub struct PanicMessageFeaturesProxyMethods +where + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + wrapped_tx: Tx, +} + +#[rustfmt::skip] +impl PanicMessageFeaturesProxyMethods +where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + Gas: TxGas, +{ + pub fn init( + self, + ) -> TxProxyDeploy { + self.wrapped_tx + .raw_deploy() + .original_result() + } +} + +#[rustfmt::skip] +impl PanicMessageFeaturesProxyMethods +where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + pub fn panic_with_message< + Arg0: CodecInto, + >( + self, + some_value: Arg0, + ) -> TxProxyCall { + self.wrapped_tx + .raw_call() + .function_name("panicWithMessage") + .argument(&some_value) + .original_result() + } + + /// Logs do not get recorded in case of panic. + pub fn panic_after_log( + self, + ) -> TxProxyCall { + self.wrapped_tx + .raw_call() + .function_name("panicAfterLog") + .original_result() + } + + pub fn sc_panic( + self, + ) -> TxProxyCall { + self.wrapped_tx + .raw_call() + .function_name("sc_panic") + .original_result() + } +} diff --git a/contracts/feature-tests/panic-message-features/wasm/src/lib.rs b/contracts/feature-tests/panic-message-features/wasm/src/lib.rs index 2a024b15d2..a347a1aaa3 100644 --- a/contracts/feature-tests/panic-message-features/wasm/src/lib.rs +++ b/contracts/feature-tests/panic-message-features/wasm/src/lib.rs @@ -5,9 +5,9 @@ //////////////////////////////////////////////////// // Init: 1 -// Endpoints: 2 +// Endpoints: 3 // Async Callback (empty): 1 -// Total number of exported functions: 4 +// Total number of exported functions: 5 #![no_std] #![allow(internal_features)] @@ -22,6 +22,7 @@ multiversx_sc_wasm_adapter::endpoints! { init => init panicWithMessage => panic_with_message panicAfterLog => panic_after_log + sc_panic => sc_panic ) } diff --git a/framework/base/src/types/interaction/tx_env.rs b/framework/base/src/types/interaction/tx_env.rs index 54fbf77b90..482b0a0e8e 100644 --- a/framework/base/src/types/interaction/tx_env.rs +++ b/framework/base/src/types/interaction/tx_env.rs @@ -3,6 +3,9 @@ use crate::{api::CallTypeApi, types::ManagedAddress}; pub trait TxEnv: Sized { type Api: CallTypeApi; + /// Type built by result handlers that translates into the "expect" section in scenarios. + type RHExpect: Default; + fn resolve_sender_address(&self) -> ManagedAddress; fn default_gas(&self) -> u64; diff --git a/framework/base/src/types/interaction/tx_env_sc.rs b/framework/base/src/types/interaction/tx_env_sc.rs index ce2c287d30..f22c4a3dee 100644 --- a/framework/base/src/types/interaction/tx_env_sc.rs +++ b/framework/base/src/types/interaction/tx_env_sc.rs @@ -41,6 +41,8 @@ where { type Api = Api; + type RHExpect = (); + fn resolve_sender_address(&self) -> ManagedAddress { BlockchainWrapper::::new().get_sc_address() } diff --git a/framework/base/src/types/interaction/tx_rh_list/tx_rh_list_exec.rs b/framework/base/src/types/interaction/tx_rh_list/tx_rh_list_exec.rs index 64f8a0a487..06b2184366 100644 --- a/framework/base/src/types/interaction/tx_rh_list/tx_rh_list_exec.rs +++ b/framework/base/src/types/interaction/tx_rh_list/tx_rh_list_exec.rs @@ -1,4 +1,8 @@ -use crate::{proxy_imports::OriginalResultMarker, types::TxEnv}; +use crate::{ + api::CallTypeApi, + proxy_imports::{ManagedBuffer, OriginalResultMarker}, + types::TxEnv, +}; use super::{ConsNoRet, ConsRet, RHList, RHListItem}; @@ -10,10 +14,19 @@ pub trait RHListItemExec: RHListItem where Env: TxEnv, { - fn is_error_handled(&self) -> bool { - false + /// Part of the execution pre-processing, each result handler needs to produce an "expect" field, + /// as defined in the environment. + /// + /// The operation is chained, so all result handlers can contribute, hence the `prev` argument, + /// which represents the "expect" field produces by the other result handlers. + /// + /// The default behavior is to leave it unchanged. + fn item_tx_expect(&self, prev: Env::RHExpect) -> Env::RHExpect { + prev } + /// The main functionality of a result handler, it either does some computation internally + /// (e.g. execution of a lambda function), or produces a result, or both. fn item_process_result(self, raw_result: &RawResult) -> Self::Returns; } @@ -22,8 +35,14 @@ pub trait RHListExec: RHList where Env: TxEnv, { - fn is_error_handled(&self) -> bool; + /// Provides the execution pre-processing, in which result handlers collectively produce an "expect" field. + /// + /// The operation starts with the default "expect" field, which normally has all fields unspecified, except + /// for the "status", which is by default set to "0". This means that failing transactions will cause a panic + /// unless explicitly stated in one of the result handlers. + fn list_tx_expect(&self) -> Env::RHExpect; + /// Aggregates the executions of all result handlers, as configured for a transaction. fn list_process_result(self, raw_result: &RawResult) -> Self::ListReturns; } @@ -31,8 +50,8 @@ impl RHListExec for () where Env: TxEnv, { - fn is_error_handled(&self) -> bool { - false + fn list_tx_expect(&self) -> Env::RHExpect { + Env::RHExpect::default() } fn list_process_result(self, _raw_result: &RawResult) -> Self::ListReturns {} @@ -42,8 +61,8 @@ impl RHListExec for OriginalResultMarker where Env: TxEnv, { - fn is_error_handled(&self) -> bool { - false + fn list_tx_expect(&self) -> Env::RHExpect { + Env::RHExpect::default() } fn list_process_result(self, _raw_result: &RawResult) -> Self::ListReturns {} @@ -55,8 +74,8 @@ where Head: RHListItemExec, Tail: RHListExec, { - fn is_error_handled(&self) -> bool { - self.head.is_error_handled() || self.tail.is_error_handled() + fn list_tx_expect(&self) -> Env::RHExpect { + self.head.item_tx_expect(self.tail.list_tx_expect()) } fn list_process_result(self, raw_result: &RawResult) -> Self::ListReturns { @@ -72,8 +91,8 @@ where Head: RHListItemExec, Tail: RHListExec, { - fn is_error_handled(&self) -> bool { - self.head.is_error_handled() || self.tail.is_error_handled() + fn list_tx_expect(&self) -> Env::RHExpect { + self.head.item_tx_expect(self.tail.list_tx_expect()) } fn list_process_result(self, raw_result: &RawResult) -> Self::ListReturns { diff --git a/framework/meta/src/cmd/contract/generate_proxy/proxy_template_gen.rs b/framework/meta/src/cmd/contract/generate_proxy/proxy_template_gen.rs index 22e26eff5a..6145315233 100644 --- a/framework/meta/src/cmd/contract/generate_proxy/proxy_template_gen.rs +++ b/framework/meta/src/cmd/contract/generate_proxy/proxy_template_gen.rs @@ -2,17 +2,18 @@ use std::{fs::File, io::Write}; use super::proxy_naming::{proxy_methods_type_name, proxy_type_name}; -const PREFIX_AUTO_GENERATED: &str = "//////////////////////////////////////////////////// +const PRELUDE: &str = "// Code generated by the multiversx-sc proxy generator. DO NOT EDIT. + +//////////////////////////////////////////////////// ////////////////// AUTO-GENERATED ////////////////// //////////////////////////////////////////////////// -"; -const PRELUDE: &str = "#![allow(clippy::all)] +#![allow(dead_code)] +#![allow(clippy::all)] use multiversx_sc::proxy_imports::*;"; pub(crate) fn write_header(file: &mut File) { - writeln!(file, "{PREFIX_AUTO_GENERATED}").unwrap(); writeln!(file, r#"{PRELUDE}"#).unwrap(); } diff --git a/framework/scenario/src/facade/result_handlers.rs b/framework/scenario/src/facade/result_handlers.rs index 55f541d8e0..369a390a75 100644 --- a/framework/scenario/src/facade/result_handlers.rs +++ b/framework/scenario/src/facade/result_handlers.rs @@ -1,7 +1,11 @@ +mod expect_message; +mod expect_status; mod returns_message; mod returns_status; mod with_tx_raw_response; +pub use expect_message::ExpectMessage; +pub use expect_status::ExpectStatus; pub use returns_message::ReturnsMessage; pub use returns_status::ReturnsStatus; pub use with_tx_raw_response::WithRawTxResponse; diff --git a/framework/scenario/src/facade/result_handlers/expect_message.rs b/framework/scenario/src/facade/result_handlers/expect_message.rs new file mode 100644 index 0000000000..4f46d05b59 --- /dev/null +++ b/framework/scenario/src/facade/result_handlers/expect_message.rs @@ -0,0 +1,32 @@ +use multiversx_chain_scenario_format::serde_raw::ValueSubTree; +use multiversx_sc::types::{RHListItem, RHListItemExec, TxEnv}; + +use crate::scenario_model::{BytesValue, CheckValue, TxExpect, TxResponse}; + +/// Verifies that transaction result message matches the given one. +/// +/// Can only be used in tests and interactors, not available in contracts. +pub struct ExpectMessage<'a>(pub &'a str); + +impl<'a, Env, Original> RHListItem for ExpectMessage<'a> +where + Env: TxEnv, +{ + type Returns = (); +} + +impl<'a, Env, Original> RHListItemExec for ExpectMessage<'a> +where + Env: TxEnv, +{ + fn item_tx_expect(&self, mut prev: TxExpect) -> TxExpect { + let expect_message_expr = BytesValue { + value: self.0.to_string().into_bytes(), + original: ValueSubTree::Str(format!("str:{}", self.0)), + }; + prev.message = CheckValue::Equal(expect_message_expr); + prev + } + + fn item_process_result(self, _: &TxResponse) -> Self::Returns {} +} diff --git a/framework/scenario/src/facade/result_handlers/expect_status.rs b/framework/scenario/src/facade/result_handlers/expect_status.rs new file mode 100644 index 0000000000..127982739b --- /dev/null +++ b/framework/scenario/src/facade/result_handlers/expect_status.rs @@ -0,0 +1,27 @@ +use multiversx_sc::types::{RHListItem, RHListItemExec, TxEnv}; + +use crate::scenario_model::{CheckValue, TxExpect, TxResponse}; + +/// Verifies that transaction result status matches the given one. +/// +/// Can only be used in tests and interactors, not available in contracts. +pub struct ExpectStatus(pub u64); + +impl RHListItem for ExpectStatus +where + Env: TxEnv, +{ + type Returns = (); +} + +impl RHListItemExec for ExpectStatus +where + Env: TxEnv, +{ + fn item_tx_expect(&self, mut prev: TxExpect) -> TxExpect { + prev.status = CheckValue::Equal(self.0.into()); + prev + } + + fn item_process_result(self, _: &TxResponse) -> Self::Returns {} +} diff --git a/framework/scenario/src/facade/result_handlers/returns_message.rs b/framework/scenario/src/facade/result_handlers/returns_message.rs index c9e9821055..64164e05ef 100644 --- a/framework/scenario/src/facade/result_handlers/returns_message.rs +++ b/framework/scenario/src/facade/result_handlers/returns_message.rs @@ -1,8 +1,10 @@ use multiversx_sc::types::{RHListItem, RHListItemExec, TxEnv}; -use crate::scenario_model::TxResponse; +use crate::scenario_model::{CheckValue, TxExpect, TxResponse}; /// Indicates that the error status will be returned. +/// +/// Can only be used in tests and interactors, not available in contracts. pub struct ReturnsMessage; impl RHListItem for ReturnsMessage @@ -14,10 +16,11 @@ where impl RHListItemExec for ReturnsMessage where - Env: TxEnv, + Env: TxEnv, { - fn is_error_handled(&self) -> bool { - true + fn item_tx_expect(&self, mut prev: TxExpect) -> TxExpect { + prev.message = CheckValue::Star; + prev } fn item_process_result(self, raw_result: &TxResponse) -> Self::Returns { diff --git a/framework/scenario/src/facade/result_handlers/returns_status.rs b/framework/scenario/src/facade/result_handlers/returns_status.rs index 7e81421efb..184163619d 100644 --- a/framework/scenario/src/facade/result_handlers/returns_status.rs +++ b/framework/scenario/src/facade/result_handlers/returns_status.rs @@ -1,8 +1,10 @@ use multiversx_sc::types::{RHListItem, RHListItemExec, TxEnv}; -use crate::scenario_model::TxResponse; +use crate::scenario_model::{CheckValue, TxExpect, TxResponse, U64Value}; /// Indicates that the error status will be returned. +/// +/// Can only be used in tests and interactors, not available in contracts. pub struct ReturnsStatus; impl RHListItem for ReturnsStatus @@ -14,10 +16,18 @@ where impl RHListItemExec for ReturnsStatus where - Env: TxEnv, + Env: TxEnv, { - fn is_error_handled(&self) -> bool { - true + fn item_tx_expect(&self, mut prev: TxExpect) -> TxExpect { + if let CheckValue::Equal(U64Value { + value: 0, + original: _, + }) = prev.status + { + prev.status = CheckValue::Star; + } + + prev } fn item_process_result(self, raw_result: &TxResponse) -> Self::Returns { diff --git a/framework/scenario/src/facade/world_tx/scenario_env.rs b/framework/scenario/src/facade/world_tx/scenario_env.rs index 758cff63f1..4589e36599 100644 --- a/framework/scenario/src/facade/world_tx/scenario_env.rs +++ b/framework/scenario/src/facade/world_tx/scenario_env.rs @@ -2,7 +2,11 @@ use std::path::PathBuf; use multiversx_sc::types::{AnnotatedValue, ManagedAddress, TxBaseWithEnv, TxEnv}; -use crate::{api::StaticApi, scenario_model::TxResponse, ScenarioWorld}; +use crate::{ + api::StaticApi, + scenario_model::{TxExpect, TxResponse}, + ScenarioWorld, +}; /// Designates a tx environment suitable for running scenarios locally. pub trait ScenarioTxEnv: TxEnv { @@ -18,6 +22,8 @@ pub struct ScenarioTxEnvData { impl TxEnv for ScenarioTxEnvData { type Api = StaticApi; + type RHExpect = TxExpect; + fn resolve_sender_address(&self) -> ManagedAddress { panic!("Explicit sender address expected") } diff --git a/framework/scenario/src/facade/world_tx/scenario_env_deploy.rs b/framework/scenario/src/facade/world_tx/scenario_env_deploy.rs index fadd24d78d..ef7b2a243f 100644 --- a/framework/scenario/src/facade/world_tx/scenario_env_deploy.rs +++ b/framework/scenario/src/facade/world_tx/scenario_env_deploy.rs @@ -41,6 +41,7 @@ where fn run(self) -> Self::Returns { let mut step = tx_to_sc_deploy_step(&self.env, self.from, self.payment, self.gas, self.data); + step.expect = Some(self.result_handler.list_tx_expect()); self.env.world.sc_deploy(&mut step); process_result(step.response, self.result_handler) } @@ -71,6 +72,7 @@ impl ScenarioWorld { let tx = f(tx_base); let mut step = tx_to_sc_deploy_step(&tx.env, tx.from, tx.payment, tx.gas, tx.data); self.sc_deploy(&mut step); + step.expect = Some(tx.result_handler.list_tx_expect()); process_result(step.response, tx.result_handler); self } diff --git a/framework/scenario/src/facade/world_tx/scenario_env_exec.rs b/framework/scenario/src/facade/world_tx/scenario_env_exec.rs index e19c515c0b..25f738f5e4 100644 --- a/framework/scenario/src/facade/world_tx/scenario_env_exec.rs +++ b/framework/scenario/src/facade/world_tx/scenario_env_exec.rs @@ -12,7 +12,7 @@ use multiversx_sc::{ use crate::{ api::StaticApi, - scenario_model::{AddressValue, BytesValue, ScCallStep, ScDeployStep, TxResponse}, + scenario_model::{AddressValue, BytesValue, ScCallStep, ScDeployStep, TxExpect, TxResponse}, ScenarioTxEnv, ScenarioTxRun, ScenarioWorld, }; @@ -27,6 +27,8 @@ pub struct ScenarioEnvExec<'w> { impl<'w> TxEnv for ScenarioEnvExec<'w> { type Api = StaticApi; + type RHExpect = TxExpect; + fn resolve_sender_address(&self) -> ManagedAddress { panic!("Explicit sender address expected") } @@ -63,6 +65,7 @@ where self.gas, self.data, ); + step.expect = Some(self.result_handler.list_tx_expect()); self.env.world.sc_call(&mut step); process_result(step.response, self.result_handler) } @@ -91,6 +94,7 @@ impl ScenarioWorld { let tx_base = TxBaseWithEnv::new_with_env(env); let tx = f(tx_base); let mut step = tx_to_sc_call_step(&tx.env, tx.from, tx.to, tx.payment, tx.gas, tx.data); + step.expect = Some(tx.result_handler.list_tx_expect()); self.sc_call(&mut step); process_result(step.response, tx.result_handler); self diff --git a/framework/scenario/src/facade/world_tx/scenario_env_query.rs b/framework/scenario/src/facade/world_tx/scenario_env_query.rs index 993b4d9636..c25d0f61db 100644 --- a/framework/scenario/src/facade/world_tx/scenario_env_query.rs +++ b/framework/scenario/src/facade/world_tx/scenario_env_query.rs @@ -9,8 +9,9 @@ use multiversx_sc::{ }; use crate::{ - api::StaticApi, scenario_model::TxResponse, ScenarioTxEnv, ScenarioTxEnvData, ScenarioTxRun, - ScenarioWorld, + api::StaticApi, + scenario_model::{TxExpect, TxResponse}, + ScenarioTxEnv, ScenarioTxEnvData, ScenarioTxRun, ScenarioWorld, }; use super::scenario_env_util::*; @@ -23,6 +24,8 @@ pub struct ScenarioEnvQuery<'w> { impl<'w> TxEnv for ScenarioEnvQuery<'w> { type Api = StaticApi; + type RHExpect = TxExpect; + fn resolve_sender_address(&self) -> ManagedAddress { panic!("Explicit sender address expected") } @@ -49,6 +52,7 @@ where fn run(self) -> Self::Returns { let mut step = tx_to_sc_query_step(&self.env, self.to, self.data); + step.expect = Some(self.result_handler.list_tx_expect()); self.env.world.sc_query(&mut step); process_result(step.response, self.result_handler) } @@ -74,6 +78,7 @@ impl ScenarioWorld { let tx = f(tx_base); let mut step = tx_to_sc_query_step(&tx.env, tx.to, tx.data); self.sc_query(&mut step); + step.expect = Some(tx.result_handler.list_tx_expect()); process_result(step.response, tx.result_handler); self } diff --git a/framework/scenario/src/scenario/model/transaction/tx_expect.rs b/framework/scenario/src/scenario/model/transaction/tx_expect.rs index 6f64a471f0..d487a10db5 100644 --- a/framework/scenario/src/scenario/model/transaction/tx_expect.rs +++ b/framework/scenario/src/scenario/model/transaction/tx_expect.rs @@ -23,6 +23,12 @@ pub struct TxExpect { pub additional_error_message: String, } +impl Default for TxExpect { + fn default() -> Self { + Self::ok() + } +} + impl TxExpect { pub fn ok() -> Self { TxExpect { diff --git a/framework/snippets/src/itx/interactor_env_deploy.rs b/framework/snippets/src/itx/interactor_env_deploy.rs index d907db498e..6fdd9dfb48 100644 --- a/framework/snippets/src/itx/interactor_env_deploy.rs +++ b/framework/snippets/src/itx/interactor_env_deploy.rs @@ -66,9 +66,10 @@ where RH::ListReturns: NestedTupleFlatten, { pub async fn run(self) -> ::Unpacked { - let mut sc_deploy_step = self.sc_deploy_step; - self.world.sc_deploy(&mut sc_deploy_step).await; - process_result(sc_deploy_step.response, self.result_handler) + let mut step = self.sc_deploy_step; + step.expect = Some(self.result_handler.list_tx_expect()); + self.world.sc_deploy(&mut step).await; + process_result(step.response, self.result_handler) } } @@ -96,6 +97,7 @@ impl Interactor { let tx_base = TxBaseWithEnv::new_with_env(env); let tx = f(tx_base); let mut step = tx_to_sc_deploy_step(&tx.env, tx.from, tx.payment, tx.gas, tx.data); + step.expect = Some(tx.result_handler.list_tx_expect()); self.sc_deploy(&mut step).await; process_result(step.response, tx.result_handler); self @@ -128,6 +130,7 @@ impl Interactor { let tx_base = TxBaseWithEnv::new_with_env(env); let tx = f(tx_base); let mut step = tx_to_sc_deploy_step(&tx.env, tx.from, tx.payment, tx.gas, tx.data); + step.expect = Some(tx.result_handler.list_tx_expect()); self.sc_deploy(&mut step).await; process_result(step.response, tx.result_handler) } diff --git a/framework/snippets/src/itx/interactor_env_exec.rs b/framework/snippets/src/itx/interactor_env_exec.rs index bc2d9d20da..15065c9191 100644 --- a/framework/snippets/src/itx/interactor_env_exec.rs +++ b/framework/snippets/src/itx/interactor_env_exec.rs @@ -11,7 +11,7 @@ use multiversx_sc_scenario::{ }, }, scenario_env_util::*, - scenario_model::{AddressValue, BytesValue, ScCallStep, ScDeployStep, TxResponse}, + scenario_model::{AddressValue, BytesValue, ScCallStep, ScDeployStep, TxExpect, TxResponse}, ScenarioTxEnv, ScenarioTxEnvData, ScenarioTxRun, ScenarioWorld, }; @@ -26,6 +26,8 @@ pub struct InteractorEnvExec<'w> { impl<'w> TxEnv for InteractorEnvExec<'w> { type Api = StaticApi; + type RHExpect = TxExpect; + fn resolve_sender_address(&self) -> ManagedAddress { panic!("Explicit sender address expected") } @@ -86,9 +88,10 @@ where RH::ListReturns: NestedTupleFlatten, { pub async fn run(self) -> ::Unpacked { - let mut sc_call_step = self.sc_call_step; - self.world.sc_call(&mut sc_call_step).await; - process_result(sc_call_step.response, self.result_handler) + let mut step = self.sc_call_step; + step.expect = Some(self.result_handler.list_tx_expect()); + self.world.sc_call(&mut step).await; + process_result(step.response, self.result_handler) } } @@ -115,6 +118,7 @@ impl Interactor { let tx_base = TxBaseWithEnv::new_with_env(env); let tx = f(tx_base); let mut step = tx_to_sc_call_step(&tx.env, tx.from, tx.to, tx.payment, tx.gas, tx.data); + step.expect = Some(tx.result_handler.list_tx_expect()); self.sc_call(&mut step).await; process_result(step.response, tx.result_handler); self diff --git a/framework/snippets/src/itx/interactor_env_query.rs b/framework/snippets/src/itx/interactor_env_query.rs index 1628215992..1ca5be149a 100644 --- a/framework/snippets/src/itx/interactor_env_query.rs +++ b/framework/snippets/src/itx/interactor_env_query.rs @@ -10,7 +10,7 @@ use multiversx_sc_scenario::{ }, }, scenario_env_util::*, - scenario_model::{ScQueryStep, TxResponse}, + scenario_model::{ScQueryStep, TxExpect, TxResponse}, ScenarioTxEnv, ScenarioTxEnvData, ScenarioTxRun, ScenarioWorld, }; @@ -24,6 +24,8 @@ pub struct InteractorEnvQuery<'w> { impl<'w> TxEnv for InteractorEnvQuery<'w> { type Api = StaticApi; + type RHExpect = TxExpect; + fn resolve_sender_address(&self) -> ManagedAddress { panic!("Explicit sender address expected") } @@ -74,9 +76,10 @@ where RH::ListReturns: NestedTupleFlatten, { pub async fn run(self) -> ::Unpacked { - let mut sc_call_step = self.sc_query_step; - self.world.sc_query(&mut sc_call_step).await; - process_result(sc_call_step.response, self.result_handler) + let mut step = self.sc_query_step; + step.expect = Some(self.result_handler.list_tx_expect()); + self.world.sc_query(&mut step).await; + process_result(step.response, self.result_handler) } } @@ -99,6 +102,7 @@ impl Interactor { let tx_base = TxBaseWithEnv::new_with_env(env); let tx = f(tx_base); let mut step = tx_to_sc_query_step(&tx.env, tx.to, tx.data); + step.expect = Some(tx.result_handler.list_tx_expect()); self.sc_query(&mut step).await; process_result(step.response, tx.result_handler); self