Skip to content

Commit 04d2970

Browse files
committed
refactor: move code from sandbox to e2e
1 parent 88c9942 commit 04d2970

File tree

8 files changed

+198
-271
lines changed

8 files changed

+198
-271
lines changed

crates/e2e/src/assertions.rs

Lines changed: 65 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -104,27 +104,74 @@ macro_rules! assert_noop {
104104
/// ```
105105
#[macro_export]
106106
macro_rules! assert_last_event {
107-
($result:expr, $expected_event:expr) => {{
108-
// Extract the last contract event from the result
109-
let events = ($result)
107+
($result:expr, $expected_event:expr) => {{ $crate::assert_last_event_internal($result, $expected_event) }};
108+
}
109+
110+
use crate::CallResult;
111+
use ink_env::Environment;
112+
use scale::{
113+
Decode,
114+
Encode,
115+
};
116+
use subxt::{
117+
blocks::ExtrinsicEvents,
118+
config::HashFor,
119+
};
120+
121+
/// A trait for types that can expose the last contract-emitted event for assertions.
122+
#[allow(dead_code)]
123+
pub trait ContractEventReader {
124+
fn fetch_last_contract_event(self) -> Result<Vec<u8>, String>;
125+
}
126+
127+
impl<'a, E, V, C, Abi> ContractEventReader
128+
for &'a CallResult<E, V, ExtrinsicEvents<C>, Abi>
129+
where
130+
E: Environment,
131+
C: subxt::Config,
132+
HashFor<C>: Into<sp_core::H256>,
133+
{
134+
fn fetch_last_contract_event(self) -> Result<Vec<u8>, String> {
135+
let events = self
110136
.contract_emitted_events()
111-
.expect("Failed to get contract events");
137+
.map_err(|err| format!("failed to get contract events: {err:?}"))?;
112138

113-
if events.is_empty() {
114-
panic!("No events emitted, expected: {:?}", $expected_event);
115-
}
139+
let last_event = events
140+
.last()
141+
.ok_or_else(|| "no contract events were emitted".to_string())?;
116142

117-
let last_event = events.last().expect("No last event");
143+
Ok(last_event.event.data.clone())
144+
}
145+
}
118146

119-
// Compare the encoded event data
120-
let expected_bytes = ::ink::scale::Encode::encode(&$expected_event);
121-
let actual_bytes = &last_event.event.data;
147+
/// Shared implementation that decodes the last contract event and compares it against the
148+
/// expected value.
149+
#[allow(dead_code)]
150+
pub fn assert_last_event_internal<R, E>(reader: R, expected_event: E)
151+
where
152+
R: ContractEventReader,
153+
E: Decode + Encode + core::fmt::Debug,
154+
{
155+
let last_event_data = reader
156+
.fetch_last_contract_event()
157+
.unwrap_or_else(|err| panic!("Contract event assertion failed: {err}"));
122158

123-
if &expected_bytes != actual_bytes {
124-
panic!(
125-
"Event mismatch!\nExpected (encoded): {:?}\nActual (encoded): {:?}",
126-
expected_bytes, actual_bytes
127-
);
128-
}
129-
}};
159+
let expected_bytes = expected_event.encode();
160+
161+
if expected_bytes != last_event_data {
162+
let decoded_event =
163+
E::decode(&mut &last_event_data[..]).unwrap_or_else(|error| {
164+
panic!(
165+
"failed to decode last contract event as {}: bytes={:?}, error={:?}",
166+
core::any::type_name::<E>(),
167+
last_event_data,
168+
error
169+
);
170+
});
171+
172+
panic!(
173+
"event mismatch!\nExpected: {:?}\nActual: {:?}",
174+
expected_event, decoded_event
175+
);
176+
}
130177
}

crates/e2e/src/conversions.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use crate::sr25519::Keypair;
2+
use sp_core::crypto::AccountId32;
3+
4+
/// Trait for types that can be converted into an `AccountId`.
5+
pub trait IntoAccountId<TargetAccountId> {
6+
fn into_account_id(self) -> TargetAccountId;
7+
}
8+
9+
impl IntoAccountId<AccountId32> for AccountId32 {
10+
fn into_account_id(self) -> AccountId32 {
11+
self
12+
}
13+
}
14+
15+
impl IntoAccountId<AccountId32> for &AccountId32 {
16+
fn into_account_id(self) -> AccountId32 {
17+
self.clone()
18+
}
19+
}
20+
21+
impl<AccountId> IntoAccountId<AccountId> for &ink_primitives::AccountId
22+
where
23+
AccountId: From<[u8; 32]>,
24+
{
25+
fn into_account_id(self) -> AccountId {
26+
AccountId::from(*AsRef::<[u8; 32]>::as_ref(self))
27+
}
28+
}
29+
30+
impl<AccountId> IntoAccountId<AccountId> for &Keypair
31+
where
32+
AccountId: From<[u8; 32]>,
33+
{
34+
fn into_account_id(self) -> AccountId {
35+
AccountId::from(self.public_key().0)
36+
}
37+
}

crates/e2e/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,18 @@ mod builders;
2626
mod client_utils;
2727
mod contract_build;
2828
mod contract_results;
29+
mod conversions;
2930
mod error;
3031
pub mod events;
3132
mod node_proc;
3233
mod subxt_client;
3334
mod xts;
3435

3536
pub use crate::contract_build::build_root_and_contract_dependencies;
37+
pub use assertions::{
38+
ContractEventReader,
39+
assert_last_event_internal,
40+
};
3641
pub use backend::{
3742
BuilderClient,
3843
ChainBackend,
@@ -62,6 +67,7 @@ pub use contract_results::{
6267
InstantiationResult,
6368
UploadResult,
6469
};
70+
pub use conversions::IntoAccountId;
6571
pub use ink_e2e_macro::test;
6672
pub use ink_revive_types::evm::CallTrace;
6773
pub use node_proc::{

crates/e2e/src/subxt_client.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -397,11 +397,14 @@ where
397397
///
398398
/// # Arguments
399399
/// * `asset_id` - ID of the asset.
400+
/// * `owner` - The account that created/administers the asset and signs the mint
401+
/// extrinsic.
400402
/// * `beneficiary` - The account to receive the minted tokens (Keypair or AccountId).
401403
/// * `amount` - The number of tokens to mint.
402404
pub async fn mint_into(
403405
&mut self,
404406
asset_id: &u32,
407+
owner: &Keypair,
405408
beneficiary: &impl ToAccountBytes,
406409
amount: u128,
407410
) -> Result<(), Error>
@@ -410,24 +413,21 @@ where
410413
{
411414
let beneficiary_bytes = beneficiary.to_account_bytes();
412415
let beneficiary_account_id: C::AccountId = C::AccountId::from(beneficiary_bytes);
413-
self.mint_into_impl(*asset_id, beneficiary_account_id, amount)
416+
self.mint_into_impl(*asset_id, owner, beneficiary_account_id, amount)
414417
.await
415418
}
416419

417420
/// Internal implementation for minting tokens.
418421
async fn mint_into_impl(
419422
&mut self,
420423
asset_id: u32,
424+
owner: &Keypair,
421425
beneficiary_account_id: C::AccountId,
422426
amount: u128,
423427
) -> Result<(), Error> {
424-
// Use the first account (alice) as signer for now - in practice, this would be
425-
// the asset admin
426-
let signer = crate::sr25519::dev::alice();
427-
428428
let (tx_events, trace) = self
429429
.api
430-
.assets_mint(&signer, asset_id, beneficiary_account_id, amount)
430+
.assets_mint(owner, asset_id, beneficiary_account_id, amount)
431431
.await;
432432

433433
for evt in tx_events.iter() {

crates/sandbox/src/client.rs

Lines changed: 45 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ use ink_e2e::{
4949
CallBuilderFinal,
5050
CallDryRunResult,
5151
ChainBackend,
52+
ContractEventReader,
5253
ContractExecResultFor,
5354
ContractResult,
5455
ContractsBackend,
@@ -271,6 +272,50 @@ where
271272
}
272273
}
273274

275+
impl<AccountId, S: Sandbox> Client<AccountId, S>
276+
where
277+
S::Runtime: pallet_revive::Config,
278+
<S::Runtime as frame_system::Config>::RuntimeEvent:
279+
TryInto<pallet_revive::Event<S::Runtime>>,
280+
{
281+
pub fn contract_events(&mut self) -> Vec<Vec<u8>> {
282+
self.sandbox
283+
.events()
284+
.iter()
285+
.filter_map(|event_record| {
286+
if let Ok(pallet_event) = &event_record.event.clone().try_into() {
287+
match pallet_event {
288+
pallet_revive::Event::<S::Runtime>::ContractEmitted {
289+
data,
290+
..
291+
} => Some(data.clone()),
292+
_ => None,
293+
}
294+
} else {
295+
None
296+
}
297+
})
298+
.collect::<Vec<Vec<u8>>>()
299+
}
300+
301+
pub fn last_contract_event(&mut self) -> Option<Vec<u8>> {
302+
self.contract_events().last().cloned()
303+
}
304+
}
305+
306+
impl<'a, AccountId, S> ContractEventReader for &'a mut Client<AccountId, S>
307+
where
308+
S: Sandbox,
309+
S::Runtime: pallet_revive::Config,
310+
<S::Runtime as frame_system::Config>::RuntimeEvent:
311+
TryInto<pallet_revive::Event<S::Runtime>>,
312+
{
313+
fn fetch_last_contract_event(self) -> Result<Vec<u8>, String> {
314+
self.last_contract_event()
315+
.ok_or_else(|| "no contract events were emitted".to_string())
316+
}
317+
}
318+
274319
#[async_trait]
275320
impl<
276321
AccountId: Clone + Send + Sync + From<[u8; 32]> + AsRef<[u8; 32]>,
@@ -718,86 +763,6 @@ where
718763
}
719764
}
720765

721-
impl<AccountId, S: Sandbox> Client<AccountId, S>
722-
where
723-
S::Runtime: pallet_revive::Config,
724-
<S::Runtime as frame_system::Config>::RuntimeEvent:
725-
TryInto<pallet_revive::Event<S::Runtime>>,
726-
{
727-
pub fn contract_events(&mut self) -> Vec<Vec<u8>>
728-
where
729-
S::Runtime: pallet_revive::Config,
730-
<S::Runtime as frame_system::Config>::RuntimeEvent:
731-
TryInto<pallet_revive::Event<S::Runtime>>,
732-
{
733-
self.sandbox
734-
.events()
735-
.iter()
736-
.filter_map(|event_record| {
737-
if let Ok(pallet_event) = &event_record.event.clone().try_into() {
738-
match pallet_event {
739-
pallet_revive::Event::<S::Runtime>::ContractEmitted {
740-
data,
741-
..
742-
} => Some(data.clone()),
743-
_ => None,
744-
}
745-
} else {
746-
None
747-
}
748-
})
749-
.collect::<Vec<Vec<u8>>>()
750-
}
751-
752-
/// Returns the last contract event that was emitted, if any.
753-
pub fn last_contract_event(&mut self) -> Option<Vec<u8>>
754-
where
755-
S::Runtime: pallet_revive::Config,
756-
<S::Runtime as frame_system::Config>::RuntimeEvent:
757-
TryInto<pallet_revive::Event<S::Runtime>>,
758-
{
759-
self.contract_events().last().cloned()
760-
}
761-
}
762-
763-
/// Helper function for the `assert_last_contract_event!` macro.
764-
///
765-
/// # Parameters:
766-
/// - `client` - The client for interacting with the sandbox.
767-
/// - `event` - The expected event.
768-
#[track_caller]
769-
pub fn assert_last_contract_event_inner<AccountId, S, E>(
770-
client: &mut Client<AccountId, S>,
771-
event: E,
772-
) where
773-
S: Sandbox,
774-
S::Runtime: pallet_revive::Config,
775-
<S::Runtime as frame_system::Config>::RuntimeEvent:
776-
TryInto<pallet_revive::Event<S::Runtime>>,
777-
E: Decode + scale::Encode + core::fmt::Debug + std::cmp::PartialEq,
778-
{
779-
let expected_event = event;
780-
let last_event = client
781-
.last_contract_event()
782-
.unwrap_or_else(|| panic!("contract events expected but none were emitted yet"));
783-
784-
let decoded_event = E::decode(&mut &last_event[..]).unwrap_or_else(|error| {
785-
panic!(
786-
"failed to decode last contract event as {}: bytes={:?}, error={:?}",
787-
core::any::type_name::<E>(),
788-
last_event,
789-
error
790-
);
791-
});
792-
793-
if decoded_event != expected_event {
794-
panic!(
795-
"expected contract event {:?} but found {:?}",
796-
expected_event, decoded_event
797-
);
798-
}
799-
}
800-
801766
impl<
802767
AccountId: Clone + Send + Sync + From<[u8; 32]> + AsRef<[u8; 32]>,
803768
Config: Sandbox,

crates/sandbox/src/lib.rs

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ pub use client::{
7171
preset,
7272
};
7373
pub use error::E2EError;
74+
pub use ink_e2e::{
75+
IntoAccountId,
76+
assert_last_event,
77+
assert_noop,
78+
assert_ok,
79+
};
7480
pub use ink_e2e_macro::test;
7581

7682
/// Asserts that a contract call succeeded without reverting.
@@ -383,29 +389,3 @@ pub fn to_revive_storage_deposit<B>(
383389
}
384390
}
385391
}
386-
387-
/// Trait for types that can be converted into a runtime AccountId.
388-
///
389-
/// This allows sandbox APIs to accept various account types without requiring manual
390-
/// conversion.
391-
pub trait IntoAccountId<AccountId> {
392-
fn into_account_id(self) -> AccountId;
393-
}
394-
395-
impl IntoAccountId<AccountId32> for &AccountId32 {
396-
fn into_account_id(self) -> AccountId32 {
397-
self.clone()
398-
}
399-
}
400-
401-
impl IntoAccountId<AccountId32> for &ink_primitives::AccountId {
402-
fn into_account_id(self) -> AccountId32 {
403-
AccountId32::from(*AsRef::<[u8; 32]>::as_ref(self))
404-
}
405-
}
406-
407-
impl IntoAccountId<AccountId32> for &ink_e2e::Keypair {
408-
fn into_account_id(self) -> AccountId32 {
409-
AccountId32::from(self.public_key().0)
410-
}
411-
}

0 commit comments

Comments
 (0)