diff --git a/Cargo.lock b/Cargo.lock index 408c6de..6fd8d94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -725,6 +725,20 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "der" version = "0.7.9" @@ -1552,6 +1566,7 @@ dependencies = [ "cynic", "cynic-codegen", "cynic-querygen", + "dashmap", "derive_more", "dotenv", "envy", diff --git a/Cargo.toml b/Cargo.toml index cc18fd3..06a58b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ clap = { version = "4.5.11", features = ["derive", "env"] } coinbase-mesh = "0.1.0" convert_case = "0.6.0" cynic = { version = "3.7.3", features = ["http-reqwest-blocking"] } +dashmap = "6.1.0" derive_more = { version = "1.0.0", features = ["full"] } dotenv = "0.15.0" envy = "0.4.2" diff --git a/src/api.rs b/src/api.rs index d11dbb3..c6c28ad 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,5 +1,6 @@ mod account_balance; mod block; +mod cache; mod call; mod construction_combine; mod construction_derive; @@ -11,8 +12,8 @@ mod construction_preprocess; mod construction_submit; mod mempool; mod mempool_transaction; -mod network_health_check; mod network_list; mod network_options; mod network_status; mod search_transactions; +mod validate_network; diff --git a/src/api/account_balance.rs b/src/api/account_balance.rs index c5e392f..dfdb628 100644 --- a/src/api/account_balance.rs +++ b/src/api/account_balance.rs @@ -12,12 +12,10 @@ use crate::{ /// https://github.com/MinaProtocol/mina/blob/985eda49bdfabc046ef9001d3c406e688bc7ec45/src/app/rosetta/lib/account.ml#L11 impl MinaMesh { - pub async fn account_balance( - &self, - AccountBalanceRequest { account_identifier, block_identifier: maybe_block_identifier, .. }: AccountBalanceRequest, - ) -> Result { - let AccountIdentifier { address, metadata, .. } = *account_identifier; - match maybe_block_identifier { + pub async fn account_balance(&self, req: AccountBalanceRequest) -> Result { + self.validate_network(&req.network_identifier).await?; + let AccountIdentifier { address, metadata, .. } = *req.account_identifier; + match req.block_identifier { Some(block_identifier) => self.block_balance(address, metadata, *block_identifier).await, None => self.frontier_balance(address).await, } diff --git a/src/api/block.rs b/src/api/block.rs index 439da0c..c613012 100644 --- a/src/api/block.rs +++ b/src/api/block.rs @@ -14,6 +14,7 @@ use crate::{ /// https://github.com/MinaProtocol/mina/blob/985eda49bdfabc046ef9001d3c406e688bc7ec45/src/app/rosetta/lib/block.ml#L7 impl MinaMesh { pub async fn block(&self, request: BlockRequest) -> Result { + self.validate_network(&request.network_identifier).await?; let partial_block_identifier = *request.block_identifier; let metadata = match self.block_metadata(&partial_block_identifier).await? { Some(metadata) => metadata, diff --git a/src/api/cache.rs b/src/api/cache.rs new file mode 100644 index 0000000..7185b58 --- /dev/null +++ b/src/api/cache.rs @@ -0,0 +1,20 @@ +use std::time::Instant; + +use crate::{CacheKey, MinaMesh}; + +impl MinaMesh { + /// Checks the cache for a valid entry. + pub fn get_from_cache(&self, key: CacheKey) -> Option { + if let Some(cached_entry) = self.cache.get(key.to_string().as_str()) { + let (cached_value, timestamp) = &*cached_entry; + if timestamp.elapsed() < self.cache_ttl { + return Some(cached_value.clone()); + } + } + None + } + + pub fn insert_into_cache(&self, key: CacheKey, value: String) { + self.cache.insert(key.to_string(), (value, Instant::now())); + } +} diff --git a/src/api/call.rs b/src/api/call.rs index 026acdf..88c3030 100644 --- a/src/api/call.rs +++ b/src/api/call.rs @@ -5,7 +5,8 @@ use crate::MinaMesh; /// https://github.com/MinaProtocol/mina/blob/985eda49bdfabc046ef9001d3c406e688bc7ec45/src/app/rosetta/lib/construction.ml#L849 impl MinaMesh { - pub async fn call(&self, _request: CallRequest) -> Result { + pub async fn call(&self, request: CallRequest) -> Result { + self.validate_network(&request.network_identifier).await?; Ok(CallResponse::new(serde_json::Value::Null, true)) } } diff --git a/src/api/construction_combine.rs b/src/api/construction_combine.rs index 81b396d..d89311b 100644 --- a/src/api/construction_combine.rs +++ b/src/api/construction_combine.rs @@ -5,10 +5,8 @@ use crate::MinaMesh; /// https://github.com/MinaProtocol/mina/blob/985eda49bdfabc046ef9001d3c406e688bc7ec45/src/app/rosetta/lib/construction.ml#L561 impl MinaMesh { - pub async fn construction_combine( - &self, - _request: ConstructionCombineRequest, - ) -> Result { + pub async fn construction_combine(&self, request: ConstructionCombineRequest) -> Result { + self.validate_network(&request.network_identifier).await?; Ok(ConstructionCombineResponse::new("".to_string())) } } diff --git a/src/api/construction_derive.rs b/src/api/construction_derive.rs index 3fde29f..d85982e 100644 --- a/src/api/construction_derive.rs +++ b/src/api/construction_derive.rs @@ -5,7 +5,8 @@ use crate::MinaMesh; /// https://github.com/MinaProtocol/mina/blob/985eda49bdfabc046ef9001d3c406e688bc7ec45/src/app/rosetta/lib/construction.ml#L162 impl MinaMesh { - pub async fn construction_derive(&self, _request: ConstructionDeriveRequest) -> Result { + pub async fn construction_derive(&self, request: ConstructionDeriveRequest) -> Result { + self.validate_network(&request.network_identifier).await?; Ok(ConstructionDeriveResponse::new()) } } diff --git a/src/api/construction_hash.rs b/src/api/construction_hash.rs index b2f537d..5853ffc 100644 --- a/src/api/construction_hash.rs +++ b/src/api/construction_hash.rs @@ -5,7 +5,8 @@ use crate::MinaMesh; /// https://github.com/MinaProtocol/mina/blob/985eda49bdfabc046ef9001d3c406e688bc7ec45/src/app/rosetta/lib/construction.ml#L786 impl MinaMesh { - pub async fn construction_hash(&self, _request: ConstructionHashRequest) -> Result { + pub async fn construction_hash(&self, request: ConstructionHashRequest) -> Result { + self.validate_network(&request.network_identifier).await?; Ok(TransactionIdentifier::new("".to_string())) } } diff --git a/src/api/construction_metadata.rs b/src/api/construction_metadata.rs index e95171d..b8970bf 100644 --- a/src/api/construction_metadata.rs +++ b/src/api/construction_metadata.rs @@ -7,8 +7,9 @@ use crate::MinaMesh; impl MinaMesh { pub async fn construction_metadata( &self, - _request: ConstructionMetadataRequest, + request: ConstructionMetadataRequest, ) -> Result { + self.validate_network(&request.network_identifier).await?; Ok(ConstructionMetadataResponse::new(serde_json::Value::Null)) } } diff --git a/src/api/construction_parse.rs b/src/api/construction_parse.rs index 8060c41..583663c 100644 --- a/src/api/construction_parse.rs +++ b/src/api/construction_parse.rs @@ -5,7 +5,8 @@ use crate::MinaMesh; /// https://github.com/MinaProtocol/mina/blob/985eda49bdfabc046ef9001d3c406e688bc7ec45/src/app/rosetta/lib/construction.ml#L615 impl MinaMesh { - pub async fn construction_parse(&self, _request: ConstructionParseRequest) -> Result { + pub async fn construction_parse(&self, request: ConstructionParseRequest) -> Result { + self.validate_network(&request.network_identifier).await?; Ok(ConstructionParseResponse::new(vec![])) } } diff --git a/src/api/construction_payloads.rs b/src/api/construction_payloads.rs index 60cea97..289b880 100644 --- a/src/api/construction_payloads.rs +++ b/src/api/construction_payloads.rs @@ -7,8 +7,9 @@ use crate::MinaMesh; impl MinaMesh { pub async fn construction_payloads( &self, - _request: ConstructionPayloadsRequest, + request: ConstructionPayloadsRequest, ) -> Result { + self.validate_network(&request.network_identifier).await?; Ok(ConstructionPayloadsResponse::new("".to_string(), vec![])) } } diff --git a/src/api/construction_preprocess.rs b/src/api/construction_preprocess.rs index 606ef77..b1ba68d 100644 --- a/src/api/construction_preprocess.rs +++ b/src/api/construction_preprocess.rs @@ -7,8 +7,9 @@ use crate::MinaMesh; impl MinaMesh { pub async fn construction_preprocess( &self, - _request: ConstructionPreprocessRequest, + request: ConstructionPreprocessRequest, ) -> Result { + self.validate_network(&request.network_identifier).await?; Ok(ConstructionPreprocessResponse::new()) } } diff --git a/src/api/construction_submit.rs b/src/api/construction_submit.rs index 26ea93c..788149d 100644 --- a/src/api/construction_submit.rs +++ b/src/api/construction_submit.rs @@ -5,7 +5,8 @@ use crate::MinaMesh; /// https://github.com/MinaProtocol/mina/blob/985eda49bdfabc046ef9001d3c406e688bc7ec45/src/app/rosetta/lib/construction.ml#L849 impl MinaMesh { - pub async fn construction_submit(&self, _request: ConstructionSubmitRequest) -> Result { + pub async fn construction_submit(&self, request: ConstructionSubmitRequest) -> Result { + self.validate_network(&request.network_identifier).await?; Ok(TransactionIdentifier::new("".to_string())) } } diff --git a/src/api/mempool.rs b/src/api/mempool.rs index 256c85a..d1a76cd 100644 --- a/src/api/mempool.rs +++ b/src/api/mempool.rs @@ -3,14 +3,15 @@ #![allow(clippy::just_underscores_and_digits)] use anyhow::Result; -use coinbase_mesh::models::{MempoolResponse, TransactionIdentifier}; +use coinbase_mesh::models::{MempoolResponse, NetworkRequest, TransactionIdentifier}; use cynic::QueryBuilder; use crate::{graphql::QueryMempool, MinaMesh}; /// https://github.com/MinaProtocol/mina/blob/985eda49bdfabc046ef9001d3c406e688bc7ec45/src/app/rosetta/lib/mempool.ml#L56 impl MinaMesh { - pub async fn mempool(&self) -> Result { + pub async fn mempool(&self, req: NetworkRequest) -> Result { + self.validate_network(&req.network_identifier).await?; let QueryMempool { daemon_status: _0, initial_peers: _1, pooled_user_commands } = self.graphql_client.send(QueryMempool::build(())).await?; let hashes = pooled_user_commands diff --git a/src/api/mempool_transaction.rs b/src/api/mempool_transaction.rs index f59da2a..8ba7936 100644 --- a/src/api/mempool_transaction.rs +++ b/src/api/mempool_transaction.rs @@ -14,6 +14,7 @@ impl MinaMesh { &self, request: MempoolTransactionRequest, ) -> Result { + self.validate_network(&request.network_identifier).await?; let QueryMempoolTransactions { daemon_status: _daemon_status, initial_peers: _initial_peers, pooled_user_commands } = self .graphql_client diff --git a/src/api/network_health_check.rs b/src/api/network_health_check.rs deleted file mode 100644 index fdf3d88..0000000 --- a/src/api/network_health_check.rs +++ /dev/null @@ -1,18 +0,0 @@ -use anyhow::Result; -use coinbase_mesh::models::NetworkIdentifier; -use cynic::QueryBuilder; - -use crate::{graphql::QueryNetworkId, MinaMesh}; - -impl MinaMesh { - pub async fn network_health_check(self, network_identifier: NetworkIdentifier) -> Result { - let QueryNetworkId { network_id } = self.graphql_client.send(QueryNetworkId::build(())).await?; - if network_identifier.blockchain == "MINA" { - unimplemented!(); - } - if network_identifier.network == network_id { - unimplemented!(); - } - Ok(true) - } -} diff --git a/src/api/network_options.rs b/src/api/network_options.rs index 9199189..cda0ac9 100644 --- a/src/api/network_options.rs +++ b/src/api/network_options.rs @@ -1,13 +1,14 @@ // TODO: double-check the data is correct // TODO: why do long string literals in the error metadata break rustfmt? -use coinbase_mesh::models::{Allow, Case, Error, NetworkOptionsResponse, OperationStatus, Version}; +use coinbase_mesh::models::{Allow, Case, Error, NetworkOptionsResponse, NetworkRequest, OperationStatus, Version}; use crate::{MinaMesh, MinaMeshError}; /// https://github.com/MinaProtocol/mina/blob/985eda49bdfabc046ef9001d3c406e688bc7ec45/src/app/rosetta/lib/network.ml#L444 impl MinaMesh { - pub async fn network_options(&self) -> Result { + pub async fn network_options(&self, req: NetworkRequest) -> Result { + self.validate_network(&req.network_identifier).await?; let errors: Vec = MinaMeshError::all_errors().into_iter().map(Error::from).collect(); Ok(NetworkOptionsResponse::new(Version::new("1.4.9".to_string(), "1.0.0".to_string()), Allow { diff --git a/src/api/network_status.rs b/src/api/network_status.rs index f9f9d2d..8a96462 100644 --- a/src/api/network_status.rs +++ b/src/api/network_status.rs @@ -1,6 +1,6 @@ // TODO: get genesis block identifier from env -use coinbase_mesh::models::{BlockIdentifier, NetworkStatusResponse, Peer}; +use coinbase_mesh::models::{BlockIdentifier, NetworkRequest, NetworkStatusResponse, Peer}; use cynic::QueryBuilder; use crate::{ @@ -10,7 +10,8 @@ use crate::{ /// https://github.com/MinaProtocol/mina/blob/985eda49bdfabc046ef9001d3c406e688bc7ec45/src/app/rosetta/lib/network.ml#L201 impl MinaMesh { - pub async fn network_status(&self) -> Result { + pub async fn network_status(&self, req: NetworkRequest) -> Result { + self.validate_network(&req.network_identifier).await?; let QueryNetworkStatus { best_chain, daemon_status: DaemonStatus3 { peers }, sync_status } = self.graphql_client.send(QueryNetworkStatus::build(())).await?; let blocks = best_chain.ok_or(MinaMeshError::ChainInfoMissing)?; diff --git a/src/api/search_transactions.rs b/src/api/search_transactions.rs index 644ca9f..cabcb52 100644 --- a/src/api/search_transactions.rs +++ b/src/api/search_transactions.rs @@ -18,6 +18,7 @@ impl MinaMesh { &self, req: SearchTransactionsRequest, ) -> Result { + self.validate_network(&req.network_identifier).await?; let original_offset = req.offset.unwrap_or(0); let mut offset = original_offset; let mut limit = req.limit.unwrap_or(100); diff --git a/src/api/validate_network.rs b/src/api/validate_network.rs new file mode 100644 index 0000000..a7cbeb0 --- /dev/null +++ b/src/api/validate_network.rs @@ -0,0 +1,35 @@ +use anyhow::Result; +use coinbase_mesh::models::NetworkIdentifier; +use cynic::QueryBuilder; + +use crate::{graphql::QueryNetworkId, CacheKey::NetworkId, MinaMesh, MinaMeshError}; + +impl MinaMesh { + // Validate that the network identifier matches the network id of the GraphQL + // server + pub async fn validate_network(&self, network_identifier: &NetworkIdentifier) -> Result<(), MinaMeshError> { + // Check the cache + if let Some(cached_network_id) = self.get_from_cache(NetworkId) { + return self.compare_network_ids(&cached_network_id, network_identifier); + } + + // Fetch from GraphQL if cache is empty or expired + let QueryNetworkId { network_id } = self.graphql_client.send(QueryNetworkId::build(())).await?; + self.insert_into_cache(NetworkId, network_id.clone()); + self.compare_network_ids(&network_id, network_identifier) + } + + fn compare_network_ids( + &self, + fetched_network_id: &str, + network_identifier: &NetworkIdentifier, + ) -> Result<(), MinaMeshError> { + let expected_network_id = format!("{}:{}", network_identifier.blockchain, network_identifier.network); + + if fetched_network_id != expected_network_id { + Err(MinaMeshError::NetworkDne(expected_network_id, fetched_network_id.to_string())) + } else { + Ok(()) + } + } +} diff --git a/src/commands/serve.rs b/src/commands/serve.rs index a5e67ac..b6dd7ce 100644 --- a/src/commands/serve.rs +++ b/src/commands/serve.rs @@ -93,11 +93,11 @@ create_handler!(construction_parse, ConstructionParseRequest); create_handler!(construction_payloads, ConstructionPayloadsRequest); create_handler!(construction_preprocess, ConstructionPreprocessRequest); create_handler!(construction_submit, ConstructionSubmitRequest); -create_handler!(mempool); +create_handler!(mempool, NetworkRequest); create_handler!(mempool_transaction, MempoolTransactionRequest); create_handler!(network_list); -create_handler!(network_options); -create_handler!(network_status); +create_handler!(network_options, NetworkRequest); +create_handler!(network_status, NetworkRequest); create_handler!(search_transactions, SearchTransactionsRequest); #[debug_handler] diff --git a/src/config.rs b/src/config.rs index d779978..1243e9e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,6 +3,7 @@ use std::time::Duration; use anyhow::Result; use clap::{Args, Parser}; use coinbase_mesh::models::BlockIdentifier; +use dashmap::DashMap; use sqlx::postgres::PgPoolOptions; use crate::{graphql::GraphQLClient, util::default_mina_proxy_url, MinaMesh, MinaMeshError}; @@ -69,6 +70,8 @@ impl MinaMeshConfig { self.genesis_block_identifier_state_hash.to_owned(), ), search_tx_optimized: self.use_search_tx_optimizations, + cache: DashMap::new(), + cache_ttl: Duration::from_secs(300), }) } } diff --git a/src/error.rs b/src/error.rs index 6171477..804351f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -23,7 +23,7 @@ pub enum MinaMeshError { #[error("GraphQL query failed: {0}")] GraphqlMinaQuery(String), - #[error("Network doesn't exist")] + #[error("Network doesn't exist, expected: {0}, actual: {1}")] NetworkDne(String, String), #[error("Chain info missing")] @@ -113,7 +113,7 @@ impl MinaMeshError { MinaMeshError::Sql("SQL syntax error".to_string()), MinaMeshError::JsonParse(Some("Missing field".to_string())), MinaMeshError::GraphqlMinaQuery("Timeout".to_string()), - MinaMeshError::NetworkDne("blockchain".to_string(), "network".to_string()), + MinaMeshError::NetworkDne("mina:expected".to_string(), "mina:actual".to_string()), MinaMeshError::ChainInfoMissing, MinaMeshError::AccountNotFound("Account ID".to_string()), MinaMeshError::InvariantViolation, @@ -206,6 +206,9 @@ impl MinaMeshError { MinaMeshError::Exception(msg) => json!({ "error": msg, }), + MinaMeshError::NetworkDne(expected, actual) => json!({ + "error": format!("You are requesting the status for the network {}, but you are connected to the network {}", expected, actual), + }), _ => json!(""), } } diff --git a/src/lib.rs b/src/lib.rs index 4eaf07a..88eac73 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,20 +9,24 @@ mod sql_to_mesh; mod types; mod util; +use std::time::{Duration, Instant}; + pub use coinbase_mesh::models; use coinbase_mesh::models::BlockIdentifier; pub use commands::*; pub use config::*; +use dashmap::DashMap; pub use error::*; use graphql::GraphQLClient; pub use operation::*; use sqlx::PgPool; pub use types::*; - #[derive(Debug)] pub struct MinaMesh { pub graphql_client: GraphQLClient, pub pg_pool: PgPool, pub genesis_block_identifier: BlockIdentifier, pub search_tx_optimized: bool, + pub cache: DashMap, // Cache for network_id or other reusable data + pub cache_ttl: Duration, } diff --git a/src/types.rs b/src/types.rs index c9558fb..120f67d 100644 --- a/src/types.rs +++ b/src/types.rs @@ -105,3 +105,8 @@ pub struct ZkAppCommand { pub total_count: Option, pub block_id: Option, } + +#[derive(Debug, Display, Hash, PartialEq, Eq)] +pub enum CacheKey { + NetworkId, +} diff --git a/tests/account_balance.rs b/tests/account_balance.rs index 3ebf34d..4bc4699 100644 --- a/tests/account_balance.rs +++ b/tests/account_balance.rs @@ -27,7 +27,7 @@ async fn responses() -> Result<()> { currencies: None, network_identifier: Box::new(NetworkIdentifier { blockchain: "mina".into(), - network: "mainnet".into(), + network: "testnet".into(), sub_network_identifier: None, }), }) diff --git a/tests/block.rs b/tests/block.rs index 1b8e39e..0adbd86 100644 --- a/tests/block.rs +++ b/tests/block.rs @@ -46,7 +46,7 @@ fn specified_identifiers() -> &'static [PartialBlockIdentifier; 3] { fn network_identifier() -> &'static NetworkIdentifier { static NETWORK_IDENTIFIER: OnceLock = OnceLock::new(); - NETWORK_IDENTIFIER.get_or_init(|| NetworkIdentifier::new("mina".to_string(), "mainnet".to_string())) + NETWORK_IDENTIFIER.get_or_init(|| NetworkIdentifier::new("mina".to_string(), "testnet".to_string())) } #[tokio::test] diff --git a/tests/network_list.rs b/tests/network_list.rs index fac082d..b5583e1 100644 --- a/tests/network_list.rs +++ b/tests/network_list.rs @@ -3,7 +3,7 @@ use insta::assert_debug_snapshot; use mina_mesh::MinaMeshConfig; #[tokio::test] -async fn mainnet_test() -> Result<()> { +async fn test_network_list() -> Result<()> { let response = MinaMeshConfig::from_env().to_mina_mesh().await?.network_list().await?; assert_debug_snapshot!(&response.network_identifiers); Ok(()) diff --git a/tests/network_options.rs b/tests/network_options.rs index 383061b..eb140bd 100644 --- a/tests/network_options.rs +++ b/tests/network_options.rs @@ -1,10 +1,14 @@ use anyhow::Result; use insta::assert_debug_snapshot; -use mina_mesh::MinaMeshConfig; +use mina_mesh::{ + models::{NetworkIdentifier, NetworkRequest}, + MinaMeshConfig, +}; #[tokio::test] async fn test_network_options() -> Result<()> { - let response = MinaMeshConfig::from_env().to_mina_mesh().await?.network_options().await?; + let req = NetworkRequest::new(NetworkIdentifier::new("mina".to_string(), "testnet".to_string())); + let response = MinaMeshConfig::from_env().to_mina_mesh().await?.network_options(req).await?; assert_debug_snapshot!(&response.allow); Ok(()) } diff --git a/tests/search_transactions.rs b/tests/search_transactions.rs index 04b41c9..54e868e 100644 --- a/tests/search_transactions.rs +++ b/tests/search_transactions.rs @@ -42,7 +42,7 @@ async fn search_transactions_failed() -> Result<()> { let mina_mesh = MinaMeshConfig::from_env().to_mina_mesh().await?; let request = SearchTransactionsRequest { - network_identifier: Box::new(NetworkIdentifier::new("mina".to_string(), "mainnet".to_string())), + network_identifier: Box::new(NetworkIdentifier::new("mina".to_string(), "testnet".to_string())), max_block: Some(44), status: Some("failed".to_string()), limit: Some(5), @@ -61,7 +61,7 @@ async fn search_transactions_internal_command() -> Result<()> { let mina_mesh = MinaMeshConfig::from_env().to_mina_mesh().await?; let request = SearchTransactionsRequest { - network_identifier: Box::new(NetworkIdentifier::new("mina".to_string(), "mainnet".to_string())), + network_identifier: Box::new(NetworkIdentifier::new("mina".to_string(), "testnet".to_string())), max_block: Some(44), transaction_identifier: Some(Box::new(TransactionIdentifier::new( // cspell:disable-next-line diff --git a/tests/snapshots/network_list__mainnet_test.snap b/tests/snapshots/network_list__network_list.snap similarity index 100% rename from tests/snapshots/network_list__mainnet_test.snap rename to tests/snapshots/network_list__network_list.snap diff --git a/tests/snapshots/network_options__network_options.snap b/tests/snapshots/network_options__network_options.snap index 351b22f..456bf40 100644 --- a/tests/snapshots/network_options__network_options.snap +++ b/tests/snapshots/network_options__network_options.snap @@ -74,13 +74,15 @@ Allow { }, Error { code: 4, - message: "Network doesn't exist", + message: "Network doesn't exist, expected: mina:expected, actual: mina:actual", description: Some( "The specified network does not exist.", ), retriable: false, details: Some( - String(""), + Object { + "error": String("You are requesting the status for the network mina:expected, but you are connected to the network mina:actual"), + }, ), }, Error { diff --git a/tests/validate_network.rs b/tests/validate_network.rs new file mode 100644 index 0000000..2d3d5ed --- /dev/null +++ b/tests/validate_network.rs @@ -0,0 +1,34 @@ +use anyhow::Result; +use mina_mesh::{models::NetworkIdentifier, CacheKey::NetworkId, MinaMeshConfig, MinaMeshError}; + +#[tokio::test] +async fn validate_network_ok() -> Result<()> { + let mina_mesh = MinaMeshConfig::from_env().to_mina_mesh().await?; + let network = NetworkIdentifier::new("mina".to_string(), "testnet".to_string()); + + assert!(mina_mesh.get_from_cache(NetworkId).is_none(), "Cache should be empty"); + let result = mina_mesh.validate_network(&network).await; + assert!(result.is_ok(), "validate_network failed"); + if let Some(cached_network_id) = mina_mesh.get_from_cache(NetworkId) { + assert_eq!(cached_network_id, "mina:testnet", "Cached network_id does not match"); + } else { + panic!("Cache was not updated after validate_network"); + } + Ok(()) +} + +#[tokio::test] +async fn validate_network_err() -> Result<()> { + let mina_mesh = MinaMeshConfig::from_env().to_mina_mesh().await?; + let network = NetworkIdentifier::new("mina".to_string(), "unknown".to_string()); + let result = mina_mesh.validate_network(&network).await; + assert!(result.is_err(), "validate_network should have failed"); + if let Err(MinaMeshError::NetworkDne(expected, actual)) = result { + assert_eq!(expected, "mina:unknown"); + assert_eq!(actual, "mina:testnet"); + } else { + panic!("Unexpected error type"); + } + + Ok(()) +} diff --git a/words.txt b/words.txt index fbaf082..653be63 100644 --- a/words.txt +++ b/words.txt @@ -1,6 +1,7 @@ codegen coinbases darklight +dashmap datetime deamon deno