Skip to main content

thruput_native_exec/
transaction_builder.rs

1use crate::frame_result::FrameResult;
2use crate::mock_transaction_handler::{
3    MockTransactionConfig, MockTransactionHandler,
4};
5use crate::test_environment::TestEnvironmentHandle;
6use crate::transact_result::TransactResult;
7use thruput_xdr::auth_claim::AuthClaim;
8
9/// Builder for configuring a mocked transaction before executing it.
10///
11/// Obtain one from [`TestEnvironment::transaction`](crate::test_environment::TestEnvironment::transaction),
12/// chain `with_*` methods to configure it, then finish with
13/// [`transact`](Self::transact), [`transact_manual`](Self::transact_manual),
14/// or [`build`](Self::build):
15///
16/// ```ignore
17/// env.transaction()
18///     .with_auth_claim(my_claim)
19///     .transact(|tx| tx.call(&addr).run(|| { /* ... */ }));
20/// ```
21///
22/// `env.transact(f)` and `env.transact_manual(f)` are shorthand for
23/// `env.transaction().transact(f)` / `.transact_manual(f)` with the default
24/// configuration.
25#[must_use = "a TransactionBuilder does nothing until `transact`, `transact_manual`, or `build` is called"]
26pub struct TransactionBuilder {
27    env: TestEnvironmentHandle,
28    config: MockTransactionConfig,
29}
30
31impl TransactionBuilder {
32    /// Start a builder with the default [`MockTransactionConfig`].
33    pub fn new(env: TestEnvironmentHandle) -> Self {
34        Self {
35            env,
36            config: MockTransactionConfig::default(),
37        }
38    }
39
40    /// Attach `auth_claim` to the mocked transaction instead of the default
41    /// source-address claim, controlling the auth-claim the contract under
42    /// test observes (e.g. via `get_auth_claim`).
43    pub fn with_auth_claim(mut self, auth_claim: AuthClaim) -> Self {
44        self.config.auth_claim = auth_claim;
45        self
46    }
47
48    /// Create the configured [`MockTransactionHandler`] for manual
49    /// orchestration. Prefer [`transact`](Self::transact) or
50    /// [`transact_manual`](Self::transact_manual) unless you need the handler
51    /// directly.
52    pub fn build(self) -> MockTransactionHandler {
53        MockTransactionHandler::new_with_config(self.env, self.config)
54    }
55
56    /// Execute a transaction with auto-commit. See
57    /// [`TestEnvironment::transact`](crate::test_environment::TestEnvironment::transact).
58    #[must_use]
59    pub fn transact(
60        self,
61        f: impl FnOnce(&mut MockTransactionHandler) -> FrameResult,
62    ) -> TransactResult {
63        let mut tx = self.build();
64        let frame_result = f(&mut tx);
65        let exec_result = tx.try_commit_transaction().exec_result();
66        TransactResult::new(frame_result, exec_result)
67    }
68
69    /// Execute a transaction with manual commit/abort control. See
70    /// [`TestEnvironment::transact_manual`](crate::test_environment::TestEnvironment::transact_manual).
71    ///
72    /// The closure must call `tx.try_commit_transaction()` or
73    /// `tx.abort_transaction()` before returning; forgetting panics here
74    /// (a dropped handler would abort silently).
75    pub fn transact_manual<R>(
76        self,
77        f: impl FnOnce(&mut MockTransactionHandler) -> R,
78    ) -> R {
79        let mut tx = self.build();
80        let out = f(&mut tx);
81        assert!(
82            !tx.is_running(),
83            "transact_manual closure returned without finishing the \
84             transaction: call tx.try_commit_transaction() or \
85             tx.abort_transaction() before returning"
86        );
87        out
88    }
89}