Skip to content

Commit

Permalink
Unit tests for VDR (#20)
Browse files Browse the repository at this point in the history
* Added validation for txn hash len

Signed-off-by: DenisRybas <[email protected]>

* added unit tests for contract

Signed-off-by: DenisRybas <[email protected]>

* added unit tests for client

Signed-off-by: DenisRybas <[email protected]>

* added unit tests for role_control

Signed-off-by: DenisRybas <[email protected]>

* uncommented test

Signed-off-by: DenisRybas <[email protected]>

* added txn tests

Signed-off-by: DenisRybas <[email protected]>

* linted

Signed-off-by: DenisRybas <[email protected]>

* made tests work

Signed-off-by: DenisRybas <[email protected]>

* transaction tests refactoring

Signed-off-by: DenisRybas <[email protected]>

* ignored test

Signed-off-by: DenisRybas <[email protected]>

* linted

Signed-off-by: DenisRybas <[email protected]>

* processed comments

Signed-off-by: DenisRybas <[email protected]>

* updated dependencies

Signed-off-by: DenisRybas <[email protected]>

* linted

Signed-off-by: DenisRybas <[email protected]>

* processed comments

Signed-off-by: DenisRybas <[email protected]>

* processed comments

Signed-off-by: DenisRybas <[email protected]>

* Removed warnings

Signed-off-by: DenisRybas <[email protected]>

* fixed tests

Signed-off-by: DenisRybas <[email protected]>

---------

Signed-off-by: DenisRybas <[email protected]>
  • Loading branch information
DenisRybas authored Feb 13, 2024
1 parent 2051546 commit 73a5518
Show file tree
Hide file tree
Showing 12 changed files with 554 additions and 86 deletions.
42 changes: 42 additions & 0 deletions vdr/Cargo.lock

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

1 change: 1 addition & 0 deletions vdr/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ web-sys = { version = "0.3.64", optional = true, features = ["Window"] }
web3-wasm = { package = "web3", version = "0.19.0", default-features = false, features = ["wasm", "http", "http-tls"], optional = true }

[dev-dependencies]
rstest = "0.18.2"
mockall = "0.12.0"
env_logger = "0.10.0"
rand = "0.8.5"
231 changes: 193 additions & 38 deletions vdr/src/client/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,10 +213,9 @@ impl Debug for LedgerClient {
#[cfg(test)]
pub mod test {
use super::*;
use crate::types::EventLog;
use async_trait::async_trait;
use crate::{client::MockClient, signer::basic_signer::test::basic_signer};
use once_cell::sync::Lazy;
use std::{env, fs};
use std::{env, fs, sync::RwLock};

pub const CHAIN_ID: u64 = 1337;
pub const CONTRACTS_SPEC_BASE_PATH: &str = "../smart_contracts/artifacts/contracts/";
Expand All @@ -235,6 +234,7 @@ pub mod test {
"http://127.0.0.1:21004",
];
pub const DEFAULT_NONCE: u64 = 0;
pub const INVALID_ADDRESS: &str = "123";

pub static SCHEMA_REGISTRY_ADDRESS: Lazy<Address> =
Lazy::new(|| Address::from("0x0000000000000000000000000000000000005555"));
Expand Down Expand Up @@ -302,64 +302,219 @@ pub mod test {
LedgerClient::new(CHAIN_ID, RPC_NODE_ADDRESS, &contracts(), None).unwrap()
}

pub struct MockClient {}

impl Debug for MockClient {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, r#"MockClient {{ }}"#)
}
}

#[async_trait]
impl Client for MockClient {
async fn get_transaction_count(&self, _address: &Address) -> VdrResult<u64> {
Ok(0)
}

async fn submit_transaction(&self, _transaction: &[u8]) -> VdrResult<Vec<u8>> {
todo!()
}
pub fn mock_client() -> LedgerClient {
let mut ledger_client = LedgerClient::new(
CHAIN_ID,
RPC_NODE_ADDRESS,
&contracts(),
Some(&QuorumConfig::default()),
)
.unwrap();

async fn call_transaction(&self, _to: &str, _transaction: &[u8]) -> VdrResult<Vec<u8>> {
todo!()
}
let mut client = MockClient::new();
client.expect_get_transaction_count().returning(|_| Ok(0));

async fn query_events(&self, _query: &EventQuery) -> VdrResult<Vec<EventLog>> {
todo!()
}
ledger_client.client = Box::new(client);
ledger_client
}

async fn get_receipt(&self, _hash: &[u8]) -> VdrResult<String> {
todo!()
}
pub fn write_transaction() -> Transaction {
let transaction = Transaction {
type_: TransactionType::Write,
from: Some(TRUSTEE_ACC.clone()),
to: VALIDATOR_CONTROL_ADDRESS.clone(),
nonce: Some(DEFAULT_NONCE.clone()),
chain_id: CHAIN_ID,
data: vec![],
signature: RwLock::new(None),
hash: None,
};
let signer = basic_signer();
let sign_bytes = transaction.get_signing_bytes().unwrap();
let signature = signer.sign(&sign_bytes, TRUSTEE_ACC.as_ref()).unwrap();
transaction.set_signature(signature);

async fn get_block(&self, _block: Option<u64>) -> VdrResult<BlockDetails> {
todo!()
}
transaction
}

async fn get_transaction(&self, _hash: &[u8]) -> VdrResult<Option<Transaction>> {
todo!()
pub fn read_transaction() -> Transaction {
Transaction {
type_: TransactionType::Read,
from: None,
to: VALIDATOR_CONTROL_ADDRESS.clone(),
nonce: None,
chain_id: CHAIN_ID,
data: vec![],
signature: RwLock::new(None),
hash: None,
}
}

pub fn mock_client() -> LedgerClient {
let mut client = LedgerClient::new(
pub fn mock_custom_client(client: Box<dyn Client>) -> LedgerClient {
let mut ledger_client = LedgerClient::new(
CHAIN_ID,
RPC_NODE_ADDRESS,
&contracts(),
Some(&QuorumConfig::default()),
)
.unwrap();
client.client = Box::new(MockClient {});
client

ledger_client.client = client;
ledger_client
}

mod create {
use crate::validator_control::test::VALIDATOR_CONTROL_NAME;
use mockall::predicate::eq;
use rstest::rstest;
use serde_json::Value;

use super::*;

#[test]
fn create_client_test() {
client();
}

#[test]
fn create_client_invalid_node_address() {
let client_err = LedgerClient::new(CHAIN_ID, "..", &contracts(), None)
.err()
.unwrap();

assert!(matches!(
client_err, | VdrError::ClientNodeUnreachable { .. }
));
}

#[rstest]
#[case::invalid_contract_data(vec![ContractConfig {
address: VALIDATOR_CONTROL_ADDRESS.to_string(),
spec_path: None,
spec: Some(ContractSpec {
name: VALIDATOR_CONTROL_NAME.to_string(),
abi: Value::String("".to_string()),
}),
}], VdrError::ContractInvalidInputData)]
#[case::both_contract_path_and_spec_provided(vec![ContractConfig {
address: VALIDATOR_CONTROL_ADDRESS.to_string(),
spec_path: Some(build_contract_path(VALIDATOR_CONTROL_PATH)),
spec: Some(ContractSpec {
name: VALIDATOR_CONTROL_NAME.to_string(),
abi: Value::Array(vec ! []),
}),
}], VdrError::ContractInvalidSpec("".to_string()))]
#[case::non_existent_spec_path(vec![ContractConfig {
address: VALIDATOR_CONTROL_ADDRESS.to_string(),
spec_path: Some(build_contract_path("")),
spec: None,
}], VdrError::ContractInvalidSpec("".to_string()))]
#[case::empty_contract_spec(vec![ContractConfig {
address: VALIDATOR_CONTROL_ADDRESS.to_string(),
spec_path: None,
spec: None,
}], VdrError::ContractInvalidSpec("".to_string()))]
fn test_create_client_errors(
#[case] contract_config: Vec<ContractConfig>,
#[case] expected_error: VdrError,
) {
let client_err = LedgerClient::new(CHAIN_ID, RPC_NODE_ADDRESS, &contract_config, None)
.err()
.unwrap();

assert!(matches!(client_err, expected_error));
}

