wp-solana-test-core 0.1.1

Protocol-agnostic Solana test infrastructure built on LiteSVM
Documentation
//! Test context providing a shared LiteSVM instance with mock RPC access.
//!
//! [`TestContext`] wraps LiteSVM in `Arc<Mutex<>>` so that both direct SVM
//! manipulation and RPC-based code paths can coexist in the same test.
//!
//! The mutex is `std::sync::Mutex`, not `tokio::sync::Mutex`: all critical
//! sections are short (they never span an `.await` point) and every helper
//! in this crate takes the lock, performs a synchronous SVM operation, and
//! drops it before yielding. Using `tokio::sync::Mutex` here would require
//! every call site to be async and — more importantly — `blocking_lock()`
//! panics when called from a tokio runtime context, which breaks
//! `#[tokio::test]` callers of the fixture-online helpers.

use std::sync::{Arc, Mutex, MutexGuard};

use anyhow::Result;
use litesvm::LiteSVM;
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_sdk::{
    clock::Clock,
    signature::{Keypair, Signer},
};

use crate::mock_rpc::MockRpcSender;

/// Shared test context that bundles a LiteSVM instance, an RPC client backed
/// by [`MockRpcSender`], and a funded payer keypair.
pub struct TestContext {
    /// LiteSVM instance behind a synchronous mutex for shared access.
    pub svm: Arc<Mutex<LiteSVM>>,
    /// Non-blocking RPC client that dispatches to the same LiteSVM.
    pub rpc: RpcClient,
    /// Pre-funded payer keypair (1 000 SOL).
    pub payer: Arc<Keypair>,
}

impl TestContext {
    /// Acquire the shared LiteSVM guard, panicking on poison.
    ///
    /// A poisoned mutex means a prior test panicked mid-critical-section
    /// and the SVM state is no longer trustworthy, so failing fast is the
    /// right call for test infrastructure.
    pub fn lock_svm(&self) -> MutexGuard<'_, LiteSVM> {
        self.svm.lock().expect("SVM mutex poisoned")
    }
}

/// Create a new [`TestContext`] with a fresh LiteSVM, mock RPC client, and a
/// payer funded with 1 000 SOL.
pub fn new_test_context() -> Result<TestContext> {
    let mut svm = LiteSVM::new();

    // Set clock to current wall-clock time
    let current_ts = chrono::Utc::now().timestamp();
    let mut updated_clock = svm.get_sysvar::<Clock>();
    updated_clock.unix_timestamp = current_ts;
    svm.set_sysvar::<Clock>(&updated_clock);

    // Fund payer with 1 000 SOL
    let payer = Arc::new(Keypair::new());
    let payer_balance: u64 = 1_000_000_000_000;
    svm.airdrop(&payer.pubkey(), payer_balance)
        .map_err(|e| anyhow::anyhow!("Failed to fund payer account: {:?}", e))?;

    let svm = Arc::new(Mutex::new(svm));
    let rpc = MockRpcSender::new(Arc::clone(&svm)).create_rpc_client();

    Ok(TestContext { svm, rpc, payer })
}