1#![cfg_attr(not(feature = "std"), no_std)]
2
3use frame_support::storage::{with_transaction, TransactionOutcome};
4use sp_runtime::DispatchError;
5use sp_std::result::Result;
6
7pub mod offchain_worker;
8pub mod ordered_set;
9
10pub use offchain_worker::OffchainErr;
11pub use ordered_set::OrderedSet;
12
13pub fn with_transaction_result<R>(f: impl FnOnce() -> Result<R, DispatchError>) -> Result<R, DispatchError> {
21 with_transaction(|| {
22 let res = f();
23 if res.is_ok() {
24 TransactionOutcome::Commit(res)
25 } else {
26 TransactionOutcome::Rollback(res)
27 }
28 })
29}
30
31pub fn simulate_execution<R>(f: impl FnOnce() -> Result<R, DispatchError>) -> Result<R, DispatchError> {
34 with_transaction(|| {
35 let res = f();
36 TransactionOutcome::Rollback(res)
37 })
38}
39
40#[cfg(test)]
41mod tests {
42 use super::*;
43 use frame_support::{assert_noop, assert_ok, construct_runtime, derive_impl, pallet_prelude::*};
44 use sp_io::TestExternalities;
45 use sp_runtime::traits::IdentityLookup;
46 use sp_runtime::{DispatchError, DispatchResult};
47 use sp_std::result::Result;
48
49 #[allow(dead_code)]
50 #[frame_support::pallet]
51 pub mod module {
52 use super::*;
53
54 #[pallet::config]
55 pub trait Config: frame_system::Config {}
56
57 #[pallet::pallet]
58 pub struct Pallet<T>(_);
59
60 #[pallet::storage]
61 pub type Value<T: Config> = StorageValue<_, u32, ValueQuery>;
62
63 #[pallet::storage]
64 pub type Map<T: Config> = StorageMap<_, Twox64Concat, [u8; 4], u32, ValueQuery>;
65 }
66
67 use module::*;
68
69 #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
70 impl frame_system::Config for Runtime {
71 type AccountId = u128;
72 type Lookup = IdentityLookup<Self::AccountId>;
73 type Block = Block;
74 }
75
76 impl module::Config for Runtime {}
77
78 type Block = frame_system::mocking::MockBlock<Runtime>;
79
80 construct_runtime!(
81 pub enum Runtime {
82 System: frame_system,
83 TestModule: module,
84 }
85 );
86
87 #[test]
88 fn storage_transaction_basic_commit() {
89 TestExternalities::default().execute_with(|| {
90 assert_eq!(Value::<Runtime>::get(), 0);
91 assert!(!Map::<Runtime>::contains_key(b"val0"));
92
93 assert_ok!(with_transaction_result(|| -> DispatchResult {
94 Value::<Runtime>::set(99);
95 Map::<Runtime>::insert(b"val0", 99);
96 assert_eq!(Value::<Runtime>::get(), 99);
97 assert_eq!(Map::<Runtime>::get(b"val0"), 99);
98 Ok(())
99 }));
100
101 assert_eq!(Value::<Runtime>::get(), 99);
102 assert_eq!(Map::<Runtime>::get(b"val0"), 99);
103 });
104 }
105
106 #[test]
107 fn storage_transaction_basic_rollback() {
108 TestExternalities::default().execute_with(|| {
109 assert_eq!(Value::<Runtime>::get(), 0);
110 assert_eq!(Map::<Runtime>::get(b"val0"), 0);
111
112 assert_noop!(
113 with_transaction_result(|| -> DispatchResult {
114 Value::<Runtime>::set(99);
115 Map::<Runtime>::insert(b"val0", 99);
116 assert_eq!(Value::<Runtime>::get(), 99);
117 assert_eq!(Map::<Runtime>::get(b"val0"), 99);
118 Err("test".into())
119 }),
120 DispatchError::Other("test")
121 );
122
123 assert_eq!(Value::<Runtime>::get(), 0);
124 assert_eq!(Map::<Runtime>::get(b"val0"), 0);
125 });
126 }
127
128 #[test]
129 fn simulate_execution_works() {
130 TestExternalities::default().execute_with(|| {
131 assert_eq!(Value::<Runtime>::get(), 0);
132 assert_eq!(Map::<Runtime>::get(b"val0"), 0);
133
134 assert_noop!(
136 simulate_execution(|| -> DispatchResult {
137 Value::<Runtime>::set(99);
138 Map::<Runtime>::insert(b"val0", 99);
139 Err(DispatchError::Other("test"))
140 }),
141 DispatchError::Other("test")
142 );
143 assert_eq!(Value::<Runtime>::get(), 0);
144 assert_eq!(Map::<Runtime>::get(b"val0"), 0);
145
146 assert_ok!(
148 simulate_execution(|| -> Result<u32, DispatchError> {
149 Value::<Runtime>::set(99);
150 Map::<Runtime>::insert(b"val0", 99);
151 Ok(99)
152 }),
153 99
154 );
155 assert_eq!(Value::<Runtime>::get(), 0);
156 assert_eq!(Map::<Runtime>::get(b"val0"), 0);
157 });
158 }
159}