From 240551871c26c0e798d9c2125b2521c6a420ef90 Mon Sep 17 00:00:00 2001 From: findolor Date: Thu, 10 Oct 2024 12:18:21 +0300 Subject: [PATCH 01/11] feat: add config for unit test --- crates/common/src/lib.rs | 1 + crates/settings/src/lib.rs | 3 +- crates/settings/src/unit_test.rs | 74 ++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 crates/settings/src/unit_test.rs diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 318cbca88..1050addaa 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -14,6 +14,7 @@ pub mod replays; pub mod subgraph; pub mod transaction; pub mod types; +pub mod unit_tests; pub mod utils; pub mod withdraw; diff --git a/crates/settings/src/lib.rs b/crates/settings/src/lib.rs index edb9dee3a..94a43d771 100644 --- a/crates/settings/src/lib.rs +++ b/crates/settings/src/lib.rs @@ -12,6 +12,7 @@ pub mod plot_source; pub mod remote; pub mod scenario; pub mod token; +pub mod unit_test; pub(crate) use chart::*; pub(crate) use config_source::*; @@ -23,7 +24,7 @@ pub(crate) use orderbook::*; pub(crate) use plot_source::*; pub(crate) use scenario::*; pub(crate) use token::*; - +pub(crate) use unit_test::*; #[cfg(test)] pub mod test; diff --git a/crates/settings/src/unit_test.rs b/crates/settings/src/unit_test.rs new file mode 100644 index 000000000..79518aa57 --- /dev/null +++ b/crates/settings/src/unit_test.rs @@ -0,0 +1,74 @@ +use crate::*; +use alloy::primitives::Address; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::sync::Arc; +use typeshare::typeshare; +use url::Url; + +#[typeshare] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +#[serde(rename_all = "kebab-case")] +pub struct UnitTestConfigSource { + pub test: TestConfigSource, +} + +#[typeshare] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +#[serde(rename_all = "kebab-case")] +pub struct TestConfigSource { + #[serde(skip_serializing_if = "Option::is_none")] + pub calculate_entrypoint: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub handle_entrypoint: Option, + pub scenario_name: String, + pub scenario: ScenarioConfigSource, +} + +#[typeshare] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +#[serde(rename_all = "kebab-case")] +pub struct TestConfig { + pub calculate_entrypoint: Option, + pub handle_entrypoint: Option, + pub scenario_name: String, + #[typeshare(typescript(type = "Scenario"))] + pub scenario: Arc, +} + +impl TestConfigSource { + pub fn try_into_test_config(self) -> Result { + let mut bindings = HashMap::new(); + for (k, v) in &self.scenario.bindings { + bindings.insert(k.to_string(), v.to_string()); + } + + let scenario = Arc::new(Scenario { + name: self.scenario_name.clone(), + bindings: bindings.clone(), + runs: self.scenario.runs, + blocks: self.scenario.blocks.clone(), + deployer: Arc::new(Deployer { + address: Address::default(), + network: Arc::new(Network { + name: String::from("").clone(), + rpc: Url::parse("http://rpc.com").unwrap(), + chain_id: 1, + label: None, + network_id: None, + currency: None, + }), + label: None, + }), + }); + + let config = TestConfig { + calculate_entrypoint: self.calculate_entrypoint, + handle_entrypoint: self.handle_entrypoint, + scenario_name: self.scenario_name.clone(), + scenario, + }; + + Ok(config) + } +} From 75e27325cb95291a30469d5fdd565581aa695337 Mon Sep 17 00:00:00 2001 From: findolor Date: Thu, 10 Oct 2024 12:18:46 +0300 Subject: [PATCH 02/11] feat: implement unit test file parsing and stack calculation --- crates/common/src/unit_tests/mod.rs | 428 ++++++++++++++++++++++++++++ 1 file changed, 428 insertions(+) create mode 100644 crates/common/src/unit_tests/mod.rs diff --git a/crates/common/src/unit_tests/mod.rs b/crates/common/src/unit_tests/mod.rs new file mode 100644 index 000000000..82a4d7762 --- /dev/null +++ b/crates/common/src/unit_tests/mod.rs @@ -0,0 +1,428 @@ +use alloy::primitives::{private::rand, U256}; +use alloy_ethers_typecast::transaction::{ReadableClientError, ReadableClientHttp}; +use dotrain::{error::ComposeError, RainDocument, Rebind}; +use futures::TryFutureExt; +use proptest::{ + prelude::RngCore, + test_runner::{RngAlgorithm, TestRng}, +}; +use rain_interpreter_bindings::IInterpreterStoreV1::FullyQualifiedNamespace; +use rain_interpreter_eval::{ + error::ForkCallError, + eval::ForkEvalArgs, + fork::{Forker, NewForkedEvm}, + trace::{RainEvalResultError, RainEvalResults}, +}; +use rain_orderbook_app_settings::{blocks::BlockError, config::*, unit_test::TestConfig}; +use std::sync::Arc; +use thiserror::Error; + +use crate::add_order::ORDERBOOK_ORDER_ENTRYPOINTS; + +pub const UNIT_TEST_ENTRYPOINTS: &[&str] = &["pre", "post"]; + +#[derive(Clone)] +pub struct TestRunner { + pub forker: Forker, + pub dotrains: Dotrains, + pub settings: Settings, + pub rng: TestRng, +} + +#[derive(Clone)] +pub struct Dotrains { + pub main_dotrain: String, + pub test_dotrain: String, +} + +#[derive(Clone)] +pub struct Settings { + pub main_config: Config, + pub test_config: TestConfig, +} + +#[derive(Error, Debug)] +pub enum TestRunnerError { + #[error("Scenario not found")] + ScenarioNotFound(String), + #[error(transparent)] + ReadableClientHttpError(#[from] ReadableClientError), + #[error(transparent)] + BlockError(#[from] BlockError), + #[error(transparent)] + ForkCallError(#[from] ForkCallError), + #[error(transparent)] + JoinError(#[from] tokio::task::JoinError), + #[error(transparent)] + ComposeError(#[from] ComposeError), + #[error(transparent)] + RainEvalResultError(#[from] RainEvalResultError), +} + +impl TestRunner { + pub async fn new( + dotrain: &str, + test_dotrain: &str, + settings: Config, + test_settings: TestConfig, + seed: Option<[u8; 32]>, + ) -> Self { + Self { + forker: Forker::new(), + dotrains: Dotrains { + main_dotrain: dotrain.into(), + test_dotrain: test_dotrain.into(), + }, + settings: Settings { + main_config: settings, + test_config: test_settings, + }, + rng: TestRng::from_seed(RngAlgorithm::ChaCha, &seed.unwrap_or([0; 32])), + } + } + + // pub async fn get_test_scenario(&self) -> Result, TestRunnerError> { + // // self. + // } + + async fn get_pre_stack(&mut self) -> Result, TestRunnerError> { + let scenario_name: String = self.settings.test_config.scenario_name.clone(); + + let deployer = self + .settings + .main_config + .deployers + .get(&scenario_name) + .unwrap() + .clone(); + + // Fetch the latest block number + let block_number = ReadableClientHttp::new_from_url(deployer.network.rpc.to_string())? + .get_block_number() + .await?; + + let blocks = self + .settings + .test_config + .scenario + .blocks + .as_ref() + .map_or(Ok(vec![block_number]), |b| { + b.expand_to_block_numbers(block_number) + })?; + + // Create a fork with the first block number + self.forker + .add_or_select( + NewForkedEvm { + fork_url: deployer.network.rpc.clone().into(), + fork_block_number: Some(blocks[0]), + }, + None, + ) + .await?; + + // Pull out the bindings from the scenario + let scenario_bindings: Vec = vec![]; + + // Create a new RainDocument with the dotrain and the bindings + // The bindings in the dotrain string are ignored by the RainDocument + let rain_document = RainDocument::create( + self.dotrains.test_dotrain.clone(), + None, + None, + Some(scenario_bindings.clone()), + ); + + // Search the namespace hash map for NamespaceItems that are elided and make a vec of the keys + let elided_binding_keys = Arc::new( + rain_document + .namespace() + .iter() + .filter(|(_, v)| v.is_elided_binding()) + .map(|(k, _)| k.clone()) + .collect::>(), + ); + + let dotrain = Arc::new(self.dotrains.test_dotrain.clone()); + self.forker.roll_fork(Some(block_number), None)?; + let fork = Arc::new(self.forker.clone()); + let fork_clone = Arc::clone(&fork); + let elided_binding_keys = Arc::clone(&elided_binding_keys); + let deployer = Arc::clone(&deployer); + let scenario_bindings = scenario_bindings.clone(); + let dotrain = Arc::clone(&dotrain); + + let mut final_bindings: Vec = vec![]; + + for elided_binding in elided_binding_keys.as_slice() { + let mut val: [u8; 32] = [0; 32]; + self.rng.fill_bytes(&mut val); + let hex = alloy::primitives::hex::encode_prefixed(val); + final_bindings.push(Rebind(elided_binding.to_string(), hex)); + } + + let handle = tokio::spawn(async move { + final_bindings.extend(scenario_bindings.clone()); + + let rainlang_string = + RainDocument::compose_text(&dotrain, &["pre"], None, Some(final_bindings))?; + + let args = ForkEvalArgs { + rainlang_string, + source_index: 0, + deployer: deployer.address, + namespace: FullyQualifiedNamespace::default(), + context: vec![vec![U256::from(0); 1]; 1], + decode_errors: true, + }; + fork_clone + .fork_eval(args) + .map_err(TestRunnerError::ForkCallError) + .await + }); + + let result: RainEvalResults = vec![handle.await??.into()].into(); + let flattened = result.into_flattened_table().unwrap(); + Ok(flattened.rows[0].clone()) + } + + pub async fn run_unit_test(&mut self) -> Result { + let pre_stack: Vec> = self.get_pre_stack().await?; + + let input_token = pre_stack[0]; + let output_token = pre_stack[1]; + let output_cap = pre_stack[2]; + let block_number = pre_stack[3]; + + let scenario_name: String = self.settings.test_config.scenario_name.clone(); + let scenario = self + .settings + .main_config + .scenarios + .get(&scenario_name) + .ok_or(TestRunnerError::ScenarioNotFound(scenario_name))?; + + let deployer = scenario.deployer.clone(); + + // Fetch the latest block number + let block_number = ReadableClientHttp::new_from_url(deployer.network.rpc.to_string())? + .get_block_number() + .await?; + + let blocks = self + .settings + .test_config + .scenario + .blocks + .as_ref() + .map_or(Ok(vec![block_number]), |b| { + b.expand_to_block_numbers(block_number) + })?; + + // Create a fork with the first block number + self.forker + .add_or_select( + NewForkedEvm { + fork_url: deployer.network.rpc.clone().into(), + fork_block_number: Some(blocks[0]), + }, + None, + ) + .await?; + + // Pull out the bindings from the scenario + let scenario_bindings: Vec = self + .settings + .test_config + .scenario + .bindings + .clone() + .into_iter() + .map(|(k, v)| Rebind(k, v)) + .collect(); + + // Create a new RainDocument with the dotrain and the bindings + // The bindings in the dotrain string are ignored by the RainDocument + let rain_document: RainDocument = RainDocument::create( + self.dotrains.main_dotrain.clone(), + None, + None, + Some(scenario_bindings.clone()), + ); + + // Search the namespace hash map for NamespaceItems that are elided and make a vec of the keys + let elided_binding_keys = Arc::new( + rain_document + .namespace() + .iter() + .filter(|(_, v)| v.is_elided_binding()) + .map(|(k, _)| k.clone()) + .collect::>(), + ); + + let dotrain = Arc::new(self.dotrains.main_dotrain.clone()); + self.forker.roll_fork(Some(block_number), None)?; + let fork = Arc::new(self.forker.clone()); + let fork_clone = Arc::clone(&fork); + let elided_binding_keys = Arc::clone(&elided_binding_keys); + let deployer = Arc::clone(&deployer); + let scenario_bindings = scenario_bindings.clone(); + let dotrain = Arc::clone(&dotrain); + + let mut final_bindings: Vec = vec![]; + + for elided_binding in elided_binding_keys.as_slice() { + let mut val: [u8; 32] = [0; 32]; + self.rng.fill_bytes(&mut val); + let hex = alloy::primitives::hex::encode_prefixed(val); + final_bindings.push(Rebind(elided_binding.to_string(), hex)); + } + + let handle = tokio::spawn(async move { + final_bindings.extend(scenario_bindings.clone()); + + let rainlang_string = RainDocument::compose_text( + &dotrain, + &ORDERBOOK_ORDER_ENTRYPOINTS, + None, + Some(final_bindings), + )?; + + // Create a 5x5 grid of zero values for context - later we'll + // replace these with sane values based on Orderbook context + let mut context = vec![vec![U256::from(0); 5]; 5]; + // set random hash for context order hash cell + context[1][0] = rand::random(); + + // output cap + context[2][0] = output_cap; + // input token + context[3][0] = input_token; + // output token + context[4][0] = output_token; + + let args = ForkEvalArgs { + rainlang_string, + source_index: 0, + deployer: deployer.address, + namespace: FullyQualifiedNamespace::default(), + context, + decode_errors: true, + }; + fork_clone + .fork_eval(args) + .map_err(TestRunnerError::ForkCallError) + .await + }); + + let result: RainEvalResults = vec![handle.await??.into()].into(); + Ok(result) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use rain_orderbook_app_settings::{ + config_source::ConfigSource, unit_test::UnitTestConfigSource, + }; + use rain_orderbook_test_fixtures::LocalEvm; + + fn get_main_config(dotrain: &str) -> Config { + let frontmatter = RainDocument::get_front_matter(dotrain).unwrap(); + let settings = serde_yaml::from_str::(frontmatter).unwrap(); + settings + .try_into() + .map_err(|e| println!("{:?}", e)) + .unwrap() + } + + fn get_test_config(test_dotrain: &str) -> TestConfig { + let frontmatter = RainDocument::get_front_matter(test_dotrain).unwrap(); + let source = serde_yaml::from_str::(frontmatter).unwrap(); + source + .test + .try_into_test_config() + .map_err(|e| println!("{:?}", e)) + .unwrap() + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 10)] + async fn test_test_runner() { + let local_evm = LocalEvm::new().await; + let test_dotrain = format!( + r#" +test: + # calculate-entrypoint: some-custom-entrypoint + # handle-entrypoint: some-custom-entrypoint + scenario-name: some-key + scenario: + bindings: + orderbook-subparser: {orderbook_subparser} + second-binding: 10 +--- +#pre +input-token: 0x0165878a594ca255338adfa4d48449f69242eb8f, +output-token: 0xa513e6e4b8f2a923d98304ec87f64353c4d5c853, +output-cap: 10, +block-number: 100; +#post +_: 20, +_: 30; + "#, + orderbook_subparser = local_evm.orderbook_subparser.address() + ); + let dotrain = format!( + r#" +deployers: + some-key: + address: {deployer} +networks: + some-key: + rpc: {rpc_url} + chain-id: 123 +scenarios: + some-key: + bindings: + orderbook-subparser: {orderbook_subparser} + second-binding: 20 +--- +#orderbook-subparser ! +#second-binding ! + +#calculate-io +using-words-from orderbook-subparser + +/*input-token: input-token(), +output-token: output-token(), +a: 10, +b: second-binding;*/ + +_: 99, +_: 999; +#handle-io +:; + "#, + rpc_url = local_evm.url(), + deployer = local_evm.deployer.address(), + orderbook_subparser = local_evm.orderbook_subparser.address() + ); + + let main_config = get_main_config(&dotrain); + let test_config = get_test_config(&test_dotrain); + + let mut runner = + TestRunner::new(&dotrain, &test_dotrain, main_config, test_config, None).await; + + let result = runner + .run_unit_test() + .await + .map_err(|e| println!("{:#?}", e)) + .unwrap(); + + println!("result: {:?}", result.into_flattened_table().unwrap()); + + panic!("test"); + } +} From 7f8cf580779fbe9d380765edfb191de3868547d1 Mon Sep 17 00:00:00 2001 From: findolor Date: Fri, 11 Oct 2024 20:02:25 +0300 Subject: [PATCH 03/11] feat: continue implementing --- crates/common/src/unit_tests/mod.rs | 415 +++++++++++++++++----------- 1 file changed, 249 insertions(+), 166 deletions(-) diff --git a/crates/common/src/unit_tests/mod.rs b/crates/common/src/unit_tests/mod.rs index 82a4d7762..f8b9cc3b9 100644 --- a/crates/common/src/unit_tests/mod.rs +++ b/crates/common/src/unit_tests/mod.rs @@ -1,4 +1,4 @@ -use alloy::primitives::{private::rand, U256}; +use alloy::primitives::{Address, U256}; use alloy_ethers_typecast::transaction::{ReadableClientError, ReadableClientHttp}; use dotrain::{error::ComposeError, RainDocument, Rebind}; use futures::TryFutureExt; @@ -13,13 +13,12 @@ use rain_interpreter_eval::{ fork::{Forker, NewForkedEvm}, trace::{RainEvalResultError, RainEvalResults}, }; -use rain_orderbook_app_settings::{blocks::BlockError, config::*, unit_test::TestConfig}; +use rain_orderbook_app_settings::{ + blocks::BlockError, config::*, deployer::Deployer, network::Network, unit_test::TestConfig, +}; use std::sync::Arc; use thiserror::Error; - -use crate::add_order::ORDERBOOK_ORDER_ENTRYPOINTS; - -pub const UNIT_TEST_ENTRYPOINTS: &[&str] = &["pre", "post"]; +use url::Url; #[derive(Clone)] pub struct TestRunner { @@ -27,6 +26,14 @@ pub struct TestRunner { pub dotrains: Dotrains, pub settings: Settings, pub rng: TestRng, + pub test_setup: TestSetup, +} + +#[derive(Clone)] +pub struct TestSetup { + pub block_number: u64, + pub deployer: Arc, + pub scenario_name: String, } #[derive(Clone)] @@ -78,63 +85,71 @@ impl TestRunner { test_config: test_settings, }, rng: TestRng::from_seed(RngAlgorithm::ChaCha, &seed.unwrap_or([0; 32])), + test_setup: TestSetup { + block_number: 0, + deployer: Arc::new(Deployer { + address: Address::default(), + network: Arc::new(Network { + name: String::from("").clone(), + rpc: Url::parse("http://rpc.com").unwrap(), + chain_id: 1, + label: None, + network_id: None, + currency: None, + }), + label: None, + }), + scenario_name: String::new(), + }, } } - // pub async fn get_test_scenario(&self) -> Result, TestRunnerError> { - // // self. - // } - - async fn get_pre_stack(&mut self) -> Result, TestRunnerError> { - let scenario_name: String = self.settings.test_config.scenario_name.clone(); - - let deployer = self - .settings - .main_config - .deployers - .get(&scenario_name) - .unwrap() - .clone(); - - // Fetch the latest block number - let block_number = ReadableClientHttp::new_from_url(deployer.network.rpc.to_string())? - .get_block_number() - .await?; - - let blocks = self - .settings - .test_config - .scenario - .blocks - .as_ref() - .map_or(Ok(vec![block_number]), |b| { - b.expand_to_block_numbers(block_number) - })?; - - // Create a fork with the first block number - self.forker - .add_or_select( - NewForkedEvm { - fork_url: deployer.network.rpc.clone().into(), - fork_block_number: Some(blocks[0]), - }, - None, - ) - .await?; + fn get_elided_binding_keys( + &self, + is_test_dotrain: bool, + scenario_bindings: &Vec, + ) -> Arc> { + let dotrain = if is_test_dotrain { + self.dotrains.test_dotrain.clone() + } else { + self.dotrains.main_dotrain.clone() + }; + let rain_document = + RainDocument::create(dotrain, None, None, Some(scenario_bindings.clone())); + Arc::new( + rain_document + .namespace() + .iter() + .filter(|(_, v)| v.is_elided_binding()) + .map(|(k, _)| k.clone()) + .collect::>(), + ) + } - // Pull out the bindings from the scenario - let scenario_bindings: Vec = vec![]; + fn get_final_bindings(&mut self, is_test_dotrain: bool, scenario_name: &str) -> Vec { + let dotrain: String; + let mut scenario_bindings: Vec = vec![]; + let mut final_bindings: Vec = vec![]; - // Create a new RainDocument with the dotrain and the bindings - // The bindings in the dotrain string are ignored by the RainDocument - let rain_document = RainDocument::create( - self.dotrains.test_dotrain.clone(), - None, - None, - Some(scenario_bindings.clone()), - ); + if is_test_dotrain { + dotrain = self.dotrains.test_dotrain.clone(); + } else { + dotrain = self.dotrains.main_dotrain.clone(); + scenario_bindings = self + .settings + .main_config + .scenarios + .get(scenario_name) + .unwrap() + .bindings + .clone() + .into_iter() + .map(|(k, v)| Rebind(k, v)) + .collect() + } - // Search the namespace hash map for NamespaceItems that are elided and make a vec of the keys + let rain_document = + RainDocument::create(dotrain, None, None, Some(scenario_bindings.clone())); let elided_binding_keys = Arc::new( rain_document .namespace() @@ -144,16 +159,8 @@ impl TestRunner { .collect::>(), ); - let dotrain = Arc::new(self.dotrains.test_dotrain.clone()); - self.forker.roll_fork(Some(block_number), None)?; - let fork = Arc::new(self.forker.clone()); - let fork_clone = Arc::clone(&fork); let elided_binding_keys = Arc::clone(&elided_binding_keys); - let deployer = Arc::clone(&deployer); let scenario_bindings = scenario_bindings.clone(); - let dotrain = Arc::clone(&dotrain); - - let mut final_bindings: Vec = vec![]; for elided_binding in elided_binding_keys.as_slice() { let mut val: [u8; 32] = [0; 32]; @@ -162,9 +169,25 @@ impl TestRunner { final_bindings.push(Rebind(elided_binding.to_string(), hex)); } - let handle = tokio::spawn(async move { - final_bindings.extend(scenario_bindings.clone()); + final_bindings.extend(scenario_bindings); + final_bindings + } + + async fn run_pre_entrypoint( + &mut self, + scenario_name: &str, + ) -> Result, TestRunnerError> { + let final_bindings = self.get_final_bindings(true, scenario_name); + + let dotrain = Arc::new(self.dotrains.test_dotrain.clone()); + self.forker + .roll_fork(Some(self.test_setup.block_number), None)?; + let fork = Arc::new(self.forker.clone()); + let fork_clone = Arc::clone(&fork); + let deployer = Arc::clone(&self.test_setup.deployer); + let dotrain = Arc::clone(&dotrain); + let handle = tokio::spawn(async move { let rainlang_string = RainDocument::compose_text(&dotrain, &["pre"], None, Some(final_bindings))?; @@ -187,104 +210,73 @@ impl TestRunner { Ok(flattened.rows[0].clone()) } - pub async fn run_unit_test(&mut self) -> Result { - let pre_stack: Vec> = self.get_pre_stack().await?; - - let input_token = pre_stack[0]; - let output_token = pre_stack[1]; - let output_cap = pre_stack[2]; - let block_number = pre_stack[3]; + async fn run_post_entrypoint( + &mut self, + scenario_name: &str, + calculate_stack: RainEvalResults, + handle_stack: RainEvalResults, + ) -> Result { + let final_bindings = self.get_final_bindings(true, scenario_name); - let scenario_name: String = self.settings.test_config.scenario_name.clone(); - let scenario = self - .settings - .main_config - .scenarios - .get(&scenario_name) - .ok_or(TestRunnerError::ScenarioNotFound(scenario_name))?; + let dotrain = Arc::new(self.dotrains.test_dotrain.clone()); + self.forker + .roll_fork(Some(self.test_setup.block_number), None)?; + let fork = Arc::new(self.forker.clone()); + let fork_clone = Arc::clone(&fork); + let deployer = Arc::clone(&self.test_setup.deployer); + let dotrain = Arc::clone(&dotrain); - let deployer = scenario.deployer.clone(); + let handle = tokio::spawn(async move { + let rainlang_string = + RainDocument::compose_text(&dotrain, &["post"], None, Some(final_bindings))?; - // Fetch the latest block number - let block_number = ReadableClientHttp::new_from_url(deployer.network.rpc.to_string())? - .get_block_number() - .await?; + let context = vec![ + calculate_stack.results[0].stack.clone(), + handle_stack.results[0].stack.clone(), + ]; - let blocks = self - .settings - .test_config - .scenario - .blocks - .as_ref() - .map_or(Ok(vec![block_number]), |b| { - b.expand_to_block_numbers(block_number) - })?; + let args = ForkEvalArgs { + rainlang_string, + source_index: 0, + deployer: deployer.address, + namespace: FullyQualifiedNamespace::default(), + context, + decode_errors: true, + }; + fork_clone + .fork_eval(args) + .map_err(TestRunnerError::ForkCallError) + .await + }); - // Create a fork with the first block number - self.forker - .add_or_select( - NewForkedEvm { - fork_url: deployer.network.rpc.clone().into(), - fork_block_number: Some(blocks[0]), - }, - None, - ) - .await?; + let result: RainEvalResults = vec![handle.await??.into()].into(); + Ok(result) + } - // Pull out the bindings from the scenario - let scenario_bindings: Vec = self - .settings - .test_config - .scenario - .bindings - .clone() - .into_iter() - .map(|(k, v)| Rebind(k, v)) - .collect(); - - // Create a new RainDocument with the dotrain and the bindings - // The bindings in the dotrain string are ignored by the RainDocument - let rain_document: RainDocument = RainDocument::create( - self.dotrains.main_dotrain.clone(), - None, - None, - Some(scenario_bindings.clone()), - ); + async fn run_calculate_entrypoint( + &mut self, + scenario_name: &str, + pre_stack: Vec, + ) -> Result { + let input_token = pre_stack[0]; + let output_token = pre_stack[1]; + let output_cap = pre_stack[2]; + // let block_number = pre_stack[3]; - // Search the namespace hash map for NamespaceItems that are elided and make a vec of the keys - let elided_binding_keys = Arc::new( - rain_document - .namespace() - .iter() - .filter(|(_, v)| v.is_elided_binding()) - .map(|(k, _)| k.clone()) - .collect::>(), - ); + let final_bindings = self.get_final_bindings(false, scenario_name); let dotrain = Arc::new(self.dotrains.main_dotrain.clone()); - self.forker.roll_fork(Some(block_number), None)?; + self.forker + .roll_fork(Some(self.test_setup.block_number), None)?; let fork = Arc::new(self.forker.clone()); let fork_clone = Arc::clone(&fork); - let elided_binding_keys = Arc::clone(&elided_binding_keys); - let deployer = Arc::clone(&deployer); - let scenario_bindings = scenario_bindings.clone(); + let deployer = Arc::clone(&self.test_setup.deployer); let dotrain = Arc::clone(&dotrain); - let mut final_bindings: Vec = vec![]; - - for elided_binding in elided_binding_keys.as_slice() { - let mut val: [u8; 32] = [0; 32]; - self.rng.fill_bytes(&mut val); - let hex = alloy::primitives::hex::encode_prefixed(val); - final_bindings.push(Rebind(elided_binding.to_string(), hex)); - } - let handle = tokio::spawn(async move { - final_bindings.extend(scenario_bindings.clone()); - let rainlang_string = RainDocument::compose_text( &dotrain, - &ORDERBOOK_ORDER_ENTRYPOINTS, + &["calculate-io"], None, Some(final_bindings), )?; @@ -292,8 +284,6 @@ impl TestRunner { // Create a 5x5 grid of zero values for context - later we'll // replace these with sane values based on Orderbook context let mut context = vec![vec![U256::from(0); 5]; 5]; - // set random hash for context order hash cell - context[1][0] = rand::random(); // output cap context[2][0] = output_cap; @@ -319,6 +309,104 @@ impl TestRunner { let result: RainEvalResults = vec![handle.await??.into()].into(); Ok(result) } + + async fn run_handle_entrypoint( + &mut self, + scenario_name: &str, + calculate_stack: RainEvalResults, + ) -> Result { + let _io_ratio = calculate_stack.results[0].stack[0]; + let max_output = calculate_stack.results[0].stack[1]; + + let final_bindings = self.get_final_bindings(false, scenario_name); + + let dotrain = Arc::new(self.dotrains.main_dotrain.clone()); + self.forker + .roll_fork(Some(self.test_setup.block_number), None)?; + let fork = Arc::new(self.forker.clone()); + let fork_clone = Arc::clone(&fork); + let deployer = Arc::clone(&self.test_setup.deployer); + let dotrain = Arc::clone(&dotrain); + + let handle = tokio::spawn(async move { + let rainlang_string = + RainDocument::compose_text(&dotrain, &["handle-io"], None, Some(final_bindings))?; + + // Create a 5x5 grid of zero values for context - later we'll + // replace these with sane values based on Orderbook context + let mut context = vec![vec![U256::from(0); 5]; 5]; + + // output vault decrease + context[4][4] = max_output; + + let args = ForkEvalArgs { + rainlang_string, + source_index: 0, + deployer: deployer.address, + namespace: FullyQualifiedNamespace::default(), + context, + decode_errors: true, + }; + fork_clone + .fork_eval(args) + .map_err(TestRunnerError::ForkCallError) + .await + }); + + let result: RainEvalResults = vec![handle.await??.into()].into(); + Ok(result) + } + + pub async fn run_unit_test(&mut self) -> Result { + let scenario_name = self.settings.test_config.scenario_name.clone(); + + self.test_setup.deployer = self + .settings + .main_config + .deployers + .get(&scenario_name) + .unwrap() + .clone(); + + // Fetch the latest block number + let block_number = + ReadableClientHttp::new_from_url(self.test_setup.deployer.network.rpc.to_string())? + .get_block_number() + .await?; + let blocks = self + .settings + .test_config + .scenario + .blocks + .as_ref() + .map_or(Ok(vec![block_number]), |b| { + b.expand_to_block_numbers(block_number) + })?; + self.test_setup.block_number = blocks[0]; + + // Create a fork with the first block number + self.forker + .add_or_select( + NewForkedEvm { + fork_url: self.test_setup.deployer.network.rpc.clone().into(), + fork_block_number: Some(block_number), + }, + None, + ) + .await?; + + let pre_stack: Vec = self.run_pre_entrypoint(&scenario_name).await?; + let calculate_stack = self + .run_calculate_entrypoint(&scenario_name, pre_stack) + .await?; + let handle_stack = self + .run_handle_entrypoint(&scenario_name, calculate_stack.clone()) + .await?; + let results = self + .run_post_entrypoint(&scenario_name, calculate_stack, handle_stack) + .await?; + Ok(results) + } } #[cfg(test)] @@ -360,7 +448,7 @@ test: scenario: bindings: orderbook-subparser: {orderbook_subparser} - second-binding: 10 + second-binding: 999 --- #pre input-token: 0x0165878a594ca255338adfa4d48449f69242eb8f, @@ -368,8 +456,8 @@ output-token: 0xa513e6e4b8f2a923d98304ec87f64353c4d5c853, output-cap: 10, block-number: 100; #post -_: 20, -_: 30; +:ensure(equal-to(context<0 0>() 999) "io ratio should be 999"), +:ensure(equal-to(context<1 0>() 10) "output cap should be 10"); "#, orderbook_subparser = local_evm.orderbook_subparser.address() ); @@ -394,15 +482,14 @@ scenarios: #calculate-io using-words-from orderbook-subparser -/*input-token: input-token(), -output-token: output-token(), +_: input-token(), +_: output-token(), a: 10, -b: second-binding;*/ - -_: 99, -_: 999; +b: second-binding; #handle-io -:; +using-words-from orderbook-subparser + +_: output-vault-decrease(); "#, rpc_url = local_evm.url(), deployer = local_evm.deployer.address(), @@ -415,14 +502,10 @@ _: 999; let mut runner = TestRunner::new(&dotrain, &test_dotrain, main_config, test_config, None).await; - let result = runner + runner .run_unit_test() .await .map_err(|e| println!("{:#?}", e)) .unwrap(); - - println!("result: {:?}", result.into_flattened_table().unwrap()); - - panic!("test"); } } From 17774ad7a64f9f4b76126acbfff73fc5cec7fe9a Mon Sep 17 00:00:00 2001 From: findolor Date: Mon, 14 Oct 2024 10:19:49 +0300 Subject: [PATCH 04/11] refactor: update unit tester code --- crates/common/src/unit_tests/mod.rs | 141 ++++++++++------------------ 1 file changed, 50 insertions(+), 91 deletions(-) diff --git a/crates/common/src/unit_tests/mod.rs b/crates/common/src/unit_tests/mod.rs index f8b9cc3b9..769b5818d 100644 --- a/crates/common/src/unit_tests/mod.rs +++ b/crates/common/src/unit_tests/mod.rs @@ -104,29 +104,7 @@ impl TestRunner { } } - fn get_elided_binding_keys( - &self, - is_test_dotrain: bool, - scenario_bindings: &Vec, - ) -> Arc> { - let dotrain = if is_test_dotrain { - self.dotrains.test_dotrain.clone() - } else { - self.dotrains.main_dotrain.clone() - }; - let rain_document = - RainDocument::create(dotrain, None, None, Some(scenario_bindings.clone())); - Arc::new( - rain_document - .namespace() - .iter() - .filter(|(_, v)| v.is_elided_binding()) - .map(|(k, _)| k.clone()) - .collect::>(), - ) - } - - fn get_final_bindings(&mut self, is_test_dotrain: bool, scenario_name: &str) -> Vec { + fn get_final_bindings(&mut self, is_test_dotrain: bool) -> Vec { let dotrain: String; let mut scenario_bindings: Vec = vec![]; let mut final_bindings: Vec = vec![]; @@ -137,10 +115,8 @@ impl TestRunner { dotrain = self.dotrains.main_dotrain.clone(); scenario_bindings = self .settings - .main_config - .scenarios - .get(scenario_name) - .unwrap() + .test_config + .scenario .bindings .clone() .into_iter() @@ -173,11 +149,8 @@ impl TestRunner { final_bindings } - async fn run_pre_entrypoint( - &mut self, - scenario_name: &str, - ) -> Result, TestRunnerError> { - let final_bindings = self.get_final_bindings(true, scenario_name); + async fn run_pre_entrypoint(&mut self) -> Result, TestRunnerError> { + let final_bindings = self.get_final_bindings(true); let dotrain = Arc::new(self.dotrains.test_dotrain.clone()); self.forker @@ -210,15 +183,18 @@ impl TestRunner { Ok(flattened.rows[0].clone()) } - async fn run_post_entrypoint( + async fn run_calculate_entrypoint( &mut self, - scenario_name: &str, - calculate_stack: RainEvalResults, - handle_stack: RainEvalResults, + pre_stack: Vec, ) -> Result { - let final_bindings = self.get_final_bindings(true, scenario_name); + let input_token = pre_stack[0]; + let output_token = pre_stack[1]; + let output_cap = pre_stack[2]; + // let block_number = pre_stack[3]; - let dotrain = Arc::new(self.dotrains.test_dotrain.clone()); + let final_bindings = self.get_final_bindings(false); + + let dotrain = Arc::new(self.dotrains.main_dotrain.clone()); self.forker .roll_fork(Some(self.test_setup.block_number), None)?; let fork = Arc::new(self.forker.clone()); @@ -227,13 +203,20 @@ impl TestRunner { let dotrain = Arc::clone(&dotrain); let handle = tokio::spawn(async move { - let rainlang_string = - RainDocument::compose_text(&dotrain, &["post"], None, Some(final_bindings))?; + let rainlang_string = RainDocument::compose_text( + &dotrain, + &["calculate-io"], + None, + Some(final_bindings), + )?; - let context = vec![ - calculate_stack.results[0].stack.clone(), - handle_stack.results[0].stack.clone(), - ]; + let mut context = vec![vec![U256::from(0); 5]; 5]; + // output cap + context[2][0] = output_cap; + // input token + context[3][0] = input_token; + // output token + context[4][0] = output_token; let args = ForkEvalArgs { rainlang_string, @@ -253,17 +236,14 @@ impl TestRunner { Ok(result) } - async fn run_calculate_entrypoint( + async fn run_handle_entrypoint( &mut self, - scenario_name: &str, - pre_stack: Vec, + calculate_stack: RainEvalResults, ) -> Result { - let input_token = pre_stack[0]; - let output_token = pre_stack[1]; - let output_cap = pre_stack[2]; - // let block_number = pre_stack[3]; + let _io_ratio = calculate_stack.results[0].stack[0]; + let max_output = calculate_stack.results[0].stack[1]; - let final_bindings = self.get_final_bindings(false, scenario_name); + let final_bindings = self.get_final_bindings(false); let dotrain = Arc::new(self.dotrains.main_dotrain.clone()); self.forker @@ -274,23 +254,13 @@ impl TestRunner { let dotrain = Arc::clone(&dotrain); let handle = tokio::spawn(async move { - let rainlang_string = RainDocument::compose_text( - &dotrain, - &["calculate-io"], - None, - Some(final_bindings), - )?; + let rainlang_string = + RainDocument::compose_text(&dotrain, &["handle-io"], None, Some(final_bindings))?; - // Create a 5x5 grid of zero values for context - later we'll - // replace these with sane values based on Orderbook context let mut context = vec![vec![U256::from(0); 5]; 5]; - // output cap - context[2][0] = output_cap; - // input token - context[3][0] = input_token; - // output token - context[4][0] = output_token; + // output vault decrease + context[4][4] = max_output; let args = ForkEvalArgs { rainlang_string, @@ -310,17 +280,14 @@ impl TestRunner { Ok(result) } - async fn run_handle_entrypoint( + async fn run_post_entrypoint( &mut self, - scenario_name: &str, calculate_stack: RainEvalResults, + handle_stack: RainEvalResults, ) -> Result { - let _io_ratio = calculate_stack.results[0].stack[0]; - let max_output = calculate_stack.results[0].stack[1]; + let final_bindings = self.get_final_bindings(true); - let final_bindings = self.get_final_bindings(false, scenario_name); - - let dotrain = Arc::new(self.dotrains.main_dotrain.clone()); + let dotrain = Arc::new(self.dotrains.test_dotrain.clone()); self.forker .roll_fork(Some(self.test_setup.block_number), None)?; let fork = Arc::new(self.forker.clone()); @@ -330,14 +297,12 @@ impl TestRunner { let handle = tokio::spawn(async move { let rainlang_string = - RainDocument::compose_text(&dotrain, &["handle-io"], None, Some(final_bindings))?; - - // Create a 5x5 grid of zero values for context - later we'll - // replace these with sane values based on Orderbook context - let mut context = vec![vec![U256::from(0); 5]; 5]; + RainDocument::compose_text(&dotrain, &["post"], None, Some(final_bindings))?; - // output vault decrease - context[4][4] = max_output; + let context = vec![ + calculate_stack.results[0].stack.clone(), + handle_stack.results[0].stack.clone(), + ]; let args = ForkEvalArgs { rainlang_string, @@ -358,13 +323,11 @@ impl TestRunner { } pub async fn run_unit_test(&mut self) -> Result { - let scenario_name = self.settings.test_config.scenario_name.clone(); - self.test_setup.deployer = self .settings .main_config .deployers - .get(&scenario_name) + .get(&self.settings.test_config.scenario_name) .unwrap() .clone(); @@ -395,15 +358,11 @@ impl TestRunner { ) .await?; - let pre_stack: Vec = self.run_pre_entrypoint(&scenario_name).await?; - let calculate_stack = self - .run_calculate_entrypoint(&scenario_name, pre_stack) - .await?; - let handle_stack = self - .run_handle_entrypoint(&scenario_name, calculate_stack.clone()) - .await?; + let pre_stack: Vec = self.run_pre_entrypoint().await?; + let calculate_stack = self.run_calculate_entrypoint(pre_stack).await?; + let handle_stack = self.run_handle_entrypoint(calculate_stack.clone()).await?; let results = self - .run_post_entrypoint(&scenario_name, calculate_stack, handle_stack) + .run_post_entrypoint(calculate_stack, handle_stack) .await?; Ok(results) } From 9a0a63fe14efd3d95643e19454fb6b37abdbc0d2 Mon Sep 17 00:00:00 2001 From: findolor Date: Mon, 14 Oct 2024 11:38:21 +0300 Subject: [PATCH 05/11] build: fix CI issue --- crates/common/src/lib.rs | 1 + crates/settings/src/lib.rs | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 1050addaa..f8022a12f 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -14,6 +14,7 @@ pub mod replays; pub mod subgraph; pub mod transaction; pub mod types; +#[cfg(not(target_family = "wasm"))] pub mod unit_tests; pub mod utils; pub mod withdraw; diff --git a/crates/settings/src/lib.rs b/crates/settings/src/lib.rs index 94a43d771..5eb89a882 100644 --- a/crates/settings/src/lib.rs +++ b/crates/settings/src/lib.rs @@ -24,7 +24,6 @@ pub(crate) use orderbook::*; pub(crate) use plot_source::*; pub(crate) use scenario::*; pub(crate) use token::*; -pub(crate) use unit_test::*; #[cfg(test)] pub mod test; From ce8eb578f1ffda2a61b5393228a0eaf69ab43823 Mon Sep 17 00:00:00 2001 From: findolor Date: Wed, 16 Oct 2024 09:45:57 +0300 Subject: [PATCH 06/11] feat: implement dummy data for structs --- crates/settings/src/deployer.rs | 9 +++++++++ crates/settings/src/network.rs | 12 ++++++++++++ 2 files changed, 21 insertions(+) diff --git a/crates/settings/src/deployer.rs b/crates/settings/src/deployer.rs index e45193517..f950c140b 100644 --- a/crates/settings/src/deployer.rs +++ b/crates/settings/src/deployer.rs @@ -14,6 +14,15 @@ pub struct Deployer { pub network: Arc, pub label: Option, } +impl Deployer { + pub fn dummy() -> Self { + Deployer { + address: Address::default(), + network: Arc::new(Network::dummy()), + label: None, + } + } +} #[derive(Error, Debug, PartialEq)] pub enum ParseDeployerConfigSourceError { diff --git a/crates/settings/src/network.rs b/crates/settings/src/network.rs index c77cb02a5..0dfb6b04b 100644 --- a/crates/settings/src/network.rs +++ b/crates/settings/src/network.rs @@ -19,6 +19,18 @@ pub struct Network { pub network_id: Option, pub currency: Option, } +impl Network { + pub fn dummy() -> Self { + Network { + name: "".to_string(), + rpc: Url::parse("http://rpc.com").unwrap(), + chain_id: 1, + label: None, + network_id: None, + currency: None, + } + } +} #[derive(Error, Debug, PartialEq)] pub enum ParseNetworkConfigSourceError { From 4e940866d1046e2eac1c5fa2d0e2f8182f26cc5d Mon Sep 17 00:00:00 2001 From: findolor Date: Wed, 16 Oct 2024 09:46:38 +0300 Subject: [PATCH 07/11] refactor: make changes --- crates/common/src/unit_tests/mod.rs | 37 ++++++++++++----------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/crates/common/src/unit_tests/mod.rs b/crates/common/src/unit_tests/mod.rs index 769b5818d..858956d76 100644 --- a/crates/common/src/unit_tests/mod.rs +++ b/crates/common/src/unit_tests/mod.rs @@ -1,4 +1,4 @@ -use alloy::primitives::{Address, U256}; +use alloy::primitives::U256; use alloy_ethers_typecast::transaction::{ReadableClientError, ReadableClientHttp}; use dotrain::{error::ComposeError, RainDocument, Rebind}; use futures::TryFutureExt; @@ -14,11 +14,10 @@ use rain_interpreter_eval::{ trace::{RainEvalResultError, RainEvalResults}, }; use rain_orderbook_app_settings::{ - blocks::BlockError, config::*, deployer::Deployer, network::Network, unit_test::TestConfig, + blocks::BlockError, config::*, deployer::Deployer, unit_test::TestConfig, }; use std::sync::Arc; use thiserror::Error; -use url::Url; #[derive(Clone)] pub struct TestRunner { @@ -87,18 +86,7 @@ impl TestRunner { rng: TestRng::from_seed(RngAlgorithm::ChaCha, &seed.unwrap_or([0; 32])), test_setup: TestSetup { block_number: 0, - deployer: Arc::new(Deployer { - address: Address::default(), - network: Arc::new(Network { - name: String::from("").clone(), - rpc: Url::parse("http://rpc.com").unwrap(), - chain_id: 1, - label: None, - network_id: None, - currency: None, - }), - label: None, - }), + deployer: Arc::new(Deployer::dummy()), scenario_name: String::new(), }, } @@ -179,7 +167,7 @@ impl TestRunner { }); let result: RainEvalResults = vec![handle.await??.into()].into(); - let flattened = result.into_flattened_table().unwrap(); + let flattened = result.into_flattened_table()?; Ok(flattened.rows[0].clone()) } @@ -190,7 +178,6 @@ impl TestRunner { let input_token = pre_stack[0]; let output_token = pre_stack[1]; let output_cap = pre_stack[2]; - // let block_number = pre_stack[3]; let final_bindings = self.get_final_bindings(false); @@ -328,7 +315,9 @@ impl TestRunner { .main_config .deployers .get(&self.settings.test_config.scenario_name) - .unwrap() + .ok_or(TestRunnerError::ScenarioNotFound( + self.settings.test_config.scenario_name.clone(), + ))? .clone(); // Fetch the latest block number @@ -410,12 +399,16 @@ test: second-binding: 999 --- #pre -input-token: 0x0165878a594ca255338adfa4d48449f69242eb8f, -output-token: 0xa513e6e4b8f2a923d98304ec87f64353c4d5c853, -output-cap: 10, -block-number: 100; +input-token: 0x01, +output-token: 0x02, +output-cap: 10; #post +/* calculate io stack */ :ensure(equal-to(context<0 0>() 999) "io ratio should be 999"), +:ensure(equal-to(context<0 1>() 10) "max output should be 10"), +:ensure(equal-to(context<0 2>() 0x02) "output token should be 0x02"), +:ensure(equal-to(context<0 3>() 0x01) "input token should be 0x01"), +/* handle io stack */ :ensure(equal-to(context<1 0>() 10) "output cap should be 10"); "#, orderbook_subparser = local_evm.orderbook_subparser.address() From 733273c24981720c9806a5c5669d28426710d742 Mon Sep 17 00:00:00 2001 From: findolor Date: Wed, 16 Oct 2024 16:54:32 +0300 Subject: [PATCH 08/11] fix: fix context bug for output vault decrease --- crates/common/src/unit_tests/mod.rs | 37 ++++++++++++++--------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/crates/common/src/unit_tests/mod.rs b/crates/common/src/unit_tests/mod.rs index 858956d76..64f6e9706 100644 --- a/crates/common/src/unit_tests/mod.rs +++ b/crates/common/src/unit_tests/mod.rs @@ -137,7 +137,7 @@ impl TestRunner { final_bindings } - async fn run_pre_entrypoint(&mut self) -> Result, TestRunnerError> { + async fn run_pre_entrypoint(&mut self) -> Result { let final_bindings = self.get_final_bindings(true); let dotrain = Arc::new(self.dotrains.test_dotrain.clone()); @@ -166,18 +166,16 @@ impl TestRunner { .await }); - let result: RainEvalResults = vec![handle.await??.into()].into(); - let flattened = result.into_flattened_table()?; - Ok(flattened.rows[0].clone()) + Ok(vec![handle.await??.into()].into()) } async fn run_calculate_entrypoint( &mut self, - pre_stack: Vec, + pre_stack: RainEvalResults, ) -> Result { - let input_token = pre_stack[0]; - let output_token = pre_stack[1]; - let output_cap = pre_stack[2]; + let input_token = pre_stack.results[0].stack[2]; + let output_token = pre_stack.results[0].stack[1]; + let output_cap = pre_stack.results[0].stack[0]; let final_bindings = self.get_final_bindings(false); @@ -219,16 +217,17 @@ impl TestRunner { .await }); - let result: RainEvalResults = vec![handle.await??.into()].into(); - Ok(result) + Ok(vec![handle.await??.into()].into()) } async fn run_handle_entrypoint( &mut self, + pre_stack: RainEvalResults, calculate_stack: RainEvalResults, ) -> Result { - let _io_ratio = calculate_stack.results[0].stack[0]; + let output_cap = pre_stack.results[0].stack[0]; let max_output = calculate_stack.results[0].stack[1]; + let _io_ratio = calculate_stack.results[0].stack[0]; let final_bindings = self.get_final_bindings(false); @@ -247,7 +246,7 @@ impl TestRunner { let mut context = vec![vec![U256::from(0); 5]; 5]; // output vault decrease - context[4][4] = max_output; + context[4][4] = U256::min(max_output, output_cap); let args = ForkEvalArgs { rainlang_string, @@ -263,8 +262,7 @@ impl TestRunner { .await }); - let result: RainEvalResults = vec![handle.await??.into()].into(); - Ok(result) + Ok(vec![handle.await??.into()].into()) } async fn run_post_entrypoint( @@ -305,8 +303,7 @@ impl TestRunner { .await }); - let result: RainEvalResults = vec![handle.await??.into()].into(); - Ok(result) + Ok(vec![handle.await??.into()].into()) } pub async fn run_unit_test(&mut self) -> Result { @@ -347,9 +344,11 @@ impl TestRunner { ) .await?; - let pre_stack: Vec = self.run_pre_entrypoint().await?; - let calculate_stack = self.run_calculate_entrypoint(pre_stack).await?; - let handle_stack = self.run_handle_entrypoint(calculate_stack.clone()).await?; + let pre_stack = self.run_pre_entrypoint().await?; + let calculate_stack = self.run_calculate_entrypoint(pre_stack.clone()).await?; + let handle_stack = self + .run_handle_entrypoint(pre_stack, calculate_stack.clone()) + .await?; let results = self .run_post_entrypoint(calculate_stack, handle_stack) .await?; From 6207dec6731aecd8d5aa16b30e8bdd8ee9c1cc8a Mon Sep 17 00:00:00 2001 From: findolor Date: Fri, 18 Oct 2024 09:47:56 +0300 Subject: [PATCH 09/11] refactor: use dummy deployer --- crates/settings/src/unit_test.rs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/crates/settings/src/unit_test.rs b/crates/settings/src/unit_test.rs index 79518aa57..a8fbbb682 100644 --- a/crates/settings/src/unit_test.rs +++ b/crates/settings/src/unit_test.rs @@ -1,10 +1,8 @@ use crate::*; -use alloy::primitives::Address; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::sync::Arc; use typeshare::typeshare; -use url::Url; #[typeshare] #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] @@ -48,18 +46,7 @@ impl TestConfigSource { bindings: bindings.clone(), runs: self.scenario.runs, blocks: self.scenario.blocks.clone(), - deployer: Arc::new(Deployer { - address: Address::default(), - network: Arc::new(Network { - name: String::from("").clone(), - rpc: Url::parse("http://rpc.com").unwrap(), - chain_id: 1, - label: None, - network_id: None, - currency: None, - }), - label: None, - }), + deployer: Arc::new(Deployer::dummy()), }); let config = TestConfig { From 1b2149f30a5e97f49b70909627d1134462f4547e Mon Sep 17 00:00:00 2001 From: findolor Date: Fri, 18 Oct 2024 09:49:16 +0300 Subject: [PATCH 10/11] feat: use ob subparser in the tests --- crates/common/src/unit_tests/mod.rs | 70 ++++++++++++++++++----------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/crates/common/src/unit_tests/mod.rs b/crates/common/src/unit_tests/mod.rs index 64f6e9706..2767561a3 100644 --- a/crates/common/src/unit_tests/mod.rs +++ b/crates/common/src/unit_tests/mod.rs @@ -94,23 +94,22 @@ impl TestRunner { fn get_final_bindings(&mut self, is_test_dotrain: bool) -> Vec { let dotrain: String; - let mut scenario_bindings: Vec = vec![]; + let scenario_bindings: Vec = self + .settings + .test_config + .scenario + .bindings + .clone() + .into_iter() + .map(|(k, v)| Rebind(k, v)) + .collect(); let mut final_bindings: Vec = vec![]; if is_test_dotrain { dotrain = self.dotrains.test_dotrain.clone(); } else { dotrain = self.dotrains.main_dotrain.clone(); - scenario_bindings = self - .settings - .test_config - .scenario - .bindings - .clone() - .into_iter() - .map(|(k, v)| Rebind(k, v)) - .collect() - } + }; let rain_document = RainDocument::create(dotrain, None, None, Some(scenario_bindings.clone())); @@ -267,9 +266,15 @@ impl TestRunner { async fn run_post_entrypoint( &mut self, + pre_stack: RainEvalResults, calculate_stack: RainEvalResults, - handle_stack: RainEvalResults, ) -> Result { + let input_token = pre_stack.results[0].stack[2]; + let output_token = pre_stack.results[0].stack[1]; + let output_cap = pre_stack.results[0].stack[0]; + let max_output = calculate_stack.results[0].stack[1]; + let io_ratio = calculate_stack.results[0].stack[0]; + let final_bindings = self.get_final_bindings(true); let dotrain = Arc::new(self.dotrains.test_dotrain.clone()); @@ -284,10 +289,18 @@ impl TestRunner { let rainlang_string = RainDocument::compose_text(&dotrain, &["post"], None, Some(final_bindings))?; - let context = vec![ - calculate_stack.results[0].stack.clone(), - handle_stack.results[0].stack.clone(), - ]; + let mut context = vec![vec![U256::from(0); 20]; 20]; + + // input token + context[3][0] = input_token; + // output token + context[4][0] = output_token; + // max output + context[2][0] = max_output; + // io ratio + context[2][1] = io_ratio; + // output vault decrease + context[4][4] = U256::min(max_output, output_cap); let args = ForkEvalArgs { rainlang_string, @@ -346,12 +359,10 @@ impl TestRunner { let pre_stack = self.run_pre_entrypoint().await?; let calculate_stack = self.run_calculate_entrypoint(pre_stack.clone()).await?; - let handle_stack = self - .run_handle_entrypoint(pre_stack, calculate_stack.clone()) - .await?; - let results = self - .run_post_entrypoint(calculate_stack, handle_stack) + let _handle_stack = self + .run_handle_entrypoint(pre_stack.clone(), calculate_stack.clone()) .await?; + let results = self.run_post_entrypoint(pre_stack, calculate_stack).await?; Ok(results) } } @@ -397,18 +408,25 @@ test: orderbook-subparser: {orderbook_subparser} second-binding: 999 --- +#orderbook-subparser ! +#second-binding ! + #pre input-token: 0x01, output-token: 0x02, output-cap: 10; + #post +using-words-from orderbook-subparser + /* calculate io stack */ -:ensure(equal-to(context<0 0>() 999) "io ratio should be 999"), -:ensure(equal-to(context<0 1>() 10) "max output should be 10"), -:ensure(equal-to(context<0 2>() 0x02) "output token should be 0x02"), -:ensure(equal-to(context<0 3>() 0x01) "input token should be 0x01"), +:ensure(equal-to(calculated-io-ratio() 999) "io ratio should be 999"), +:ensure(equal-to(calculated-max-output() 10) "max output should be 10"), +:ensure(equal-to(output-token() 0x02) "output token should be 0x02"), +:ensure(equal-to(input-token() 0x01) "input token should be 0x01"), + /* handle io stack */ -:ensure(equal-to(context<1 0>() 10) "output cap should be 10"); +:ensure(equal-to(output-vault-decrease() 10) "output cap should be 10"); "#, orderbook_subparser = local_evm.orderbook_subparser.address() ); From 5aede3d26bc1b74e3b170df7656830e3016ec8f2 Mon Sep 17 00:00:00 2001 From: findolor Date: Mon, 21 Oct 2024 12:57:05 +0300 Subject: [PATCH 11/11] refactor: update for CI --- crates/common/src/unit_tests/mod.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/common/src/unit_tests/mod.rs b/crates/common/src/unit_tests/mod.rs index 2767561a3..7f228cc8a 100644 --- a/crates/common/src/unit_tests/mod.rs +++ b/crates/common/src/unit_tests/mod.rs @@ -93,7 +93,6 @@ impl TestRunner { } fn get_final_bindings(&mut self, is_test_dotrain: bool) -> Vec { - let dotrain: String; let scenario_bindings: Vec = self .settings .test_config @@ -105,10 +104,10 @@ impl TestRunner { .collect(); let mut final_bindings: Vec = vec![]; - if is_test_dotrain { - dotrain = self.dotrains.test_dotrain.clone(); + let dotrain = if is_test_dotrain { + self.dotrains.test_dotrain.clone() } else { - dotrain = self.dotrains.main_dotrain.clone(); + self.dotrains.main_dotrain.clone() }; let rain_document =