Skip to content

Commit 2fd747f

Browse files
Replace chain/tx assert macros with helper methods
Address comments from the issuse #2805, drop the 4 macros in favor of plain helper methods. - `assert_note_committed!` -> `MockChain::is_note_committed` - `assert_note_unspent!` -> `MockChain::is_note_unspent` - `assert_note_consumed!` -> `MockChain::is_note_consumed` - `assert_note_consumed_by!` -> `ExecutedTransaction::consumes_note` Keep `assert_note_created!` since it does spec matching with optional fields.
1 parent d38f4c0 commit 2fd747f

6 files changed

Lines changed: 144 additions & 334 deletions

File tree

crates/miden-protocol/src/testing/tx.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::note::NoteId;
12
use crate::transaction::ExecutedTransaction;
23

34
impl ExecutedTransaction {
@@ -9,4 +10,9 @@ impl ExecutedTransaction {
910
self.block_header().fee_parameters().verification_base_fee() * verification_cycles;
1011
fee_amount as u64
1112
}
13+
14+
/// Returns `true` if the transaction consumes the note with the given ID.
15+
pub fn consumes_note(&self, note_id: &NoteId) -> bool {
16+
self.input_notes().iter().any(|n| n.id() == *note_id)
17+
}
1218
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
//! Assertion macro for note-lifecycle checks in tests.
2+
3+
use alloc::vec::Vec;
4+
5+
use miden_protocol::account::AccountId;
6+
use miden_protocol::asset::Asset;
7+
use miden_protocol::note::NoteType;
8+
use miden_protocol::transaction::ExecutedTransaction;
9+
10+
/// Spec for [`assert_note_created!`]. Fields left as `None` are skipped.
11+
#[doc(hidden)]
12+
#[derive(Default, Debug, Clone)]
13+
pub struct OutputNoteSpec {
14+
pub note_type: Option<NoteType>,
15+
pub sender: Option<AccountId>,
16+
pub assets: Option<Vec<Asset>>,
17+
}
18+
19+
/// Returns `true` if at least one output note in `tx` matches `spec`.
20+
#[doc(hidden)]
21+
pub fn check_output_note_created(tx: &ExecutedTransaction, spec: &OutputNoteSpec) -> bool {
22+
tx.output_notes().iter().any(|note| {
23+
if let Some(expected) = spec.note_type
24+
&& note.metadata().note_type() != expected
25+
{
26+
return false;
27+
}
28+
if let Some(expected) = spec.sender
29+
&& note.metadata().sender() != expected
30+
{
31+
return false;
32+
}
33+
if let Some(expected) = spec.assets.as_ref() {
34+
let actual = note.assets();
35+
if actual.num_assets() != expected.len() {
36+
return false;
37+
}
38+
// Each actual matches at most once (otherwise [A,A] would match [A,B]).
39+
let mut consumed = vec![false; expected.len()];
40+
let matched = expected.iter().all(|exp| {
41+
let slot = actual.iter().enumerate().find(|(i, a)| !consumed[*i] && *a == exp);
42+
if let Some((i, _)) = slot {
43+
consumed[i] = true;
44+
true
45+
} else {
46+
false
47+
}
48+
});
49+
if !matched {
50+
return false;
51+
}
52+
}
53+
true
54+
})
55+
}
56+
57+
/// Asserts the tx emitted a note matching the spec. Fields are optional; unset ones are skipped.
58+
///
59+
/// # Example
60+
/// ```ignore
61+
/// use miden_testing::assert_note_created;
62+
/// use miden_protocol::note::NoteType;
63+
///
64+
/// assert_note_created!(
65+
/// executed_tx,
66+
/// note_type: NoteType::Public,
67+
/// sender: faucet.id(),
68+
/// assets: [FungibleAsset::new(faucet.id(), amount)?.into()],
69+
/// );
70+
/// ```
71+
#[macro_export]
72+
macro_rules! assert_note_created {
73+
($tx:expr $(, $key:ident : $val:expr)* $(,)?) => {{
74+
#[allow(unused_mut)]
75+
let mut spec = $crate::asserts::OutputNoteSpec::default();
76+
$(
77+
$crate::__assert_note_created_field!(spec, $key, $val);
78+
)*
79+
let tx: &::miden_protocol::transaction::ExecutedTransaction = &$tx;
80+
assert!(
81+
$crate::asserts::check_output_note_created(tx, &spec),
82+
"no output note matches spec: {:?}\n tx produced {} output note(s)",
83+
spec,
84+
tx.output_notes().num_notes(),
85+
);
86+
}};
87+
}
88+
89+
#[doc(hidden)]
90+
#[macro_export]
91+
macro_rules! __assert_note_created_field {
92+
($spec:ident,note_type, $val:expr) => {
93+
$spec.note_type = ::core::option::Option::Some($val);
94+
};
95+
($spec:ident,sender, $val:expr) => {
96+
$spec.sender = ::core::option::Option::Some($val);
97+
};
98+
($spec:ident,assets, $val:expr) => {
99+
$spec.assets = ::core::option::Option::Some(
100+
::core::iter::IntoIterator::into_iter($val)
101+
.map(::core::convert::Into::into)
102+
.collect::<::alloc::vec::Vec<::miden_protocol::asset::Asset>>(),
103+
);
104+
};
105+
($spec:ident, $key:ident, $val:expr) => {
106+
::core::compile_error!(concat!(
107+
"unknown field in assert_note_created!: `",
108+
stringify!($key),
109+
"`. Supported fields: note_type, sender, assets",
110+
));
111+
};
112+
}

crates/miden-testing/src/asserts/mod.rs

Lines changed: 0 additions & 6 deletions
This file was deleted.

0 commit comments

Comments
 (0)