1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
//! Property-based tests for accounts parsing and classification.
//!
//! These tests verify correctness properties of:
//! - `InfraFieldClassifier::classify` - Infrastructure field classification
//! - `InfraFields::set` - Infrastructure field state management
#[cfg(test)]
mod tests {
use proptest::prelude::*;
use syn::Ident;
// Access infra module from parsing
use crate::light_pdas::parsing::infra::{InfraFieldClassifier, InfraFieldType, InfraFields};
// ========================================================================
// Helper functions
// ========================================================================
/// Creates an Ident from a string (for testing purposes)
fn make_ident(name: &str) -> Ident {
syn::parse_str::<Ident>(name)
.unwrap_or_else(|_| syn::parse_str::<Ident>("test_ident").unwrap())
}
/// All InfraFieldType variants
fn all_infra_types() -> Vec<InfraFieldType> {
vec![
InfraFieldType::FeePayer,
InfraFieldType::CompressionConfig,
InfraFieldType::PdaRentSponsor,
InfraFieldType::LightTokenConfig,
InfraFieldType::LightTokenRentSponsor,
InfraFieldType::LightTokenProgram,
InfraFieldType::LightTokenCpiAuthority,
]
}
/// All known field names that map to InfraFieldType
fn all_known_field_names() -> Vec<&'static str> {
vec![
"fee_payer",
"payer",
"creator",
"compression_config",
"pda_rent_sponsor",
"light_token_config",
"light_token_rent_sponsor",
"light_token_program",
"light_token_cpi_authority",
]
}
// ========================================================================
// Strategies for generating test inputs
// ========================================================================
/// Strategy for generating known field names
fn arb_known_field_name() -> impl Strategy<Value = &'static str> {
prop::sample::select(all_known_field_names())
}
/// Strategy for generating random lowercase identifiers (likely unknown)
fn arb_random_field_name() -> impl Strategy<Value = String> {
"[a-z][a-z0-9_]{2,20}"
}
/// Strategy for generating a random InfraFieldType
fn arb_infra_field_type() -> impl Strategy<Value = InfraFieldType> {
prop::sample::select(all_infra_types())
}
// ========================================================================
// Property Tests: InfraFieldClassifier::classify
// ========================================================================
proptest! {
/// Known field names should be classified correctly.
#[test]
fn prop_known_names_classified(name in arb_known_field_name()) {
let result = InfraFieldClassifier::classify(name);
prop_assert!(
result.is_some(),
"Known field name '{}' should be classified",
name
);
}
/// All accepted names for each type should classify to that type.
#[test]
fn prop_all_accepted_names_work(_seed in 0u32..1000) {
for field_type in all_infra_types() {
for name in field_type.accepted_names() {
let result = InfraFieldClassifier::classify(name);
prop_assert!(
result == Some(field_type),
"Name '{}' should classify to {:?}, got {:?}",
name, field_type, result
);
}
}
}
/// Unknown field names should return None.
#[test]
fn prop_unknown_names_return_none(name in arb_random_field_name()) {
// Skip if randomly generated a known name
prop_assume!(!all_known_field_names().contains(&name.as_str()));
let result = InfraFieldClassifier::classify(&name);
prop_assert!(
result.is_none(),
"Unknown field name '{}' should return None, got {:?}",
name, result
);
}
/// Classification should be deterministic.
#[test]
fn prop_classify_deterministic(name in arb_random_field_name()) {
let result1 = InfraFieldClassifier::classify(&name);
let result2 = InfraFieldClassifier::classify(&name);
prop_assert_eq!(
result1, result2,
"Classification should be deterministic for '{}'",
name
);
}
/// Each accepted name should map to exactly one type (bijection check).
#[test]
fn prop_bijection(_seed in 0u32..1000) {
let infra_types = all_infra_types();
for name in all_known_field_names() {
let result = InfraFieldClassifier::classify(name);
// Count how many types accept this name
let matching_count = infra_types
.iter()
.filter(|t| t.accepted_names().contains(&name))
.count();
prop_assert_eq!(
matching_count, 1,
"Name '{}' should map to exactly one type",
name
);
prop_assert!(
result.is_some(),
"Known name '{}' should classify successfully",
name
);
}
}
/// All 7 InfraFieldType variants should be reachable via classification.
#[test]
fn prop_exhaustive_coverage(_seed in 0u32..1000) {
let mut covered = vec![false; 7];
for name in all_known_field_names() {
if let Some(field_type) = InfraFieldClassifier::classify(name) {
let index = match field_type {
InfraFieldType::FeePayer => 0,
InfraFieldType::CompressionConfig => 1,
InfraFieldType::PdaRentSponsor => 2,
InfraFieldType::LightTokenConfig => 3,
InfraFieldType::LightTokenRentSponsor => 4,
InfraFieldType::LightTokenProgram => 5,
InfraFieldType::LightTokenCpiAuthority => 6,
};
covered[index] = true;
}
}
prop_assert!(
covered.iter().all(|&c| c),
"Not all InfraFieldType variants are reachable: {:?}",
covered
);
}
}
// ========================================================================
// Property Tests: InfraFields::set
// ========================================================================
proptest! {
/// First insert of any field type should succeed.
#[test]
fn prop_first_insert_succeeds(field_type in arb_infra_field_type()) {
let mut fields = InfraFields::default();
let ident = make_ident("test_field");
let result = fields.set(field_type, ident);
prop_assert!(
result.is_ok(),
"First insert of {:?} should succeed",
field_type
);
}
/// Duplicate insert of same field type should fail.
#[test]
fn prop_duplicate_insert_fails(field_type in arb_infra_field_type()) {
let mut fields = InfraFields::default();
let ident1 = make_ident("first_field");
let ident2 = make_ident("second_field");
// First insert should succeed
let result1 = fields.set(field_type, ident1);
prop_assert!(result1.is_ok(), "First insert should succeed");
// Second insert of same type should fail
let result2 = fields.set(field_type, ident2);
prop_assert!(
result2.is_err(),
"Duplicate insert of {:?} should fail",
field_type
);
}
/// Different field types can coexist.
#[test]
fn prop_different_types_coexist(_seed in 0u32..1000) {
let mut fields = InfraFields::default();
for (i, field_type) in all_infra_types().into_iter().enumerate() {
let ident = make_ident(&format!("field_{}", i));
let result = fields.set(field_type, ident);
prop_assert!(
result.is_ok(),
"Insert of different type {:?} should succeed",
field_type
);
}
}
/// After set, corresponding Option field should be Some.
#[test]
fn prop_state_mutation_correct(field_type in arb_infra_field_type()) {
let mut fields = InfraFields::default();
let ident = make_ident("test_field");
fields.set(field_type, ident).unwrap();
let is_set = match field_type {
InfraFieldType::FeePayer => fields.fee_payer.is_some(),
InfraFieldType::CompressionConfig => fields.compression_config.is_some(),
InfraFieldType::PdaRentSponsor => fields.pda_rent_sponsor.is_some(),
InfraFieldType::LightTokenConfig => fields.light_token_config.is_some(),
InfraFieldType::LightTokenRentSponsor => fields.light_token_rent_sponsor.is_some(),
InfraFieldType::LightTokenProgram => fields.light_token_program.is_some(),
InfraFieldType::LightTokenCpiAuthority => fields.light_token_cpi_authority.is_some(),
};
prop_assert!(
is_set,
"After set({:?}), corresponding field should be Some",
field_type
);
}
/// Error message should identify the duplicate field type.
#[test]
fn prop_error_identifies_field(field_type in arb_infra_field_type()) {
let mut fields = InfraFields::default();
let ident1 = make_ident("first");
let ident2 = make_ident("second");
fields.set(field_type, ident1).unwrap();
let result = fields.set(field_type, ident2);
if let Err(err) = result {
let err_msg = err.to_string();
prop_assert!(
err_msg.contains("Duplicate") || err_msg.contains("duplicate"),
"Error message should mention 'duplicate', got: {}",
err_msg
);
}
}
/// Other fields should remain None after setting one field.
#[test]
fn prop_other_fields_unchanged(field_type in arb_infra_field_type()) {
let mut fields = InfraFields::default();
let ident = make_ident("test_field");
fields.set(field_type, ident).unwrap();
// Count how many fields are set
let set_count = [
fields.fee_payer.is_some(),
fields.compression_config.is_some(),
fields.pda_rent_sponsor.is_some(),
fields.light_token_config.is_some(),
fields.light_token_rent_sponsor.is_some(),
fields.light_token_program.is_some(),
fields.light_token_cpi_authority.is_some(),
].iter().filter(|&&x| x).count();
prop_assert_eq!(
set_count, 1,
"Only one field should be set after single set() call for {:?}",
field_type
);
}
}
// ========================================================================
// Property Tests: InfraFieldType methods
// ========================================================================
proptest! {
/// Each InfraFieldType should have at least one accepted name.
#[test]
fn prop_each_type_has_accepted_names(field_type in arb_infra_field_type()) {
let names = field_type.accepted_names();
prop_assert!(
!names.is_empty(),
"{:?} should have at least one accepted name",
field_type
);
}
/// Each InfraFieldType should have a non-empty description.
#[test]
fn prop_each_type_has_description(field_type in arb_infra_field_type()) {
let desc = field_type.description();
prop_assert!(
!desc.is_empty(),
"{:?} should have a non-empty description",
field_type
);
}
/// accepted_names should be deterministic.
#[test]
fn prop_accepted_names_deterministic(field_type in arb_infra_field_type()) {
let names1 = field_type.accepted_names();
let names2 = field_type.accepted_names();
prop_assert_eq!(
names1, names2,
"accepted_names should be deterministic for {:?}",
field_type
);
}
}
}