@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,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
+ }