#[rstest]
#[case::empty_recipient_address("", VdrError::ClientInvalidTransaction("".to_string()))]
#[case::invalid_recipient_address(INVALID_ADDRESS, VdrError::ClientInvalidTransaction("".to_string()))]
async fn call_transaction_various_recipient_addresses(
#[case] recipient_address: &str,
#[case] expected_error: VdrError,
) {
let transaction = Transaction {
to: Address::from(recipient_address),
..read_transaction()
};
let client = client();

let error = client.submit_transaction(&transaction).await.unwrap_err();

assert!(matches!(error, expected_error));
}

#[async_std::test]
async fn get_receipt_invalid_transaction_hash() {
let client = client();
let txn_hash = vec![1; 4];

let receipt_err = client.get_receipt(&txn_hash).await.unwrap_err();

assert!(matches!(
receipt_err, | VdrError::CommonInvalidData { .. }
));
}

#[async_std::test]
async fn get_receipt_transaction_does_not_exist() {
let mut client_mock = MockClient::new();
let txn_hash = vec![1; 32];
client_mock
.expect_get_receipt()
.with(eq(txn_hash.clone()))
.returning(|_| {
Err(VdrError::ClientInvalidResponse(
"Missing transaction receipt".to_string(),
))
});

let client = mock_custom_client(Box::new(client_mock));

let receipt_err = client.get_receipt(&txn_hash).await.unwrap_err();

assert!(matches!(
receipt_err, | VdrError::ClientInvalidResponse { .. }
));
}

