scrypto_test/environment/
env.rs

1#![allow(clippy::test_attr_in_doctest)]
2
3//! This module defines and implements the [`TestEnvironment`] struct.
4
5use super::*;
6use crate::prelude::*;
7
8/// The environment that all tests of this testing framework are run against.
9///
10/// This struct may be thought of as the main struct in this testing framework which encapsulates a
11/// a self-contained instance of the Radix Engine ([`EncapsulatedRadixEngine`]). The functionality
12/// of the Radix Engine is exposed through the [`SystemApi`] which makes this testing environment no
13/// less capable than Scrypto code.
14///
15/// ## Introduction
16///
17/// This testing framework is designed to allow you to write Scrypto-like code and use that to test
18/// your packages and blueprints and follows a different approach from the `LedgerSimulator` class. The
19/// test-runner is an in-memory ledger simulator which you can interact with as a user that submits
20/// transactions to the network. The approach followed by this testing framework is different,
21/// instead of submitting transactions, you're making invocations to the Radix Engine, getting
22/// results back, and then writing assertions against what you got back.
23///
24/// Both the LedgerSimulator and this testing framework will prove to be useful throughout your blueprint
25/// development journey. As an example, this testing framework allows you to disable some of kernel
26/// modules that may get in your way when writing tests so it may be an optimal framework to use to
27/// ensure that the "math checks out" in your blueprint code without needing to think about costing
28/// or auth. However, when you're reaching the final stages of developing a blueprint you may want
29/// tests that check that interactions with your blueprint will succeed in a simulated setting that
30/// is close to the real setting, which is when the `LedgerSimulator` comes in. Overall, we may put these
31/// two frameworks into two categories: This framework (named scrypto-test) is a framework for unit
32/// testing your blueprints and is a good framework to use to check that your DeFi logic is correct.
33/// The `LedgerSimulator` is an integration testing or an end-to-end testing framework to test that your
34/// blueprints work in a simulated ledger with all of the costing limits, substate limits, and other
35/// limits applied.
36///
37/// ## Features
38///
39/// This framework has many new features that developers may find useful when testing their packages
40/// some of those features are:
41///
42/// * The ability to create mock [`Bucket`]s and [`Proof`]s through two main ways: by creating them
43///   out of thin air, and by disabling the auth module and minting them. This functionality can be
44///   found in the [`BucketFactory`] and [`ProofFactory`] structs and the [`CreationStrategy`].
45/// * The ability to query the contents of [`Bucket`]s and [`Proof`]s for the purpose of writing
46///   assertions against them. Not only that, but this testing framework allows you to call any
47///   method you wish on these nodes. As an example, in a test, you can get a [`Bucket`] and then
48///   create a proof out of it in manner similar to Scrypto.
49/// * The ability to enable and disable kernel modules at runtime. The Radix Engine kernel is quite
50///   modular with concepts such as auth, costing, and limits being implemented as kernel modules.
51///   Disabling or enabling kernel modules at runtime can prove to be quite useful when writing DeFi
52///   tests. As an example, you may want to not think about costing at all when writing tests and
53///   thus you may opt to disable the costing module entirely and continue your test without it.
54///   This can be done through [`TestEnvironment::disable_costing_module`].
55/// * This testing framework uses test bindings to provide a higher-level API for calling methods
56///   and functions on a blueprint without the need to do raw [`TestEnvironment::call_method_typed`]
57///   or [`TestEnvironment::call_function_typed`]. The test bindings are generated by the blueprint
58///   macro and are feature gated behind a `test` feature.
59///
60/// ## Getting Started
61///
62/// The following example shows a very simple test that gets XRD from the faucet and then asserts
63/// that the amount is equal to what we expect.
64///
65/// ```
66/// use scrypto_test::prelude::*;
67///
68/// fn my_test() -> Result<(), RuntimeError> {
69///     // Arrange
70///     let mut env = TestEnvironment::new();
71///
72///     // Act
73///     let bucket = env.call_method_typed::<_, _, Bucket>(
74///         FAUCET,
75///         "free",
76///         &(),
77///     )?;
78///
79///     // Assert
80///     let amount = bucket.amount(&mut env)?;
81///     assert_eq!(amount, dec!("10000"));
82///
83///     Ok(())
84/// }
85/// ```
86///
87/// A few things to note about the code you see above:
88///
89/// * There is no transactions, worktop, receipt, manifests or anything of that sort! This part is
90///   "not just hidden" from this testing framework but is actually non existent! The approach that
91///   framework of wrapping a self-contained Radix Engine means that there is no need for manifests
92///   or other transaction concepts.
93/// * Methods such as [`Bucket::amount`] can be called to get the amount of resources in a bucket
94///   and then assert against that.
95///
96/// ## Manipulating Kernel Modules
97///
98/// At runtime, the kernel modules can be enabled or disabled. For each kernel module there are four
99/// methods on the [`TestEnvironment`]:
100///
101/// * A method to enable the kernel module (e.g., [`TestEnvironment::enable_costing_module`]).
102/// * A method to disable the kernel module (e.g., [`TestEnvironment::disable_costing_module`]).
103/// * A method to perform some action in a callback with the module enabled (e.g.,
104///   [`TestEnvironment::with_costing_module_enabled`]).
105/// * A method to perform some action in a callback with the module disabled (e.g.,
106///   [`TestEnvironment::with_costing_module_disabled`]).
107///
108/// The simple enable and disable methods are quite straightforward: call them to enable or disable
109/// a kernel module. The `with_*` methods are a little bit more intricate, they allow you to perform
110/// some actions with a specific kernel either enabled or disabled and then resets the state of the
111/// kernel modules afterwards. As an example:
112///
113/// ```
114/// use scrypto_test::prelude::*;
115///
116/// // Arrange
117/// let mut env = TestEnvironment::new();
118///
119/// // Act
120/// let bucket = env.with_auth_module_disabled(|env| {
121///     /* Auth Module is disabled just before this point */
122///     ResourceManager(XRD).mint_fungible(100.into(), env)
123///     /* Kernel modules are reset just after this point. */
124/// })?;
125///
126/// // Assert
127/// let amount = bucket.amount(&mut env)?;
128/// assert_eq!(amount, dec!("100"));
129///
130/// # Ok::<(), RuntimeError>(())
131/// ```
132///
133/// ## Common Arranges or Teardowns
134///
135/// There are cases where you may have many tests that all share a large portion of your arrange
136/// or teardown logic. While this framework does not specifically provide solutions for this, there
137/// are many useful Rust patterns that may be employed here to allow you to do this: the simplest
138/// and the most elegant is probably by using callback functions.
139///
140/// Imagine this, you're building a Dex and many of the tests you write require you to have two
141/// resources with a very large supply so you can write your tests with. You can achieve this by
142/// doing something like:
143///
144/// ```
145/// use scrypto_test::prelude::*;
146///
147/// pub fn two_resource_environment<F>(func: F)
148/// where
149///     F: FnOnce(DefaultTestEnvironment, FungibleBucket, FungibleBucket),
150/// {
151///     let mut env = TestEnvironment::new();
152///     let bucket1 = ResourceBuilder::new_fungible(OwnerRole::None)
153///         .mint_initial_supply(dec!("100000000000"), &mut env)
154///         .unwrap();
155///     let bucket2 = ResourceBuilder::new_fungible(OwnerRole::None)
156///         .mint_initial_supply(dec!("100000000000"), &mut env)
157///         .unwrap();
158///     func(env, bucket1, bucket2)
159///
160///     /* Potential teardown happens here */
161/// }
162///
163/// #[test]
164/// fn contribution_provides_expected_amount_of_pool_units() {
165///     two_resource_environment(|mut env, bucket1, bucket2| {
166///         /* Your test goes here */
167///     })
168/// }
169/// ```
170///
171/// You may have a function like `two_resource_environment` seen above which sets up the environment
172/// and then some callback and potentially then executes some teardown code. Another way to do this
173/// would be through simple factory and destructor methods.
174/// ```
175pub struct TestEnvironment<D>(pub(super) EncapsulatedRadixEngine<D>)
176where
177    D: SubstateDatabase + CommittableSubstateDatabase + 'static;
178
179pub type DefaultTestEnvironment = TestEnvironment<InMemorySubstateDatabase>;
180
181impl DefaultTestEnvironment {
182    pub fn new() -> Self {
183        TestEnvironmentBuilder::new().build()
184    }
185}
186
187impl<D> TestEnvironment<D>
188where
189    D: SubstateDatabase + CommittableSubstateDatabase + 'static,
190{
191    // ===================
192    // region:invocations
193    // ===================
194
195    /// Invokes a function on the provided blueprint and package with the given arguments.
196    ///
197    /// This method is a typed version of the [`SystemBlueprintApi::call_function`] which Scrypto
198    /// encodes the arguments and Scrypto decodes the returns on behalf of the caller. This method
199    /// assumes that the caller is correct about the argument and return types and panics if the
200    /// encoding or decoding fails.
201    ///
202    /// # Arguments
203    ///
204    /// * `package_address`: [`PackageAddress`] - The address of the package that contains the
205    ///   blueprint.
206    /// * `blueprint_name`: [`&str`] - The name of the blueprint.
207    /// * `function_name`: [`&str`] - The nae of the function.
208    /// * `args`: `&I` - The arguments to invoke the method with. This is a generic arguments that
209    ///   is fulfilled by any type that implements [`ScryptoEncode`].
210    ///
211    /// # Returns
212    ///
213    /// * [`Result<O, RuntimeError>`] - The returns from the method invocation. If the invocation
214    ///   was successful a [`Result::Ok`] is returned, otherwise a [`Result::Err`] is returned. The
215    ///   [`Result::Ok`] variant is a generic that's fulfilled by any type that implements
216    ///   [`ScryptoDecode`].
217    ///
218    /// # Panics
219    ///
220    /// This method panics in the following two cases:
221    ///
222    /// * Through an unwrap when calling [`scrypto_encode`] on the method arguments. Please consult
223    ///   the SBOR documentation on more information on why SBOR encoding may fail.
224    /// * Through an unwrap when calling [`scrypto_decode`] on the returns. This panics if the type
225    ///   could be decoded as the desired output type.
226    pub fn call_function_typed<I, O>(
227        &mut self,
228        package_address: PackageAddress,
229        blueprint_name: &str,
230        function_name: &str,
231        args: &I,
232    ) -> Result<O, RuntimeError>
233    where
234        I: ScryptoEncode,
235        O: ScryptoDecode,
236    {
237        let args = scrypto_encode(args).expect("Scrypto encoding of args failed");
238        self.call_function(package_address, blueprint_name, function_name, args)
239            .map(|rtn| scrypto_decode(&rtn).expect("Scrypto decoding of returns failed"))
240    }
241
242    /// Invokes a method on the main module of a node with the provided typed arguments.
243    ///
244    /// This method is a typed version of the [`SystemObjectApi::call_method`] which Scrypto encodes
245    /// the arguments and Scrypto decodes the returns on behalf of the caller. This method assumes
246    /// that the caller is correct about the argument and return types and panics if the encoding or
247    /// decoding fails.
248    ///
249    /// # Arguments
250    ///
251    /// * `node_id`: `T` - The node to invoke the method on. This is a generic argument that's
252    ///   fulfilled by any type that implements [`Into<NodeId>`], thus, any address type can be used.
253    /// * `method_name`: [`&str`] - The name of the method to invoke.
254    /// * `args`: `&I` - The arguments to invoke the method with. This is a generic arguments that
255    ///   is fulfilled by any type that implements [`ScryptoEncode`].
256    ///
257    /// # Returns
258    ///
259    /// * [`Result<O, RuntimeError>`] - The returns from the method invocation. If the invocation
260    ///   was successful a [`Result::Ok`] is returned, otherwise a [`Result::Err`] is returned. The
261    ///   [`Result::Ok`] variant is a generic that's fulfilled by any type that implements
262    ///   [`ScryptoDecode`].
263    ///
264    /// # Panics
265    ///
266    /// This method panics in the following two cases:
267    ///
268    /// * Through an unwrap when calling [`scrypto_encode`] on the method arguments. Please consult
269    ///   the SBOR documentation on more information on why SBOR encoding may fail.
270    /// * Through an unwrap when calling [`scrypto_decode`] on the returns. This panics if the type
271    ///   could be decoded as the desired output type.
272    pub fn call_method_typed<N, I, O>(
273        &mut self,
274        node_id: N,
275        method_name: &str,
276        args: &I,
277    ) -> Result<O, RuntimeError>
278    where
279        N: Into<NodeId>,
280        I: ScryptoEncode,
281        O: ScryptoDecode,
282    {
283        let args = scrypto_encode(args).expect("Scrypto encoding of args failed");
284        self.call_method(&node_id.into(), method_name, args)
285            .map(|rtn| scrypto_decode(&rtn).expect("Scrypto decoding of returns failed"))
286    }
287
288    /// Invokes a method on the main module of a node with the provided typed arguments.
289    ///
290    /// This method is a typed version of the [`SystemObjectApi::call_method`] which Scrypto encodes
291    /// the arguments and Scrypto decodes the returns on behalf of the caller. This method assumes
292    /// that the caller is correct about the argument and return types and panics if the encoding or
293    /// decoding fails.
294    ///
295    /// # Arguments
296    ///
297    /// * `node_id`: `T` - The node to invoke the method on. This is a generic argument that's
298    ///   fulfilled by any type that implements [`Into<NodeId>`], thus, any address type can be used.
299    /// * `method_name`: [`&str`] - The name of the method to invoke.
300    /// * `args`: `&I` - The arguments to invoke the method with. This is a generic arguments that
301    ///   is fulfilled by any type that implements [`ScryptoEncode`].
302    ///
303    /// # Returns
304    ///
305    /// * [`Result<O, RuntimeError>`] - The returns from the method invocation. If the invocation
306    ///   was successful a [`Result::Ok`] is returned, otherwise a [`Result::Err`] is returned. The
307    ///   [`Result::Ok`] variant is a generic that's fulfilled by any type that implements
308    ///   [`ScryptoDecode`].
309    ///
310    /// # Panics
311    ///
312    /// This method panics in the following two cases:
313    ///
314    /// * Through an unwrap when calling [`scrypto_encode`] on the method arguments. Please consult
315    ///   the SBOR documentation on more information on why SBOR encoding may fail.
316    /// * Through an unwrap when calling [`scrypto_decode`] on the returns. This panics if the type
317    ///   could be decoded as the desired output type.
318    pub fn call_direct_access_method_typed<N, I, O>(
319        &mut self,
320        node_id: N,
321        method_name: &str,
322        args: &I,
323    ) -> Result<O, RuntimeError>
324    where
325        N: Into<NodeId>,
326        I: ScryptoEncode,
327        O: ScryptoDecode,
328    {
329        let args = scrypto_encode(args).expect("Scrypto encoding of args failed");
330        self.call_direct_access_method(&node_id.into(), method_name, args)
331            .map(|rtn| scrypto_decode(&rtn).expect("Scrypto decoding of returns failed"))
332    }
333
334    /// Invokes a method on a module of a node with the provided typed arguments.
335    ///
336    /// This method is a typed version of the [`SystemObjectApi::call_method`] which Scrypto encodes
337    /// the arguments and Scrypto decodes the returns on behalf of the caller. This method assumes
338    /// that the caller is correct about the argument and return types and panics if the encoding or
339    /// decoding fails.
340    ///
341    /// # Arguments
342    ///
343    /// * `node_id`: `T` - The node to invoke the method on. This is a generic argument that's
344    ///   fulfilled by any type that implements [`Into<NodeId>`], thus, any address type can be used.
345    /// * `module`: [`AttachedModuleId`] - The module id.
346    /// * `method_name`: [`&str`] - The name of the method to invoke.
347    /// * `args`: `&I` - The arguments to invoke the method with. This is a generic arguments that
348    ///   is fulfilled by any type that implements [`ScryptoEncode`].
349    ///
350    /// # Returns
351    ///
352    /// * [`Result<O, RuntimeError>`] - The returns from the method invocation. If the invocation
353    ///   was successful a [`Result::Ok`] is returned, otherwise a [`Result::Err`] is returned. The
354    ///   [`Result::Ok`] variant is a generic that's fulfilled by any type that implements
355    ///   [`ScryptoDecode`].
356    ///
357    /// # Panics
358    ///
359    /// This method panics in the following two cases:
360    ///
361    /// * Through an unwrap when calling [`scrypto_encode`] on the method arguments. Please consult
362    ///   the SBOR documentation on more information on why SBOR encoding may fail.
363    /// * Through an unwrap when calling [`scrypto_decode`] on the returns. This panics if the type
364    ///   could be decoded as the desired output type.
365    pub fn call_module_method_typed<N, I, O>(
366        &mut self,
367        node_id: N,
368        module: AttachedModuleId,
369        method_name: &str,
370        args: &I,
371    ) -> Result<O, RuntimeError>
372    where
373        N: Into<NodeId>,
374        I: ScryptoEncode,
375        O: ScryptoDecode,
376    {
377        let args = scrypto_encode(args).expect("Scrypto encoding of args failed");
378        self.call_module_method(&node_id.into(), module, method_name, args)
379            .map(|rtn| scrypto_decode(&rtn).expect("Scrypto decoding of returns failed"))
380    }
381
382    //=======================
383    // endregion:invocations
384    // region:kernel-access
385    //=======================
386
387    pub fn with_kernel<F, O>(&mut self, callback: F) -> O
388    where
389        F: FnOnce(&TestKernel<'_, D>) -> O,
390    {
391        self.0.with_kernel(callback)
392    }
393
394    pub fn with_kernel_mut<F, O>(&mut self, callback: F) -> O
395    where
396        F: FnOnce(&mut TestKernel<'_, D>) -> O,
397    {
398        self.0.with_kernel_mut(callback)
399    }
400
401    //=========================
402    // endregion:kernel-access
403    // region:kernel-modules
404    //=========================
405
406    /// Enables the kernel trace kernel module of the Radix Engine.
407    pub fn enable_kernel_trace_module(&mut self) {
408        self.enable_module(EnabledModules::KERNEL_TRACE)
409    }
410
411    /// Enables the limits kernel module of the Radix Engine.
412    pub fn enable_limits_module(&mut self) {
413        self.enable_module(EnabledModules::LIMITS)
414    }
415
416    /// Enables the costing kernel module of the Radix Engine.
417    pub fn enable_costing_module(&mut self) {
418        self.enable_module(EnabledModules::COSTING)
419    }
420
421    /// Enables the auth kernel module of the Radix Engine.
422    pub fn enable_auth_module(&mut self) {
423        self.enable_module(EnabledModules::AUTH)
424    }
425
426    /// Enables the transaction env kernel module of the Radix Engine.
427    pub fn enable_transaction_runtime_module(&mut self) {
428        self.enable_module(EnabledModules::TRANSACTION_RUNTIME)
429    }
430
431    /// Enables the execution trace kernel module of the Radix Engine.
432    pub fn enable_execution_trace_module(&mut self) {
433        self.enable_module(EnabledModules::EXECUTION_TRACE)
434    }
435
436    /// Disables the kernel trace kernel module of the Radix Engine.
437    pub fn disable_kernel_trace_module(&mut self) {
438        self.disable_module(EnabledModules::KERNEL_TRACE)
439    }
440
441    /// Disables the limits kernel module of the Radix Engine.
442    pub fn disable_limits_module(&mut self) {
443        self.disable_module(EnabledModules::LIMITS)
444    }
445
446    /// Disables the costing kernel module of the Radix Engine.
447    pub fn disable_costing_module(&mut self) {
448        self.disable_module(EnabledModules::COSTING)
449    }
450
451    /// Disables the auth kernel module of the Radix Engine.
452    pub fn disable_auth_module(&mut self) {
453        self.disable_module(EnabledModules::AUTH)
454    }
455
456    /// Disables the transaction env kernel module of the Radix Engine.
457    pub fn disable_transaction_runtime_module(&mut self) {
458        self.disable_module(EnabledModules::TRANSACTION_RUNTIME)
459    }
460
461    /// Disables the execution trace kernel module of the Radix Engine.
462    pub fn disable_execution_trace_module(&mut self) {
463        self.disable_module(EnabledModules::EXECUTION_TRACE)
464    }
465
466    /// Calls the passed `callback` with the kernel trace kernel module enabled and then resets the
467    /// state of the kernel modules.
468    pub fn with_kernel_trace_module_enabled<F, O>(&mut self, callback: F) -> O
469    where
470        F: FnOnce(&mut Self) -> O,
471    {
472        let enabled_modules = self.enabled_modules();
473        self.enable_kernel_trace_module();
474        let rtn = callback(self);
475        self.set_enabled_modules(enabled_modules);
476        rtn
477    }
478
479    /// Calls the passed `callback` with the limits kernel module enabled and then resets the state
480    /// of the kernel modules.
481    pub fn with_limits_module_enabled<F, O>(&mut self, callback: F) -> O
482    where
483        F: FnOnce(&mut Self) -> O,
484    {
485        let enabled_modules = self.enabled_modules();
486        self.enable_limits_module();
487        let rtn = callback(self);
488        self.set_enabled_modules(enabled_modules);
489        rtn
490    }
491
492    /// Calls the passed `callback` with the costing kernel module enabled and then resets the state
493    /// of the kernel modules.
494    pub fn with_costing_module_enabled<F, O>(&mut self, callback: F) -> O
495    where
496        F: FnOnce(&mut Self) -> O,
497    {
498        let enabled_modules = self.enabled_modules();
499        self.enable_costing_module();
500        let rtn = callback(self);
501        self.set_enabled_modules(enabled_modules);
502        rtn
503    }
504
505    /// Calls the passed `callback` with the auth kernel module enabled and then resets the state of
506    /// the kernel modules.
507    pub fn with_auth_module_enabled<F, O>(&mut self, callback: F) -> O
508    where
509        F: FnOnce(&mut Self) -> O,
510    {
511        let enabled_modules = self.enabled_modules();
512        self.enable_auth_module();
513        let rtn = callback(self);
514        self.set_enabled_modules(enabled_modules);
515        rtn
516    }
517
518    /// Calls the passed `callback` with the transaction env kernel module enabled and then
519    /// resets the state of the kernel modules.
520    pub fn with_transaction_runtime_module_enabled<F, O>(&mut self, callback: F) -> O
521    where
522        F: FnOnce(&mut Self) -> O,
523    {
524        let enabled_modules = self.enabled_modules();
525        self.enable_transaction_runtime_module();
526        let rtn = callback(self);
527        self.set_enabled_modules(enabled_modules);
528        rtn
529    }
530
531    /// Calls the passed `callback` with the execution trace kernel module enabled and then resets
532    /// the state of the kernel modules.
533    pub fn with_execution_trace_module_enabled<F, O>(&mut self, callback: F) -> O
534    where
535        F: FnOnce(&mut Self) -> O,
536    {
537        let enabled_modules = self.enabled_modules();
538        self.enable_execution_trace_module();
539        let rtn = callback(self);
540        self.set_enabled_modules(enabled_modules);
541        rtn
542    }
543
544    /// Calls the passed `callback` with the kernel trace kernel module disabled and then resets the
545    /// state of the kernel modules.
546    pub fn with_kernel_trace_module_disabled<F, O>(&mut self, callback: F) -> O
547    where
548        F: FnOnce(&mut Self) -> O,
549    {
550        let enabled_modules = self.enabled_modules();
551        self.disable_kernel_trace_module();
552        let rtn = callback(self);
553        self.set_enabled_modules(enabled_modules);
554        rtn
555    }
556
557    /// Calls the passed `callback` with the limits kernel module disabled and then resets the state
558    /// of the kernel modules.
559    pub fn with_limits_module_disabled<F, O>(&mut self, callback: F) -> O
560    where
561        F: FnOnce(&mut Self) -> O,
562    {
563        let enabled_modules = self.enabled_modules();
564        self.disable_limits_module();
565        let rtn = callback(self);
566        self.set_enabled_modules(enabled_modules);
567        rtn
568    }
569
570    /// Calls the passed `callback` with the costing kernel module disabled and then resets the
571    /// state of the kernel modules.
572    pub fn with_costing_module_disabled<F, O>(&mut self, callback: F) -> O
573    where
574        F: FnOnce(&mut Self) -> O,
575    {
576        let enabled_modules = self.enabled_modules();
577        self.disable_costing_module();
578        let rtn = callback(self);
579        self.set_enabled_modules(enabled_modules);
580        rtn
581    }
582
583    /// Calls the passed `callback` with the auth kernel module disabled and then resets the state
584    /// of the kernel modules.
585    pub fn with_auth_module_disabled<F, O>(&mut self, callback: F) -> O
586    where
587        F: FnOnce(&mut Self) -> O,
588    {
589        let enabled_modules = self.enabled_modules();
590        self.disable_auth_module();
591        let rtn = callback(self);
592        self.set_enabled_modules(enabled_modules);
593        rtn
594    }
595
596    /// Calls the passed `callback` with the transaction env kernel module disabled and then
597    /// resets the state of the kernel modules.
598    pub fn with_transaction_runtime_module_disabled<F, O>(&mut self, callback: F) -> O
599    where
600        F: FnOnce(&mut Self) -> O,
601    {
602        let enabled_modules = self.enabled_modules();
603        self.disable_transaction_runtime_module();
604        let rtn = callback(self);
605        self.set_enabled_modules(enabled_modules);
606        rtn
607    }
608
609    /// Calls the passed `callback` with the execution trace kernel module disabled and then resets
610    /// the state of the kernel modules.
611    pub fn with_execution_trace_module_disabled<F, O>(&mut self, callback: F) -> O
612    where
613        F: FnOnce(&mut Self) -> O,
614    {
615        let enabled_modules = self.enabled_modules();
616        self.disable_execution_trace_module();
617        let rtn = callback(self);
618        self.set_enabled_modules(enabled_modules);
619        rtn
620    }
621
622    /// Returns the bit flags representing the currently enabled kernel modules.
623    pub fn enabled_modules(&self) -> EnabledModules {
624        self.0
625            .with_kernel(|kernel| kernel.kernel_callback().modules.enabled_modules)
626    }
627
628    /// Sets the bit flags representing the enabled kernel modules.
629    pub fn set_enabled_modules(&mut self, enabled_modules: EnabledModules) {
630        self.0.with_kernel_mut(|kernel| {
631            kernel.kernel_callback_mut().modules.enabled_modules = enabled_modules
632        })
633    }
634
635    /// Enables specific kernel module(s).
636    pub fn enable_module(&mut self, module: EnabledModules) {
637        self.0.with_kernel_mut(|kernel| {
638            kernel.kernel_callback_mut().modules.enabled_modules |= module
639        })
640    }
641
642    /// Disables specific kernel module(s).
643    pub fn disable_module(&mut self, module: EnabledModules) {
644        self.0.with_kernel_mut(|kernel| {
645            kernel.kernel_callback_mut().modules.enabled_modules &= !module
646        })
647    }
648
649    //==========================
650    // endregion:kernel-modules
651    // region:state
652    //==========================
653
654    /// Reads the state of a component and allows for a callback to be executed over the decoded
655    /// state.
656    ///
657    /// This method performs the steps needed to read the state of a component and then perform the
658    /// various steps needed before the state can be read or used such as the locking, reading, and
659    /// decoding of the substate and the various steps that need to be performed after the state is
660    /// read such as unlocking the substate.
661    ///
662    /// Users of this method are expected to pass in a callback function to operate over the state
663    /// as this is the main way that this method ensures that references do not escape out of this
664    /// method after the substate is closed.
665    ///
666    /// # Arguments
667    ///
668    /// * `node_id`: [`N`] - The address of the component to read the state of. This is a generic
669    ///   type parameter that's satisfied by any type that implements [`Into<NodeId>`].
670    /// * `callback`: [`F`] - A callback function to call after the component state has been read
671    ///   and decoded into the type specified by the generic parameter [`S`]. Anything returned from
672    ///   this callback is returned from this method unless an error happens after the callback is
673    ///   executed.
674    ///
675    /// # Returns
676    ///
677    /// * [`Result<O, RuntimeError>`] - The output of the callback function passed in or a runtime
678    ///   error if one of the steps failed.
679    ///
680    /// # Panics
681    ///
682    /// This method panics if the component state can not be decoded as the generic type parameter
683    /// [`S`].
684    pub fn with_component_state<S, N, F, O>(
685        &mut self,
686        node_id: N,
687        mut callback: F,
688    ) -> Result<O, RuntimeError>
689    where
690        S: ScryptoDecode,
691        N: Into<NodeId>,
692        F: FnMut(&mut S, &mut Self) -> O,
693    {
694        let (handle, state) = self.0.with_kernel_mut(|kernel| {
695            // Lock
696            let handle = kernel.kernel_open_substate(
697                &node_id.into(),
698                MAIN_BASE_PARTITION,
699                &SubstateKey::Field(ComponentField::State0.into()),
700                LockFlags::read_only(),
701                SystemLockData::Field(FieldLockData::Read),
702            )?;
703
704            // Read
705            let state = kernel.kernel_read_substate(handle).map(|v| {
706                let FieldSubstate::<ScryptoValue>::V1(FieldSubstateV1 { payload, .. }) =
707                    v.as_typed().unwrap();
708                scrypto_encode(&payload).unwrap()
709            })?;
710
711            Ok::<_, RuntimeError>((handle, state))
712        })?;
713
714        // Decode
715        let mut state = scrypto_decode::<S>(&state).unwrap();
716
717        // Callback
718        let rtn = callback(&mut state, self);
719
720        // Unlock
721        self.0
722            .with_kernel_mut(|kernel| kernel.kernel_close_substate(handle))?;
723
724        Ok(rtn)
725    }
726
727    //========================
728    // endregion:state
729    // region:epoch&timestamp
730    //========================
731
732    /// Gets the current epoch from the Consensus Manager.
733    pub fn get_current_epoch(&mut self) -> Epoch {
734        Runtime::current_epoch(self).unwrap()
735    }
736
737    /// Sets the current epoch.
738    pub fn set_current_epoch(&mut self, epoch: Epoch) {
739        self.as_method_actor(
740            CONSENSUS_MANAGER,
741            ModuleId::Main,
742            CONSENSUS_MANAGER_NEXT_ROUND_IDENT,
743            |env| -> Result<(), RuntimeError> {
744                let manager_handle = env
745                    .actor_open_field(
746                        ACTOR_STATE_SELF,
747                        ConsensusManagerField::State.into(),
748                        LockFlags::MUTABLE,
749                    )
750                    .unwrap();
751                let mut manager_substate =
752                    env.field_read_typed::<VersionedConsensusManagerState>(manager_handle)?;
753
754                manager_substate.as_unique_version_mut().epoch = epoch;
755
756                env.field_write_typed(manager_handle, &manager_substate)?;
757                env.field_close(manager_handle)?;
758                Ok(())
759            },
760        )
761        .unwrap()
762        .unwrap();
763    }
764
765    /// Gets the current time stamp from the Consensus Manager.
766    pub fn get_current_time(&mut self) -> Instant {
767        Runtime::current_time(TimePrecision::Second, self).unwrap()
768    }
769
770    pub fn set_current_time(&mut self, instant: Instant) {
771        self.as_method_actor(
772            CONSENSUS_MANAGER,
773            ModuleId::Main,
774            CONSENSUS_MANAGER_NEXT_ROUND_IDENT,
775            |env| -> Result<(), RuntimeError> {
776                let handle = env.actor_open_field(
777                    ACTOR_STATE_SELF,
778                    ConsensusManagerField::ProposerMinuteTimestamp.into(),
779                    LockFlags::MUTABLE,
780                )?;
781                let mut proposer_minute_timestamp =
782                    env.field_read_typed::<ConsensusManagerProposerMinuteTimestampFieldPayload>(
783                        handle,
784                    )?;
785                proposer_minute_timestamp
786                    .as_unique_version_mut()
787                    .epoch_minute =
788                    i32::try_from(instant.seconds_since_unix_epoch / 60).map_err(|_| {
789                        RuntimeError::ApplicationError(ApplicationError::ConsensusManagerError(
790                            ConsensusManagerError::InvalidProposerTimestampUpdate {
791                                from_millis: env.get_current_time().seconds_since_unix_epoch * 1000,
792                                to_millis: instant.seconds_since_unix_epoch * 1000,
793                            },
794                        ))
795                    })?;
796                env.field_write_typed(handle, &proposer_minute_timestamp)?;
797                env.field_close(handle)?;
798
799                let handle = env.actor_open_field(
800                    ACTOR_STATE_SELF,
801                    ConsensusManagerField::ProposerMilliTimestamp.into(),
802                    LockFlags::MUTABLE,
803                )?;
804                let mut proposer_milli_timestamp =
805                    env.field_read_typed::<ConsensusManagerProposerMilliTimestampFieldPayload>(
806                        handle,
807                    )?;
808                proposer_milli_timestamp.as_unique_version_mut().epoch_milli =
809                    instant.seconds_since_unix_epoch * 1000;
810                env.field_write_typed(handle, &proposer_milli_timestamp)?;
811                env.field_close(handle)?;
812
813                Ok(())
814            },
815        )
816        .unwrap()
817        .unwrap();
818    }
819
820    //===========================
821    // endregion:epoch&timestamp
822    // region:helpers
823    //===========================
824
825    /// Allows us to perform some action as another actor.
826    ///
827    /// This function pushes a new call-frame onto the stack with the actor information we desire,
828    /// performs the call-back, and then pops the call-frame off.
829    pub(crate) fn as_method_actor<N, F, O>(
830        &mut self,
831        node_id: N,
832        module_id: ModuleId,
833        method_ident: &str,
834        callback: F,
835    ) -> Result<O, RuntimeError>
836    where
837        N: Into<NodeId> + Copy,
838        F: FnOnce(&mut Self) -> O,
839        O: ScryptoEncode,
840    {
841        let object_info = self.0.with_kernel_mut(|kernel| {
842            SystemService::new(kernel).get_object_info(&node_id.into())
843        })?;
844        let auth_zone = self.0.with_kernel_mut(|kernel| {
845            let mut system_service = SystemService::new(kernel);
846            AuthModule::on_call_fn_mock(
847                &mut system_service,
848                Some((&node_id.into(), false)),
849                Default::default(),
850                Default::default(),
851            )
852        })?;
853        let actor = Actor::Method(MethodActor {
854            method_type: match module_id {
855                ModuleId::Main => MethodType::Main,
856                ModuleId::Royalty => MethodType::Module(AttachedModuleId::Royalty),
857                ModuleId::Metadata => MethodType::Module(AttachedModuleId::Metadata),
858                ModuleId::RoleAssignment => MethodType::Module(AttachedModuleId::RoleAssignment),
859            },
860            ident: method_ident.to_owned(),
861            node_id: node_id.into(),
862            auth_zone,
863            object_info,
864        });
865        self.as_actor(actor, callback)
866    }
867
868    /// Allows us to perform some action as another actor.
869    ///
870    /// This function pushes a new call-frame onto the stack with the actor information we desire,
871    /// performs the call-back, and then pops the call-frame off.
872    pub(crate) fn as_actor<F, O>(&mut self, actor: Actor, callback: F) -> Result<O, RuntimeError>
873    where
874        F: FnOnce(&mut Self) -> O,
875        O: ScryptoEncode,
876    {
877        // Creating the next frame.
878        let mut message =
879            CallFrameMessage::from_input(&IndexedScryptoValue::from_typed(&()), &actor);
880        self.0.with_kernel_mut(|kernel| {
881            let (substate_io, current_frame) = kernel.kernel_current_frame_mut();
882            message
883                .copy_global_references
884                .extend(current_frame.stable_references().keys());
885            let new_frame =
886                CallFrame::new_child_from_parent(substate_io, current_frame, actor, message)
887                    .unwrap();
888            let old = core::mem::replace(current_frame, new_frame);
889            kernel.kernel_prev_frame_stack_mut().push(old);
890        });
891
892        // Executing the callback
893        let rtn = callback(self);
894
895        // Constructing the message from the returns
896        let message = {
897            let indexed_scrypto_value = IndexedScryptoValue::from_typed(&rtn);
898            CallFrameMessage {
899                move_nodes: indexed_scrypto_value.owned_nodes().clone(),
900                copy_global_references: indexed_scrypto_value.references().clone(),
901                ..Default::default()
902            }
903        };
904
905        // Popping the last frame & Passing message
906        self.0
907            .with_kernel_mut(|kernel| -> Result<(), RuntimeError> {
908                let mut previous_frame = kernel.kernel_prev_frame_stack_mut().pop().unwrap();
909                let (substate_io, current_frame) = kernel.kernel_current_frame_mut();
910
911                CallFrame::pass_message(
912                    substate_io,
913                    current_frame,
914                    &mut previous_frame,
915                    message.clone(),
916                )
917                .map_err(CallFrameError::PassMessageError)
918                .map_err(KernelError::CallFrameError)?;
919
920                *current_frame = previous_frame;
921                Ok(())
922            })?;
923
924        Ok(rtn)
925    }
926
927    //===================
928    // endregion:helpers
929    //===================
930}
931
932impl Default for TestEnvironment<InMemorySubstateDatabase> {
933    fn default() -> Self {
934        Self::new()
935    }
936}
937
938#[cfg(test)]
939mod tests {
940    use crate::prelude::*;
941
942    #[test]
943    pub fn test_env_can_be_created() {
944        let _ = TestEnvironment::new();
945    }
946
947    #[test]
948    pub fn test_time_set() {
949        // Arrange
950        let mut env = TestEnvironment::new();
951        let mut current_time = env.get_current_time();
952        current_time.seconds_since_unix_epoch += 60; // add 1 minute
953
954        // Act
955        env.set_current_time(current_time);
956
957        // Assert
958        let t1 = Runtime::current_time(TimePrecision::Second, &mut env).unwrap();
959        let t2 = Runtime::current_time(TimePrecision::Minute, &mut env).unwrap();
960
961        assert_eq!(t1, t2)
962    }
963}