//! Contract Testing Framework for Synor Smart Contracts. //! //! This crate provides a comprehensive testing framework for Synor smart contracts, //! including test environments, mock storage, test accounts, and assertion utilities. //! //! # Quick Start //! //! ```rust,ignore //! use synor_contract_test::prelude::*; //! //! #[tokio::test] //! async fn test_token_contract() { //! // Create test environment with accounts //! let mut env = TestEnvironment::builder() //! .with_account(TestAccount::with_balance(1000)) //! .with_account(TestAccount::with_balance(500)) //! .build(); //! //! // Deploy contract //! let bytecode = include_bytes!("../contracts/token.wasm"); //! let contract = env.deploy_contract(bytecode, &init_params).unwrap(); //! //! // Call contract methods //! let result = env.call_contract(contract, "transfer", &args).unwrap(); //! //! // Assert results //! assert_success!(result); //! assert_event_emitted!(result, "Transfer"); //! } //! ``` //! //! # Features //! //! - **TestEnvironment**: Wraps VmEngine with test utilities //! - **MockStorage**: Isolated storage for testing //! - **TestAccount**: Pre-funded accounts with keypairs //! - **Assertions**: Macros for testing results, storage, and events //! //! # Architecture //! //! ```text //! +------------------+ //! | TestEnvironment | //! +------------------+ //! | //! v //! +------------------+ +------------------+ //! | VmEngine |<--->| MockStorage | //! +------------------+ +------------------+ //! | //! v //! +------------------+ //! | TestAccount[] | //! +------------------+ //! ``` #![allow(dead_code)] pub mod assertions; mod environment; mod mock_storage; mod test_account; // Re-export core types pub use environment::{TestEnvironment, TestEnvironmentBuilder}; pub use mock_storage::MockStorage; pub use test_account::{TestAccount, TestKeypair}; // Re-export assertion utilities pub use assertions::{ event_selector, to_return_bytes, to_storage_bytes, to_topic_bytes, AssertionChain, AssertionError, ReturnEncodable, StorageEncodable, TopicEncodable, }; // Re-export VM types for convenience pub use synor_vm; pub use synor_vm::{ CallParams, ContractId, ContractLog, DeployParams, ExecutionResult, StorageChange, StorageKey, StorageValue, VmEngine, VmError, }; /// Errors that can occur during contract testing. #[derive(Debug, thiserror::Error)] pub enum TestError { /// VM error during execution. #[error("VM error: {0}")] VmError(#[from] VmError), /// Contract deployment failed. #[error("Deployment failed: {0}")] DeploymentFailed(String), /// Contract call failed. #[error("Call failed: {0}")] CallFailed(String), /// Account not found. #[error("Account not found: {0}")] AccountNotFound(String), /// Insufficient balance. #[error("Insufficient balance: have {have}, need {need}")] InsufficientBalance { have: u64, need: u64 }, /// Invalid bytecode. #[error("Invalid bytecode: {0}")] InvalidBytecode(String), /// Serialization error. #[error("Serialization error: {0}")] SerializationError(String), /// Invalid state. #[error("Invalid state: {0}")] InvalidState(String), /// Configuration error. #[error("Configuration error: {0}")] ConfigError(String), } /// Result type for contract test operations. pub type TestResult = Result; /// Prelude module for convenient imports. pub mod prelude { pub use crate::assertions::{ event_selector, to_return_bytes, to_storage_bytes, to_topic_bytes, AssertionChain, ReturnEncodable, StorageEncodable, TopicEncodable, }; pub use crate::{ assert_event_emitted, assert_gas_used, assert_no_events, assert_return_data, assert_revert, assert_storage_empty, assert_storage_eq, assert_storage_none, assert_success, }; pub use crate::{ CallParams, ContractId, DeployParams, ExecutionResult, MockStorage, StorageKey, StorageValue, TestAccount, TestEnvironment, TestError, TestKeypair, TestResult, VmError, }; pub use synor_types::{Address, Amount, Hash256, Network}; } /// Utility to encode method call arguments. pub fn encode_call_args(method: &str, args: &[u8]) -> Vec { let selector = synor_sdk::method_selector(method); let mut data = Vec::with_capacity(4 + args.len()); data.extend_from_slice(&selector); data.extend_from_slice(args); data } /// Utility to decode return data. pub fn decode_return_data(data: &[u8]) -> TestResult { borsh::from_slice(data).map_err(|e| TestError::SerializationError(e.to_string())) } /// Utility to encode arguments using borsh. pub fn encode_args(args: &T) -> TestResult> { borsh::to_vec(args).map_err(|e| TestError::SerializationError(e.to_string())) } #[cfg(test)] mod tests { use super::*; #[test] fn test_encode_call_args() { let args = encode_call_args("transfer", &[1, 2, 3]); assert!(args.len() >= 4); // Method selector should be first 4 bytes let selector = synor_sdk::method_selector("transfer"); assert_eq!(&args[..4], &selector); assert_eq!(&args[4..], &[1, 2, 3]); } #[test] fn test_encode_decode_roundtrip() { let original: u64 = 12345; let encoded = encode_args(&original).unwrap(); let decoded: u64 = decode_return_data(&encoded).unwrap(); assert_eq!(original, decoded); } }