Skip to main content

wp_solana_test_core/
context.rs

1//! Test context providing a shared LiteSVM instance with mock RPC access.
2//!
3//! [`TestContext`] wraps LiteSVM in `Arc<Mutex<>>` so that both direct SVM
4//! manipulation and RPC-based code paths can coexist in the same test.
5//!
6//! The mutex is `std::sync::Mutex`, not `tokio::sync::Mutex`: all critical
7//! sections are short (they never span an `.await` point) and every helper
8//! in this crate takes the lock, performs a synchronous SVM operation, and
9//! drops it before yielding. Using `tokio::sync::Mutex` here would require
10//! every call site to be async and — more importantly — `blocking_lock()`
11//! panics when called from a tokio runtime context, which breaks
12//! `#[tokio::test]` callers of the fixture-online helpers.
13
14use std::sync::{Arc, Mutex, MutexGuard};
15
16use anyhow::Result;
17use litesvm::LiteSVM;
18use solana_client::nonblocking::rpc_client::RpcClient;
19use solana_sdk::{
20    clock::Clock,
21    signature::{Keypair, Signer},
22};
23
24use crate::mock_rpc::MockRpcSender;
25
26/// Shared test context that bundles a LiteSVM instance, an RPC client backed
27/// by [`MockRpcSender`], and a funded payer keypair.
28pub struct TestContext {
29    /// LiteSVM instance behind a synchronous mutex for shared access.
30    pub svm: Arc<Mutex<LiteSVM>>,
31    /// Non-blocking RPC client that dispatches to the same LiteSVM.
32    pub rpc: RpcClient,
33    /// Pre-funded payer keypair (1 000 SOL).
34    pub payer: Arc<Keypair>,
35}
36
37impl TestContext {
38    /// Acquire the shared LiteSVM guard, panicking on poison.
39    ///
40    /// A poisoned mutex means a prior test panicked mid-critical-section
41    /// and the SVM state is no longer trustworthy, so failing fast is the
42    /// right call for test infrastructure.
43    pub fn lock_svm(&self) -> MutexGuard<'_, LiteSVM> {
44        self.svm.lock().expect("SVM mutex poisoned")
45    }
46}
47
48/// Create a new [`TestContext`] with a fresh LiteSVM, mock RPC client, and a
49/// payer funded with 1 000 SOL.
50pub fn new_test_context() -> Result<TestContext> {
51    let mut svm = LiteSVM::new();
52
53    // Set clock to current wall-clock time
54    let current_ts = chrono::Utc::now().timestamp();
55    let mut updated_clock = svm.get_sysvar::<Clock>();
56    updated_clock.unix_timestamp = current_ts;
57    svm.set_sysvar::<Clock>(&updated_clock);
58
59    // Fund payer with 1 000 SOL
60    let payer = Arc::new(Keypair::new());
61    let payer_balance: u64 = 1_000_000_000_000;
62    svm.airdrop(&payer.pubkey(), payer_balance)
63        .map_err(|e| anyhow::anyhow!("Failed to fund payer account: {:?}", e))?;
64
65    let svm = Arc::new(Mutex::new(svm));
66    let rpc = MockRpcSender::new(Arc::clone(&svm)).create_rpc_client();
67
68    Ok(TestContext { svm, rpc, payer })
69}