1use rand::Rng;
4use std::collections::HashMap;
5
6#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
8pub struct FieldConfig {
9 pub length_preference: Option<LengthPreference>,
11 pub value_range: Option<ValueRange>,
13 pub fixed_values: Option<Vec<String>>,
15 pub pattern: Option<String>,
17}
18
19#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
21pub struct MessageConfig {
22 pub include_optional: bool,
24 pub field_configs: HashMap<String, FieldConfig>,
26 pub scenario: Option<MessageScenario>,
28}
29
30#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
32pub enum MessageScenario {
33 Standard,
35 StpCompliant,
37 CoverPayment,
39 Minimal,
41 Full,
43}
44
45#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
47pub enum LengthPreference {
48 Exact(usize),
50 Range(usize, usize),
52 Short,
54 Long,
56}
57
58#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
60pub enum ValueRange {
61 Amount {
63 min: f64,
64 max: f64,
65 currency: Option<String>,
66 },
67 Date { start: String, end: String },
69 Integer { min: i64, max: i64 },
71}
72
73pub fn generate_numeric(length: usize) -> String {
75 let mut rng = rand::thread_rng();
76 (0..length)
77 .map(|_| rng.gen_range(0..10).to_string())
78 .collect()
79}
80
81pub fn generate_alphabetic(length: usize) -> String {
83 let mut rng = rand::thread_rng();
84 (0..length)
85 .map(|_| {
86 let ch = rng.gen_range(0..26) as u8 + b'A';
87 ch as char
88 })
89 .collect()
90}
91
92pub fn generate_alphanumeric(length: usize) -> String {
94 let mut rng = rand::thread_rng();
95 let chars: Vec<char> = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".chars().collect();
96 (0..length)
97 .map(|_| chars[rng.gen_range(0..chars.len())])
98 .collect()
99}
100
101pub fn generate_any_character(length: usize) -> String {
103 let mut rng = rand::thread_rng();
104 let chars: Vec<char> = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 /-.,"
106 .chars()
107 .collect();
108 (0..length)
109 .map(|_| chars[rng.gen_range(0..chars.len())])
110 .collect()
111}
112
113pub fn generate_decimal(length: usize, decimals: usize) -> String {
115 generate_decimal_with_range(length, decimals, None, None)
116}
117
118pub fn generate_decimal_with_range(
120 length: usize,
121 decimals: usize,
122 min: Option<f64>,
123 max: Option<f64>,
124) -> String {
125 if decimals >= length {
126 return "0,00".to_string();
127 }
128
129 let mut rng = rand::thread_rng();
130
131 if let (Some(min_val), Some(max_val)) = (min, max) {
133 let amount = rng.gen_range(min_val..=max_val);
134 let formatted = format!("{amount:.2}").replace('.', ",");
135 if formatted.len() <= length {
136 return formatted;
137 }
138 }
139
140 let realistic_amounts = [
142 "1250,00",
143 "850,50",
144 "2000,75",
145 "500,25",
146 "10000,00",
147 "750,80",
148 "3500,45",
149 "125,60",
150 "25000,00",
151 "1875,90",
152 "650,15",
153 "4200,35",
154 "50000,00",
155 "75000,00",
156 "100000,00",
157 "250000,00",
158 "500000,00",
159 "1000000,00",
160 "2500000,00",
161 "5000000,00",
162 ];
163
164 if length <= 10 {
166 let amount = realistic_amounts[rng.gen_range(0..realistic_amounts.len())];
167 if amount.len() <= length {
168 return amount.to_string();
169 }
170 }
171
172 let amount_ranges = [
175 (100.0, 9999.0), (10000.0, 99999.0), (100000.0, 999999.0), (1000000.0, 9999999.0), ];
180
181 let (min_amt, max_amt) = amount_ranges[rng.gen_range(0..amount_ranges.len())];
182 let amount = rng.gen_range(min_amt..=max_amt);
183 let formatted = format!("{amount:.2}").replace('.', ",");
184
185 if formatted.len() <= length {
186 return formatted;
187 }
188
189 let integer_part_len = if length > decimals + 1 {
191 length - decimals - 1 } else {
193 1 };
195
196 let mut integer_part = String::new();
197 if integer_part_len > 0 {
198 let max_val = 10_u64.pow(integer_part_len as u32) - 1;
200 let amount = rng.gen_range(100..=max_val.min(999999)); integer_part = amount.to_string();
202
203 while integer_part.len() < integer_part_len {
205 integer_part = format!("0{integer_part}");
206 }
207 } else {
208 integer_part.push_str(&rng.gen_range(1..10).to_string());
210 }
211
212 let decimal_part = generate_numeric(decimals);
213 format!("{integer_part},{decimal_part}")
214}
215
216pub fn generate_valid_bic() -> String {
218 let mut rng = rand::thread_rng();
219 let bics = [
220 "CHASUS33", "BOFAUS3N", "CITIUS33", "WFBIUS6W", "USBKUS44", "PNCCUS33",
222 "DEUTDEFF", "HSBCGB2L", "BNPAFRPP", "UBSWCHZH", "ABNANL2A", "INGBNL2A", "CRESCHZZ",
224 "BARCGB22", "LOYDGB2L", "NWBKGB2L", "RBOSGB2L",
225 "SCBLSGSG", "DBSSSGSG", "OCBCSGSG", "HSBCHKHH", "CITIHKAX", "BOTKJPJT", "SMFGJPJT",
227 "MHCBJPJT", "ROYCCAT2", "BOFACATT", "ANZBAU3M", "CTBAAU2S",
229 "ICICINBB", "HDFCINBB", "SBININBB", "BBVASPBX",
231 ];
232 bics[rng.gen_range(0..bics.len())].to_string()
233}
234
235pub fn generate_valid_currency() -> String {
237 let mut rng = rand::thread_rng();
238
239 let weighted_selection = rng.gen_range(1..=100);
241
242 match weighted_selection {
243 1..=30 => "USD".to_string(), 31..=45 => "EUR".to_string(), 46..=55 => "GBP".to_string(), 56..=60 => "JPY".to_string(), 61..=64 => "CHF".to_string(), 65..=67 => "CAD".to_string(), 68..=70 => "AUD".to_string(), 71..=73 => "SGD".to_string(), 74..=76 => "HKD".to_string(), 77..=79 => "CNY".to_string(), 80..=82 => "SEK".to_string(), 83..=85 => "NOK".to_string(), 86..=87 => "DKK".to_string(), 88..=89 => "NZD".to_string(), 90..=91 => "INR".to_string(), 92..=93 => "KRW".to_string(), 94..=95 => "BRL".to_string(), 96..=97 => "ZAR".to_string(), 98..=99 => "AED".to_string(), _ => "MXN".to_string(), }
264}
265
266pub fn generate_valid_country_code() -> String {
268 let mut rng = rand::thread_rng();
269 let countries = vec![
270 "US", "GB", "DE", "FR", "IT", "ES", "NL", "BE", "CH", "AT", "JP", "CN", "IN", "AU", "CA",
271 "BR", "MX", "SG", "HK", "KR",
272 ];
273 countries[rng.gen_range(0..countries.len())].to_string()
274}
275
276pub fn generate_date_yymmdd() -> String {
278 let mut rng = rand::thread_rng();
279 let year = rng.gen_range(20..30);
280 let month = rng.gen_range(1..=12);
281 let day = match month {
282 2 => rng.gen_range(1..=28),
283 4 | 6 | 9 | 11 => rng.gen_range(1..=30),
284 _ => rng.gen_range(1..=31),
285 };
286 format!("{year:02}{month:02}{day:02}")
287}
288
289pub fn generate_date_yyyymmdd() -> String {
291 let mut rng = rand::thread_rng();
292 let year = rng.gen_range(2020..2030);
293 let month = rng.gen_range(1..=12);
294 let day = match month {
295 2 => rng.gen_range(1..=28),
296 4 | 6 | 9 | 11 => rng.gen_range(1..=30),
297 _ => rng.gen_range(1..=31),
298 };
299 format!("{year:04}{month:02}{day:02}")
300}
301
302pub fn generate_time_hhmm() -> String {
304 let mut rng = rand::thread_rng();
305 let hour = rng.gen_range(0..24);
306 let minute = rng.gen_range(0..60);
307 format!("{hour:02}{minute:02}")
308}
309
310pub fn generate_by_format_spec(format: &str) -> String {
312 generate_by_format_spec_with_config(format, &FieldConfig::default())
313}
314
315pub fn generate_by_format_spec_with_config(format: &str, config: &FieldConfig) -> String {
317 if let Some(fixed_values) = &config.fixed_values {
319 if !fixed_values.is_empty() {
320 let mut rng = rand::thread_rng();
321 return fixed_values[rng.gen_range(0..fixed_values.len())].clone();
322 }
323 }
324
325 let mut chars = format.chars().peekable();
327 let mut length_str = String::new();
328 let mut is_exact = false;
329 let mut char_type = 'x';
330
331 while let Some(&ch) = chars.peek() {
333 if ch.is_ascii_digit() {
334 length_str.push(ch);
335 chars.next();
336 } else {
337 break;
338 }
339 }
340
341 if chars.peek() == Some(&'!') {
343 is_exact = true;
344 chars.next();
345 }
346
347 if let Some(ch) = chars.next() {
349 char_type = ch;
350 }
351
352 let max_length: usize = length_str.parse().unwrap_or(1);
353
354 let length = match &config.length_preference {
356 Some(LengthPreference::Exact(len)) => *len.min(&max_length),
357 Some(LengthPreference::Range(min, max)) => {
358 let mut rng = rand::thread_rng();
359 let actual_min = *min.min(&max_length);
360 let actual_max = (*max).min(max_length);
361 if actual_min <= actual_max {
362 rng.gen_range(actual_min..=actual_max)
363 } else {
364 max_length
365 }
366 }
367 Some(LengthPreference::Short) => {
368 let mut rng = rand::thread_rng();
369 rng.gen_range(1..=(max_length / 2).max(1))
370 }
371 Some(LengthPreference::Long) => {
372 let mut rng = rand::thread_rng();
373 rng.gen_range((max_length / 2).max(1)..=max_length)
374 }
375 None => {
376 if is_exact {
377 max_length
378 } else {
379 if char_type == 'd' {
381 max_length
382 } else {
383 let mut rng = rand::thread_rng();
384 rng.gen_range(1..=max_length)
385 }
386 }
387 }
388 };
389
390 match char_type {
391 'n' => generate_numeric(length),
392 'a' => generate_alphabetic(length),
393 'c' => generate_alphanumeric(length),
394 'd' => {
395 let decimals = 2;
397 if let Some(ValueRange::Amount { min, max, .. }) = &config.value_range {
399 generate_decimal_with_range(length, decimals, Some(*min), Some(*max))
400 } else {
401 generate_decimal(length, decimals)
402 }
403 }
404 _ => generate_any_character(length),
405 }
406}
407
408pub fn generate_account_number() -> String {
410 let mut rng = rand::thread_rng();
411 let length = rng.gen_range(10..=34);
412 generate_alphanumeric(length)
413}
414
415pub fn generate_reference() -> String {
417 generate_alphanumeric(16)
418}
419
420pub fn generate_transaction_code() -> String {
422 let mut rng = rand::thread_rng();
423 let codes = ["NTRF", "CHQB", "PMNT", "MCOP", "DMCT"];
424 codes[rng.gen_range(0..codes.len())].to_string()
425}
426
427pub fn generate_bank_operation_code() -> String {
429 let mut rng = rand::thread_rng();
430 let codes = ["CRED", "CRTS", "SPAY", "SSTD"];
431 codes[rng.gen_range(0..codes.len())].to_string()
432}
433
434pub fn generate_details_of_charges() -> String {
436 let mut rng = rand::thread_rng();
437 let codes = ["BEN", "OUR", "SHA"];
438 codes[rng.gen_range(0..codes.len())].to_string()
439}
440
441pub fn generate_instruction_code() -> String {
443 let mut rng = rand::thread_rng();
444 let codes = ["REPA", "URGP", "CORT", "INTC", "PHON"];
445 codes[rng.gen_range(0..codes.len())].to_string()
446}
447
448pub fn generate_name_and_address(lines: usize) -> Vec<String> {
450 let mut rng = rand::thread_rng();
451 let names = [
452 "GLOBAL TRADE SOLUTIONS LTD",
453 "INTERNATIONAL EXPORT CORP",
454 "PRIME FINANCIAL SERVICES",
455 "METROPOLITAN TRADING CO",
456 "CONSOLIDATED INDUSTRIES INC",
457 "PACIFIC RIM ENTERPRISES",
458 "EUROPEAN COMMERCE GROUP",
459 "ATLANTIC BUSINESS PARTNERS",
460 "CONTINENTAL HOLDINGS LLC",
461 "WORLDWIDE LOGISTICS CORP",
462 "STERLING INVESTMENT GROUP",
463 "MERIDIAN COMMERCIAL LTD",
464 "APEX TRADING COMPANY",
465 "NEXUS FINANCIAL CORP",
466 "HORIZON BUSINESS SOLUTIONS",
467 ];
468
469 let streets = [
470 "125 CORPORATE PLAZA",
471 "450 BUSINESS PARK DRIVE",
472 "789 FINANCIAL DISTRICT",
473 "1200 COMMERCE STREET",
474 "650 EXECUTIVE BOULEVARD",
475 "300 TRADE CENTER WAY",
476 "850 INTERNATIONAL AVENUE",
477 "1500 ENTERPRISE PARKWAY",
478 "275 INVESTMENT PLAZA",
479 "920 BANKING SQUARE",
480 "1750 CORPORATE CENTER",
481 "425 PROFESSIONAL DRIVE",
482 "680 MARKET STREET",
483 "1100 INDUSTRIAL WAY",
484 "550 COMMERCIAL BOULEVARD",
485 ];
486
487 let cities = [
488 "NEW YORK NY 10005",
489 "LONDON EC2V 8RF",
490 "ZURICH 8001",
491 "SINGAPORE 048624",
492 "TOKYO 100-6590",
493 "FRANKFURT AM MAIN 60311",
494 "PARIS 75001",
495 "MILAN 20121",
496 "GENEVA 1204",
497 "DUBLIN 2",
498 "AMSTERDAM 1017 XX",
499 "BRUSSELS 1000",
500 "MADRID 28001",
501 "BARCELONA 08002",
502 "VIENNA 1010",
503 ];
504
505 let mut result = vec![];
506
507 if lines > 0 {
508 result.push(names[rng.gen_range(0..names.len())].to_string());
509 }
510 if lines > 1 {
511 result.push(streets[rng.gen_range(0..streets.len())].to_string());
512 }
513 if lines > 2 {
514 result.push(cities[rng.gen_range(0..cities.len())].to_string());
515 }
516 if lines > 3 {
517 result.push(generate_valid_country_code());
518 }
519
520 while result.len() < lines {
522 let additional_info = [
523 "CORPORATE HEADQUARTERS",
524 "MAIN OFFICE",
525 "TREASURY DEPARTMENT",
526 "INTERNATIONAL DIVISION",
527 "FINANCIAL SERVICES",
528 ];
529 result.push(additional_info[rng.gen_range(0..additional_info.len())].to_string());
530 }
531
532 result
533}
534
535pub fn generate_uetr() -> String {
538 uuid::Uuid::new_v4().to_string()
539}
540
541#[cfg(test)]
542mod tests {
543 use super::*;
544
545 #[test]
546 fn test_generate_numeric() {
547 let result = generate_numeric(6);
548 assert_eq!(result.len(), 6);
549 assert!(result.chars().all(|c| c.is_ascii_digit()));
550
551 for _ in 0..10 {
553 let result = generate_numeric(8);
554 assert_eq!(result.len(), 8);
555 assert!(result.chars().all(|c| c.is_ascii_digit()));
556 }
557 }
558
559 #[test]
560 fn test_generate_alphabetic() {
561 let result = generate_alphabetic(4);
562 assert_eq!(result.len(), 4);
563 assert!(result.chars().all(|c| c.is_ascii_uppercase()));
564
565 let empty = generate_alphabetic(0);
567 assert_eq!(empty.len(), 0);
568
569 let single = generate_alphabetic(1);
570 assert_eq!(single.len(), 1);
571 assert!(single.chars().all(|c| c.is_ascii_uppercase()));
572 }
573
574 #[test]
575 fn test_generate_alphanumeric() {
576 let result = generate_alphanumeric(10);
577 assert_eq!(result.len(), 10);
578 assert!(
579 result.chars().all(
580 |c| c.is_ascii_alphanumeric() && (c.is_ascii_uppercase() || c.is_ascii_digit())
581 )
582 );
583 }
584
585 #[test]
586 fn test_generate_any_character() {
587 let result = generate_any_character(20);
588 assert_eq!(result.len(), 20);
589
590 let allowed_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 /-.,";
592 assert!(result.chars().all(|c| allowed_chars.contains(c)));
593 }
594
595 #[test]
596 fn test_generate_decimal() {
597 let result = generate_decimal(10, 2);
598 assert!(result.contains(','));
599 let parts: Vec<&str> = result.split(',').collect();
600 assert_eq!(parts.len(), 2);
601 assert_eq!(parts[1].len(), 2);
602 assert!(parts[0].chars().all(|c| c.is_ascii_digit()));
603 assert!(parts[1].chars().all(|c| c.is_ascii_digit()));
604
605 let edge_case = generate_decimal(3, 3);
607 assert_eq!(edge_case, "0,00");
608 }
609
610 #[test]
611 fn test_generate_valid_bic() {
612 for _ in 0..20 {
613 let bic = generate_valid_bic();
614 assert!(bic.len() == 8 || bic.len() == 11);
615 assert!(bic.chars().all(|c| c.is_ascii_alphanumeric()));
616 }
617 }
618
619 #[test]
620 fn test_generate_valid_currency() {
621 for _ in 0..20 {
622 let currency = generate_valid_currency();
623 assert_eq!(currency.len(), 3);
624 assert!(currency.chars().all(|c| c.is_ascii_uppercase()));
625 }
626
627 let currencies = vec![
629 "USD", "EUR", "GBP", "JPY", "CHF", "CAD", "AUD", "NZD", "SEK", "NOK", "DKK", "SGD",
630 "HKD", "CNY", "INR", "KRW", "MXN", "BRL", "ZAR", "AED",
631 ];
632 let generated = generate_valid_currency();
633 assert!(currencies.contains(&generated.as_str()));
634 }
635
636 #[test]
637 fn test_generate_valid_country_code() {
638 let country = generate_valid_country_code();
639 assert_eq!(country.len(), 2);
640 assert!(country.chars().all(|c| c.is_ascii_uppercase()));
641 }
642
643 #[test]
644 fn test_generate_date_yymmdd() {
645 let date = generate_date_yymmdd();
646 assert_eq!(date.len(), 6);
647 assert!(date.chars().all(|c| c.is_ascii_digit()));
648
649 let year: u32 = date[0..2].parse().unwrap();
651 let month: u32 = date[2..4].parse().unwrap();
652 let day: u32 = date[4..6].parse().unwrap();
653
654 assert!((20..=29).contains(&year));
655 assert!((1..=12).contains(&month));
656 assert!((1..=31).contains(&day));
657 }
658
659 #[test]
660 fn test_generate_date_yyyymmdd() {
661 let date = generate_date_yyyymmdd();
662 assert_eq!(date.len(), 8);
663 assert!(date.chars().all(|c| c.is_ascii_digit()));
664
665 let year: u32 = date[0..4].parse().unwrap();
667 let month: u32 = date[4..6].parse().unwrap();
668 let day: u32 = date[6..8].parse().unwrap();
669
670 assert!((2020..=2029).contains(&year));
671 assert!((1..=12).contains(&month));
672 assert!((1..=31).contains(&day));
673 }
674
675 #[test]
676 fn test_generate_time_hhmm() {
677 let time = generate_time_hhmm();
678 assert_eq!(time.len(), 4);
679 assert!(time.chars().all(|c| c.is_ascii_digit()));
680
681 let hour: u32 = time[0..2].parse().unwrap();
683 let minute: u32 = time[2..4].parse().unwrap();
684
685 assert!(hour <= 23);
686 assert!(minute <= 59);
687 }
688
689 #[test]
690 fn test_generate_by_format_spec() {
691 let result1 = generate_by_format_spec("3!a");
693 assert_eq!(result1.len(), 3);
694 assert!(result1.chars().all(|c| c.is_ascii_uppercase()));
695
696 let result2 = generate_by_format_spec("6!n");
697 assert_eq!(result2.len(), 6);
698 assert!(result2.chars().all(|c| c.is_ascii_digit()));
699
700 let result3 = generate_by_format_spec("4!c");
701 assert_eq!(result3.len(), 4);
702 assert!(result3.chars().all(|c| c.is_ascii_alphanumeric()));
703
704 let result4 = generate_by_format_spec("16x");
706 assert!(!result4.is_empty() && result4.len() <= 16);
707
708 let result5 = generate_by_format_spec("35a");
709 assert!(!result5.is_empty() && result5.len() <= 35);
710 assert!(result5.chars().all(|c| c.is_ascii_uppercase()));
711
712 let result6 = generate_by_format_spec("15d");
714 assert!(result6.contains(','));
715 }
716
717 #[test]
718 fn test_generate_account_number() {
719 let account = generate_account_number();
720 assert!(account.len() >= 10 && account.len() <= 34);
721 assert!(account.chars().all(|c| c.is_ascii_alphanumeric()));
722 }
723
724 #[test]
725 fn test_generate_reference() {
726 let reference = generate_reference();
727 assert_eq!(reference.len(), 16);
728 assert!(reference.chars().all(|c| c.is_ascii_alphanumeric()));
729 }
730
731 #[test]
732 fn test_generate_transaction_code() {
733 let codes = ["NTRF", "CHQB", "PMNT", "MCOP", "DMCT"];
734 let code = generate_transaction_code();
735 assert!(codes.contains(&code.as_str()));
736 }
737
738 #[test]
739 fn test_generate_bank_operation_code() {
740 let codes = ["CRED", "CRTS", "SPAY", "SSTD"];
741 let code = generate_bank_operation_code();
742 assert!(codes.contains(&code.as_str()));
743 }
744
745 #[test]
746 fn test_generate_details_of_charges() {
747 let codes = ["BEN", "OUR", "SHA"];
748 let code = generate_details_of_charges();
749 assert!(codes.contains(&code.as_str()));
750 }
751
752 #[test]
753 fn test_generate_instruction_code() {
754 let codes = ["REPA", "URGP", "CORT", "INTC", "PHON"];
755 let code = generate_instruction_code();
756 assert!(codes.contains(&code.as_str()));
757 }
758
759 #[test]
760 fn test_generate_name_and_address() {
761 for line_count in 1..=5 {
763 let lines = generate_name_and_address(line_count);
764 assert_eq!(lines.len(), line_count);
765 assert!(lines.iter().all(|line| !line.is_empty()));
766 assert!(lines.iter().all(|line| line.len() <= 35)); }
768
769 let empty_lines = generate_name_and_address(0);
771 assert_eq!(empty_lines.len(), 0);
772 }
773
774 #[test]
775 fn test_configuration_types() {
776 let field_config = FieldConfig {
778 length_preference: Some(LengthPreference::Exact(10)),
779 value_range: Some(ValueRange::Amount {
780 min: 100.0,
781 max: 1000.0,
782 currency: Some("USD".to_string()),
783 }),
784 fixed_values: Some(vec!["TEST1".to_string(), "TEST2".to_string()]),
785 pattern: Some(r"^[A-Z]{3}\d{7}$".to_string()),
786 };
787
788 assert!(field_config.length_preference.is_some());
789 assert!(field_config.value_range.is_some());
790 assert!(field_config.fixed_values.is_some());
791 assert!(field_config.pattern.is_some());
792
793 let mut field_configs = std::collections::HashMap::new();
795 field_configs.insert("32A".to_string(), field_config);
796
797 let message_config = MessageConfig {
798 include_optional: true,
799 field_configs,
800 scenario: Some(MessageScenario::StpCompliant),
801 };
802
803 assert!(message_config.include_optional);
804 assert!(message_config.field_configs.contains_key("32A"));
805 assert_eq!(message_config.scenario, Some(MessageScenario::StpCompliant));
806 }
807
808 #[test]
809 fn test_message_scenarios() {
810 let scenarios = vec![
812 MessageScenario::Standard,
813 MessageScenario::StpCompliant,
814 MessageScenario::CoverPayment,
815 MessageScenario::Minimal,
816 MessageScenario::Full,
817 ];
818
819 for scenario in scenarios {
820 assert_eq!(scenario, scenario);
822 }
823 }
824
825 #[test]
826 fn test_value_range_variants() {
827 let amount_range = ValueRange::Amount {
829 min: 100.0,
830 max: 1000.0,
831 currency: Some("EUR".to_string()),
832 };
833
834 match amount_range {
835 ValueRange::Amount { min, max, currency } => {
836 assert_eq!(min, 100.0);
837 assert_eq!(max, 1000.0);
838 assert_eq!(currency, Some("EUR".to_string()));
839 }
840 _ => panic!("Expected Amount variant"),
841 }
842
843 let date_range = ValueRange::Date {
845 start: "20230101".to_string(),
846 end: "20231231".to_string(),
847 };
848
849 match date_range {
850 ValueRange::Date { start, end } => {
851 assert_eq!(start, "20230101");
852 assert_eq!(end, "20231231");
853 }
854 _ => panic!("Expected Date variant"),
855 }
856
857 let integer_range = ValueRange::Integer { min: 1, max: 100 };
859
860 match integer_range {
861 ValueRange::Integer { min, max } => {
862 assert_eq!(min, 1);
863 assert_eq!(max, 100);
864 }
865 _ => panic!("Expected Integer variant"),
866 }
867 }
868
869 #[test]
870 fn test_length_preference_variants() {
871 let exact = LengthPreference::Exact(10);
873 assert_eq!(exact, LengthPreference::Exact(10));
874
875 let range = LengthPreference::Range(5, 15);
876 assert_eq!(range, LengthPreference::Range(5, 15));
877
878 let short = LengthPreference::Short;
879 assert_eq!(short, LengthPreference::Short);
880
881 let long = LengthPreference::Long;
882 assert_eq!(long, LengthPreference::Long);
883 }
884
885 #[test]
886 fn test_randomness_distribution() {
887 let mut bics = std::collections::HashSet::new();
889 let mut currencies = std::collections::HashSet::new();
890 let mut references = std::collections::HashSet::new();
891
892 for _ in 0..50 {
893 bics.insert(generate_valid_bic());
894 currencies.insert(generate_valid_currency());
895 references.insert(generate_reference());
896 }
897
898 assert!(bics.len() > 1, "BIC generation should have variety");
900 assert!(
901 currencies.len() > 1,
902 "Currency generation should have variety"
903 );
904 assert!(
905 references.len() > 1,
906 "Reference generation should have variety"
907 );
908 }
909}