@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.
Files changed (58) hide show
  1. package/Cargo.toml +21 -0
  2. package/LICENSE +23 -0
  3. package/clippy.toml +7 -0
  4. package/package.json +37 -0
  5. package/rust-toolchain.toml +4 -0
  6. package/rustfmt.toml +15 -0
  7. package/src/auth.rs +95 -0
  8. package/src/contract_ttl.rs +92 -0
  9. package/src/error.rs +43 -0
  10. package/src/lib.rs +585 -0
  11. package/src/lz_contract.rs +105 -0
  12. package/src/rbac.rs +90 -0
  13. package/src/storage.rs +522 -0
  14. package/src/tests/auth.rs +230 -0
  15. package/src/tests/contract_ttl.rs +695 -0
  16. package/src/tests/error.rs +156 -0
  17. package/src/tests/lz_contract.rs +87 -0
  18. package/src/tests/mod.rs +11 -0
  19. package/src/tests/rbac.rs +523 -0
  20. package/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_multisig_code.snap +31 -0
  21. package/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_ownable_code.snap +39 -0
  22. package/src/tests/snapshots/common_macros__tests__auth__snapshot_only_auth_preserves_function_signature.snap +19 -0
  23. package/src/tests/snapshots/common_macros__tests__contract_ttl__snapshot_generated_contractimpl_code.snap +77 -0
  24. package/src/tests/snapshots/common_macros__tests__contract_ttl__snapshot_generated_contracttrait_code.snap +46 -0
  25. package/src/tests/snapshots/common_macros__tests__error__snapshot_generated_contract_error_code.snap +20 -0
  26. package/src/tests/snapshots/common_macros__tests__lz_contract__snapshot_generated_lz_contract_code.snap +51 -0
  27. package/src/tests/snapshots/common_macros__tests__rbac__snapshot_authorizer_role.snap +21 -0
  28. package/src/tests/snapshots/common_macros__tests__rbac__snapshot_preserve_function_signature.snap +21 -0
  29. package/src/tests/snapshots/common_macros__tests__ttl_configurable__snapshot_generated_ttl_configurable_code.snap +10 -0
  30. package/src/tests/snapshots/common_macros__tests__ttl_extendable__snapshot_generated_ttl_extendable_code.snap +8 -0
  31. package/src/tests/snapshots/common_macros__tests__upgradeable__snapshot_generated_upgradeable_code.snap +28 -0
  32. package/src/tests/storage/extract_fields.rs +87 -0
  33. package/src/tests/storage/gen_accessor_methods.rs +223 -0
  34. package/src/tests/storage/gen_args.rs +65 -0
  35. package/src/tests/storage/gen_enum_variant.rs +78 -0
  36. package/src/tests/storage/gen_key.rs +108 -0
  37. package/src/tests/storage/gen_params.rs +105 -0
  38. package/src/tests/storage/generate_storage.rs +410 -0
  39. package/src/tests/storage/is_primitive_type.rs +48 -0
  40. package/src/tests/storage/mod.rs +16 -0
  41. package/src/tests/storage/parse_default.rs +164 -0
  42. package/src/tests/storage/parse_name.rs +158 -0
  43. package/src/tests/storage/parse_no_ttl_extension.rs +124 -0
  44. package/src/tests/storage/parse_storage_type.rs +174 -0
  45. package/src/tests/storage/snapshots/common_macros__tests__storage__generate_storage__snapshot_generated_storage_code.snap +412 -0
  46. package/src/tests/storage/storage_kind.rs +39 -0
  47. package/src/tests/storage/test_setup.rs +25 -0
  48. package/src/tests/storage/validate_attrs.rs +138 -0
  49. package/src/tests/storage/variant_config.rs +226 -0
  50. package/src/tests/test_helpers.rs +87 -0
  51. package/src/tests/ttl_configurable.rs +34 -0
  52. package/src/tests/ttl_extendable.rs +32 -0
  53. package/src/tests/upgradeable.rs +169 -0
  54. package/src/tests/utils.rs +267 -0
  55. package/src/ttl_configurable.rs +24 -0
  56. package/src/ttl_extendable.rs +28 -0
  57. package/src/upgradeable.rs +136 -0
  58. 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
+ }
@@ -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
+ }
@@ -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
+ }