1use super::common::{merge_optional_string_vecs, NetworkConfigCommon};
13use crate::config::ConfigFileError;
14use serde::{Deserialize, Serialize};
15
16fn default_gas_cache_enabled() -> bool {
18 false
19}
20
21fn default_gas_cache_stale_after_ms() -> u64 {
23 20_000 }
25
26fn default_gas_cache_expire_after_ms() -> u64 {
28 45_000 }
30
31#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
33#[serde(deny_unknown_fields)]
34pub struct GasPriceCacheConfig {
35 #[serde(default = "default_gas_cache_enabled")]
37 pub enabled: bool,
38
39 #[serde(default = "default_gas_cache_stale_after_ms")]
41 pub stale_after_ms: u64,
42
43 #[serde(default = "default_gas_cache_expire_after_ms")]
45 pub expire_after_ms: u64,
46}
47
48impl Default for GasPriceCacheConfig {
49 fn default() -> Self {
50 Self {
51 enabled: default_gas_cache_enabled(),
52 stale_after_ms: default_gas_cache_stale_after_ms(),
53 expire_after_ms: default_gas_cache_expire_after_ms(),
54 }
55 }
56}
57
58impl GasPriceCacheConfig {
59 pub fn validate(&self) -> Result<(), ConfigFileError> {
65 if self.stale_after_ms == 0 {
67 return Err(ConfigFileError::InvalidFormat(
68 "Gas price cache stale_after_ms must be greater than zero".into(),
69 ));
70 }
71
72 if self.expire_after_ms == 0 {
73 return Err(ConfigFileError::InvalidFormat(
74 "Gas price cache expire_after_ms must be greater than zero".into(),
75 ));
76 }
77
78 if self.expire_after_ms <= self.stale_after_ms {
80 return Err(ConfigFileError::InvalidFormat(
81 "Gas price cache expire_after_ms must be greater than stale_after_ms".into(),
82 ));
83 }
84
85 Ok(())
86 }
87}
88
89#[derive(Debug, Serialize, Deserialize, Clone)]
91#[serde(deny_unknown_fields)]
92pub struct EvmNetworkConfig {
93 #[serde(flatten)]
95 pub common: NetworkConfigCommon,
96
97 pub chain_id: Option<u64>,
99 pub required_confirmations: Option<u64>,
101 pub features: Option<Vec<String>>,
103 pub symbol: Option<String>,
105 pub gas_price_cache: Option<GasPriceCacheConfig>,
107}
108
109impl EvmNetworkConfig {
110 pub fn validate(&self) -> Result<(), ConfigFileError> {
116 self.common.validate()?;
117
118 if self.chain_id.is_none() {
120 return Err(ConfigFileError::MissingField("chain_id".into()));
121 }
122
123 if self.required_confirmations.is_none() {
124 return Err(ConfigFileError::MissingField(
125 "required_confirmations".into(),
126 ));
127 }
128
129 if self.symbol.is_none() || self.symbol.as_ref().unwrap_or(&String::new()).is_empty() {
130 return Err(ConfigFileError::MissingField("symbol".into()));
131 }
132
133 if let Some(gas_price_cache) = &self.gas_price_cache {
135 gas_price_cache.validate()?;
136 }
137
138 Ok(())
139 }
140
141 pub fn merge_with_parent(&self, parent: &Self) -> Self {
149 Self {
150 common: self.common.merge_with_parent(&parent.common),
151 chain_id: self.chain_id.or(parent.chain_id),
152 required_confirmations: self
153 .required_confirmations
154 .or(parent.required_confirmations),
155 features: merge_optional_string_vecs(&self.features, &parent.features),
156 symbol: self.symbol.clone().or_else(|| parent.symbol.clone()),
157 gas_price_cache: self
158 .gas_price_cache
159 .clone()
160 .or_else(|| parent.gas_price_cache.clone()),
161 }
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 use crate::config::config_file::network::test_utils::*;
169 use crate::models::RpcConfig;
170 #[test]
171 fn test_validate_success_complete_config() {
172 let config = create_evm_network("ethereum-mainnet");
173 let result = config.validate();
174 assert!(result.is_ok());
175 }
176
177 #[test]
178 fn test_validate_success_minimal_config() {
179 let mut config = create_evm_network("minimal-evm");
180 config.features = None;
181 let result = config.validate();
182 assert!(result.is_ok());
183 }
184
185 #[test]
186 fn test_validate_missing_chain_id() {
187 let mut config = create_evm_network("ethereum-mainnet");
188 config.chain_id = None;
189
190 let result = config.validate();
191 assert!(result.is_err());
192 assert!(matches!(
193 result.unwrap_err(),
194 ConfigFileError::MissingField(_)
195 ));
196 }
197
198 #[test]
199 fn test_validate_missing_required_confirmations() {
200 let mut config = create_evm_network("ethereum-mainnet");
201 config.required_confirmations = None;
202
203 let result = config.validate();
204 assert!(result.is_err());
205 assert!(matches!(
206 result.unwrap_err(),
207 ConfigFileError::MissingField(_)
208 ));
209 }
210
211 #[test]
212 fn test_validate_missing_symbol() {
213 let mut config = create_evm_network("ethereum-mainnet");
214 config.symbol = None;
215
216 let result = config.validate();
217 assert!(result.is_err());
218 assert!(matches!(
219 result.unwrap_err(),
220 ConfigFileError::MissingField(_)
221 ));
222 }
223
224 #[test]
225 fn test_validate_invalid_common_fields() {
226 let mut config = create_evm_network("ethereum-mainnet");
227 config.common.network = String::new(); let result = config.validate();
230 assert!(result.is_err());
231 assert!(matches!(
232 result.unwrap_err(),
233 ConfigFileError::MissingField(_)
234 ));
235 }
236
237 #[test]
238 fn test_validate_invalid_rpc_urls() {
239 let mut config = create_evm_network("ethereum-mainnet");
240 config.common.rpc_urls = Some(vec![RpcConfig::new("invalid-url".to_string())]);
241
242 let result = config.validate();
243 assert!(result.is_err());
244 assert!(matches!(
245 result.unwrap_err(),
246 ConfigFileError::InvalidFormat(_)
247 ));
248 }
249
250 #[test]
251 fn test_validate_with_zero_chain_id() {
252 let mut config = create_evm_network("ethereum-mainnet");
253 config.chain_id = Some(0);
254
255 let result = config.validate();
256 assert!(result.is_ok()); }
258
259 #[test]
260 fn test_validate_with_large_chain_id() {
261 let mut config = create_evm_network("ethereum-mainnet");
262 config.chain_id = Some(u64::MAX);
263
264 let result = config.validate();
265 assert!(result.is_ok());
266 }
267
268 #[test]
269 fn test_validate_with_zero_confirmations() {
270 let mut config = create_evm_network("ethereum-mainnet");
271 config.required_confirmations = Some(0);
272
273 let result = config.validate();
274 assert!(result.is_ok()); }
276
277 #[test]
278 fn test_validate_with_empty_features() {
279 let mut config = create_evm_network("ethereum-mainnet");
280 config.features = Some(vec![]);
281
282 let result = config.validate();
283 assert!(result.is_ok());
284 }
285
286 #[test]
287 fn test_validate_with_empty_symbol() {
288 let mut config = create_evm_network("ethereum-mainnet");
289 config.symbol = Some(String::new());
290
291 let result = config.validate();
292 assert!(result.is_err());
293 }
294
295 #[test]
296 fn test_merge_with_parent_child_overrides() {
297 let parent = EvmNetworkConfig {
298 common: NetworkConfigCommon {
299 network: "parent".to_string(),
300 from: Some("parent".to_string()),
301 rpc_urls: Some(vec![RpcConfig::new(
302 "https://parent-rpc.example.com".to_string(),
303 )]),
304 explorer_urls: Some(vec!["https://parent-explorer.example.com".to_string()]),
305 average_blocktime_ms: Some(10000),
306 is_testnet: Some(true),
307 tags: Some(vec!["parent-tag".to_string()]),
308 },
309 chain_id: Some(1),
310 required_confirmations: Some(6),
311 features: Some(vec!["legacy".to_string()]),
312 symbol: Some("PETH".to_string()),
313 gas_price_cache: Some(GasPriceCacheConfig {
314 enabled: true,
315 stale_after_ms: 20_000,
316 expire_after_ms: 100_000,
317 }),
318 };
319
320 let child = EvmNetworkConfig {
321 common: NetworkConfigCommon {
322 network: "child".to_string(),
323 from: Some("parent".to_string()),
324 rpc_urls: Some(vec![RpcConfig::new(
325 "https://child-rpc.example.com".to_string(),
326 )]),
327 explorer_urls: Some(vec!["https://child-explorer.example.com".to_string()]),
328 average_blocktime_ms: Some(15000),
329 is_testnet: Some(false),
330 tags: Some(vec!["child-tag".to_string()]),
331 },
332 chain_id: Some(31337),
333 required_confirmations: Some(1),
334 features: Some(vec!["eip1559".to_string()]),
335 symbol: Some("CETH".to_string()),
336 gas_price_cache: Some(GasPriceCacheConfig {
337 enabled: false,
338 stale_after_ms: 40_000,
339 expire_after_ms: 200_000,
340 }),
341 };
342
343 let result = child.merge_with_parent(&parent);
344
345 assert_eq!(result.common.network, "child");
347 assert_eq!(result.common.from, Some("parent".to_string()));
348 assert_eq!(
349 result.common.rpc_urls,
350 Some(vec![RpcConfig::new(
351 "https://child-rpc.example.com".to_string()
352 )])
353 );
354 assert_eq!(
355 result.common.explorer_urls,
356 Some(vec!["https://child-explorer.example.com".to_string()])
357 );
358 assert_eq!(result.common.average_blocktime_ms, Some(15000));
359 assert_eq!(result.common.is_testnet, Some(false));
360 assert_eq!(
361 result.common.tags,
362 Some(vec!["parent-tag".to_string(), "child-tag".to_string()])
363 );
364 assert_eq!(result.chain_id, Some(31337));
365 assert_eq!(result.required_confirmations, Some(1));
366 assert_eq!(
367 result.features,
368 Some(vec!["legacy".to_string(), "eip1559".to_string()])
369 );
370 assert_eq!(result.symbol, Some("CETH".to_string()));
371 assert_eq!(
372 result.gas_price_cache,
373 Some(GasPriceCacheConfig {
374 enabled: false,
375 stale_after_ms: 40_000,
376 expire_after_ms: 200_000,
377 })
378 );
379 }
380
381 #[test]
382 fn test_merge_with_parent_child_inherits() {
383 let parent = EvmNetworkConfig {
384 common: NetworkConfigCommon {
385 network: "parent".to_string(),
386 from: None,
387 rpc_urls: Some(vec![RpcConfig::new(
388 "https://parent-rpc.example.com".to_string(),
389 )]),
390 explorer_urls: Some(vec!["https://parent-explorer.example.com".to_string()]),
391 average_blocktime_ms: Some(10000),
392 is_testnet: Some(true),
393 tags: Some(vec!["parent-tag".to_string()]),
394 },
395 chain_id: Some(1),
396 required_confirmations: Some(6),
397 features: Some(vec!["eip1559".to_string()]),
398 symbol: Some("ETH".to_string()),
399 gas_price_cache: Some(GasPriceCacheConfig {
400 enabled: true,
401 stale_after_ms: 20_000,
402 expire_after_ms: 100_000,
403 }),
404 };
405
406 let child = create_evm_network_for_inheritance_test("ethereum-testnet", "ethereum-mainnet");
407
408 let result = child.merge_with_parent(&parent);
409
410 assert_eq!(result.common.network, "ethereum-testnet");
412 assert_eq!(result.common.from, Some("ethereum-mainnet".to_string()));
413 assert_eq!(
414 result.common.rpc_urls,
415 Some(vec![RpcConfig::new(
416 "https://parent-rpc.example.com".to_string()
417 )])
418 );
419 assert_eq!(
420 result.common.explorer_urls,
421 Some(vec!["https://parent-explorer.example.com".to_string()])
422 );
423 assert_eq!(result.common.average_blocktime_ms, Some(10000));
424 assert_eq!(result.common.is_testnet, Some(true));
425 assert_eq!(result.common.tags, Some(vec!["parent-tag".to_string()]));
426 assert_eq!(result.chain_id, Some(1));
427 assert_eq!(result.required_confirmations, Some(6));
428 assert_eq!(result.features, Some(vec!["eip1559".to_string()]));
429 assert_eq!(result.symbol, Some("ETH".to_string()));
430 assert_eq!(
431 result.gas_price_cache,
432 Some(GasPriceCacheConfig {
433 enabled: true,
434 stale_after_ms: 20_000,
435 expire_after_ms: 100_000,
436 })
437 );
438 }
439
440 #[test]
441 fn test_merge_with_parent_mixed_inheritance() {
442 let parent = EvmNetworkConfig {
443 common: NetworkConfigCommon {
444 network: "parent".to_string(),
445 from: None,
446 rpc_urls: Some(vec![RpcConfig::new(
447 "https://parent-rpc.example.com".to_string(),
448 )]),
449 explorer_urls: Some(vec!["https://parent-explorer.example.com".to_string()]),
450 average_blocktime_ms: Some(10000),
451 is_testnet: Some(true),
452 tags: Some(vec!["parent-tag1".to_string(), "parent-tag2".to_string()]),
453 },
454 chain_id: Some(1),
455 required_confirmations: Some(6),
456 features: Some(vec!["eip155".to_string(), "eip1559".to_string()]),
457 symbol: Some("ETH".to_string()),
458 gas_price_cache: Some(GasPriceCacheConfig {
459 enabled: true,
460 stale_after_ms: 20_000,
461 expire_after_ms: 100_000,
462 }),
463 };
464
465 let child = EvmNetworkConfig {
466 common: NetworkConfigCommon {
467 network: "child".to_string(),
468 from: Some("parent".to_string()),
469 rpc_urls: Some(vec![RpcConfig::new(
470 "https://child-rpc.example.com".to_string(),
471 )]), explorer_urls: Some(vec!["https://child-explorer.example.com".to_string()]), average_blocktime_ms: None, is_testnet: Some(false), tags: Some(vec!["child-tag".to_string()]), },
477 chain_id: Some(31337), required_confirmations: None, features: Some(vec!["eip2930".to_string()]), symbol: None, gas_price_cache: Some(GasPriceCacheConfig {
482 enabled: false,
483 stale_after_ms: 40_000,
484 expire_after_ms: 200_000,
485 }),
486 };
487
488 let result = child.merge_with_parent(&parent);
489
490 assert_eq!(result.common.network, "child");
491 assert_eq!(
492 result.common.rpc_urls,
493 Some(vec![RpcConfig::new(
494 "https://child-rpc.example.com".to_string()
495 )])
496 ); assert_eq!(
498 result.common.explorer_urls,
499 Some(vec!["https://child-explorer.example.com".to_string()])
500 ); assert_eq!(result.common.average_blocktime_ms, Some(10000)); assert_eq!(result.common.is_testnet, Some(false)); assert_eq!(
504 result.common.tags,
505 Some(vec![
506 "parent-tag1".to_string(),
507 "parent-tag2".to_string(),
508 "child-tag".to_string()
509 ])
510 ); assert_eq!(result.chain_id, Some(31337)); assert_eq!(result.required_confirmations, Some(6)); assert_eq!(
514 result.features,
515 Some(vec![
516 "eip155".to_string(),
517 "eip1559".to_string(),
518 "eip2930".to_string()
519 ])
520 ); assert_eq!(result.symbol, Some("ETH".to_string())); assert_eq!(
523 result.gas_price_cache,
524 Some(GasPriceCacheConfig {
525 enabled: false,
526 stale_after_ms: 40_000,
527 expire_after_ms: 200_000,
528 })
529 );
530 }
531
532 #[test]
533 fn test_merge_with_parent_both_empty() {
534 let parent = EvmNetworkConfig {
535 common: NetworkConfigCommon {
536 network: "parent".to_string(),
537 from: None,
538 rpc_urls: None,
539 explorer_urls: None,
540 average_blocktime_ms: None,
541 is_testnet: None,
542 tags: None,
543 },
544 chain_id: None,
545 required_confirmations: None,
546 features: None,
547 symbol: None,
548 gas_price_cache: None,
549 };
550
551 let child = EvmNetworkConfig {
552 common: NetworkConfigCommon {
553 network: "child".to_string(),
554 from: Some("parent".to_string()),
555 rpc_urls: None,
556 explorer_urls: None,
557 average_blocktime_ms: None,
558 is_testnet: None,
559 tags: None,
560 },
561 chain_id: None,
562 required_confirmations: None,
563 features: None,
564 symbol: None,
565 gas_price_cache: None,
566 };
567
568 let result = child.merge_with_parent(&parent);
569
570 assert_eq!(result.common.network, "child");
571 assert_eq!(result.common.from, Some("parent".to_string()));
572 assert_eq!(result.common.rpc_urls, None);
573 assert_eq!(result.common.average_blocktime_ms, None);
574 assert_eq!(result.common.is_testnet, None);
575 assert_eq!(result.common.tags, None);
576 assert_eq!(result.chain_id, None);
577 assert_eq!(result.required_confirmations, None);
578 assert_eq!(result.features, None);
579 assert_eq!(result.symbol, None);
580 assert_eq!(result.gas_price_cache, None);
581 }
582
583 #[test]
584 fn test_merge_with_parent_complex_features_merging() {
585 let parent = EvmNetworkConfig {
586 common: NetworkConfigCommon {
587 network: "parent".to_string(),
588 from: None,
589 rpc_urls: Some(vec![RpcConfig::new("https://rpc.example.com".to_string())]),
590 explorer_urls: Some(vec!["https://explorer.example.com".to_string()]),
591 average_blocktime_ms: Some(12000),
592 is_testnet: Some(false),
593 tags: None,
594 },
595 chain_id: Some(1),
596 required_confirmations: Some(12),
597 features: Some(vec![
598 "eip155".to_string(),
599 "eip1559".to_string(),
600 "shared".to_string(),
601 ]),
602 symbol: Some("ETH".to_string()),
603 gas_price_cache: Some(GasPriceCacheConfig {
604 enabled: true,
605 stale_after_ms: 20_000,
606 expire_after_ms: 100_000,
607 }),
608 };
609
610 let child = EvmNetworkConfig {
611 common: NetworkConfigCommon {
612 network: "child".to_string(),
613 from: Some("parent".to_string()),
614 rpc_urls: None,
615 explorer_urls: None,
616 average_blocktime_ms: None,
617 is_testnet: None,
618 tags: None,
619 },
620 chain_id: None,
621 required_confirmations: None,
622 features: Some(vec![
623 "shared".to_string(),
624 "eip2930".to_string(),
625 "custom".to_string(),
626 ]),
627 symbol: None,
628 gas_price_cache: None,
629 };
630
631 let result = child.merge_with_parent(&parent);
632
633 let expected_features = vec![
635 "eip155".to_string(),
636 "eip1559".to_string(),
637 "shared".to_string(), "eip2930".to_string(),
639 "custom".to_string(),
640 ];
641 assert_eq!(result.features, Some(expected_features));
642 assert_eq!(
643 result.gas_price_cache,
644 Some(GasPriceCacheConfig {
645 enabled: true,
646 stale_after_ms: 20_000,
647 expire_after_ms: 100_000,
648 })
649 );
650 }
651
652 #[test]
653 fn test_merge_with_parent_preserves_child_network_name() {
654 let parent = create_evm_network("ethereum-mainnet");
655 let mut child =
656 create_evm_network_for_inheritance_test("ethereum-testnet", "ethereum-mainnet");
657 child.common.network = "custom-child-name".to_string();
658
659 let result = child.merge_with_parent(&parent);
660
661 assert_eq!(result.common.network, "custom-child-name");
663 }
664
665 #[test]
666 fn test_merge_with_parent_preserves_child_from_field() {
667 let parent = EvmNetworkConfig {
668 common: NetworkConfigCommon {
669 network: "parent".to_string(),
670 from: Some("grandparent".to_string()),
671 rpc_urls: Some(vec![RpcConfig::new(
672 "https://parent.example.com".to_string(),
673 )]),
674 explorer_urls: Some(vec!["https://parent-explorer.example.com".to_string()]),
675 average_blocktime_ms: Some(10000),
676 is_testnet: Some(true),
677 tags: None,
678 },
679 chain_id: Some(1),
680 required_confirmations: Some(6),
681 features: None,
682 symbol: Some("ETH".to_string()),
683 gas_price_cache: Some(GasPriceCacheConfig {
684 enabled: true,
685 stale_after_ms: 20_000,
686 expire_after_ms: 100_000,
687 }),
688 };
689
690 let child = EvmNetworkConfig {
691 common: NetworkConfigCommon {
692 network: "child".to_string(),
693 from: Some("parent".to_string()),
694 rpc_urls: None,
695 explorer_urls: None,
696 average_blocktime_ms: None,
697 is_testnet: None,
698 tags: None,
699 },
700 chain_id: None,
701 required_confirmations: None,
702 features: None,
703 symbol: None,
704 gas_price_cache: None,
705 };
706
707 let result = child.merge_with_parent(&parent);
708
709 assert_eq!(result.common.from, Some("parent".to_string()));
711 assert_eq!(
712 result.gas_price_cache,
713 Some(GasPriceCacheConfig {
714 enabled: true,
715 stale_after_ms: 20_000,
716 expire_after_ms: 100_000,
717 })
718 );
719 }
720
721 #[test]
722 fn test_validate_with_unicode_symbol() {
723 let mut config = create_evm_network("ethereum-mainnet");
724 config.symbol = Some("Ξ".to_string()); let result = config.validate();
727 assert!(result.is_ok());
728 }
729
730 #[test]
731 fn test_validate_with_unicode_features() {
732 let mut config = create_evm_network("ethereum-mainnet");
733 config.features = Some(vec!["eip1559".to_string(), "测试功能".to_string()]);
734
735 let result = config.validate();
736 assert!(result.is_ok());
737 }
738
739 #[test]
740 fn test_merge_with_parent_with_empty_features() {
741 let parent = EvmNetworkConfig {
742 common: NetworkConfigCommon {
743 network: "parent".to_string(),
744 from: None,
745 rpc_urls: Some(vec![RpcConfig::new("https://rpc.example.com".to_string())]),
746 explorer_urls: Some(vec!["https://explorer.example.com".to_string()]),
747 average_blocktime_ms: Some(12000),
748 is_testnet: Some(false),
749 tags: None,
750 },
751 chain_id: Some(1),
752 required_confirmations: Some(12),
753 features: Some(vec![]),
754 symbol: Some("ETH".to_string()),
755 gas_price_cache: Some(GasPriceCacheConfig {
756 enabled: true,
757 stale_after_ms: 20_000,
758 expire_after_ms: 100_000,
759 }),
760 };
761
762 let child = EvmNetworkConfig {
763 common: NetworkConfigCommon {
764 network: "child".to_string(),
765 from: Some("parent".to_string()),
766 rpc_urls: None,
767 explorer_urls: None,
768 average_blocktime_ms: None,
769 is_testnet: None,
770 tags: None,
771 },
772 chain_id: None,
773 required_confirmations: None,
774 features: Some(vec!["eip1559".to_string()]),
775 symbol: None,
776 gas_price_cache: None,
777 };
778
779 let result = child.merge_with_parent(&parent);
780
781 assert_eq!(result.features, Some(vec!["eip1559".to_string()]));
783 assert_eq!(
784 result.gas_price_cache,
785 Some(GasPriceCacheConfig {
786 enabled: true,
787 stale_after_ms: 20_000,
788 expire_after_ms: 100_000,
789 })
790 );
791 }
792
793 #[test]
794 fn test_validate_with_very_large_confirmations() {
795 let mut config = create_evm_network("ethereum-mainnet");
796 config.required_confirmations = Some(u64::MAX);
797
798 let result = config.validate();
799 assert!(result.is_ok());
800 }
801
802 #[test]
803 fn test_merge_with_parent_identical_configs() {
804 let config = create_evm_network("ethereum-mainnet");
805 let result = config.merge_with_parent(&config);
806
807 assert_eq!(result.common.network, config.common.network);
809 assert_eq!(result.chain_id, config.chain_id);
810 assert_eq!(result.required_confirmations, config.required_confirmations);
811 assert_eq!(result.features, config.features);
812 assert_eq!(result.symbol, config.symbol);
813 assert_eq!(result.gas_price_cache, config.gas_price_cache);
814 }
815
816 #[test]
817 fn test_validate_propagates_common_validation_errors() {
818 let mut config = create_evm_network("ethereum-mainnet");
819 config.common.rpc_urls = None; let result = config.validate();
822 assert!(result.is_err());
823 assert!(matches!(
824 result.unwrap_err(),
825 ConfigFileError::MissingField(_)
826 ));
827 }
828
829 #[test]
830 fn test_gas_price_cache_validation_zero_stale_after() {
831 let mut config = create_evm_network("ethereum-mainnet");
832 config.gas_price_cache = Some(GasPriceCacheConfig {
833 enabled: true,
834 stale_after_ms: 0, expire_after_ms: 45_000,
836 });
837
838 let result = config.validate();
839 assert!(result.is_err());
840 assert!(matches!(
841 result.unwrap_err(),
842 ConfigFileError::InvalidFormat(_)
843 ));
844 }
845
846 #[test]
847 fn test_gas_price_cache_validation_zero_expire_after() {
848 let mut config = create_evm_network("ethereum-mainnet");
849 config.gas_price_cache = Some(GasPriceCacheConfig {
850 enabled: true,
851 stale_after_ms: 20_000,
852 expire_after_ms: 0, });
854
855 let result = config.validate();
856 assert!(result.is_err());
857 assert!(matches!(
858 result.unwrap_err(),
859 ConfigFileError::InvalidFormat(_)
860 ));
861 }
862
863 #[test]
864 fn test_gas_price_cache_validation_expire_less_than_stale() {
865 let mut config = create_evm_network("ethereum-mainnet");
866 config.gas_price_cache = Some(GasPriceCacheConfig {
867 enabled: true,
868 stale_after_ms: 45_000,
869 expire_after_ms: 20_000, });
871
872 let result = config.validate();
873 assert!(result.is_err());
874 assert!(matches!(
875 result.unwrap_err(),
876 ConfigFileError::InvalidFormat(_)
877 ));
878 }
879
880 #[test]
881 fn test_gas_price_cache_validation_expire_equal_to_stale() {
882 let mut config = create_evm_network("ethereum-mainnet");
883 config.gas_price_cache = Some(GasPriceCacheConfig {
884 enabled: true,
885 stale_after_ms: 20_000,
886 expire_after_ms: 20_000, });
888
889 let result = config.validate();
890 assert!(result.is_err());
891 assert!(matches!(
892 result.unwrap_err(),
893 ConfigFileError::InvalidFormat(_)
894 ));
895 }
896
897 #[test]
898 fn test_gas_price_cache_validation_valid_config() {
899 let mut config = create_evm_network("ethereum-mainnet");
900 config.gas_price_cache = Some(GasPriceCacheConfig {
901 enabled: true,
902 stale_after_ms: 20_000,
903 expire_after_ms: 45_000, });
905
906 let result = config.validate();
907 assert!(result.is_ok());
908 }
909
910 #[test]
911 fn test_gas_price_cache_default_values() {
912 let config = GasPriceCacheConfig::default();
913
914 assert!(!config.enabled);
915 assert_eq!(config.stale_after_ms, 20_000);
916 assert_eq!(config.expire_after_ms, 45_000);
917
918 assert!(config.validate().is_ok());
920 }
921}