From 448c5caf159fed961a0b929b13aa83e0016c0539 Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Mon, 25 Mar 2024 18:27:45 +0200 Subject: [PATCH 1/6] result handler cleanup --- .../src/types/interaction/tx_rh_list/tx_rh_list_exec.rs | 7 ------- 1 file changed, 7 deletions(-) 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 cc39d08561..b8121d40d8 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 @@ -13,13 +13,6 @@ where fn item_process_result(self, raw_result: &RawResult) -> Self::Returns; } -impl RHListItemExec for () -where - Env: TxEnv, -{ - fn item_process_result(self, _raw_result: &RawResult) -> Self::Returns {} -} - /// Indicates how result processing will undergo for an ensemble of result handlers. pub trait RHListExec: RHList where From dcd48415b5df41c529a96c958bf17948501ca2f8 Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Tue, 26 Mar 2024 10:28:20 +0200 Subject: [PATCH 2/6] Result handlers folder in scenario --- framework/scenario/src/facade.rs | 1 + framework/scenario/src/facade/result_handlers.rs | 3 +++ .../{world_tx => result_handlers}/with_tx_raw_response.rs | 4 ---- framework/scenario/src/facade/world_tx.rs | 2 -- framework/scenario/src/lib.rs | 2 +- 5 files changed, 5 insertions(+), 7 deletions(-) create mode 100644 framework/scenario/src/facade/result_handlers.rs rename framework/scenario/src/facade/{world_tx => result_handlers}/with_tx_raw_response.rs (91%) diff --git a/framework/scenario/src/facade.rs b/framework/scenario/src/facade.rs index 51989165df..a37265e0b4 100644 --- a/framework/scenario/src/facade.rs +++ b/framework/scenario/src/facade.rs @@ -1,5 +1,6 @@ mod contract_info; mod debugger_backend; +pub(crate) mod result_handlers; mod scenario_world; mod scenario_world_runner; mod scenario_world_steps; diff --git a/framework/scenario/src/facade/result_handlers.rs b/framework/scenario/src/facade/result_handlers.rs new file mode 100644 index 0000000000..6bb2de8216 --- /dev/null +++ b/framework/scenario/src/facade/result_handlers.rs @@ -0,0 +1,3 @@ +mod with_tx_raw_response; + +pub use with_tx_raw_response::WithRawTxResponse; diff --git a/framework/scenario/src/facade/world_tx/with_tx_raw_response.rs b/framework/scenario/src/facade/result_handlers/with_tx_raw_response.rs similarity index 91% rename from framework/scenario/src/facade/world_tx/with_tx_raw_response.rs rename to framework/scenario/src/facade/result_handlers/with_tx_raw_response.rs index 512338aab8..f67d3b0f48 100644 --- a/framework/scenario/src/facade/world_tx/with_tx_raw_response.rs +++ b/framework/scenario/src/facade/result_handlers/with_tx_raw_response.rs @@ -1,5 +1,3 @@ -use core::marker::PhantomData; - use multiversx_sc::{ codec::TopDecodeMulti, types::{RHListItem, RHListItemExec, TxEnv}, @@ -7,8 +5,6 @@ use multiversx_sc::{ use crate::scenario_model::TxResponse; -use super::ScenarioTxEnvData; - /// Wraps a closure that handles a `TxResponse` object. pub struct WithRawTxResponse(pub F) where diff --git a/framework/scenario/src/facade/world_tx.rs b/framework/scenario/src/facade/world_tx.rs index 4a6fd86317..0ccbdd9514 100644 --- a/framework/scenario/src/facade/world_tx.rs +++ b/framework/scenario/src/facade/world_tx.rs @@ -7,10 +7,8 @@ mod scenario_env_exec; mod scenario_env_query; pub mod scenario_env_util; mod scenario_rh_impl; -mod with_tx_raw_response; pub use expr::*; pub use scenario_env::*; pub use scenario_env_exec::ScenarioEnvExec; pub use scenario_env_query::ScenarioEnvQuery; -pub use with_tx_raw_response::WithRawTxResponse; diff --git a/framework/scenario/src/lib.rs b/framework/scenario/src/lib.rs index 1fb9b4c63e..64229851af 100644 --- a/framework/scenario/src/lib.rs +++ b/framework/scenario/src/lib.rs @@ -39,7 +39,7 @@ pub use crate::scenario as mandos_system; // Re-exporting the whole mandos crate for easier use in tests. pub use multiversx_chain_scenario_format as scenario_format; -pub use facade::{world_tx::*, ContractInfo, ScenarioWorld, WhiteboxContract}; +pub use facade::{result_handlers::*, world_tx::*, ContractInfo, ScenarioWorld, WhiteboxContract}; use std::path::Path; From e83cb660db4a0595a700a6d5e8a98c5939676013 Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Tue, 26 Mar 2024 10:46:23 +0200 Subject: [PATCH 3/6] renamed adder unit test --- .../examples/adder/tests/{adder_test.rs => adder_unit_test.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename contracts/examples/adder/tests/{adder_test.rs => adder_unit_test.rs} (100%) diff --git a/contracts/examples/adder/tests/adder_test.rs b/contracts/examples/adder/tests/adder_unit_test.rs similarity index 100% rename from contracts/examples/adder/tests/adder_test.rs rename to contracts/examples/adder/tests/adder_unit_test.rs From 819c43868a39dace3f0e25f92c2c760b9372e761 Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Tue, 26 Mar 2024 19:51:19 +0200 Subject: [PATCH 4/6] address expression conversions --- .../types/interaction/expr/address_expr.rs | 5 +++++ .../src/types/interaction/expr/sc_expr.rs | 5 +++++ .../src/scenario/model/value/address_key.rs | 20 +++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/framework/base/src/types/interaction/expr/address_expr.rs b/framework/base/src/types/interaction/expr/address_expr.rs index a5b9756638..ce2fa10fb5 100644 --- a/framework/base/src/types/interaction/expr/address_expr.rs +++ b/framework/base/src/types/interaction/expr/address_expr.rs @@ -63,6 +63,11 @@ impl AddressExpr { } result } + + #[cfg(feature = "alloc")] + pub fn eval_to_expr(&self) -> alloc::string::String { + alloc::format!("{ADDRESS_PREFIX}{}", self.0) + } } #[cfg(test)] diff --git a/framework/base/src/types/interaction/expr/sc_expr.rs b/framework/base/src/types/interaction/expr/sc_expr.rs index 551d4eaf0a..1613587402 100644 --- a/framework/base/src/types/interaction/expr/sc_expr.rs +++ b/framework/base/src/types/interaction/expr/sc_expr.rs @@ -74,6 +74,11 @@ impl<'a> ScExpr<'a> { } result } + + #[cfg(feature = "alloc")] + pub fn eval_to_expr(&self) -> alloc::string::String { + alloc::format!("{SC_PREFIX}{}", self.0) + } } #[cfg(test)] diff --git a/framework/scenario/src/scenario/model/value/address_key.rs b/framework/scenario/src/scenario/model/value/address_key.rs index 5bbc1d79ae..889cca69ae 100644 --- a/framework/scenario/src/scenario/model/value/address_key.rs +++ b/framework/scenario/src/scenario/model/value/address_key.rs @@ -1,3 +1,5 @@ +use multiversx_sc::types::{AddressExpr, ScExpr}; + use super::{value_from_slice, AddressValue}; use crate::{ multiversx_sc::types::Address, @@ -114,3 +116,21 @@ impl From<&Address> for AddressKey { } } } + +impl From for AddressKey { + fn from(from: AddressExpr) -> Self { + AddressKey { + value: from.eval_to_array().into(), + original: from.eval_to_expr(), + } + } +} + +impl From> for AddressKey { + fn from(from: ScExpr) -> Self { + AddressKey { + value: from.eval_to_array().into(), + original: from.eval_to_expr(), + } + } +} From d28adf093020337f96f077f7f361cb9dc2b043c3 Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Tue, 26 Mar 2024 19:54:24 +0200 Subject: [PATCH 5/6] result handlers for errors, v1 --- .../interaction/tx_rh_list/tx_rh_list_exec.rs | 22 ++++++++++++++++ .../scenario/src/facade/result_handlers.rs | 4 +++ .../facade/result_handlers/returns_message.rs | 26 +++++++++++++++++++ .../facade/result_handlers/returns_status.rs | 26 +++++++++++++++++++ 4 files changed, 78 insertions(+) create mode 100644 framework/scenario/src/facade/result_handlers/returns_message.rs create mode 100644 framework/scenario/src/facade/result_handlers/returns_status.rs 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 b8121d40d8..64f8a0a487 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 @@ -10,6 +10,10 @@ pub trait RHListItemExec: RHListItem where Env: TxEnv, { + fn is_error_handled(&self) -> bool { + false + } + fn item_process_result(self, raw_result: &RawResult) -> Self::Returns; } @@ -18,6 +22,8 @@ pub trait RHListExec: RHList where Env: TxEnv, { + fn is_error_handled(&self) -> bool; + fn list_process_result(self, raw_result: &RawResult) -> Self::ListReturns; } @@ -25,6 +31,10 @@ impl RHListExec for () where Env: TxEnv, { + fn is_error_handled(&self) -> bool { + false + } + fn list_process_result(self, _raw_result: &RawResult) -> Self::ListReturns {} } @@ -32,6 +42,10 @@ impl RHListExec for OriginalResultMarker where Env: TxEnv, { + fn is_error_handled(&self) -> bool { + false + } + fn list_process_result(self, _raw_result: &RawResult) -> Self::ListReturns {} } @@ -41,6 +55,10 @@ where Head: RHListItemExec, Tail: RHListExec, { + fn is_error_handled(&self) -> bool { + self.head.is_error_handled() || self.tail.is_error_handled() + } + fn list_process_result(self, raw_result: &RawResult) -> Self::ListReturns { let head_result = self.head.item_process_result(raw_result); let tail_result = self.tail.list_process_result(raw_result); @@ -54,6 +72,10 @@ where Head: RHListItemExec, Tail: RHListExec, { + fn is_error_handled(&self) -> bool { + self.head.is_error_handled() || self.tail.is_error_handled() + } + fn list_process_result(self, raw_result: &RawResult) -> Self::ListReturns { self.head.item_process_result(raw_result); self.tail.list_process_result(raw_result) diff --git a/framework/scenario/src/facade/result_handlers.rs b/framework/scenario/src/facade/result_handlers.rs index 6bb2de8216..55f541d8e0 100644 --- a/framework/scenario/src/facade/result_handlers.rs +++ b/framework/scenario/src/facade/result_handlers.rs @@ -1,3 +1,7 @@ +mod returns_message; +mod returns_status; mod with_tx_raw_response; +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/returns_message.rs b/framework/scenario/src/facade/result_handlers/returns_message.rs new file mode 100644 index 0000000000..c9e9821055 --- /dev/null +++ b/framework/scenario/src/facade/result_handlers/returns_message.rs @@ -0,0 +1,26 @@ +use multiversx_sc::types::{RHListItem, RHListItemExec, TxEnv}; + +use crate::scenario_model::TxResponse; + +/// Indicates that the error status will be returned. +pub struct ReturnsMessage; + +impl RHListItem for ReturnsMessage +where + Env: TxEnv, +{ + type Returns = String; +} + +impl RHListItemExec for ReturnsMessage +where + Env: TxEnv, +{ + fn is_error_handled(&self) -> bool { + true + } + + fn item_process_result(self, raw_result: &TxResponse) -> Self::Returns { + raw_result.tx_error.message.clone() + } +} diff --git a/framework/scenario/src/facade/result_handlers/returns_status.rs b/framework/scenario/src/facade/result_handlers/returns_status.rs new file mode 100644 index 0000000000..7e81421efb --- /dev/null +++ b/framework/scenario/src/facade/result_handlers/returns_status.rs @@ -0,0 +1,26 @@ +use multiversx_sc::types::{RHListItem, RHListItemExec, TxEnv}; + +use crate::scenario_model::TxResponse; + +/// Indicates that the error status will be returned. +pub struct ReturnsStatus; + +impl RHListItem for ReturnsStatus +where + Env: TxEnv, +{ + type Returns = u64; +} + +impl RHListItemExec for ReturnsStatus +where + Env: TxEnv, +{ + fn is_error_handled(&self) -> bool { + true + } + + fn item_process_result(self, raw_result: &TxResponse) -> Self::Returns { + raw_result.tx_error.status + } +} From b3f1e9e7cf44f38ae222db631a202e0c7befdf3a Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Tue, 26 Mar 2024 21:51:34 +0200 Subject: [PATCH 6/6] 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