1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! System API for the sandbox.

use frame_support::sp_runtime::{traits::Dispatchable, DispatchResultWithInfo};
use frame_system::pallet_prelude::BlockNumberFor;

use super::Sandbox;
use crate::{EventRecordOf, Runtime, RuntimeCall};

impl<R: Runtime> Sandbox<R> {
    /// Return the current height of the chain.
    pub fn block_number(&mut self) -> BlockNumberFor<R::Config> {
        self.execute_with(|| frame_system::Pallet::<R::Config>::block_number())
    }

    /// Return the events of the current block so far.
    pub fn events(&mut self) -> Vec<EventRecordOf<R::Config>> {
        self.execute_with(frame_system::Pallet::<R::Config>::events)
    }

    /// Reset the events of the current block.
    pub fn reset_events(&mut self) {
        self.execute_with(frame_system::Pallet::<R::Config>::reset_events)
    }

    /// Execute a runtime call (dispatchable).
    ///
    /// # Arguments
    ///
    /// * `call` - The runtime call to execute.
    /// * `origin` - The origin of the call.
    pub fn runtime_call<Origin: Into<<RuntimeCall<R::Config> as Dispatchable>::RuntimeOrigin>>(
        &mut self,
        call: RuntimeCall<R::Config>,
        origin: Origin,
    ) -> DispatchResultWithInfo<<RuntimeCall<R::Config> as Dispatchable>::PostInfo> {
        self.execute_with(|| call.dispatch(origin.into()))
    }
}

#[cfg(test)]
mod tests {
    use frame_support::sp_runtime::{traits::Dispatchable, DispatchResultWithInfo};

    use crate::{
        runtime::{minimal::RuntimeEvent, ConfigOf, MinimalRuntime, Runtime},
        AccountId32, RuntimeCall, Sandbox,
    };

    type RuntimeCallOf<R> = RuntimeCall<ConfigOf<R>>;

    fn make_transfer(
        sandbox: &mut Sandbox<MinimalRuntime>,
        dest: AccountId32,
        value: u128,
    ) -> DispatchResultWithInfo<<RuntimeCallOf<MinimalRuntime> as Dispatchable>::PostInfo> {
        assert_ne!(
            MinimalRuntime::default_actor(),
            dest,
            "make_transfer should send to account different than default_actor"
        );
        sandbox.runtime_call(
            RuntimeCall::<ConfigOf<MinimalRuntime>>::Balances(pallet_balances::Call::<
                ConfigOf<MinimalRuntime>,
            >::transfer_allow_death {
                dest: dest.into(),
                value,
            }),
            Some(MinimalRuntime::default_actor()),
        )
    }

    #[test]
    fn dry_run_works() {
        let mut sandbox = Sandbox::<MinimalRuntime>::new().expect("Failed to create sandbox");
        let actor = MinimalRuntime::default_actor();
        let initial_balance = sandbox.free_balance(&actor);

        sandbox.dry_run(|runtime| {
            runtime.mint_into(actor.clone(), 100).unwrap();
            assert_eq!(runtime.free_balance(&actor), initial_balance + 100);
        });

        assert_eq!(sandbox.free_balance(&actor), initial_balance);
    }

    #[test]
    fn runtime_call_works() {
        let mut sandbox = Sandbox::<MinimalRuntime>::new().expect("Failed to create sandbox");

        const RECIPIENT: AccountId32 = AccountId32::new([2u8; 32]);
        let initial_balance = sandbox.free_balance(&RECIPIENT);

        let result = make_transfer(&mut sandbox, RECIPIENT, 100);
        assert!(result.is_ok());

        let expected_balance = initial_balance + 100;
        assert_eq!(sandbox.free_balance(&RECIPIENT), expected_balance);
    }

    #[test]
    fn current_events() {
        let mut sandbox = Sandbox::<MinimalRuntime>::new().expect("Failed to create sandbox");
        const RECIPIENT: AccountId32 = AccountId32::new([2u8; 32]);

        let events_before = sandbox.events();
        assert!(events_before.is_empty());

        make_transfer(&mut sandbox, RECIPIENT, 1).expect("Failed to make transfer");

        let events_after = sandbox.events();
        assert!(!events_after.is_empty());
        assert!(matches!(
            events_after.last().unwrap().event,
            RuntimeEvent::Balances(_)
        ));
    }

    #[test]
    fn resetting_events() {
        let mut sandbox = Sandbox::<MinimalRuntime>::new().expect("Failed to create sandbox");
        const RECIPIENT: AccountId32 = AccountId32::new([3u8; 32]);

        make_transfer(&mut sandbox, RECIPIENT.clone(), 1).expect("Failed to make transfer");

        assert!(!sandbox.events().is_empty());
        sandbox.reset_events();
        assert!(sandbox.events().is_empty());

        make_transfer(&mut sandbox, RECIPIENT, 1).expect("Failed to make transfer");
        assert!(!sandbox.events().is_empty());
    }
}