From a046d9002345918bc39dd53c8b13885b2db84263 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Sat, 12 Oct 2024 02:25:46 -0500 Subject: replace main.rs demo with integration tests TODO unit and docs tests --- impls/monero.rs/Cargo.lock | 150 ++++++++++++++++ impls/monero.rs/Cargo.toml | 2 + impls/monero.rs/src/lib.rs | 22 +-- impls/monero.rs/src/main.rs | 26 --- impls/monero.rs/tests/integration_tests.rs | 265 +++++++++++++++++++++++++++++ 5 files changed, 428 insertions(+), 37 deletions(-) delete mode 100644 impls/monero.rs/src/main.rs create mode 100644 impls/monero.rs/tests/integration_tests.rs (limited to 'impls/monero.rs') diff --git a/impls/monero.rs/Cargo.lock b/impls/monero.rs/Cargo.lock index bf7f0e5..9e79d0f 100644 --- a/impls/monero.rs/Cargo.lock +++ b/impls/monero.rs/Cargo.lock @@ -11,6 +11,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + [[package]] name = "bindgen" version = "0.70.1" @@ -63,12 +69,40 @@ dependencies = [ "libloading", ] +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + [[package]] name = "glob" version = "0.3.1" @@ -100,6 +134,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "log" version = "0.4.22" @@ -118,6 +158,32 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "mockall" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c28b3fb6d753d28c20e826cd46ee611fda1cf3cde03a443a974043247c065a" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "341014e7f530314e9a1fdbc7400b244efea7122662c96bfa248c31da5bfb2020" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "monero_rust" version = "0.0.1" @@ -125,6 +191,8 @@ dependencies = [ "bindgen", "libc", "libloading", + "mockall", + "tempfile", ] [[package]] @@ -137,6 +205,38 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "predicates" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" +dependencies = [ + "anstyle", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" + +[[package]] +name = "predicates-tree" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "prettyplease" version = "0.2.22" @@ -200,6 +300,19 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "shlex" version = "1.3.0" @@ -217,12 +330,49 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + [[package]] name = "unicode-ident" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.52.6" diff --git a/impls/monero.rs/Cargo.toml b/impls/monero.rs/Cargo.toml index b1b7c27..fa0e6ab 100644 --- a/impls/monero.rs/Cargo.toml +++ b/impls/monero.rs/Cargo.toml @@ -15,6 +15,8 @@ crate-type = ["lib", "cdylib"] [dependencies] libc = "0.2" libloading = "0.8.5" +mockall = "0.13.0" +tempfile = "3.13.0" [build-dependencies] bindgen = "0.70.1" diff --git a/impls/monero.rs/src/lib.rs b/impls/monero.rs/src/lib.rs index 7c21c9d..c9b1d81 100644 --- a/impls/monero.rs/src/lib.rs +++ b/impls/monero.rs/src/lib.rs @@ -6,6 +6,17 @@ use std::sync::Arc; use libloading::{Library, Symbol}; +#[cfg(target_os = "android")] +const LIB_NAME: &str = "libmonero_libwallet2_api_c.so"; +#[cfg(target_os = "ios")] +const LIB_NAME: &str = "MoneroWallet.framework/MoneroWallet"; +#[cfg(target_os = "linux")] +const LIB_NAME: &str = "monero_libwallet2_api_c.so"; +#[cfg(target_os = "macos")] +const LIB_NAME: &str = "monero_libwallet2_api_c.dylib"; +#[cfg(target_os = "windows")] +const LIB_NAME: &str = "monero_libwallet2_api_c.dll"; + pub mod network { use std::os::raw::c_int; pub const MAINNET: c_int = 0; @@ -28,17 +39,6 @@ pub struct WalletManager { library: Library, } -#[cfg(target_os = "android")] -const LIB_NAME: &str = "libmonero_libwallet2_api_c.so"; -#[cfg(target_os = "ios")] -const LIB_NAME: &str = "MoneroWallet.framework/MoneroWallet"; -#[cfg(target_os = "linux")] -const LIB_NAME: &str = "monero_libwallet2_api_c.so"; -#[cfg(target_os = "macos")] -const LIB_NAME: &str = "monero_libwallet2_api_c.dylib"; -#[cfg(target_os = "windows")] -const LIB_NAME: &str = "monero_libwallet2_api_c.dll"; - impl WalletManager { /// Creates a new `WalletManager`, loading the Monero wallet library (`wallet2_api_c`). pub fn new(lib_path: Option<&str>) -> WalletResult> { diff --git a/impls/monero.rs/src/main.rs b/impls/monero.rs/src/main.rs deleted file mode 100644 index 099c6d4..0000000 --- a/impls/monero.rs/src/main.rs +++ /dev/null @@ -1,26 +0,0 @@ -use monero_rust::{network, WalletError, WalletManager}; - -fn main() -> Result<(), WalletError> { - let wallet_manager = WalletManager::new(None)?; - - let wallet = wallet_manager.create_wallet( - "wallet_name", - "password", - "English", - network::MAINNET, - )?; - - println!("Wallet created successfully."); - - match wallet.get_seed("") { - Ok(seed) => println!("Seed: {}", seed), - Err(e) => eprintln!("Failed to get seed: {:?}", e), - } - - match wallet.get_address(0, 0) { - Ok(address) => println!("Primary address: {}", address), - Err(e) => eprintln!("Failed to get address: {:?}", e), - } - - Ok(()) -} diff --git a/impls/monero.rs/tests/integration_tests.rs b/impls/monero.rs/tests/integration_tests.rs new file mode 100644 index 0000000..168e882 --- /dev/null +++ b/impls/monero.rs/tests/integration_tests.rs @@ -0,0 +1,265 @@ +use monero_rust::{WalletManager, network, WalletError, WalletResult}; +use std::fs; +use std::sync::Arc; +use std::time::Instant; +use tempfile::TempDir; + +const TEST_WALLET_NAMES: &[&str] = &[ + "test_wallet", + "mainnet_wallet", + "testnet_wallet", + "stagenet_wallet", +]; + +/// Helper function to clean up existing wallet files in a temporary directory. +fn check_and_delete_existing_wallets(temp_dir: &TempDir) -> std::io::Result<()> { + for name in TEST_WALLET_NAMES { + // Construct absolute paths for wallet files. + let wallet_file = temp_dir.path().join(name); + let keys_file = temp_dir.path().join(format!("{}.keys", name)); + let address_file = temp_dir.path().join(format!("{}.address.txt", name)); // Added + + // Delete wallet file if it exists. + if wallet_file.exists() { + if let Err(e) = fs::remove_file(&wallet_file) { + println!("Warning: Failed to delete wallet file {:?}: {}", wallet_file, e); + } else { + println!("Deleted existing wallet file: {:?}", wallet_file); + } + } + + // Delete keys file if it exists. + if keys_file.exists() { + if let Err(e) = fs::remove_file(&keys_file) { + println!("Warning: Failed to delete keys file {:?}: {}", keys_file, e); + } else { + println!("Deleted existing keys file: {:?}", keys_file); + } + } + + // Delete address file if it exists. + if address_file.exists() { + if let Err(e) = fs::remove_file(&address_file) { + println!("Warning: Failed to delete address file {:?}: {}", address_file, e); + } else { + println!("Deleted existing address file: {:?}", address_file); + } + } + } + Ok(()) +} + +/// Sets up the test environment by creating a temporary directory and initializing the WalletManager. +/// +/// Returns: +/// - An `Arc` wrapped `WalletManager` instance. +/// - A `TempDir` representing the temporary directory. +fn setup() -> WalletResult<(Arc, TempDir)> { + println!("Setting up test environment..."); + let temp_dir = tempfile::tempdir().expect("Failed to create temporary directory"); + check_and_delete_existing_wallets(&temp_dir).expect("Failed to clean up existing wallets"); + + println!("Creating WalletManager..."); + let start = Instant::now(); + let manager = WalletManager::new(None)?; + println!("WalletManager creation took {:?}", start.elapsed()); + + Ok((manager, temp_dir)) +} + +/// Tears down the test environment by deleting wallet files. +/// +/// Args: +/// - `temp_dir`: Reference to the temporary directory. +/// +/// Returns: +/// - `Result<(), std::io::Error>` indicating success or failure. +fn teardown(temp_dir: &TempDir) -> std::io::Result<()> { + println!("Tearing down test environment..."); + check_and_delete_existing_wallets(temp_dir) +} + +#[test] +fn test_wallet_manager_creation() { + println!("Running test_wallet_manager_creation"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Construct the full path for the wallet within temp_dir. + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path.to_str().expect("Failed to convert wallet path to string"); + + let wallet_result = manager.create_wallet(wallet_str, "password123", "English", network::MAINNET); + assert!(wallet_result.is_ok(), "WalletManager creation seems to have failed"); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_wallet_creation() { + println!("Running test_wallet_creation"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Construct the full path for the wallet within temp_dir. + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path.to_str().expect("Failed to convert wallet path to string"); + + let wallet = manager.create_wallet(wallet_str, "password123", "English", network::MAINNET); + assert!(wallet.is_ok(), "Failed to create wallet"); + let wallet = wallet.unwrap(); + assert!(wallet.is_deterministic().is_ok(), "Wallet creation seems to have failed"); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_get_seed() { + println!("Running test_get_seed"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Construct the full path for the wallet within temp_dir. + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path.to_str().expect("Failed to convert wallet path to string"); + + let wallet = manager + .create_wallet(wallet_str, "password123", "English", network::MAINNET) + .expect("Failed to create wallet"); + println!("Attempting to get seed..."); + let start = Instant::now(); + let result = wallet.get_seed(""); + println!("get_seed took {:?}", start.elapsed()); + assert!(result.is_ok(), "Failed to get seed: {:?}", result.err()); + assert!(!result.unwrap().is_empty(), "Seed is empty"); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_get_address() { + println!("Running test_get_address"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Construct the full path for the wallet within temp_dir. + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path.to_str().expect("Failed to convert wallet path to string"); + + let wallet = manager + .create_wallet(wallet_str, "password123", "English", network::MAINNET) + .expect("Failed to create wallet"); + println!("Attempting to get address..."); + let start = Instant::now(); + let result = wallet.get_address(0, 0); + println!("get_address took {:?}", start.elapsed()); + assert!(result.is_ok(), "Failed to get address: {:?}", result.err()); + assert!(!result.unwrap().is_empty(), "Address is empty"); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_is_deterministic() { + println!("Running test_is_deterministic"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Construct the full path for the wallet within temp_dir. + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path.to_str().expect("Failed to convert wallet path to string"); + + let wallet = manager + .create_wallet(wallet_str, "password123", "English", network::MAINNET) + .expect("Failed to create wallet"); + println!("Checking if wallet is deterministic..."); + let start = Instant::now(); + let result = wallet.is_deterministic(); + println!("is_deterministic check took {:?}", start.elapsed()); + assert!(result.is_ok(), "Failed to check if wallet is deterministic: {:?}", result.err()); + assert!(result.unwrap(), "Wallet should be deterministic"); + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_wallet_creation_with_different_networks() { + println!("Running test_wallet_creation_with_different_networks"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Define wallet names and corresponding network types. + let wallets = vec![ + ("mainnet_wallet", network::MAINNET), + ("testnet_wallet", network::TESTNET), + ("stagenet_wallet", network::STAGENET), + ]; + + for (name, net_type) in wallets { + println!("Creating wallet: {} on network type {}", name, net_type); + + // Construct the full path for each wallet within temp_dir. + let wallet_path = temp_dir.path().join(name); + let wallet_str = wallet_path.to_str().expect("Failed to convert wallet path to string"); + + let wallet = manager.create_wallet(wallet_str, "password", "English", net_type); + assert!(wallet.is_ok(), "Failed to create wallet: {}", name); + } + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_multiple_address_generation() { + println!("Running test_multiple_address_generation"); + let (manager, temp_dir) = setup().expect("Failed to set up test environment"); + + // Construct the full path for the wallet within temp_dir. + let wallet_path = temp_dir.path().join("test_wallet"); + let wallet_str = wallet_path.to_str().expect("Failed to convert wallet path to string"); + + let wallet = manager + .create_wallet(wallet_str, "password123", "English", network::MAINNET) + .expect("Failed to create wallet"); + + for i in 0..5 { + println!("Generating address {}...", i); + let start = Instant::now(); + let result = wallet.get_address(0, i); + println!("Address generation took {:?}", start.elapsed()); + assert!(result.is_ok(), "Failed to get address {}: {:?}", i, result.err()); + assert!(!result.unwrap().is_empty(), "Address {} is empty", i); + } + + teardown(&temp_dir).expect("Failed to clean up after test"); +} + +#[test] +fn test_wallet_error_display() { + println!("Running test_wallet_error_display"); + + // Test WalletError::FfiError variant. + let error = WalletError::FfiError("Test error".to_string()); + match error { + WalletError::FfiError(msg) => assert_eq!(msg, "Test error"), + _ => panic!("Expected FfiError variant"), + } + + // Test WalletError::NullPointer variant. + let error = WalletError::NullPointer; + match error { + WalletError::NullPointer => assert!(true), + _ => panic!("Expected NullPointer variant"), + } + + // Test WalletError::WalletErrorCode variant. + let error = WalletError::WalletErrorCode(2, "Sample wallet error".to_string()); + match error { + WalletError::WalletErrorCode(code, msg) => { + assert_eq!(code, 2); + assert_eq!(msg, "Sample wallet error"); + }, + _ => panic!("Expected WalletErrorCode variant"), + } + + // Test WalletError::LibraryLoadError variant. + let error = WalletError::LibraryLoadError("Failed to load library".to_string()); + match error { + WalletError::LibraryLoadError(msg) => assert_eq!(msg, "Failed to load library"), + _ => panic!("Expected LibraryLoadError variant"), + } +} -- cgit v1.2.3