@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,226 @@
|
|
|
1
|
+
//! Unit tests for the `VariantConfig` struct and its `TryFrom` implementation via wrapper functions.
|
|
2
|
+
|
|
3
|
+
use crate::storage::test::{get_variant_config_for_test, get_variant_method_names_for_test};
|
|
4
|
+
use quote::quote;
|
|
5
|
+
|
|
6
|
+
use super::test_setup::parse_variant;
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// TryFrom<&Variant> Tests
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
#[test]
|
|
13
|
+
fn test_try_from_instance_variant() {
|
|
14
|
+
let variant = parse_variant(quote! {
|
|
15
|
+
enum Test {
|
|
16
|
+
#[instance(u32)]
|
|
17
|
+
Counter,
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
let config = get_variant_config_for_test(&variant);
|
|
22
|
+
assert!(config.is_ok());
|
|
23
|
+
let config = config.unwrap();
|
|
24
|
+
assert_eq!(config.kind_name, "instance");
|
|
25
|
+
assert_eq!(config.value_type, "u32");
|
|
26
|
+
assert_eq!(config.name, "counter"); // snake_case conversion
|
|
27
|
+
assert!(!config.auto_ttl); // instance storage doesn't have auto TTL
|
|
28
|
+
assert!(!config.has_default);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
#[test]
|
|
32
|
+
fn test_try_from_persistent_variant() {
|
|
33
|
+
let variant = parse_variant(quote! {
|
|
34
|
+
enum Test {
|
|
35
|
+
#[persistent(Address)]
|
|
36
|
+
Owner,
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
let config = get_variant_config_for_test(&variant);
|
|
41
|
+
assert!(config.is_ok());
|
|
42
|
+
let config = config.unwrap();
|
|
43
|
+
assert_eq!(config.kind_name, "persistent");
|
|
44
|
+
assert_eq!(config.value_type, "Address");
|
|
45
|
+
assert_eq!(config.name, "owner");
|
|
46
|
+
assert!(config.auto_ttl); // persistent has auto TTL by default
|
|
47
|
+
assert!(!config.has_default);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#[test]
|
|
51
|
+
fn test_try_from_temporary_variant() {
|
|
52
|
+
let variant = parse_variant(quote! {
|
|
53
|
+
enum Test {
|
|
54
|
+
#[temporary(bool)]
|
|
55
|
+
Flag,
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
let config = get_variant_config_for_test(&variant);
|
|
60
|
+
assert!(config.is_ok());
|
|
61
|
+
let config = config.unwrap();
|
|
62
|
+
assert_eq!(config.kind_name, "temporary");
|
|
63
|
+
assert_eq!(config.value_type, "bool");
|
|
64
|
+
assert_eq!(config.name, "flag");
|
|
65
|
+
assert!(!config.auto_ttl); // temporary doesn't have auto TTL
|
|
66
|
+
assert!(!config.has_default);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
#[test]
|
|
70
|
+
fn test_try_from_with_default() {
|
|
71
|
+
let variant = parse_variant(quote! {
|
|
72
|
+
enum Test {
|
|
73
|
+
#[instance(u32)]
|
|
74
|
+
#[default(0)]
|
|
75
|
+
Counter,
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
let config = get_variant_config_for_test(&variant);
|
|
80
|
+
assert!(config.is_ok());
|
|
81
|
+
let config = config.unwrap();
|
|
82
|
+
assert!(config.has_default);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
#[test]
|
|
86
|
+
fn test_try_from_with_custom_name() {
|
|
87
|
+
let variant = parse_variant(quote! {
|
|
88
|
+
enum Test {
|
|
89
|
+
#[instance(u32)]
|
|
90
|
+
#[name("custom_name")]
|
|
91
|
+
Counter,
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
let config = get_variant_config_for_test(&variant);
|
|
96
|
+
assert!(config.is_ok());
|
|
97
|
+
let config = config.unwrap();
|
|
98
|
+
assert_eq!(config.name, "custom_name");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
#[test]
|
|
102
|
+
fn test_try_from_snake_case_conversion() {
|
|
103
|
+
let variant = parse_variant(quote! {
|
|
104
|
+
enum Test {
|
|
105
|
+
#[instance(u32)]
|
|
106
|
+
MyLongVariantName,
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
let config = get_variant_config_for_test(&variant);
|
|
111
|
+
assert!(config.is_ok());
|
|
112
|
+
let config = config.unwrap();
|
|
113
|
+
assert_eq!(config.name, "my_long_variant_name");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
#[test]
|
|
117
|
+
fn test_try_from_persistent_with_no_ttl_extension() {
|
|
118
|
+
let variant = parse_variant(quote! {
|
|
119
|
+
enum Test {
|
|
120
|
+
#[persistent(u32)]
|
|
121
|
+
#[no_ttl_extension]
|
|
122
|
+
CachedValue,
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
let config = get_variant_config_for_test(&variant);
|
|
127
|
+
assert!(config.is_ok());
|
|
128
|
+
let config = config.unwrap();
|
|
129
|
+
assert_eq!(config.kind_name, "persistent");
|
|
130
|
+
assert!(!config.auto_ttl); // disabled by no_ttl_extension
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
#[test]
|
|
134
|
+
fn test_try_from_error_no_ttl_extension_on_instance() {
|
|
135
|
+
let variant = parse_variant(quote! {
|
|
136
|
+
enum Test {
|
|
137
|
+
#[instance(u32)]
|
|
138
|
+
#[no_ttl_extension]
|
|
139
|
+
Counter,
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
let config = get_variant_config_for_test(&variant);
|
|
144
|
+
assert!(config.is_err());
|
|
145
|
+
let err = config.unwrap_err();
|
|
146
|
+
assert!(err.contains("can only be used with #[persistent"));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
#[test]
|
|
150
|
+
fn test_try_from_error_no_ttl_extension_on_temporary() {
|
|
151
|
+
let variant = parse_variant(quote! {
|
|
152
|
+
enum Test {
|
|
153
|
+
#[temporary(u32)]
|
|
154
|
+
#[no_ttl_extension]
|
|
155
|
+
TempData,
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
let config = get_variant_config_for_test(&variant);
|
|
160
|
+
assert!(config.is_err());
|
|
161
|
+
let err = config.unwrap_err();
|
|
162
|
+
assert!(err.contains("can only be used with #[persistent"));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
#[test]
|
|
166
|
+
fn test_try_from_error_unknown_attribute() {
|
|
167
|
+
let variant = parse_variant(quote! {
|
|
168
|
+
enum Test {
|
|
169
|
+
#[persistent(u32)]
|
|
170
|
+
#[unknown_attr]
|
|
171
|
+
Counter,
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
let config = get_variant_config_for_test(&variant);
|
|
176
|
+
assert!(config.is_err());
|
|
177
|
+
let err = config.unwrap_err();
|
|
178
|
+
assert!(err.contains("unknown attribute"));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ============================================================================
|
|
182
|
+
// method_names() Tests
|
|
183
|
+
// ============================================================================
|
|
184
|
+
|
|
185
|
+
#[test]
|
|
186
|
+
fn test_method_names_basic() {
|
|
187
|
+
let variant = parse_variant(quote! {
|
|
188
|
+
enum Test {
|
|
189
|
+
#[instance(u32)]
|
|
190
|
+
Counter,
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
let result = get_variant_method_names_for_test(&variant);
|
|
195
|
+
assert!(result.is_ok());
|
|
196
|
+
let (getter, setter, remover, set_or_remove, has, ttl_extender) = result.unwrap();
|
|
197
|
+
|
|
198
|
+
assert_eq!(getter, "counter");
|
|
199
|
+
assert_eq!(setter, "set_counter");
|
|
200
|
+
assert_eq!(remover, "remove_counter");
|
|
201
|
+
assert_eq!(set_or_remove, "set_or_remove_counter");
|
|
202
|
+
assert_eq!(has, "has_counter");
|
|
203
|
+
assert_eq!(ttl_extender, "extend_counter_ttl");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
#[test]
|
|
207
|
+
fn test_method_names_with_custom_name() {
|
|
208
|
+
let variant = parse_variant(quote! {
|
|
209
|
+
enum Test {
|
|
210
|
+
#[instance(u32)]
|
|
211
|
+
#[name("custom")]
|
|
212
|
+
Counter,
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
let result = get_variant_method_names_for_test(&variant);
|
|
217
|
+
assert!(result.is_ok());
|
|
218
|
+
let (getter, setter, remover, set_or_remove, has, ttl_extender) = result.unwrap();
|
|
219
|
+
|
|
220
|
+
assert_eq!(getter, "custom");
|
|
221
|
+
assert_eq!(setter, "set_custom");
|
|
222
|
+
assert_eq!(remover, "remove_custom");
|
|
223
|
+
assert_eq!(set_or_remove, "set_or_remove_custom");
|
|
224
|
+
assert_eq!(has, "has_custom");
|
|
225
|
+
assert_eq!(ttl_extender, "extend_custom_ttl");
|
|
226
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#![allow(unused_macros)]
|
|
2
|
+
|
|
3
|
+
pub(in crate::tests) fn assert_panics_contains<F>(case: &str, expected_substring: &str, f: F)
|
|
4
|
+
where
|
|
5
|
+
F: FnOnce() + std::panic::UnwindSafe,
|
|
6
|
+
{
|
|
7
|
+
let result = std::panic::catch_unwind(f);
|
|
8
|
+
assert!(result.is_err(), "{case}: expected panic, but function returned normally");
|
|
9
|
+
|
|
10
|
+
let payload = result.expect_err("checked above");
|
|
11
|
+
let msg = if let Some(s) = payload.downcast_ref::<&str>() {
|
|
12
|
+
(*s).to_string()
|
|
13
|
+
} else if let Some(s) = payload.downcast_ref::<String>() {
|
|
14
|
+
s.clone()
|
|
15
|
+
} else {
|
|
16
|
+
format!("{payload:?}")
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
assert!(
|
|
20
|
+
msg.contains(expected_substring),
|
|
21
|
+
"{case}: expected panic message to contain '{expected_substring}', got '{msg}'"
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
pub(in crate::tests) fn filter_item_inputs_excluding_labels(
|
|
26
|
+
excluded: &[&str],
|
|
27
|
+
) -> Vec<(&'static str, proc_macro2::TokenStream)> {
|
|
28
|
+
item_inputs().into_iter().filter(|(label, _)| !excluded.iter().any(|x| x == label)).collect()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/// Canonical list of syntactically-valid `syn::Item` inputs.
|
|
32
|
+
///
|
|
33
|
+
/// Keep this list broad and reusable; other helpers should filter it by AST kind.
|
|
34
|
+
pub(in crate::tests) fn item_inputs() -> Vec<(&'static str, proc_macro2::TokenStream)> {
|
|
35
|
+
vec![
|
|
36
|
+
(
|
|
37
|
+
"struct",
|
|
38
|
+
quote::quote! {
|
|
39
|
+
struct AStruct {
|
|
40
|
+
field: u32,
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
),
|
|
44
|
+
(
|
|
45
|
+
"enum",
|
|
46
|
+
quote::quote! {
|
|
47
|
+
enum AnEnum {
|
|
48
|
+
Variant1,
|
|
49
|
+
Variant2,
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
),
|
|
53
|
+
("function", quote::quote! { fn a_function() {} }),
|
|
54
|
+
("macro invocation", quote::quote! { some_macro!(); }),
|
|
55
|
+
("const item", quote::quote! { const A_CONST: u32 = 1; }),
|
|
56
|
+
("static item", quote::quote! { static A_STATIC: u32 = 1; }),
|
|
57
|
+
("use item", quote::quote! { use core::mem; }),
|
|
58
|
+
("mod item", quote::quote! { mod a_module {} }),
|
|
59
|
+
("extern crate", quote::quote! { extern crate core; }),
|
|
60
|
+
(
|
|
61
|
+
"union",
|
|
62
|
+
quote::quote! {
|
|
63
|
+
union AUnion {
|
|
64
|
+
a: u32,
|
|
65
|
+
b: u32,
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
),
|
|
69
|
+
(
|
|
70
|
+
"impl block",
|
|
71
|
+
quote::quote! {
|
|
72
|
+
impl SomeType {
|
|
73
|
+
fn method(&self) {}
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
),
|
|
77
|
+
(
|
|
78
|
+
"trait",
|
|
79
|
+
quote::quote! {
|
|
80
|
+
trait ATrait {
|
|
81
|
+
fn method(&self);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
),
|
|
85
|
+
("type alias", quote::quote! { type AnAlias = u32; }),
|
|
86
|
+
]
|
|
87
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
use quote::quote;
|
|
2
|
+
|
|
3
|
+
use crate::tests::test_helpers::{assert_panics_contains, filter_item_inputs_excluding_labels};
|
|
4
|
+
|
|
5
|
+
// ============================================
|
|
6
|
+
// Snapshot Test: TtlConfigurable Code Generation
|
|
7
|
+
// ============================================
|
|
8
|
+
|
|
9
|
+
#[test]
|
|
10
|
+
fn snapshot_generated_ttl_configurable_code() {
|
|
11
|
+
// Test with a public unit struct
|
|
12
|
+
let input = quote! {
|
|
13
|
+
pub struct MyContract {
|
|
14
|
+
some_field: u32,
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
let result = crate::ttl_configurable::generate_ttl_configurable_impl(input);
|
|
18
|
+
let formatted = prettyplease::unparse(&syn::parse2::<syn::File>(result).expect("failed to parse generated code"));
|
|
19
|
+
|
|
20
|
+
insta::assert_snapshot!(formatted);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ============================================
|
|
24
|
+
// Error Cases: Non-Struct Input
|
|
25
|
+
// ============================================
|
|
26
|
+
|
|
27
|
+
#[test]
|
|
28
|
+
fn test_ttl_configurable_rejects_non_struct_inputs() {
|
|
29
|
+
for (case, input) in filter_item_inputs_excluding_labels(&["struct"]) {
|
|
30
|
+
assert_panics_contains(case, "failed to parse struct", || {
|
|
31
|
+
crate::ttl_configurable::generate_ttl_configurable_impl(input.clone());
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
use quote::quote;
|
|
2
|
+
|
|
3
|
+
use crate::tests::test_helpers::{assert_panics_contains, filter_item_inputs_excluding_labels};
|
|
4
|
+
|
|
5
|
+
// ============================================
|
|
6
|
+
// Snapshot Test: TtlExtendable Code Generation
|
|
7
|
+
// ============================================
|
|
8
|
+
|
|
9
|
+
#[test]
|
|
10
|
+
fn snapshot_generated_ttl_extendable_code() {
|
|
11
|
+
let input = quote! {
|
|
12
|
+
pub struct MyContract;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
let result = crate::ttl_extendable::generate_ttl_extendable_impl(input);
|
|
16
|
+
let formatted = prettyplease::unparse(&syn::parse2::<syn::File>(result).expect("failed to parse generated code"));
|
|
17
|
+
|
|
18
|
+
insta::assert_snapshot!(formatted);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ============================================
|
|
22
|
+
// Error Cases: Non-Struct Input
|
|
23
|
+
// ============================================
|
|
24
|
+
|
|
25
|
+
#[test]
|
|
26
|
+
fn test_ttl_extendable_rejects_non_struct_inputs() {
|
|
27
|
+
for (case, input) in filter_item_inputs_excluding_labels(&["struct"]) {
|
|
28
|
+
assert_panics_contains(case, "failed to parse struct", || {
|
|
29
|
+
crate::ttl_extendable::generate_ttl_extendable_impl(input.clone());
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
use proc_macro2::TokenStream;
|
|
2
|
+
use quote::quote;
|
|
3
|
+
|
|
4
|
+
use crate::tests::test_helpers::{assert_panics_contains, filter_item_inputs_excluding_labels};
|
|
5
|
+
|
|
6
|
+
static CARGO_PKG_VERSION_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
|
|
7
|
+
|
|
8
|
+
/// Small RAII helper for temporarily mutating a single environment variable in tests.
|
|
9
|
+
///
|
|
10
|
+
/// Why this exists:
|
|
11
|
+
/// - `generate_upgradeable_impl` reads `CARGO_PKG_VERSION` at runtime to decide whether to emit
|
|
12
|
+
/// `soroban_sdk::contractmeta!(key = "binver", ...)`.
|
|
13
|
+
/// - Tests need to set/unset that env var deterministically and then restore it to avoid polluting
|
|
14
|
+
/// other tests (especially snapshot tests).
|
|
15
|
+
/// - `Drop` guarantees restoration even if the test panics.
|
|
16
|
+
struct EnvVarGuard {
|
|
17
|
+
key: &'static str,
|
|
18
|
+
prev: Option<String>,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
impl EnvVarGuard {
|
|
22
|
+
/// Captures the current value of `key` (if any) so it can be restored on drop.
|
|
23
|
+
fn new(key: &'static str) -> Self {
|
|
24
|
+
Self { key, prev: std::env::var(key).ok() }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// Sets the environment variable for the duration of this guard's lifetime.
|
|
28
|
+
fn set(&self, value: &str) {
|
|
29
|
+
std::env::set_var(self.key, value);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/// Removes the environment variable for the duration of this guard's lifetime.
|
|
33
|
+
fn remove(&self) {
|
|
34
|
+
std::env::remove_var(self.key);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
impl Drop for EnvVarGuard {
|
|
39
|
+
/// Restores the original env var state captured in `new()`.
|
|
40
|
+
fn drop(&mut self) {
|
|
41
|
+
match &self.prev {
|
|
42
|
+
Some(v) => std::env::set_var(self.key, v),
|
|
43
|
+
None => std::env::remove_var(self.key),
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ============================================
|
|
49
|
+
// Snapshot Test: Upgradeable Code Generation
|
|
50
|
+
// ============================================
|
|
51
|
+
|
|
52
|
+
#[test]
|
|
53
|
+
fn snapshot_generated_upgradeable_code() {
|
|
54
|
+
let _lock = CARGO_PKG_VERSION_LOCK.lock().expect("lock poisoned");
|
|
55
|
+
let input = quote! {
|
|
56
|
+
pub struct MyContract;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Test default behavior (requires manual UpgradeableInternal impl)
|
|
60
|
+
let default_result = crate::upgradeable::generate_upgradeable_impl(TokenStream::new(), input.clone());
|
|
61
|
+
let default_formatted =
|
|
62
|
+
prettyplease::unparse(&syn::parse2::<syn::File>(default_result).expect("failed to parse generated code"));
|
|
63
|
+
|
|
64
|
+
// Test with no_migration (auto-generates UpgradeableInternal impl)
|
|
65
|
+
let no_migration_result = crate::upgradeable::generate_upgradeable_impl(quote! { no_migration }, input);
|
|
66
|
+
let no_migration_formatted =
|
|
67
|
+
prettyplease::unparse(&syn::parse2::<syn::File>(no_migration_result).expect("failed to parse generated code"));
|
|
68
|
+
|
|
69
|
+
let combined = format!(
|
|
70
|
+
"// ============================================\n\
|
|
71
|
+
// Default: requires manual UpgradeableInternal\n\
|
|
72
|
+
// ============================================\n\n\
|
|
73
|
+
{default_formatted}\n\
|
|
74
|
+
// ============================================\n\
|
|
75
|
+
// With no_migration: auto-generates impl\n\
|
|
76
|
+
// ============================================\n\n\
|
|
77
|
+
{no_migration_formatted}"
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
insta::assert_snapshot!(combined);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ============================================
|
|
84
|
+
// Error Cases: upgradeable macro non-struct input
|
|
85
|
+
// ============================================
|
|
86
|
+
|
|
87
|
+
#[test]
|
|
88
|
+
fn test_upgradeable_skips_binver_when_version_is_0_0_0() {
|
|
89
|
+
let _lock = CARGO_PKG_VERSION_LOCK.lock().expect("lock poisoned");
|
|
90
|
+
let guard = EnvVarGuard::new("CARGO_PKG_VERSION");
|
|
91
|
+
guard.set("0.0.0");
|
|
92
|
+
|
|
93
|
+
let input = quote! {
|
|
94
|
+
pub struct MyContract;
|
|
95
|
+
};
|
|
96
|
+
let result = crate::upgradeable::generate_upgradeable_impl(TokenStream::new(), input);
|
|
97
|
+
let result_str = result.to_string();
|
|
98
|
+
|
|
99
|
+
assert!(
|
|
100
|
+
!result_str.contains("contractmeta ! (key = \"binver\""),
|
|
101
|
+
"should skip binver contractmeta when version is 0.0.0. Got: {}",
|
|
102
|
+
result_str
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
#[test]
|
|
107
|
+
fn test_upgradeable_skips_binver_when_version_is_missing() {
|
|
108
|
+
let _lock = CARGO_PKG_VERSION_LOCK.lock().expect("lock poisoned");
|
|
109
|
+
let guard = EnvVarGuard::new("CARGO_PKG_VERSION");
|
|
110
|
+
guard.remove();
|
|
111
|
+
|
|
112
|
+
let input = quote! {
|
|
113
|
+
pub struct MyContract;
|
|
114
|
+
};
|
|
115
|
+
let result = crate::upgradeable::generate_upgradeable_impl(TokenStream::new(), input);
|
|
116
|
+
let result_str = result.to_string();
|
|
117
|
+
|
|
118
|
+
assert!(
|
|
119
|
+
!result_str.contains("contractmeta ! (key = \"binver\""),
|
|
120
|
+
"should skip binver contractmeta when version env is missing. Got: {}",
|
|
121
|
+
result_str
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
#[test]
|
|
126
|
+
fn test_upgradeable_rejects_non_struct_inputs() {
|
|
127
|
+
let attr = TokenStream::new();
|
|
128
|
+
for (case, input) in filter_item_inputs_excluding_labels(&["struct"]) {
|
|
129
|
+
let attr_clone = attr.clone();
|
|
130
|
+
assert_panics_contains(case, "failed to parse struct", || {
|
|
131
|
+
crate::upgradeable::generate_upgradeable_impl(attr_clone, input.clone());
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
#[test]
|
|
137
|
+
fn test_upgradeable_emits_binver_when_version_is_set() {
|
|
138
|
+
let _lock = CARGO_PKG_VERSION_LOCK.lock().expect("lock poisoned");
|
|
139
|
+
let guard = EnvVarGuard::new("CARGO_PKG_VERSION");
|
|
140
|
+
guard.set("9.9.9");
|
|
141
|
+
|
|
142
|
+
let input = quote! {
|
|
143
|
+
pub struct MyContract;
|
|
144
|
+
};
|
|
145
|
+
let result = crate::upgradeable::generate_upgradeable_impl(TokenStream::new(), input);
|
|
146
|
+
let result_str = result.to_string();
|
|
147
|
+
|
|
148
|
+
assert!(
|
|
149
|
+
result_str.contains("contractmeta ! (key = \"binver\" , val = \"9.9.9\""),
|
|
150
|
+
"should emit binver contractmeta when version is set. Got: {}",
|
|
151
|
+
result_str
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
#[test]
|
|
156
|
+
fn test_upgradeable_rejects_invalid_config_table_driven() {
|
|
157
|
+
let input = quote! { pub struct MyContract; };
|
|
158
|
+
let cases: Vec<(&str, TokenStream, &str)> = vec![
|
|
159
|
+
("unknown option", quote! { not_migration }, "failed to parse upgradeable config"),
|
|
160
|
+
("invalid attr syntax", quote! { 123 }, "failed to parse upgradeable config"),
|
|
161
|
+
("extra tokens", quote! { no_migration, extra }, "failed to parse upgradeable config"),
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
for (case, attr, expected) in cases {
|
|
165
|
+
assert_panics_contains(case, expected, || {
|
|
166
|
+
crate::upgradeable::generate_upgradeable_impl(attr.clone(), input.clone());
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|