@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,108 @@
|
|
|
1
|
+
//! Unit tests for the `gen_key` function.
|
|
2
|
+
|
|
3
|
+
use crate::storage::test::gen_key_for_test;
|
|
4
|
+
use quote::{format_ident, quote};
|
|
5
|
+
|
|
6
|
+
use super::test_setup::{normalize, parse_variant, parse_variant_str};
|
|
7
|
+
|
|
8
|
+
#[test]
|
|
9
|
+
fn test_unit_variant_key() {
|
|
10
|
+
let variant = parse_variant(quote! {
|
|
11
|
+
enum TestEnum {
|
|
12
|
+
#[persistent(u32)]
|
|
13
|
+
UnitVariant,
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
let enum_name = format_ident!("TestEnum");
|
|
17
|
+
|
|
18
|
+
let generated_key = gen_key_for_test(&enum_name, &variant);
|
|
19
|
+
let expected = quote! { TestEnum::UnitVariant };
|
|
20
|
+
assert_eq!(normalize(generated_key), normalize(expected));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#[test]
|
|
24
|
+
fn test_named_variant_single_field_non_primitive() {
|
|
25
|
+
let variant = parse_variant(quote! {
|
|
26
|
+
enum TestEnum {
|
|
27
|
+
#[persistent(u32)]
|
|
28
|
+
NamedVariant { owner: Address },
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
let enum_name = format_ident!("TestEnum");
|
|
32
|
+
|
|
33
|
+
let generated_key = gen_key_for_test(&enum_name, &variant);
|
|
34
|
+
// Non-primitive field identifiers should be cloned in the generated key.
|
|
35
|
+
let expected = quote! { TestEnum::NamedVariant(owner.clone()) };
|
|
36
|
+
assert_eq!(normalize(generated_key), normalize(expected));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
#[test]
|
|
40
|
+
fn test_named_variant_single_field_primitive() {
|
|
41
|
+
let variant = parse_variant(quote! {
|
|
42
|
+
enum TestEnum {
|
|
43
|
+
#[persistent(u32)]
|
|
44
|
+
NamedVariant { counter: u32 },
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
let enum_name = format_ident!("TestEnum");
|
|
48
|
+
|
|
49
|
+
let generated_key = gen_key_for_test(&enum_name, &variant);
|
|
50
|
+
// Primitive field identifiers should NOT be cloned in the generated key.
|
|
51
|
+
let expected = quote! { TestEnum::NamedVariant(counter) };
|
|
52
|
+
assert_eq!(normalize(generated_key), normalize(expected));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
#[test]
|
|
56
|
+
fn test_named_variant_multiple_fields_mixed() {
|
|
57
|
+
let variant = parse_variant(quote! {
|
|
58
|
+
enum TestEnum {
|
|
59
|
+
#[persistent(u32)]
|
|
60
|
+
MixedVariant { id: u64, name: String, count: i128 },
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
let enum_name = format_ident!("TestEnum");
|
|
64
|
+
|
|
65
|
+
let generated_key = gen_key_for_test(&enum_name, &variant);
|
|
66
|
+
// u64 and i128 are primitives (no clone), String is not (clone)
|
|
67
|
+
let expected = quote! { TestEnum::MixedVariant(id, name.clone(), count) };
|
|
68
|
+
assert_eq!(normalize(generated_key), normalize(expected));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
#[test]
|
|
72
|
+
fn test_uses_provided_enum_name() {
|
|
73
|
+
let variant = parse_variant(quote! {
|
|
74
|
+
enum Dummy {
|
|
75
|
+
#[persistent(u32)]
|
|
76
|
+
Variant,
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
let enum_name = format_ident!("CustomEnumName");
|
|
80
|
+
|
|
81
|
+
let generated_key = gen_key_for_test(&enum_name, &variant);
|
|
82
|
+
let expected = quote! { CustomEnumName::Variant };
|
|
83
|
+
assert_eq!(normalize(generated_key), normalize(expected));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#[test]
|
|
87
|
+
fn test_all_primitives_not_cloned() {
|
|
88
|
+
let primitives = ["u32", "i32", "u64", "i64", "u128", "i128", "bool"];
|
|
89
|
+
|
|
90
|
+
for ty in primitives {
|
|
91
|
+
let input = format!(
|
|
92
|
+
r#"
|
|
93
|
+
enum TestEnum {{
|
|
94
|
+
#[persistent(u32)]
|
|
95
|
+
Variant {{ key: {} }},
|
|
96
|
+
}}
|
|
97
|
+
"#,
|
|
98
|
+
ty
|
|
99
|
+
);
|
|
100
|
+
let variant = parse_variant_str(&input);
|
|
101
|
+
let enum_name = format_ident!("TestEnum");
|
|
102
|
+
|
|
103
|
+
let generated_key = gen_key_for_test(&enum_name, &variant);
|
|
104
|
+
let generated_str = generated_key.to_string();
|
|
105
|
+
|
|
106
|
+
assert!(!generated_str.contains("clone"), "primitive type {} should not be cloned, got: {}", ty, generated_str);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
//! Unit tests for the `gen_params` function.
|
|
2
|
+
|
|
3
|
+
use crate::storage::test::gen_params_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_param() {
|
|
10
|
+
let variant = parse_variant(quote! {
|
|
11
|
+
enum Test {
|
|
12
|
+
#[persistent(u32)]
|
|
13
|
+
UnitVariant,
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
let params = gen_params_for_test(&variant);
|
|
18
|
+
let expected = quote! { env: &soroban_sdk::Env };
|
|
19
|
+
assert_eq!(normalize(params), normalize(expected));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#[test]
|
|
23
|
+
fn test_named_variant_with_primitive_field_by_value() {
|
|
24
|
+
let variant = parse_variant(quote! {
|
|
25
|
+
enum Test {
|
|
26
|
+
#[persistent(u32)]
|
|
27
|
+
NamedVariant { key: u32 },
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
let params = gen_params_for_test(&variant);
|
|
32
|
+
let expected = quote! { env: &soroban_sdk::Env, key: u32 };
|
|
33
|
+
assert_eq!(normalize(params), normalize(expected));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
#[test]
|
|
37
|
+
fn test_named_variant_with_non_primitive_field_by_reference() {
|
|
38
|
+
let variant = parse_variant(quote! {
|
|
39
|
+
enum Test {
|
|
40
|
+
#[persistent(u32)]
|
|
41
|
+
NamedVariant { key: Address },
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
let params = gen_params_for_test(&variant);
|
|
46
|
+
let expected = quote! { env: &soroban_sdk::Env, key: &Address };
|
|
47
|
+
assert_eq!(normalize(params), normalize(expected));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#[test]
|
|
51
|
+
fn test_named_variant_mixed_primitive_and_non_primitive() {
|
|
52
|
+
let variant = parse_variant(quote! {
|
|
53
|
+
enum Test {
|
|
54
|
+
#[persistent(u32)]
|
|
55
|
+
MixedVariant { id: u64, name: String, count: i128, address: Address },
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
let params = gen_params_for_test(&variant);
|
|
60
|
+
// u64 and i128 are primitives (by value), String and Address are not (by reference)
|
|
61
|
+
let expected = quote! { env: &soroban_sdk::Env, id: u64, name: &String, count: i128, address: &Address };
|
|
62
|
+
assert_eq!(normalize(params), normalize(expected));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
#[test]
|
|
66
|
+
fn test_all_primitive_types_by_value() {
|
|
67
|
+
let primitives = [
|
|
68
|
+
("u32", "u32"),
|
|
69
|
+
("i32", "i32"),
|
|
70
|
+
("u64", "u64"),
|
|
71
|
+
("i64", "i64"),
|
|
72
|
+
("u128", "u128"),
|
|
73
|
+
("i128", "i128"),
|
|
74
|
+
("bool", "bool"),
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
for (ty_str, expected_ty) in primitives {
|
|
78
|
+
let input = format!(
|
|
79
|
+
r#"
|
|
80
|
+
enum Test {{
|
|
81
|
+
#[persistent(u32)]
|
|
82
|
+
Variant {{ key: {} }},
|
|
83
|
+
}}
|
|
84
|
+
"#,
|
|
85
|
+
ty_str
|
|
86
|
+
);
|
|
87
|
+
let variant = parse_variant(input.parse().unwrap());
|
|
88
|
+
let params = gen_params_for_test(&variant);
|
|
89
|
+
let params_str = params.to_string();
|
|
90
|
+
|
|
91
|
+
// Should be by value (no &)
|
|
92
|
+
assert!(
|
|
93
|
+
params_str.contains(&format!("key : {}", expected_ty)),
|
|
94
|
+
"primitive type {} should be passed by value, got: {}",
|
|
95
|
+
ty_str,
|
|
96
|
+
params_str
|
|
97
|
+
);
|
|
98
|
+
assert!(
|
|
99
|
+
!params_str.contains(&format!("key : & {}", expected_ty)),
|
|
100
|
+
"primitive type {} should NOT be passed by reference, got: {}",
|
|
101
|
+
ty_str,
|
|
102
|
+
params_str
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
use quote::quote;
|
|
2
|
+
|
|
3
|
+
use crate::tests::test_helpers::{assert_panics_contains, filter_item_inputs_excluding_labels};
|
|
4
|
+
|
|
5
|
+
#[test]
|
|
6
|
+
fn test_non_enum_input() {
|
|
7
|
+
for (case, input) in filter_item_inputs_excluding_labels(&["enum"]) {
|
|
8
|
+
assert_panics_contains(case, "failed to parse enum", || {
|
|
9
|
+
crate::storage::generate_storage(input.clone());
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
#[test]
|
|
15
|
+
fn test_tuple_variant_rejected() {
|
|
16
|
+
let cases = vec![
|
|
17
|
+
(
|
|
18
|
+
"instance tuple variant",
|
|
19
|
+
quote! {
|
|
20
|
+
enum TestEnum {
|
|
21
|
+
#[instance(u32)]
|
|
22
|
+
TupleVariant(String, u32),
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
),
|
|
26
|
+
(
|
|
27
|
+
"persistent tuple variant",
|
|
28
|
+
quote! {
|
|
29
|
+
enum TestEnum {
|
|
30
|
+
#[persistent(u32)]
|
|
31
|
+
TupleVariant(String, u32),
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
),
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
for (case, input) in cases {
|
|
38
|
+
assert_panics_contains(case, "only unit variants or named fields are supported", || {
|
|
39
|
+
crate::storage::generate_storage(input.clone());
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#[test]
|
|
45
|
+
fn test_attribute_errors() {
|
|
46
|
+
let cases_storage_type = vec![
|
|
47
|
+
(
|
|
48
|
+
"missing storage type",
|
|
49
|
+
"storage type must be specified exactly once",
|
|
50
|
+
quote! {
|
|
51
|
+
enum TestEnum { Counter }
|
|
52
|
+
},
|
|
53
|
+
),
|
|
54
|
+
(
|
|
55
|
+
"multiple storage types",
|
|
56
|
+
"storage type must be specified exactly once",
|
|
57
|
+
quote! {
|
|
58
|
+
enum TestEnum {
|
|
59
|
+
#[instance(u32)]
|
|
60
|
+
#[persistent(u32)]
|
|
61
|
+
Counter,
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
),
|
|
65
|
+
(
|
|
66
|
+
"invalid storage type with another valid storage type",
|
|
67
|
+
"failed to parse storage type",
|
|
68
|
+
quote! {
|
|
69
|
+
enum TestEnum {
|
|
70
|
+
#[persistent(u32)]
|
|
71
|
+
#[instance]
|
|
72
|
+
Counter,
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
),
|
|
76
|
+
(
|
|
77
|
+
"missing type param",
|
|
78
|
+
"failed to parse storage type for",
|
|
79
|
+
quote! {
|
|
80
|
+
enum TestEnum {
|
|
81
|
+
#[instance]
|
|
82
|
+
Counter,
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
),
|
|
86
|
+
(
|
|
87
|
+
"invalid type param",
|
|
88
|
+
"failed to parse storage type for",
|
|
89
|
+
quote! {
|
|
90
|
+
enum TestEnum {
|
|
91
|
+
#[persistent(u32, String)]
|
|
92
|
+
Counter,
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
),
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
for (case, expected, input) in cases_storage_type {
|
|
99
|
+
assert_panics_contains(case, expected, || {
|
|
100
|
+
crate::storage::generate_storage(input.clone());
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let cases_default = vec![
|
|
105
|
+
(
|
|
106
|
+
"multiple defaults",
|
|
107
|
+
"multiple default values specified",
|
|
108
|
+
quote! {
|
|
109
|
+
enum TestEnum {
|
|
110
|
+
#[persistent(u32)]
|
|
111
|
+
#[default(0)]
|
|
112
|
+
#[default(1)]
|
|
113
|
+
Counter,
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
),
|
|
117
|
+
(
|
|
118
|
+
"invalid default value",
|
|
119
|
+
"failed to parse default value",
|
|
120
|
+
quote! {
|
|
121
|
+
enum TestEnum {
|
|
122
|
+
#[persistent(u32)]
|
|
123
|
+
#[default(!@#$%)]
|
|
124
|
+
Counter,
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
),
|
|
128
|
+
(
|
|
129
|
+
"default without parens",
|
|
130
|
+
"failed to parse default value",
|
|
131
|
+
quote! {
|
|
132
|
+
enum TestEnum {
|
|
133
|
+
#[persistent(u32)]
|
|
134
|
+
#[default]
|
|
135
|
+
Counter,
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
),
|
|
139
|
+
(
|
|
140
|
+
"default with empty parens",
|
|
141
|
+
"failed to parse default value",
|
|
142
|
+
quote! {
|
|
143
|
+
enum TestEnum {
|
|
144
|
+
#[persistent(u32)]
|
|
145
|
+
#[default()]
|
|
146
|
+
Counter,
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
),
|
|
150
|
+
];
|
|
151
|
+
|
|
152
|
+
for (case, expected, input) in cases_default {
|
|
153
|
+
assert_panics_contains(case, expected, || {
|
|
154
|
+
crate::storage::generate_storage(input.clone());
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
let cases_name = vec![
|
|
159
|
+
(
|
|
160
|
+
"multiple name attrs",
|
|
161
|
+
"multiple name attributes specified",
|
|
162
|
+
quote! {
|
|
163
|
+
enum TestEnum {
|
|
164
|
+
#[persistent(u32)]
|
|
165
|
+
#[name("foo")]
|
|
166
|
+
#[name("bar")]
|
|
167
|
+
Counter,
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
),
|
|
171
|
+
(
|
|
172
|
+
"invalid name attr",
|
|
173
|
+
"failed to parse name attribute",
|
|
174
|
+
quote! {
|
|
175
|
+
enum TestEnum {
|
|
176
|
+
#[persistent(u32)]
|
|
177
|
+
#[name(123)]
|
|
178
|
+
Counter,
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
),
|
|
182
|
+
(
|
|
183
|
+
"name without parens",
|
|
184
|
+
"failed to parse name attribute",
|
|
185
|
+
quote! {
|
|
186
|
+
enum TestEnum {
|
|
187
|
+
#[persistent(u32)]
|
|
188
|
+
#[name]
|
|
189
|
+
Counter,
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
),
|
|
193
|
+
(
|
|
194
|
+
"name with empty parens",
|
|
195
|
+
"failed to parse name attribute",
|
|
196
|
+
quote! {
|
|
197
|
+
enum TestEnum {
|
|
198
|
+
#[persistent(u32)]
|
|
199
|
+
#[name()]
|
|
200
|
+
Counter,
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
),
|
|
204
|
+
];
|
|
205
|
+
|
|
206
|
+
for (case, expected, input) in cases_name {
|
|
207
|
+
assert_panics_contains(case, expected, || {
|
|
208
|
+
crate::storage::generate_storage(input.clone());
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
assert_panics_contains("unknown attribute", "unknown attribute", || {
|
|
213
|
+
let input = quote! {
|
|
214
|
+
enum TestEnum {
|
|
215
|
+
#[persistent(u32)]
|
|
216
|
+
#[unknown_attr]
|
|
217
|
+
Counter,
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
crate::storage::generate_storage(input);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Test #[no_ttl_extension] validation
|
|
224
|
+
let cases_no_ttl = vec![
|
|
225
|
+
(
|
|
226
|
+
"no_ttl_extension does not accept arguments",
|
|
227
|
+
"does not accept arguments",
|
|
228
|
+
quote! {
|
|
229
|
+
enum TestEnum {
|
|
230
|
+
#[persistent(u32)]
|
|
231
|
+
#[no_ttl_extension(foo)]
|
|
232
|
+
Counter,
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
),
|
|
236
|
+
(
|
|
237
|
+
"multiple no_ttl_extension",
|
|
238
|
+
"multiple #[no_ttl_extension]",
|
|
239
|
+
quote! {
|
|
240
|
+
enum TestEnum {
|
|
241
|
+
#[persistent(u32)]
|
|
242
|
+
#[no_ttl_extension]
|
|
243
|
+
#[no_ttl_extension]
|
|
244
|
+
Counter,
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
),
|
|
248
|
+
(
|
|
249
|
+
"triple no_ttl_extension",
|
|
250
|
+
"multiple #[no_ttl_extension]",
|
|
251
|
+
quote! {
|
|
252
|
+
enum TestEnum {
|
|
253
|
+
#[persistent(u32)]
|
|
254
|
+
#[no_ttl_extension]
|
|
255
|
+
#[no_ttl_extension]
|
|
256
|
+
#[no_ttl_extension]
|
|
257
|
+
Counter,
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
),
|
|
261
|
+
(
|
|
262
|
+
"no_ttl_extension on instance",
|
|
263
|
+
"can only be used with #[persistent",
|
|
264
|
+
quote! {
|
|
265
|
+
enum TestEnum {
|
|
266
|
+
#[instance(u32)]
|
|
267
|
+
#[no_ttl_extension]
|
|
268
|
+
Counter,
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
),
|
|
272
|
+
(
|
|
273
|
+
"no_ttl_extension on instance with default",
|
|
274
|
+
"can only be used with #[persistent",
|
|
275
|
+
quote! {
|
|
276
|
+
enum TestEnum {
|
|
277
|
+
#[instance(u32)]
|
|
278
|
+
#[default(0)]
|
|
279
|
+
#[no_ttl_extension]
|
|
280
|
+
Counter,
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
),
|
|
284
|
+
(
|
|
285
|
+
"no_ttl_extension before instance storage type",
|
|
286
|
+
"can only be used with #[persistent",
|
|
287
|
+
quote! {
|
|
288
|
+
enum TestEnum {
|
|
289
|
+
#[no_ttl_extension]
|
|
290
|
+
#[instance(u32)]
|
|
291
|
+
Counter,
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
),
|
|
295
|
+
(
|
|
296
|
+
"no_ttl_extension on temporary",
|
|
297
|
+
"can only be used with #[persistent",
|
|
298
|
+
quote! {
|
|
299
|
+
enum TestEnum {
|
|
300
|
+
#[temporary(u32)]
|
|
301
|
+
#[no_ttl_extension]
|
|
302
|
+
Counter,
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
),
|
|
306
|
+
(
|
|
307
|
+
"no_ttl_extension before temporary storage type",
|
|
308
|
+
"can only be used with #[persistent",
|
|
309
|
+
quote! {
|
|
310
|
+
enum TestEnum {
|
|
311
|
+
#[no_ttl_extension]
|
|
312
|
+
#[temporary(u32)]
|
|
313
|
+
Counter,
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
),
|
|
317
|
+
(
|
|
318
|
+
"no_ttl_extension on instance in multi-variant enum",
|
|
319
|
+
"can only be used with #[persistent",
|
|
320
|
+
quote! {
|
|
321
|
+
enum TestEnum {
|
|
322
|
+
#[persistent(u32)]
|
|
323
|
+
ValidVariant,
|
|
324
|
+
|
|
325
|
+
#[instance(u32)]
|
|
326
|
+
#[no_ttl_extension]
|
|
327
|
+
InvalidVariant,
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
),
|
|
331
|
+
];
|
|
332
|
+
|
|
333
|
+
for (case, expected, input) in cases_no_ttl {
|
|
334
|
+
assert_panics_contains(case, expected, || {
|
|
335
|
+
crate::storage::generate_storage(input.clone());
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// ============================================
|
|
341
|
+
// Valid Cases: Snapshot Tests for Generated Code
|
|
342
|
+
// ============================================
|
|
343
|
+
|
|
344
|
+
/// Comprehensive snapshot test covering all storage macro features:
|
|
345
|
+
/// - All storage types: instance, persistent, temporary
|
|
346
|
+
/// - Unit variants and named field variants (single and multiple fields)
|
|
347
|
+
/// - Default values (with and without)
|
|
348
|
+
/// - TTL extension control (auto for persistent, opt-out with #[no_ttl_extension])
|
|
349
|
+
/// - Custom name attribute
|
|
350
|
+
/// - snake_case naming conversion (TempData -> temp_data)
|
|
351
|
+
/// - Primitive vs non-primitive key types (by value vs by reference + clone)
|
|
352
|
+
///
|
|
353
|
+
/// Note: Primitive type detection is exhaustively tested in test_is_primitive_type_* unit tests.
|
|
354
|
+
/// This snapshot only needs one of each to verify the generated code integrates correctly.
|
|
355
|
+
#[test]
|
|
356
|
+
fn snapshot_generated_storage_code() {
|
|
357
|
+
let input = quote! {
|
|
358
|
+
/// Enum-level doc comment
|
|
359
|
+
pub enum StorageKeys {
|
|
360
|
+
/// Instance storage with default value
|
|
361
|
+
#[instance(u32)]
|
|
362
|
+
#[default(0)]
|
|
363
|
+
Counter,
|
|
364
|
+
|
|
365
|
+
/// Persistent storage with single field and default (auto TTL)
|
|
366
|
+
#[persistent(String)]
|
|
367
|
+
#[default(String::from_str(env, "hello"))]
|
|
368
|
+
Message { sender: Address },
|
|
369
|
+
|
|
370
|
+
/// Temporary storage with single field
|
|
371
|
+
#[temporary(bool)]
|
|
372
|
+
Flag { key: String },
|
|
373
|
+
|
|
374
|
+
/// Persistent storage without fields or default
|
|
375
|
+
#[persistent(Address)]
|
|
376
|
+
Owner,
|
|
377
|
+
|
|
378
|
+
/// Custom #[name()] override
|
|
379
|
+
#[persistent(Option<Address>)]
|
|
380
|
+
#[name("custom_key_name")]
|
|
381
|
+
OptionalData { key: BytesN<32> },
|
|
382
|
+
|
|
383
|
+
/// Temporary storage unit variant (also tests snake_case: TempData -> temp_data)
|
|
384
|
+
#[temporary(u64)]
|
|
385
|
+
TempData,
|
|
386
|
+
|
|
387
|
+
/// Primitive key type: passed by value, no clone
|
|
388
|
+
#[persistent(u32)]
|
|
389
|
+
PrimitiveKey { key: u32 },
|
|
390
|
+
|
|
391
|
+
/// Non-primitive key type: passed by reference, cloned
|
|
392
|
+
#[instance(u32)]
|
|
393
|
+
NonPrimitiveKey { key: String },
|
|
394
|
+
|
|
395
|
+
/// Multiple fields with mixed primitive/non-primitive types
|
|
396
|
+
#[persistent(u32)]
|
|
397
|
+
NamedVariant { first: u32, second: String, third: Address },
|
|
398
|
+
|
|
399
|
+
/// #[no_ttl_extension] opt-out for persistent storage
|
|
400
|
+
#[persistent(u64)]
|
|
401
|
+
#[no_ttl_extension]
|
|
402
|
+
CachedValue { key: Address },
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
let result = crate::storage::generate_storage(input);
|
|
407
|
+
let formatted = prettyplease::unparse(&syn::parse2::<syn::File>(result).expect("failed to parse generated code"));
|
|
408
|
+
|
|
409
|
+
insta::assert_snapshot!(formatted);
|
|
410
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
//! Unit tests for the `is_primitive_type` function.
|
|
2
|
+
|
|
3
|
+
use crate::storage::test::is_primitive_type_for_test;
|
|
4
|
+
|
|
5
|
+
#[test]
|
|
6
|
+
fn test_recognizes_primitive_types() {
|
|
7
|
+
let primitives = ["u32", "i32", "u64", "i64", "u128", "i128", "bool"];
|
|
8
|
+
for ty_str in primitives {
|
|
9
|
+
let ty = syn::parse_str::<syn::Type>(ty_str).expect("failed to parse type");
|
|
10
|
+
assert!(is_primitive_type_for_test(&ty), "{ty_str} should be recognized as primitive");
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
#[test]
|
|
15
|
+
fn test_rejects_non_primitive_types() {
|
|
16
|
+
let non_primitives = [
|
|
17
|
+
"u8", // not in primitive list
|
|
18
|
+
"usize", // not in primitive list
|
|
19
|
+
"String",
|
|
20
|
+
"Address",
|
|
21
|
+
"Vec<u8>",
|
|
22
|
+
"Option<u32>",
|
|
23
|
+
"std::u32", // multi-segment path
|
|
24
|
+
"soroban_sdk::Address",
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
for ty_str in non_primitives {
|
|
28
|
+
let ty = syn::parse_str::<syn::Type>(ty_str).expect("failed to parse type");
|
|
29
|
+
assert!(!is_primitive_type_for_test(&ty), "{ty_str} should NOT be recognized as primitive");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
#[test]
|
|
34
|
+
fn test_rejects_other_type_variants() {
|
|
35
|
+
let other_variants = [
|
|
36
|
+
("&u32", "reference"),
|
|
37
|
+
("&mut u32", "mutable reference"),
|
|
38
|
+
("(u32, i32)", "tuple"),
|
|
39
|
+
("[u32; 4]", "array"),
|
|
40
|
+
("[u32]", "slice"),
|
|
41
|
+
("!", "never"),
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
for (ty_str, label) in other_variants {
|
|
45
|
+
let ty = syn::parse_str::<syn::Type>(ty_str).expect("failed to parse type");
|
|
46
|
+
assert!(!is_primitive_type_for_test(&ty), "{label} type {ty_str} should NOT be recognized as primitive");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
mod extract_fields;
|
|
2
|
+
mod gen_accessor_methods;
|
|
3
|
+
mod gen_args;
|
|
4
|
+
mod gen_enum_variant;
|
|
5
|
+
mod gen_key;
|
|
6
|
+
mod gen_params;
|
|
7
|
+
mod generate_storage;
|
|
8
|
+
mod is_primitive_type;
|
|
9
|
+
mod parse_default;
|
|
10
|
+
mod parse_name;
|
|
11
|
+
mod parse_no_ttl_extension;
|
|
12
|
+
mod parse_storage_type;
|
|
13
|
+
mod storage_kind;
|
|
14
|
+
mod test_setup;
|
|
15
|
+
mod validate_attrs;
|
|
16
|
+
mod variant_config;
|