@layerzerolabs/common-utils-macros-stellar-contracts 0.2.122
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.toml +21 -0
- package/LICENSE +23 -0
- package/clippy.toml +7 -0
- package/package.json +37 -0
- package/rust-toolchain.toml +4 -0
- package/rustfmt.toml +15 -0
- package/src/auth.rs +95 -0
- package/src/contract_ttl.rs +92 -0
- package/src/error.rs +43 -0
- package/src/lib.rs +585 -0
- package/src/lz_contract.rs +105 -0
- package/src/rbac.rs +90 -0
- package/src/storage.rs +522 -0
- package/src/tests/auth.rs +230 -0
- package/src/tests/contract_ttl.rs +695 -0
- package/src/tests/error.rs +156 -0
- package/src/tests/lz_contract.rs +87 -0
- package/src/tests/mod.rs +11 -0
- package/src/tests/rbac.rs +523 -0
- package/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_multisig_code.snap +31 -0
- package/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_ownable_code.snap +39 -0
- package/src/tests/snapshots/common_macros__tests__auth__snapshot_only_auth_preserves_function_signature.snap +19 -0
- package/src/tests/snapshots/common_macros__tests__contract_ttl__snapshot_generated_contractimpl_code.snap +77 -0
- package/src/tests/snapshots/common_macros__tests__contract_ttl__snapshot_generated_contracttrait_code.snap +46 -0
- package/src/tests/snapshots/common_macros__tests__error__snapshot_generated_contract_error_code.snap +20 -0
- package/src/tests/snapshots/common_macros__tests__lz_contract__snapshot_generated_lz_contract_code.snap +51 -0
- package/src/tests/snapshots/common_macros__tests__rbac__snapshot_authorizer_role.snap +21 -0
- package/src/tests/snapshots/common_macros__tests__rbac__snapshot_preserve_function_signature.snap +21 -0
- package/src/tests/snapshots/common_macros__tests__ttl_configurable__snapshot_generated_ttl_configurable_code.snap +10 -0
- package/src/tests/snapshots/common_macros__tests__ttl_extendable__snapshot_generated_ttl_extendable_code.snap +8 -0
- package/src/tests/snapshots/common_macros__tests__upgradeable__snapshot_generated_upgradeable_code.snap +28 -0
- package/src/tests/storage/extract_fields.rs +87 -0
- package/src/tests/storage/gen_accessor_methods.rs +223 -0
- package/src/tests/storage/gen_args.rs +65 -0
- package/src/tests/storage/gen_enum_variant.rs +78 -0
- package/src/tests/storage/gen_key.rs +108 -0
- package/src/tests/storage/gen_params.rs +105 -0
- package/src/tests/storage/generate_storage.rs +410 -0
- package/src/tests/storage/is_primitive_type.rs +48 -0
- package/src/tests/storage/mod.rs +16 -0
- package/src/tests/storage/parse_default.rs +164 -0
- package/src/tests/storage/parse_name.rs +158 -0
- package/src/tests/storage/parse_no_ttl_extension.rs +124 -0
- package/src/tests/storage/parse_storage_type.rs +174 -0
- package/src/tests/storage/snapshots/common_macros__tests__storage__generate_storage__snapshot_generated_storage_code.snap +412 -0
- package/src/tests/storage/storage_kind.rs +39 -0
- package/src/tests/storage/test_setup.rs +25 -0
- package/src/tests/storage/validate_attrs.rs +138 -0
- package/src/tests/storage/variant_config.rs +226 -0
- package/src/tests/test_helpers.rs +87 -0
- package/src/tests/ttl_configurable.rs +34 -0
- package/src/tests/ttl_extendable.rs +32 -0
- package/src/tests/upgradeable.rs +169 -0
- package/src/tests/utils.rs +267 -0
- package/src/ttl_configurable.rs +24 -0
- package/src/ttl_extendable.rs +28 -0
- package/src/upgradeable.rs +136 -0
- package/src/utils.rs +56 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
source: contracts/common-macros/src/tests/contract_ttl.rs
|
|
3
|
+
expression: combined
|
|
4
|
+
---
|
|
5
|
+
// === Trait (no attr) ===
|
|
6
|
+
|
|
7
|
+
#[soroban_sdk::contracttrait]
|
|
8
|
+
pub trait MyTrait: Sized {
|
|
9
|
+
const A_CONST: u32;
|
|
10
|
+
type Alias;
|
|
11
|
+
/// Default method with Env - should have TTL extension
|
|
12
|
+
fn default_with_env(env: Env, value: u32) -> u32 {
|
|
13
|
+
utils::ttl_configurable::extend_instance_ttl(&env);
|
|
14
|
+
value * 2
|
|
15
|
+
}
|
|
16
|
+
/// Default method with qualified Env path - should have TTL extension
|
|
17
|
+
fn with_qualified_env(env: soroban_sdk::Env) -> u32 {
|
|
18
|
+
utils::ttl_configurable::extend_instance_ttl(&env);
|
|
19
|
+
42
|
|
20
|
+
}
|
|
21
|
+
/// Default method with Env not as first parameter - should have TTL extension
|
|
22
|
+
fn env_second(value: u32, env: &Env) -> u32 {
|
|
23
|
+
utils::ttl_configurable::extend_instance_ttl(env);
|
|
24
|
+
value * 2
|
|
25
|
+
}
|
|
26
|
+
/// Default method without Env - should NOT have TTL extension
|
|
27
|
+
fn default_without_env(value: u32) -> u32 {
|
|
28
|
+
value * 4
|
|
29
|
+
}
|
|
30
|
+
/// Abstract method with Env - should NOT have TTL extension (no body)
|
|
31
|
+
fn abstract_with_env(env: Env, value: u32) -> u32;
|
|
32
|
+
/// Abstract method without Env - should NOT have TTL extension (no body)
|
|
33
|
+
fn abstract_without_env(value: u32) -> u32;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
// === Trait (crate attr) ===
|
|
38
|
+
|
|
39
|
+
#[soroban_sdk::contracttrait(crate = "other_sdk")]
|
|
40
|
+
pub trait AnotherTrait {
|
|
41
|
+
/// Default method with Env - should have TTL extension
|
|
42
|
+
fn method_with_env(env: &Env, value: u32) -> u32 {
|
|
43
|
+
utils::ttl_configurable::extend_instance_ttl(env);
|
|
44
|
+
value * 3
|
|
45
|
+
}
|
|
46
|
+
}
|
package/src/tests/snapshots/common_macros__tests__error__snapshot_generated_contract_error_code.snap
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
source: contracts/common-macros/src/tests/error.rs
|
|
3
|
+
expression: formatted
|
|
4
|
+
---
|
|
5
|
+
#[soroban_sdk::contracterror]
|
|
6
|
+
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
|
7
|
+
#[repr(u32)]
|
|
8
|
+
/// Example error enum
|
|
9
|
+
pub enum MyError {
|
|
10
|
+
/// Implicit (should start at 1)
|
|
11
|
+
A = 1u32,
|
|
12
|
+
/// Implicit (should be 2)
|
|
13
|
+
B = 2u32,
|
|
14
|
+
/// Explicit (must be >= previous + 1)
|
|
15
|
+
C = 10,
|
|
16
|
+
/// Implicit (should be 11)
|
|
17
|
+
D = 11u32,
|
|
18
|
+
/// Explicit max boundary (u32::MAX)
|
|
19
|
+
E = 4294967295,
|
|
20
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
source: contracts/common-macros/src/tests/lz_contract.rs
|
|
3
|
+
expression: combined
|
|
4
|
+
---
|
|
5
|
+
// === Default (ownable) ===
|
|
6
|
+
|
|
7
|
+
#[soroban_sdk::contract]
|
|
8
|
+
#[common_macros::ttl_configurable]
|
|
9
|
+
#[common_macros::ttl_extendable]
|
|
10
|
+
#[common_macros::ownable]
|
|
11
|
+
pub struct MyContract;
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
// === MultiSig + Upgradeable ===
|
|
15
|
+
|
|
16
|
+
#[soroban_sdk::contract]
|
|
17
|
+
#[common_macros::ttl_configurable]
|
|
18
|
+
#[common_macros::ttl_extendable]
|
|
19
|
+
#[common_macros::multisig]
|
|
20
|
+
#[common_macros::upgradeable]
|
|
21
|
+
pub struct MyContract;
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
// === Upgradeable (no_migration) ===
|
|
25
|
+
|
|
26
|
+
#[soroban_sdk::contract]
|
|
27
|
+
#[common_macros::ttl_configurable]
|
|
28
|
+
#[common_macros::ttl_extendable]
|
|
29
|
+
#[common_macros::ownable]
|
|
30
|
+
#[common_macros::upgradeable(no_migration)]
|
|
31
|
+
pub struct MyContract;
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
// === Upgradeable (rbac) ===
|
|
35
|
+
|
|
36
|
+
#[soroban_sdk::contract]
|
|
37
|
+
#[common_macros::ttl_configurable]
|
|
38
|
+
#[common_macros::ttl_extendable]
|
|
39
|
+
#[common_macros::ownable]
|
|
40
|
+
#[common_macros::upgradeable(rbac)]
|
|
41
|
+
pub struct MyContract;
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
// === Upgradeable (rbac, no_migration) pass-through ===
|
|
45
|
+
|
|
46
|
+
#[soroban_sdk::contract]
|
|
47
|
+
#[common_macros::ttl_configurable]
|
|
48
|
+
#[common_macros::ttl_extendable]
|
|
49
|
+
#[common_macros::ownable]
|
|
50
|
+
#[common_macros::upgradeable(rbac, no_migration)]
|
|
51
|
+
pub struct MyContract;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
source: contracts/common-macros/src/tests/rbac.rs
|
|
3
|
+
expression: combined
|
|
4
|
+
---
|
|
5
|
+
// === has_role(operator, AUTHORIZER) ===
|
|
6
|
+
|
|
7
|
+
pub fn admin_action(env: Env, operator: Address) {
|
|
8
|
+
utils::rbac::ensure_role::<
|
|
9
|
+
Self,
|
|
10
|
+
>(&env, &soroban_sdk::Symbol::new(&env, AUTHORIZER), &operator);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
// === only_role(operator, AUTHORIZER) ===
|
|
15
|
+
|
|
16
|
+
pub fn admin_action(env: Env, operator: Address) {
|
|
17
|
+
utils::rbac::ensure_role::<
|
|
18
|
+
Self,
|
|
19
|
+
>(&env, &soroban_sdk::Symbol::new(&env, AUTHORIZER), &operator);
|
|
20
|
+
operator.require_auth();
|
|
21
|
+
}
|
package/src/tests/snapshots/common_macros__tests__rbac__snapshot_preserve_function_signature.snap
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
source: contracts/common-macros/src/tests/rbac.rs
|
|
3
|
+
expression: combined
|
|
4
|
+
---
|
|
5
|
+
// === has_role ===
|
|
6
|
+
|
|
7
|
+
pub fn mint(env: Env, caller: Address, amount: i128) {
|
|
8
|
+
utils::rbac::ensure_role::<
|
|
9
|
+
Self,
|
|
10
|
+
>(&env, &soroban_sdk::Symbol::new(&env, "minter"), &caller);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
// === only_role ===
|
|
15
|
+
|
|
16
|
+
pub fn mint(env: Env, caller: Address, amount: i128) {
|
|
17
|
+
utils::rbac::ensure_role::<
|
|
18
|
+
Self,
|
|
19
|
+
>(&env, &soroban_sdk::Symbol::new(&env, "minter"), &caller);
|
|
20
|
+
caller.require_auth();
|
|
21
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
source: contracts/common-macros/src/tests/ttl_configurable.rs
|
|
3
|
+
expression: formatted
|
|
4
|
+
---
|
|
5
|
+
pub struct MyContract {
|
|
6
|
+
some_field: u32,
|
|
7
|
+
}
|
|
8
|
+
use utils::ttl_configurable::TtlConfigurable as _;
|
|
9
|
+
#[common_macros::contract_impl(contracttrait)]
|
|
10
|
+
impl utils::ttl_configurable::TtlConfigurable for MyContract {}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
source: contracts/common-macros/src/tests/ttl_extendable.rs
|
|
3
|
+
expression: formatted
|
|
4
|
+
---
|
|
5
|
+
pub struct MyContract;
|
|
6
|
+
use utils::ttl_extendable::TtlExtendable as _;
|
|
7
|
+
#[soroban_sdk::contractimpl(contracttrait)]
|
|
8
|
+
impl utils::ttl_extendable::TtlExtendable for MyContract {}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
source: contracts/common-macros/src/tests/upgradeable.rs
|
|
3
|
+
assertion_line: 80
|
|
4
|
+
expression: combined
|
|
5
|
+
---
|
|
6
|
+
// ============================================
|
|
7
|
+
// Default: requires manual UpgradeableInternal
|
|
8
|
+
// ============================================
|
|
9
|
+
|
|
10
|
+
pub struct MyContract;
|
|
11
|
+
use utils::upgradeable::Upgradeable as _;
|
|
12
|
+
soroban_sdk::contractmeta!(key = "binver", val = "0.0.1");
|
|
13
|
+
#[common_macros::contract_impl(contracttrait)]
|
|
14
|
+
impl utils::upgradeable::Upgradeable for MyContract {}
|
|
15
|
+
|
|
16
|
+
// ============================================
|
|
17
|
+
// With no_migration: auto-generates impl
|
|
18
|
+
// ============================================
|
|
19
|
+
|
|
20
|
+
pub struct MyContract;
|
|
21
|
+
use utils::upgradeable::Upgradeable as _;
|
|
22
|
+
soroban_sdk::contractmeta!(key = "binver", val = "0.0.1");
|
|
23
|
+
impl utils::upgradeable::UpgradeableInternal for MyContract {
|
|
24
|
+
type MigrationData = ();
|
|
25
|
+
fn __migrate(_env: &soroban_sdk::Env, _migration_data: &Self::MigrationData) {}
|
|
26
|
+
}
|
|
27
|
+
#[common_macros::contract_impl(contracttrait)]
|
|
28
|
+
impl utils::upgradeable::Upgradeable for MyContract {}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
//! Unit tests for the `extract_fields` function.
|
|
2
|
+
|
|
3
|
+
use crate::storage::test::extract_fields_for_test;
|
|
4
|
+
use crate::tests::test_helpers::assert_panics_contains;
|
|
5
|
+
use quote::{quote, ToTokens};
|
|
6
|
+
|
|
7
|
+
use super::test_setup::parse_variant;
|
|
8
|
+
|
|
9
|
+
#[test]
|
|
10
|
+
fn test_unit_variant_returns_empty() {
|
|
11
|
+
let variant = parse_variant(quote! {
|
|
12
|
+
enum Test {
|
|
13
|
+
#[persistent(u32)]
|
|
14
|
+
UnitVariant,
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
let fields = extract_fields_for_test(&variant);
|
|
19
|
+
assert!(fields.is_empty(), "unit variant should have no fields");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#[test]
|
|
23
|
+
fn test_named_variant_single_field() {
|
|
24
|
+
let variant = parse_variant(quote! {
|
|
25
|
+
enum Test {
|
|
26
|
+
#[persistent(u32)]
|
|
27
|
+
NamedVariant { key: Address },
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
let fields = extract_fields_for_test(&variant);
|
|
32
|
+
assert_eq!(fields.len(), 1, "should have exactly one field");
|
|
33
|
+
assert_eq!(fields[0].0.to_string(), "key");
|
|
34
|
+
assert_eq!(fields[0].1.to_token_stream().to_string(), "Address");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
#[test]
|
|
38
|
+
fn test_named_variant_multiple_fields() {
|
|
39
|
+
let variant = parse_variant(quote! {
|
|
40
|
+
enum Test {
|
|
41
|
+
#[persistent(u32)]
|
|
42
|
+
NamedVariant { first: u32, second: String, third: Address },
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
let fields = extract_fields_for_test(&variant);
|
|
47
|
+
assert_eq!(fields.len(), 3, "should have exactly three fields");
|
|
48
|
+
assert_eq!(fields[0].0.to_string(), "first");
|
|
49
|
+
assert_eq!(fields[1].0.to_string(), "second");
|
|
50
|
+
assert_eq!(fields[2].0.to_string(), "third");
|
|
51
|
+
|
|
52
|
+
assert_eq!(fields[0].1.to_token_stream().to_string(), "u32");
|
|
53
|
+
assert_eq!(fields[1].1.to_token_stream().to_string(), "String");
|
|
54
|
+
assert_eq!(fields[2].1.to_token_stream().to_string(), "Address");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#[test]
|
|
58
|
+
fn test_tuple_variant_panics() {
|
|
59
|
+
let variant = parse_variant(quote! {
|
|
60
|
+
enum Test {
|
|
61
|
+
TupleVariant(u32, String),
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
assert_panics_contains("tuple variant", "only unit variants or named fields are supported", || {
|
|
66
|
+
extract_fields_for_test(&variant);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#[test]
|
|
71
|
+
fn test_preserves_field_order() {
|
|
72
|
+
let variant = parse_variant(quote! {
|
|
73
|
+
enum Test {
|
|
74
|
+
#[persistent(u32)]
|
|
75
|
+
OrderedVariant { alpha: u32, beta: String, gamma: Address, delta: bool },
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
let fields = extract_fields_for_test(&variant);
|
|
80
|
+
let names: Vec<_> = fields.iter().map(|(name, _)| name.to_string()).collect();
|
|
81
|
+
assert_eq!(names, vec!["alpha", "beta", "gamma", "delta"]);
|
|
82
|
+
|
|
83
|
+
assert_eq!(fields[0].1.to_token_stream().to_string(), "u32");
|
|
84
|
+
assert_eq!(fields[1].1.to_token_stream().to_string(), "String");
|
|
85
|
+
assert_eq!(fields[2].1.to_token_stream().to_string(), "Address");
|
|
86
|
+
assert_eq!(fields[3].1.to_token_stream().to_string(), "bool");
|
|
87
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
//! Unit tests for the `gen_accessor_methods` function.
|
|
2
|
+
|
|
3
|
+
use crate::storage::test::gen_accessor_methods_for_test;
|
|
4
|
+
use proc_macro2::Ident;
|
|
5
|
+
use quote::{format_ident, quote, ToTokens};
|
|
6
|
+
use syn::{ImplItem, ImplItemFn, ItemImpl, ReturnType, Type, Variant};
|
|
7
|
+
|
|
8
|
+
use super::test_setup::{normalize, parse_variant};
|
|
9
|
+
|
|
10
|
+
fn gen_impl(enum_name: &Ident, variant: &Variant) -> ItemImpl {
|
|
11
|
+
let methods = gen_accessor_methods_for_test(enum_name, variant);
|
|
12
|
+
syn::parse2::<ItemImpl>(quote! { impl #enum_name { #methods } }).expect("failed to parse generated methods")
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
fn impl_fn_names(item_impl: &ItemImpl) -> Vec<String> {
|
|
16
|
+
item_impl
|
|
17
|
+
.items
|
|
18
|
+
.iter()
|
|
19
|
+
.filter_map(|it| match it {
|
|
20
|
+
ImplItem::Fn(f) => Some(f.sig.ident.to_string()),
|
|
21
|
+
_ => None,
|
|
22
|
+
})
|
|
23
|
+
.collect()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
fn find_fn<'a>(item_impl: &'a ItemImpl, name: &str) -> &'a ImplItemFn {
|
|
27
|
+
item_impl
|
|
28
|
+
.items
|
|
29
|
+
.iter()
|
|
30
|
+
.find_map(|it| match it {
|
|
31
|
+
ImplItem::Fn(f) if f.sig.ident == name => Some(f),
|
|
32
|
+
_ => None,
|
|
33
|
+
})
|
|
34
|
+
.unwrap_or_else(|| panic!("expected to find function '{name}' in generated impl"))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
fn output_type(sig: &syn::Signature) -> Option<&Type> {
|
|
38
|
+
match &sig.output {
|
|
39
|
+
ReturnType::Default => None,
|
|
40
|
+
ReturnType::Type(_, ty) => Some(ty.as_ref()),
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#[test]
|
|
45
|
+
fn generates_all_expected_methods_for_unit_variant() {
|
|
46
|
+
let variant = parse_variant(quote! {
|
|
47
|
+
enum TestEnum {
|
|
48
|
+
#[persistent(u32)]
|
|
49
|
+
Counter,
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
let enum_name = format_ident!("TestEnum");
|
|
53
|
+
|
|
54
|
+
let item_impl = gen_impl(&enum_name, &variant);
|
|
55
|
+
let mut names = impl_fn_names(&item_impl);
|
|
56
|
+
names.sort();
|
|
57
|
+
|
|
58
|
+
// Getter + CRUD-ish methods + TTL extender are always generated.
|
|
59
|
+
assert_eq!(
|
|
60
|
+
names,
|
|
61
|
+
vec!["counter", "extend_counter_ttl", "has_counter", "remove_counter", "set_counter", "set_or_remove_counter",]
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
#[test]
|
|
66
|
+
fn uses_correct_storage_accessor_for_each_kind() {
|
|
67
|
+
let enum_name = format_ident!("TestEnum");
|
|
68
|
+
|
|
69
|
+
let variant = parse_variant(quote! {
|
|
70
|
+
enum TestEnum {
|
|
71
|
+
#[instance(u32)]
|
|
72
|
+
Counter,
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
let instance = gen_accessor_methods_for_test(&enum_name, &variant);
|
|
76
|
+
let instance_norm = normalize(instance);
|
|
77
|
+
let expected_instance = normalize(quote!(env.storage().instance()));
|
|
78
|
+
assert!(instance_norm.contains(&expected_instance));
|
|
79
|
+
|
|
80
|
+
let variant = parse_variant(quote! {
|
|
81
|
+
enum TestEnum {
|
|
82
|
+
#[persistent(u32)]
|
|
83
|
+
Counter,
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
let persistent = gen_accessor_methods_for_test(&enum_name, &variant);
|
|
87
|
+
let persistent_norm = normalize(persistent);
|
|
88
|
+
let expected_persistent = normalize(quote!(env.storage().persistent()));
|
|
89
|
+
assert!(persistent_norm.contains(&expected_persistent));
|
|
90
|
+
|
|
91
|
+
let variant = parse_variant(quote! {
|
|
92
|
+
enum TestEnum {
|
|
93
|
+
#[temporary(u32)]
|
|
94
|
+
Counter,
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
let temporary = gen_accessor_methods_for_test(&enum_name, &variant);
|
|
98
|
+
let temporary_norm = normalize(temporary);
|
|
99
|
+
let expected_temporary = normalize(quote!(env.storage().temporary()));
|
|
100
|
+
assert!(temporary_norm.contains(&expected_temporary));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
#[test]
|
|
104
|
+
fn persistent_includes_auto_ttl_and_can_be_disabled() {
|
|
105
|
+
let variant = parse_variant(quote! {
|
|
106
|
+
enum TestEnum {
|
|
107
|
+
#[persistent(u32)]
|
|
108
|
+
Counter,
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
let enum_name = format_ident!("TestEnum");
|
|
112
|
+
|
|
113
|
+
let methods = gen_accessor_methods_for_test(&enum_name, &variant);
|
|
114
|
+
let methods_str = normalize(methods);
|
|
115
|
+
|
|
116
|
+
assert!(
|
|
117
|
+
methods_str.contains("utils :: ttl_configurable :: extend_persistent_ttl"),
|
|
118
|
+
"persistent storage should include auto TTL extension"
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
let variant = parse_variant(quote! {
|
|
122
|
+
enum TestEnum {
|
|
123
|
+
#[persistent(u32)]
|
|
124
|
+
#[no_ttl_extension]
|
|
125
|
+
Counter,
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
let methods = gen_accessor_methods_for_test(&enum_name, &variant);
|
|
129
|
+
let methods_str = normalize(methods);
|
|
130
|
+
|
|
131
|
+
assert!(
|
|
132
|
+
!methods_str.contains("utils :: ttl_configurable :: extend_persistent_ttl"),
|
|
133
|
+
"no_ttl_extension should disable auto TTL extension"
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
#[test]
|
|
138
|
+
fn default_value_changes_getter_return_type_and_body() {
|
|
139
|
+
let variant = parse_variant(quote! {
|
|
140
|
+
enum TestEnum {
|
|
141
|
+
#[instance(u32)]
|
|
142
|
+
#[default(0)]
|
|
143
|
+
Counter,
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
let enum_name = format_ident!("TestEnum");
|
|
147
|
+
|
|
148
|
+
let item_impl = gen_impl(&enum_name, &variant);
|
|
149
|
+
let getter = find_fn(&item_impl, "counter");
|
|
150
|
+
|
|
151
|
+
// With default, return type should be the value type directly, not Option<...>
|
|
152
|
+
let out = output_type(&getter.sig).expect("getter should have an explicit return type");
|
|
153
|
+
assert_eq!(normalize(out.to_token_stream()), normalize(quote!(u32)));
|
|
154
|
+
|
|
155
|
+
let body = normalize(getter.block.to_token_stream());
|
|
156
|
+
assert!(body.contains("unwrap_or"), "getter should apply default via unwrap_or(...)");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
#[test]
|
|
160
|
+
fn no_default_getter_returns_option() {
|
|
161
|
+
let variant = parse_variant(quote! {
|
|
162
|
+
enum TestEnum {
|
|
163
|
+
#[instance(u32)]
|
|
164
|
+
Counter,
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
let enum_name = format_ident!("TestEnum");
|
|
168
|
+
|
|
169
|
+
let item_impl = gen_impl(&enum_name, &variant);
|
|
170
|
+
let getter = find_fn(&item_impl, "counter");
|
|
171
|
+
|
|
172
|
+
let out = output_type(&getter.sig).expect("getter should have an explicit return type");
|
|
173
|
+
assert_eq!(normalize(out.to_token_stream()), normalize(quote!(Option<u32>)));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
#[test]
|
|
177
|
+
fn snake_case_and_custom_name_attribute_are_applied() {
|
|
178
|
+
let variant = parse_variant(quote! {
|
|
179
|
+
enum TestEnum {
|
|
180
|
+
#[instance(u32)]
|
|
181
|
+
MyLongVariantName,
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
let enum_name = format_ident!("TestEnum");
|
|
185
|
+
|
|
186
|
+
let item_impl = gen_impl(&enum_name, &variant);
|
|
187
|
+
let names = impl_fn_names(&item_impl);
|
|
188
|
+
|
|
189
|
+
assert!(names.iter().any(|n| n == "my_long_variant_name"), "should convert to snake_case");
|
|
190
|
+
assert!(names.iter().any(|n| n == "set_my_long_variant_name"), "setter should use snake_case");
|
|
191
|
+
|
|
192
|
+
let variant = parse_variant(quote! {
|
|
193
|
+
enum TestEnum {
|
|
194
|
+
#[instance(u32)]
|
|
195
|
+
#[name("custom_name")]
|
|
196
|
+
Counter,
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
let item_impl = gen_impl(&enum_name, &variant);
|
|
200
|
+
let names = impl_fn_names(&item_impl);
|
|
201
|
+
|
|
202
|
+
assert!(names.iter().any(|n| n == "custom_name"), "should use custom name for getter");
|
|
203
|
+
assert!(names.iter().any(|n| n == "set_custom_name"), "should use custom name for setter");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
#[test]
|
|
207
|
+
fn named_variant_generates_expected_param_passing_rules() {
|
|
208
|
+
let variant = parse_variant(quote! {
|
|
209
|
+
enum TestEnum {
|
|
210
|
+
#[instance(u32)]
|
|
211
|
+
Nonce { nonce: u32, user: Address },
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
let enum_name = format_ident!("TestEnum");
|
|
215
|
+
|
|
216
|
+
let item_impl = gen_impl(&enum_name, &variant);
|
|
217
|
+
let getter = find_fn(&item_impl, "nonce");
|
|
218
|
+
let sig_norm = normalize(getter.sig.to_token_stream());
|
|
219
|
+
|
|
220
|
+
// Non-primitive types are passed by reference, primitives by value.
|
|
221
|
+
assert!(sig_norm.contains("nonce : u32"), "primitive field should be passed by value");
|
|
222
|
+
assert!(sig_norm.contains("user : & Address"), "non-primitive field should be passed by reference");
|
|
223
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
//! Unit tests for the `gen_args` function.
|
|
2
|
+
|
|
3
|
+
use crate::storage::test::gen_args_for_test;
|
|
4
|
+
use quote::quote;
|
|
5
|
+
|
|
6
|
+
use super::test_setup::{normalize, parse_variant};
|
|
7
|
+
|
|
8
|
+
#[test]
|
|
9
|
+
fn test_unit_variant_only_env_arg() {
|
|
10
|
+
let variant = parse_variant(quote! {
|
|
11
|
+
enum Test {
|
|
12
|
+
#[persistent(u32)]
|
|
13
|
+
UnitVariant,
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
let args = gen_args_for_test(&variant);
|
|
18
|
+
let expected = quote! { env };
|
|
19
|
+
assert_eq!(normalize(args), normalize(expected));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#[test]
|
|
23
|
+
fn test_named_variant_single_field() {
|
|
24
|
+
let variant = parse_variant(quote! {
|
|
25
|
+
enum Test {
|
|
26
|
+
#[persistent(u32)]
|
|
27
|
+
NamedVariant { key: Address },
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
let args = gen_args_for_test(&variant);
|
|
32
|
+
let expected = quote! { env, key };
|
|
33
|
+
assert_eq!(normalize(args), normalize(expected));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
#[test]
|
|
37
|
+
fn test_named_variant_multiple_fields() {
|
|
38
|
+
let variant = parse_variant(quote! {
|
|
39
|
+
enum Test {
|
|
40
|
+
#[persistent(u32)]
|
|
41
|
+
NamedVariant { first: u32, second: String, third: Address },
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
let args = gen_args_for_test(&variant);
|
|
46
|
+
let expected = quote! { env, first, second, third };
|
|
47
|
+
assert_eq!(normalize(args), normalize(expected));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#[test]
|
|
51
|
+
fn test_args_do_not_include_types() {
|
|
52
|
+
let variant = parse_variant(quote! {
|
|
53
|
+
enum Test {
|
|
54
|
+
#[persistent(u32)]
|
|
55
|
+
NamedVariant { key: SomeComplexType },
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
let args = gen_args_for_test(&variant);
|
|
60
|
+
let args_str = args.to_string();
|
|
61
|
+
|
|
62
|
+
// Args should only contain identifiers, not types
|
|
63
|
+
assert!(!args_str.contains("SomeComplexType"), "args should not include types");
|
|
64
|
+
assert!(args_str.contains("key"), "args should include field name");
|
|
65
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
//! Unit tests for the `gen_enum_variant` function.
|
|
2
|
+
|
|
3
|
+
use crate::storage::test::gen_enum_variant_for_test;
|
|
4
|
+
use crate::tests::test_helpers::assert_panics_contains;
|
|
5
|
+
use quote::quote;
|
|
6
|
+
|
|
7
|
+
use super::test_setup::{normalize, parse_variant};
|
|
8
|
+
|
|
9
|
+
#[test]
|
|
10
|
+
fn test_unit_variant() {
|
|
11
|
+
let variant = parse_variant(quote! {
|
|
12
|
+
enum Test {
|
|
13
|
+
#[persistent(u32)]
|
|
14
|
+
UnitVariant,
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
let generated = gen_enum_variant_for_test(&variant);
|
|
19
|
+
let expected = quote! { UnitVariant };
|
|
20
|
+
assert_eq!(normalize(generated), normalize(expected));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#[test]
|
|
24
|
+
fn test_named_variant_single_field() {
|
|
25
|
+
let variant = parse_variant(quote! {
|
|
26
|
+
enum Test {
|
|
27
|
+
#[persistent(u32)]
|
|
28
|
+
NamedVariant { key: Address },
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
let generated = gen_enum_variant_for_test(&variant);
|
|
33
|
+
// Named fields become tuple variant with types only
|
|
34
|
+
let expected = quote! { NamedVariant(Address) };
|
|
35
|
+
assert_eq!(normalize(generated), normalize(expected));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
#[test]
|
|
39
|
+
fn test_named_variant_multiple_fields() {
|
|
40
|
+
let variant = parse_variant(quote! {
|
|
41
|
+
enum Test {
|
|
42
|
+
#[persistent(u32)]
|
|
43
|
+
NamedVariant { first: u32, second: String, third: Address },
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
let generated = gen_enum_variant_for_test(&variant);
|
|
48
|
+
// Multiple fields become tuple variant with all types
|
|
49
|
+
let expected = quote! { NamedVariant(u32, String, Address) };
|
|
50
|
+
assert_eq!(normalize(generated), normalize(expected));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#[test]
|
|
54
|
+
fn test_tuple_variant_panics() {
|
|
55
|
+
let variant = parse_variant(quote! {
|
|
56
|
+
enum Test {
|
|
57
|
+
TupleVariant(u32, String),
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
assert_panics_contains("tuple variant", "only unit variants or named fields are supported", || {
|
|
62
|
+
gen_enum_variant_for_test(&variant);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
#[test]
|
|
67
|
+
fn test_complex_generic_types() {
|
|
68
|
+
let variant = parse_variant(quote! {
|
|
69
|
+
enum Test {
|
|
70
|
+
#[persistent(u32)]
|
|
71
|
+
GenericVariant { key: BytesN<32>, value: Vec<u8> },
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
let generated = gen_enum_variant_for_test(&variant);
|
|
76
|
+
let expected = quote! { GenericVariant(BytesN<32>, Vec<u8>) };
|
|
77
|
+
assert_eq!(normalize(generated), normalize(expected));
|
|
78
|
+
}
|