Balance assertions (#133)

* Make metadata serializable

* Refactor tests to use steps

* Add a balance assertion test step

* Test balance deserialization

* Box the test steps

* Permit size difference in step output
This commit is contained in:
Omar
2025-08-11 15:11:16 +03:00
committed by GitHub
parent 90b2dd4cfe
commit f7fbe094ec
9 changed files with 216 additions and 40 deletions
+15 -9
View File
@@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
use revive_dt_common::macros::define_wrapper_type;
use crate::{
input::{Expected, Input},
input::{Expected, Step},
mode::Mode,
};
@@ -12,21 +12,27 @@ pub struct Case {
pub name: Option<String>,
pub comment: Option<String>,
pub modes: Option<Vec<Mode>>,
pub inputs: Vec<Input>,
#[serde(rename = "inputs")]
pub steps: Vec<Step>,
pub group: Option<String>,
pub expected: Option<Expected>,
pub ignore: Option<bool>,
}
impl Case {
pub fn inputs_iterator(&self) -> impl Iterator<Item = Input> {
let inputs_len = self.inputs.len();
self.inputs
#[allow(irrefutable_let_patterns)]
pub fn steps_iterator(&self) -> impl Iterator<Item = Step> {
let steps_len = self.steps.len();
self.steps
.clone()
.into_iter()
.enumerate()
.map(move |(idx, mut input)| {
if idx + 1 == inputs_len {
.map(move |(idx, mut step)| {
let Step::FunctionCall(ref mut input) = step else {
return step;
};
if idx + 1 == steps_len {
if input.expected.is_none() {
input.expected = self.expected.clone();
}
@@ -36,9 +42,9 @@ impl Case {
// the case? What are we supposed to do with that final expected field on the
// case?
input
step
} else {
input
step
}
})
}
+27
View File
@@ -17,6 +17,19 @@ use revive_dt_common::macros::define_wrapper_type;
use crate::traits::ResolverApi;
use crate::{metadata::ContractInstance, traits::ResolutionContext};
/// A test step.
///
/// A test step can be anything. It could be an invocation to a function, an assertion, or any other
/// action that needs to be run or executed on the nodes used in the tests.
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(untagged)]
pub enum Step {
/// A function call or an invocation to some function on some smart contract.
FunctionCall(Box<Input>),
/// A step for performing a balance assertion on some account or contract.
BalanceAssertion(Box<BalanceAssertion>),
}
#[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq)]
pub struct Input {
#[serde(default = "Input::default_caller")]
@@ -33,6 +46,20 @@ pub struct Input {
pub variable_assignments: Option<VariableAssignments>,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq)]
pub struct BalanceAssertion {
/// The address that the balance assertion should be done on.
///
/// This is a string which will be resolved into an address when being processed. Therefore,
/// this could be a normal hex address, a variable such as `Test.address`, or perhaps even a
/// full on variable like `$VARIABLE:Uniswap`. It follows the same resolution rules that are
/// followed in the calldata.
pub address: String,
/// The amount of balance to assert that the account or contract has.
pub expected_balance: U256,
}
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(untagged)]
pub enum Expected {