#[async_std::test]
async fn get_receipt_positive() {
let mut client_mock = MockClient::new();
let txn_hash = vec![1; 32];
client_mock
.expect_get_receipt()
.with(eq(txn_hash.clone()))
.returning(|_| Ok("".to_string()));

let client = mock_custom_client(Box::new(client_mock));

client.get_receipt(&txn_hash).await.unwrap();
}

#[async_std::test]
async fn get_transaction_count_invalid_address() {
let client = client();

let get_nonce_err = client
.get_transaction_count(&Address::from(INVALID_ADDRESS))
.await
.unwrap_err();

assert!(matches!(
get_nonce_err, | VdrError::ClientInvalidTransaction { .. }
));
}

#[async_std::test]
async fn get_contract_does_not_exist() {
let client = client();

let contract_err = client.contract(INVALID_ADDRESS).err().unwrap();

assert!(matches!(
contract_err, | VdrError::ContractInvalidName { .. }
));
}
}

#[cfg(feature = "ledger_test")]
Expand Down
9 changes: 9 additions & 0 deletions vdr/src/client/implementation/web3/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,15 @@ impl Client for Web3Client {
async fn get_receipt(&self, hash: &[u8]) -> VdrResult<String> {
trace!("Web3Client::get_receipt(hash: {:?})", hash);

if hash.len() != 32 {
let vdr_error =
VdrError::CommonInvalidData("Transaction hash length != 32 bytes".to_string());

warn!("Error: {} getting receipt", vdr_error,);

return Err(vdr_error);
}

let receipt = self
.client
.eth()
Expand Down
23 changes: 23 additions & 0 deletions vdr/src/client/implementation/web3/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,26 @@ impl Debug for Web3Contract {
write!(f, r#"Web3Contract {{ address: {:?} }}"#, self.address)
}
}

#[cfg(test)]
pub mod test {
use crate::{
client::client::test::{mock_client, INVALID_ADDRESS},
validator_control::test::VALIDATOR_CONTROL_NAME,
};

use super::*;
#[async_std::test]
async fn function_method_does_not_exist() {
let client = mock_client();
let contract = client
.contract(&VALIDATOR_CONTROL_NAME.to_string())
.unwrap();

let err = contract.function(INVALID_ADDRESS).unwrap_err();

assert!(matches!(
err, | VdrError::ContractInvalidName { .. }
));
}
}
Loading

0 comments on commit 73a5518

Please sign in to comment.