synor/crates/synor-contract-test/src/lib.rs
Gulshan Yadav 48949ebb3f Initial commit: Synor blockchain monorepo
A complete blockchain implementation featuring:
- synord: Full node with GHOSTDAG consensus
- explorer-web: Modern React blockchain explorer with 3D DAG visualization
- CLI wallet and tools
- Smart contract SDK and example contracts (DEX, NFT, token)
- WASM crypto library for browser/mobile
2026-01-08 05:22:17 +05:30

183 lines
5.5 KiB
Rust

//! 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<T> = Result<T, TestError>;
/// 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<u8> {
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<T: borsh::BorshDeserialize>(data: &[u8]) -> TestResult<T> {
borsh::from_slice(data).map_err(|e| TestError::SerializationError(e.to_string()))
}
/// Utility to encode arguments using borsh.
pub fn encode_args<T: borsh::BorshSerialize>(args: &T) -> TestResult<Vec<u8>> {
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);
}
}