Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

create utilities for writing unit tests with a Mock Provider (no rpc eth node required) #42

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 6 additions & 28 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@ async-trait = "0.1"
jsonrpsee = { version = "0.21", features = ["macros", "server", "client-core"] }
anyhow = "1.0"
thiserror = "1.0"
ethers = { version = "2.0.11", features = ["abigen"] }
ethers = { path = "../../../../gakonst/ethers-rs/ethers", features = ["abigen"] }
ctor = "0.2"
lib-didethresolver = { git = "https://github.com/xmtp/didethresolver", package = "lib-didethresolver", branch = "main" }
gateway-types = { path = "./gateway-types" }
rustc-hex = "2.1"
hex = "0.4"

[patch.crates-io]
ethers = { path = "../../../../gakonst/ethers-rs/ethers", features = ["abigen"] }

24 changes: 12 additions & 12 deletions registry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,18 +81,18 @@ where
String::from_utf8_lossy(&attribute)
);

self.registry
.revoke_attribute_signed(
address,
signature.v.try_into()?,
signature.r.into(),
signature.s.into(),
attribute,
value.into(),
)
.send()
.await?
.await?;
let res = self.registry.revoke_attribute_signed(
address,
signature.v.try_into()?,
signature.r.into(),
signature.s.into(),
attribute,
value.into(),
);

let res = res.send().await;
println!("{:?}", res);
res?.await?;

Ok(())
}
Expand Down
1 change: 0 additions & 1 deletion xps-gateway/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,3 @@ jsonrpsee = { workspace = true, features = ["macros", "server", "client"]}
tokio = { workspace = true, features = ["macros", "rt", "time"]}
futures = "0.3"
serde_json.workspace = true

134 changes: 132 additions & 2 deletions xps-gateway/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ use gateway_types::DID_ETH_REGISTRY;
use jsonrpsee::server::Server;
use std::str::FromStr;

pub use crate::rpc::{XpsMethods, XpsServer};
use crate::types::GatewayContext;
pub use crate::{
rpc::{XpsMethods, XpsServer},
types::GatewayContext,
};

/// Entrypoint for the xps Gateway
pub async fn run(host: String, port: u16) -> Result<()> {
Expand All @@ -37,3 +39,131 @@ pub async fn run(host: String, port: u16) -> Result<()> {
handle.stopped().await;
Ok(())
}

#[cfg(test)]
mod test {
use ethers::{
abi::AbiEncode,
prelude::{Transaction, TransactionReceipt},
providers::MockProvider,
types::{Block, FeeHistory, TxHash, U256, U64},
};
use serde::Serialize;

pub trait MockProviderExt {
/// Set the response for a call to a contract
/// This must be called for each transaction that a function might send.
///
///
/// # Example
/// ```
/// use ethers::{
/// providers::{MockProvider, Provider},
/// prelude::TransactionRequest,
/// types::{Address, Transaction}
/// };
/// use std::time::Duration;
///
/// let (mut provider, mut mock) = Provider::mocked();
/// provider.set_interval(Duration::from_millis(1));
///
/// let to = Address::from_str("0x7e575682a8e450e33eb0493f9972821ae333cd7f").unwrap();
/// let from = Address::from_str("0x0000000000000000000000000000000000000000").unwrap();
/// let tx = TransactionRequest::new().to(to).value(1000).from(from);
/// mock.set_transaction_response(TransactionReceipt::default());
/// let pending = provider.send_transaction(tx, None).await.unwrap().await.unwrap();
/// ```
fn set_transaction_response(&mut self, response: TransactionReceipt);

/// Set the response for a transaction to a Contract
///
/// # Example
/// ```
///
/// let (context, mut mock) = GatewayContext::mocked().await;
/// let methods = XpsMethods::new(&context);
/// let attr = XmtpAttribute {
/// encoding: KeyEncoding::Hex,
/// purpose: XmtpKeyPurpose::Installation,
/// };
/// let value = vec![0x01, 0x02, 0x03];
/// mock.set_contract_response(Default::default());
/// let res = methods
/// .revoke_installation(
/// Address::default().to_string(),
/// attr,
/// value,
/// Signature {
/// r: [0x01; 32].into(),
/// s: [0x02; 32].into(),
/// v: 0x01,
/// },
/// )
/// .await;
/// ```
fn set_contract_response(&mut self, response: TransactionReceipt);

/// Set the response for a call to a contract
///
/// # Example
///
/// ```
/// let (context, mut mock) = GatewayContext::mocked().await;
/// let registry = DIDRegistry::new(Address::default(), context.signer.clone());
/// mock.set_call_response(ChangedReturn(U256::zero()));
/// registry.changed(Address::default()).call().await.unwrap();
///
/// ```
///
fn set_call_response<T: Serialize + Send + Sync + AbiEncode>(&mut self, response: T);
}

impl MockProviderExt for MockProvider {
fn set_transaction_response(&mut self, response: TransactionReceipt) {
self.push(response).unwrap();
self.push(Transaction {
block_number: Some(1.into()),
..Transaction::default()
})
.unwrap(); // eth_getTransaction
self.push(TxHash::default()).unwrap(); // eth_sendTransaction
self.push(U64::from(0)).unwrap(); // eth_estimateGas
self.push(U64::from(0)).unwrap(); // eth_GasPrice
}

fn set_contract_response(&mut self, response: TransactionReceipt) {
self.push(response).unwrap();
self.push(Transaction {
block_number: Some(1.into()),
..Transaction::default()
})
.unwrap(); // eth_getTransaction
self.push(TxHash::default()).unwrap(); // eth_sendRawTransaction
self.push(U64::from(0)).unwrap(); // estimateGas
self.push(fee_history()).unwrap(); // eth_feeHistory
self.push(Block {
//eth_getBlockByNumber
// base_fee_per_gas needs to be Some() for EIP-1559
base_fee_per_gas: Some(U256::zero()),
..Block::<TxHash>::default()
})
.unwrap();
self.push(U64::from(0)).unwrap(); // transactionCount
}

fn set_call_response<T: Serialize + Send + Sync + AbiEncode>(&mut self, response: T) {
self.push::<String, &String>(&response.encode_hex())
.unwrap();
}
}

// internal fn to return an empty fee history
fn fee_history() -> FeeHistory {
FeeHistory {
base_fee_per_gas: vec![U256::zero()],
gas_used_ratio: vec![0.0],
oldest_block: U256::zero(),
reward: Vec::new(),
}
}
}
Loading
Loading