From 0e6d4345d5a59b76cf288b3009db4cb407b82f30 Mon Sep 17 00:00:00 2001 From: Trantorian Date: Fri, 12 Jan 2024 14:02:56 +0000 Subject: [PATCH] feat(macros): :sparkles: Added spec_version to `#[require]` macro You can now specify the rpc node spec version required to run tests. This is in addition to the existing `block_min` and `block_max` requirements. A spec version of "0.5.1" has been applied to all tests done so far. --- README.md | 48 +------------------ macro/src/lib.rs | 41 ++++++++++++---- macro_utils/src/lib.rs | 38 +++++++++++---- unit_tests/src/constants.rs | 3 ++ .../tests/test_block_hash_and_number.rs | 2 + unit_tests/tests/test_block_number.rs | 1 + unit_tests/tests/test_call.rs | 9 ++++ unit_tests/tests/test_chain_id.rs | 1 + unit_tests/tests/test_estimate_message_fee.rs | 4 ++ .../tests/test_get_block_transaction_count.rs | 6 +++ .../tests/test_get_block_with_tx_hashes.rs | 4 ++ unit_tests/tests/test_get_block_with_txs.rs | 6 +++ unit_tests/tests/test_get_class_at.rs | 5 +- unit_tests/tests/test_get_class_hash_at.rs | 4 ++ unit_tests/tests/test_get_events.rs | 10 +++- unit_tests/tests/test_get_nonce.rs | 10 ++-- unit_tests/tests/test_get_state_update.rs | 2 + unit_tests/tests/test_get_storage_at.rs | 4 ++ ...t_get_transaction_by_block_id_and_index.rs | 6 +++ .../tests/test_get_transaction_by_hash.rs | 5 ++ .../tests/test_get_transaction_receipt.rs | 5 ++ .../tests/test_get_transaction_status.rs | 4 ++ unit_tests/tests/test_syncing.rs | 1 + 23 files changed, 148 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index a96942a..d127a16 100644 --- a/README.md +++ b/README.md @@ -79,50 +79,4 @@ async fn work_existing_block(clients: HashMap ⚠️ Structure members must have the **exact same name** as the json fields expected as an RPC call result. - -- For fields which are themselves a JSON object, use another struct to represent this sub-object. -- For fields that might be optional, use `Option`. - -Try and test as many edge cases as possible. Ths mostly includes optional parameters. You can find a list -of Starknet RPC call and their arguments / return data [here](https://playground.open-rpc.org/?uiSchema%5BappBar%5D%5Bui:splitView%5D=false&schemaUrl=https://raw.githubusercontent.com/starkware-libs/starknet-specs/master/api/starknet_api_openrpc.json&uiSchema%5BappBar%5D%5Bui:input%5D=false&uiSchema%5BappBar%5D%5Bui:darkMode%5D=true&uiSchema%5BappBar%5D%5Bui:examplesDropdown%5D=false) -======= -``` ->>>>>>> Stashed changes +} \ No newline at end of file diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 2c1f661..e8ed70f 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -1,4 +1,4 @@ -use macro_utils::{extract_u64_from_expr, get_block_number}; +use macro_utils::{extract_expr_to_str, extract_expr_to_u64, get_rpc_data, RpcData}; use proc_macro::TokenStream; use quote::{quote, ToTokens}; use syn::{ @@ -24,19 +24,21 @@ pub fn logging(_: TokenStream, input: TokenStream) -> TokenStream { input.into_token_stream().into() } -struct ArgsRequire { +struct MacroDataRequire { pub block_min: u64, pub block_max: u64, + pub spec_version: Option, pub err: Result<(), Path>, } -impl Parse for ArgsRequire { +impl Parse for MacroDataRequire { fn parse(input: ParseStream) -> syn::Result { let args = input.parse_terminated(MetaNameValue::parse, Token![,])?; let mut parsed_params = Self { block_min: 0, - block_max: 0, + block_max: u64::MAX, + spec_version: None, err: Ok(()), }; @@ -44,11 +46,17 @@ impl Parse for ArgsRequire { match arg.path.get_ident() { Some(ident) => match ident.to_string().as_str() { "block_min" => { - parsed_params.block_min = extract_u64_from_expr(arg.value).unwrap_or(0); + parsed_params.block_min = extract_expr_to_u64(arg.value).unwrap_or(0); } "block_max" => { parsed_params.block_max = - extract_u64_from_expr(arg.value).unwrap_or(u64::MAX); + extract_expr_to_u64(arg.value).unwrap_or(u64::MAX); + } + "spec_version" => { + parsed_params.spec_version = match extract_expr_to_str(arg.value) { + Ok(s) => Some(s), + Err(_) => None, + } } _ => { parsed_params.err = Err(arg.path); @@ -62,12 +70,27 @@ impl Parse for ArgsRequire { } } +impl MacroDataRequire { + fn should_ignore(self, data: RpcData) -> bool { + let Self { + block_min, + block_max, + spec_version, + err: _, + } = self; + + (data.block_number >= block_min) + && (data.block_number <= block_max) + && (data.spec_version == spec_version.unwrap_or(String::from(""))) + } +} + #[proc_macro_attribute] pub fn require(args: TokenStream, item: TokenStream) -> TokenStream { - let bn = get_block_number(); - let args_parsed = parse_macro_input!(args as ArgsRequire); + let block_data = get_rpc_data(); + let macro_data = parse_macro_input!(args as MacroDataRequire); - if bn >= args_parsed.block_min && bn <= args_parsed.block_max { + if macro_data.should_ignore(block_data) { item } else { let mut func = parse_macro_input!(item as ItemFn); diff --git a/macro_utils/src/lib.rs b/macro_utils/src/lib.rs index da75061..6011160 100644 --- a/macro_utils/src/lib.rs +++ b/macro_utils/src/lib.rs @@ -1,3 +1,4 @@ +use anyhow::anyhow; use serde::Deserialize; use starknet_providers::{jsonrpc::HttpTransport, JsonRpcClient, Provider}; use std::{fs::File, io::Read}; @@ -25,7 +26,12 @@ impl TestConfig { } } -pub fn get_block_number() -> u64 { +pub struct RpcData { + pub block_number: u64, + pub spec_version: String, +} + +pub fn get_rpc_data() -> RpcData { let config = TestConfig::new("./secret.json").expect("'./secret.json' must contain correct node urls"); let deoxys = JsonRpcClient::new(HttpTransport::new( @@ -34,17 +40,33 @@ pub fn get_block_number() -> u64 { let rt = runtime::Runtime::new().unwrap(); - rt.block_on(async { deoxys.block_number().await.unwrap() }) + rt.block_on(async { + RpcData { + block_number: deoxys.block_number().await.unwrap(), + spec_version: deoxys.spec_version().await.unwrap(), + } + }) +} + +pub fn extract_expr_to_str(expr: Expr) -> anyhow::Result { + match expr { + Expr::Lit(expr_lit) => match expr_lit.lit { + Lit::Str(lit_str) => anyhow::Ok(lit_str.value()), + _ => Err(anyhow!("Not a string literal")), + }, + _ => Err(anyhow!("Not a literal expression")), + } } -pub fn extract_u64_from_expr(expr: Expr) -> Result { +pub fn extract_expr_to_u64(expr: Expr) -> anyhow::Result { match expr { Expr::Lit(expr_lit) => match expr_lit.lit { - Lit::Int(lit_int) => lit_int - .base10_parse::() - .map_err(|_| "Failed to parse integer".to_string()), - _ => Err("Not an integer literal".to_string()), + Lit::Int(lit_int) => match lit_int.base10_parse::() { + Ok(n) => anyhow::Ok(n), + Err(_) => Err(anyhow!("Failed to convert literal")), + }, + _ => Err(anyhow!("Not an integer literal")), }, - _ => Err("Not a literal expression".to_string()), + _ => Err(anyhow!("Not a literal expression")), } } diff --git a/unit_tests/src/constants.rs b/unit_tests/src/constants.rs index e8efaec..fde2d97 100644 --- a/unit_tests/src/constants.rs +++ b/unit_tests/src/constants.rs @@ -144,3 +144,6 @@ pub const SELECTOR_NAME: &str = ""; pub const ERR_DEOXYS: &str = "Error waiting for response from Deoxys client"; pub const ERR_PATHFINDER: &str = "Error waiting for response from Pathfinder client"; + +pub const SPEC_0_5_1: &str = "0.5.1"; +pub const SPEC_0_6_0: &str = "0.6.0"; diff --git a/unit_tests/tests/test_block_hash_and_number.rs b/unit_tests/tests/test_block_hash_and_number.rs index 90153f1..3cbcc0f 100644 --- a/unit_tests/tests/test_block_hash_and_number.rs +++ b/unit_tests/tests/test_block_hash_and_number.rs @@ -1,5 +1,6 @@ mod common; use common::*; +use r#macro::require; use std::collections::HashMap; @@ -14,6 +15,7 @@ use starknet_providers::{ /// purpose: get block hash and number on latest block. /// success case: retrieves correct block hash and number. /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn work_latest_block(clients: HashMap>) { diff --git a/unit_tests/tests/test_block_number.rs b/unit_tests/tests/test_block_number.rs index c7721d7..f545ffb 100644 --- a/unit_tests/tests/test_block_number.rs +++ b/unit_tests/tests/test_block_number.rs @@ -14,6 +14,7 @@ use starknet_providers::{ /// purpose: call blockNumber on latest block. /// success case: must return valid non-zero block number. /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn work_existing_block(clients: HashMap>) { diff --git a/unit_tests/tests/test_call.rs b/unit_tests/tests/test_call.rs index 832ce3e..eb36950 100644 --- a/unit_tests/tests/test_call.rs +++ b/unit_tests/tests/test_call.rs @@ -20,6 +20,7 @@ use starknet_providers::{ /// purpose: function request `name` to StarkGate ETH bridge contract /// fail case: invalid block /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn fail_non_existing_block(clients: HashMap>) { @@ -52,6 +53,7 @@ async fn fail_non_existing_block(clients: HashMap>) { @@ -84,6 +86,7 @@ async fn fail_non_existing_contract(clients: HashMap>) { @@ -150,6 +154,7 @@ async fn fail_missing_contract_call_data(clients: HashMap>) { @@ -179,6 +184,7 @@ async fn fail_invalid_contract_call_data(clients: HashMap>) { @@ -211,6 +217,7 @@ async fn fail_too_many_call_data(clients: HashMap>) { @@ -253,6 +260,7 @@ async fn work_correct_call(clients: HashMap /// purpose: function request `balanceOf` to StarkGate ETH bridge contract /// success case: must return non-zero balance /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn work_correct_call_with_args(clients: HashMap>) { @@ -295,6 +303,7 @@ async fn work_correct_call_with_args(clients: HashMap>) { diff --git a/unit_tests/tests/test_chain_id.rs b/unit_tests/tests/test_chain_id.rs index 325e66f..b359365 100644 --- a/unit_tests/tests/test_chain_id.rs +++ b/unit_tests/tests/test_chain_id.rs @@ -13,6 +13,7 @@ use std::collections::HashMap; /// purpose: get currently configured Starknet chain id /// success case: retrieve correct chain id /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn chain_id(clients: HashMap>) { diff --git a/unit_tests/tests/test_estimate_message_fee.rs b/unit_tests/tests/test_estimate_message_fee.rs index b2c61ac..a03f86b 100644 --- a/unit_tests/tests/test_estimate_message_fee.rs +++ b/unit_tests/tests/test_estimate_message_fee.rs @@ -40,6 +40,7 @@ pub fn get_message_from_l1( } } +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] #[ignore = "Need to fix unwrap on error due to empty constants"] @@ -64,6 +65,7 @@ async fn fail_non_existing_block(clients: HashMap>) { @@ -32,6 +33,7 @@ async fn fail_non_existing_block(clients: HashMap>) { @@ -53,6 +55,7 @@ async fn work_with_latest_block(clients: HashMap>) { @@ -74,6 +77,7 @@ async fn work_with_block_one_num(clients: HashMap>) { @@ -100,6 +104,7 @@ async fn work_with_block_one_hash(clients: HashMap>) { @@ -44,6 +45,7 @@ async fn fail_non_existing_block(clients: HashMap>) { @@ -74,6 +76,7 @@ async fn work_existing_block(clients: HashMap>) { @@ -32,6 +33,7 @@ async fn fail_non_existing_block(clients: HashMap>) { @@ -53,6 +55,7 @@ async fn work_with_latest_block(clients: HashMap>) { @@ -74,6 +77,7 @@ async fn work_with_block_one_num(clients: HashMap>) { @@ -100,6 +104,7 @@ async fn work_with_block_one_hash(clients: HashMap>) { @@ -47,6 +48,7 @@ async fn fail_non_existing_block(clients: HashMap>) { @@ -72,6 +74,7 @@ async fn fail_non_existing_contract(clients: HashMap>) { @@ -45,6 +46,7 @@ async fn fail_non_existing_block(clients: HashMap>) { @@ -73,6 +75,7 @@ async fn fail_non_existing_contract(clients: HashMap>) { @@ -103,6 +106,7 @@ async fn work_block_latest(clients: HashMap /// purpose: call getClassHashAt on pending block. /// success case: retrieve valid class hash. /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn work_block_pending(clients: HashMap>) { diff --git a/unit_tests/tests/test_get_events.rs b/unit_tests/tests/test_get_events.rs index d72bd6c..0015fec 100644 --- a/unit_tests/tests/test_get_events.rs +++ b/unit_tests/tests/test_get_events.rs @@ -34,6 +34,7 @@ use tokio::task::JoinSet; /// purpose: call getEvents on an invalid block number. /// fail case: invalid block number (invalid param). /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] #[logging] @@ -61,6 +62,7 @@ async fn fail_invalid_block_number(deoxys: JsonRpcClient) { /// purpose: call getEvents on an invalid event selector. /// fail case: invalid event selector. /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] #[logging] @@ -86,6 +88,7 @@ async fn fail_invalid_keys(deoxys: JsonRpcClient) { /// purpose: call getEvents on an invalid event selector. /// fail case: invalid event selector. /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] #[logging] @@ -114,6 +117,7 @@ async fn fail_invalid_block_range(deoxys: JsonRpcClient) { /// purpose: call getEvents on a valid block with a no selector. /// success case: retrieves the first 100 events of that block. /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] #[logging] @@ -149,6 +153,7 @@ async fn work_valid_call_no_selector( /// purpose: call getEvents on a valid block with a single selector. /// success case: valid events format, events point to valid transactions. /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] #[logging] @@ -185,6 +190,7 @@ async fn work_valid_call_single_selector( /// success case: retrieves all events matching the selector in the first 100 events of that block /// + valid event format and valid transactions. /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] #[logging] @@ -218,7 +224,7 @@ async fn work_valid_call_multiple_selector( async fn get_events( client: &JsonRpcClient, - keys: &Vec>, + keys: &[Vec], block_nu: u64, block_range: u64, ) -> Result { @@ -233,7 +239,7 @@ async fn get_events( address: None, // and keys used to filter out events. Keys can include a hash of the event // and even event return values for further filtering - keys: Some(keys.clone()), + keys: Some(keys.to_vec()), }, // in cases were a first search does not yield enough results, a continuation key // can be used to keep searching from the point of the last getEvent search diff --git a/unit_tests/tests/test_get_nonce.rs b/unit_tests/tests/test_get_nonce.rs index 1fe33f8..797ae93 100644 --- a/unit_tests/tests/test_get_nonce.rs +++ b/unit_tests/tests/test_get_nonce.rs @@ -32,6 +32,7 @@ use starknet_providers::{ /// purpose: call getNonce on invalid block. /// fail case: invalid block. /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn fail_non_existing_block(clients: HashMap>) { @@ -60,6 +61,7 @@ async fn fail_non_existing_block(clients: HashMap>) { @@ -91,7 +93,7 @@ async fn fail_non_existing_contract(clients: HashMap>) { @@ -114,7 +116,7 @@ async fn work_erc721_contract(clients: HashMap>) { @@ -137,7 +139,7 @@ async fn work_erc20_contract(clients: HashMap>) { @@ -170,7 +172,7 @@ async fn work_account_contract(clients: HashMap>) { diff --git a/unit_tests/tests/test_get_state_update.rs b/unit_tests/tests/test_get_state_update.rs index 37abb05..8e37965 100644 --- a/unit_tests/tests/test_get_state_update.rs +++ b/unit_tests/tests/test_get_state_update.rs @@ -23,6 +23,7 @@ use std::collections::HashMap; // # Errors // * `block_not_found` - If the block is not found or invalid +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] #[ignore = "Need to fix unwrap on error due to empty constants"] @@ -37,6 +38,7 @@ async fn fail_non_existing_block(clients: HashMap>) { @@ -47,6 +48,7 @@ async fn fail_non_existing_block(clients: HashMap>) { @@ -76,6 +78,7 @@ async fn fail_non_existing_contract(clients: HashMap>) { @@ -99,6 +102,7 @@ async fn fail_invalid_storage_key(clients: HashMap>) { diff --git a/unit_tests/tests/test_get_transaction_by_block_id_and_index.rs b/unit_tests/tests/test_get_transaction_by_block_id_and_index.rs index c207583..3580ca9 100644 --- a/unit_tests/tests/test_get_transaction_by_block_id_and_index.rs +++ b/unit_tests/tests/test_get_transaction_by_block_id_and_index.rs @@ -16,6 +16,7 @@ use starknet_providers::{ /// purpose: call on non-existent block. /// fail case: invalid block /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn fail_non_existent_block(clients: HashMap>) { @@ -41,6 +42,7 @@ async fn fail_non_existent_block(clients: HashMap>) { @@ -66,6 +68,7 @@ async fn fail_non_existent_block_index(clients: HashMap>) { @@ -105,6 +108,7 @@ async fn work_deploy_invoke(clients: HashMap>) { @@ -144,6 +148,7 @@ async fn work_deploy_l1_handler(clients: HashMap>) { @@ -183,6 +188,7 @@ async fn work_deploy_declare(clients: HashMap>) { diff --git a/unit_tests/tests/test_get_transaction_by_hash.rs b/unit_tests/tests/test_get_transaction_by_hash.rs index 699d196..c878cbb 100644 --- a/unit_tests/tests/test_get_transaction_by_hash.rs +++ b/unit_tests/tests/test_get_transaction_by_hash.rs @@ -16,6 +16,7 @@ use starknet_providers::{ /// purpose: call getTransactionHash on non existent transaction. /// fail case: transaction does not exist. /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn fail_non_existing_transaction(clients: HashMap>) { @@ -41,6 +42,7 @@ async fn fail_non_existing_transaction(clients: HashMap>) { @@ -67,6 +69,7 @@ async fn work_transaction_invoke(clients: HashMap>) { @@ -93,6 +96,7 @@ async fn work_transaction_l1_handler(clients: HashMap>) { @@ -121,6 +125,7 @@ async fn work_transaction_declare(clients: HashMap>) { diff --git a/unit_tests/tests/test_get_transaction_receipt.rs b/unit_tests/tests/test_get_transaction_receipt.rs index 9909698..de88c87 100644 --- a/unit_tests/tests/test_get_transaction_receipt.rs +++ b/unit_tests/tests/test_get_transaction_receipt.rs @@ -12,6 +12,7 @@ use starknet_providers::{ }; // invalid transaction_hash +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn fail_invalid_transaction_hash(clients: HashMap>) { @@ -32,6 +33,7 @@ async fn fail_invalid_transaction_hash(clients: HashMap>) { @@ -57,6 +59,7 @@ async fn work_with_rejected_transaction(clients: HashMap>) { @@ -82,6 +85,7 @@ async fn work_with_first_transaction(clients: HashMap>) { @@ -107,6 +111,7 @@ async fn work_with_deploy(clients: HashMap> } ///invoke transaction +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn work_with_invoke(clients: HashMap>) { diff --git a/unit_tests/tests/test_get_transaction_status.rs b/unit_tests/tests/test_get_transaction_status.rs index 8f74bb4..42ac808 100644 --- a/unit_tests/tests/test_get_transaction_status.rs +++ b/unit_tests/tests/test_get_transaction_status.rs @@ -18,6 +18,7 @@ use starknet_providers::{ /// purpose: call getTransactionStatus on non-existent transaction hash. /// fail case: non-existent transaction hash. /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn fail_invalid_transaction(clients: HashMap>) { @@ -43,6 +44,7 @@ async fn fail_invalid_transaction(clients: HashMap>) { @@ -69,6 +71,7 @@ async fn work_transaction_accepted_on_l1(clients: HashMap>) { @@ -110,6 +113,7 @@ async fn work_transaction_accepted_on_l2(clients: HashMap>) { diff --git a/unit_tests/tests/test_syncing.rs b/unit_tests/tests/test_syncing.rs index 9d51229..aa53f6d 100644 --- a/unit_tests/tests/test_syncing.rs +++ b/unit_tests/tests/test_syncing.rs @@ -16,6 +16,7 @@ use std::collections::HashMap; /// purpose: returns starknet sync status /// success case: sync status matches between providers (NOT DETERMINISTIC) /// +#[require(spec_version = "0.5.1")] #[rstest] #[tokio::test] async fn syncing(clients: HashMap>) {