#[cfg(test)]
mod tests {
use crate::core::{SolanaRecoverError, WalletScanner, TokenAccountInfo, LAMPORTS_PER_SOL, ScanStatus};
use solana_sdk::pubkey::Pubkey;
use std::sync::Arc;
use std::str::FromStr;
use chrono::Utc;
use solana_account_decoder::UiAccountEncoding;
use base64::{Engine as _, engine::general_purpose};
fn create_mock_keyed_account(pubkey_str: &str, owner: &str, lamports: u64, data: Vec<u8>) -> solana_client::rpc_response::RpcKeyedAccount {
solana_client::rpc_response::RpcKeyedAccount {
pubkey: pubkey_str.to_string(),
account: solana_account_decoder::UiAccount {
lamports,
data: solana_account_decoder::UiAccountData::Binary(
general_purpose::STANDARD.encode(&data),
solana_account_decoder::UiAccountEncoding::Base64
),
owner: owner.to_string(),
executable: false,
rent_epoch: 0,
space: Some(data.len() as u64),
},
}
}
fn create_mock_token_account(pubkey_str: &str, lamports: u64, amount: u64) -> solana_client::rpc_response::RpcKeyedAccount {
let mut data = vec![0u8; 82];
let mint_pubkey = Pubkey::from_str("11111111111111111111111111111111").unwrap();
data[0..32].copy_from_slice(&mint_pubkey.to_bytes());
let owner_pubkey = Pubkey::from_str("11111111111111111111111111111112").unwrap();
data[32..64].copy_from_slice(&owner_pubkey.to_bytes());
data[64..72].copy_from_slice(&amount.to_le_bytes());
create_mock_keyed_account(pubkey_str, &spl_token::id().to_string(), lamports, data)
}
fn create_mock_system_account(pubkey_str: &str, lamports: u64) -> solana_client::rpc_response::RpcKeyedAccount {
create_mock_keyed_account(pubkey_str, &solana_program::system_program::id().to_string(), lamports, vec![])
}
#[tokio::test]
async fn test_parse_token_account_from_binary_base64() {
let scanner = WalletScanner::new(Arc::new(MockConnectionPool::new_simple()));
let token_account = create_mock_token_account("TestTokenAccount", 1000000, 0);
let data_str = match &token_account.account.data {
solana_account_decoder::UiAccountData::Binary(data, _) => data.clone(),
_ => panic!("Expected binary data"),
};
let result = scanner.parse_token_account_from_binary(&data_str, &UiAccountEncoding::Base64);
assert!(result.is_ok());
let token_info = result.unwrap();
assert_eq!(token_info.amount, 0);
assert_eq!(token_info.mint, "11111111111111111111111111111111");
}
#[tokio::test]
async fn test_parse_token_account_from_binary_base58() {
let scanner = WalletScanner::new(Arc::new(MockConnectionPool::new_simple()));
let token_account = create_mock_token_account("TestTokenAccount", 1000000, 1000000);
let data_str = match &token_account.account.data {
solana_account_decoder::UiAccountData::Binary(data, _) => data.clone(),
_ => panic!("Expected binary data"),
};
let result = scanner.parse_token_account_from_binary(&data_str, &UiAccountEncoding::Base64);
assert!(result.is_ok());
let token_info = result.unwrap();
assert_eq!(token_info.amount, 1000000);
assert_eq!(token_info.mint, "11111111111111111111111111111111");
}
#[tokio::test]
async fn test_parse_token_account_invalid_data() {
let scanner = WalletScanner::new(Arc::new(MockConnectionPool::new_simple()));
let short_data = "YWJj"; let result = scanner.parse_token_account_from_binary(short_data, &UiAccountEncoding::Base64);
assert!(result.is_err());
let invalid_base64 = "invalid_base64!";
let result = scanner.parse_token_account_from_binary(invalid_base64, &UiAccountEncoding::Base64);
assert!(result.is_err());
}
#[tokio::test]
async fn test_check_empty_token_account() {
let scanner = WalletScanner::new(Arc::new(MockConnectionPool::new_simple()));
let empty_token_account = create_mock_token_account("EmptyTokenAccount", 2039280, 0);
let wallet_address = "11111111111111111111111111111112";
let result = scanner.check_empty_account(&empty_token_account, wallet_address).await;
assert!(result.is_ok());
let empty_account = result.unwrap();
assert!(empty_account.is_some());
let account = empty_account.unwrap();
assert_eq!(account.address, "EmptyTokenAccount");
assert_eq!(account.lamports, 2039280);
assert_eq!(account.owner, spl_token::id().to_string());
assert!(account.mint.is_some());
assert_eq!(account.mint.unwrap(), "11111111111111111111111111111111");
}
#[tokio::test]
async fn test_check_non_empty_token_account() {
let scanner = WalletScanner::new(Arc::new(MockConnectionPool::new_simple()));
let non_empty_token_account = create_mock_token_account("NonEmptyTokenAccount", 2039280, 1000000);
let wallet_address = "11111111111111111111111111111112";
let result = scanner.check_empty_account(&non_empty_token_account, wallet_address).await;
assert!(result.is_ok());
let empty_account = result.unwrap();
assert!(empty_account.is_none()); }
#[tokio::test]
async fn test_check_empty_system_account() {
let scanner = WalletScanner::new(Arc::new(MockConnectionPool::new_simple()));
let rent_exempt_amount = 2039280;
let system_account = create_mock_system_account("SystemAccount", rent_exempt_amount);
let wallet_address = "11111111111111111111111111111112";
let result = scanner.check_empty_account(&system_account, wallet_address).await;
assert!(result.is_ok());
let empty_account = result.unwrap();
assert!(empty_account.is_some());
let account = empty_account.unwrap();
assert_eq!(account.address, "SystemAccount");
assert_eq!(account.lamports, rent_exempt_amount);
assert_eq!(account.owner, solana_program::system_program::id().to_string());
assert!(account.mint.is_none());
}
#[tokio::test]
async fn test_check_main_wallet_address() {
let scanner = WalletScanner::new(Arc::new(MockConnectionPool::new_simple()));
let wallet_address = "11111111111111111111111111111112";
let wallet_account = create_mock_system_account(wallet_address, 1000000);
let result = scanner.check_empty_account(&wallet_account, wallet_address).await;
assert!(result.is_ok());
let empty_account = result.unwrap();
assert!(empty_account.is_none()); }
#[tokio::test]
async fn test_scan_wallet_invalid_address() {
let scanner = WalletScanner::new(Arc::new(MockConnectionPool::new_simple()));
let invalid_address = "invalid_wallet_address";
let result = scanner.scan_wallet(invalid_address).await;
assert!(result.is_ok());
let scan_result = result.unwrap();
assert_eq!(scan_result.status, ScanStatus::Failed);
assert!(scan_result.error_message.is_some());
}
#[tokio::test]
async fn test_scan_wallet_structure() {
let scanner = WalletScanner::new(Arc::new(MockConnectionPool::new_simple()));
let wallet_address = "11111111111111111111111111111112";
let result = scanner.scan_wallet(wallet_address).await;
assert!(result.is_ok());
let scan_result = result.unwrap();
assert_eq!(scan_result.wallet_address, wallet_address);
assert_eq!(scan_result.id.to_string().len(), 36); assert!(scan_result.created_at <= Utc::now());
}
struct MockConnectionPool;
impl MockConnectionPool {
fn new() -> Self {
Self
}
fn new_simple() -> Self {
Self
}
}
#[async_trait::async_trait]
impl crate::rpc::ConnectionPoolTrait for MockConnectionPool {
async fn get_client(&self) -> Result<Arc<crate::rpc::RpcClientWrapper>, SolanaRecoverError> {
crate::rpc::RpcClientWrapper::new_with_url("https://api.mainnet-beta.solana.com", 30000)
.map(|client| Arc::new(client))
}
}
#[test]
fn test_token_account_info_structure() {
let token_info = TokenAccountInfo {
mint: "11111111111111111111111111111111".to_string(),
amount: 1000000,
};
assert_eq!(token_info.mint, "11111111111111111111111111111111");
assert_eq!(token_info.amount, 1000000);
}
#[test]
fn test_constants() {
assert_eq!(LAMPORTS_PER_SOL, 1_000_000_000.0);
}
#[test]
fn test_wallet_scanner_creation() {
let mock_pool = Arc::new(MockConnectionPool::new_simple());
let _scanner = WalletScanner::new(mock_pool);
assert!(true);
}
}