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
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!["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!["https://parent-rpc.example.com".to_string()]),
302 explorer_urls: Some(vec!["https://parent-explorer.example.com".to_string()]),
303 average_blocktime_ms: Some(10000),
304 is_testnet: Some(true),
305 tags: Some(vec!["parent-tag".to_string()]),
306 },
307 chain_id: Some(1),
308 required_confirmations: Some(6),
309 features: Some(vec!["legacy".to_string()]),
310 symbol: Some("PETH".to_string()),
311 gas_price_cache: Some(GasPriceCacheConfig {
312 enabled: true,
313 stale_after_ms: 20_000,
314 expire_after_ms: 100_000,
315 }),
316 };
317
318 let child = EvmNetworkConfig {
319 common: NetworkConfigCommon {
320 network: "child".to_string(),
321 from: Some("parent".to_string()),
322 rpc_urls: Some(vec!["https://child-rpc.example.com".to_string()]),
323 explorer_urls: Some(vec!["https://child-explorer.example.com".to_string()]),
324 average_blocktime_ms: Some(15000),
325 is_testnet: Some(false),
326 tags: Some(vec!["child-tag".to_string()]),
327 },
328 chain_id: Some(31337),
329 required_confirmations: Some(1),
330 features: Some(vec!["eip1559".to_string()]),
331 symbol: Some("CETH".to_string()),
332 gas_price_cache: Some(GasPriceCacheConfig {
333 enabled: false,
334 stale_after_ms: 40_000,
335 expire_after_ms: 200_000,
336 }),
337 };
338
339 let result = child.merge_with_parent(&parent);
340
341 assert_eq!(result.common.network, "child");
343 assert_eq!(result.common.from, Some("parent".to_string()));
344 assert_eq!(
345 result.common.rpc_urls,
346 Some(vec!["https://child-rpc.example.com".to_string()])
347 );
348 assert_eq!(
349 result.common.explorer_urls,
350 Some(vec!["https://child-explorer.example.com".to_string()])
351 );
352 assert_eq!(result.common.average_blocktime_ms, Some(15000));
353 assert_eq!(result.common.is_testnet, Some(false));
354 assert_eq!(
355 result.common.tags,
356 Some(vec!["parent-tag".to_string(), "child-tag".to_string()])
357 );
358 assert_eq!(result.chain_id, Some(31337));
359 assert_eq!(result.required_confirmations, Some(1));
360 assert_eq!(
361 result.features,
362 Some(vec!["legacy".to_string(), "eip1559".to_string()])
363 );
364 assert_eq!(result.symbol, Some("CETH".to_string()));
365 assert_eq!(
366 result.gas_price_cache,
367 Some(GasPriceCacheConfig {
368 enabled: false,
369 stale_after_ms: 40_000,
370 expire_after_ms: 200_000,
371 })
372 );
373 }
374
375 #[test]
376 fn test_merge_with_parent_child_inherits() {
377 let parent = EvmNetworkConfig {
378 common: NetworkConfigCommon {
379 network: "parent".to_string(),
380 from: None,
381 rpc_urls: Some(vec!["https://parent-rpc.example.com".to_string()]),
382 explorer_urls: Some(vec!["https://parent-explorer.example.com".to_string()]),
383 average_blocktime_ms: Some(10000),
384 is_testnet: Some(true),
385 tags: Some(vec!["parent-tag".to_string()]),
386 },
387 chain_id: Some(1),
388 required_confirmations: Some(6),
389 features: Some(vec!["eip1559".to_string()]),
390 symbol: Some("ETH".to_string()),
391 gas_price_cache: Some(GasPriceCacheConfig {
392 enabled: true,
393 stale_after_ms: 20_000,
394 expire_after_ms: 100_000,
395 }),
396 };
397
398 let child = create_evm_network_for_inheritance_test("ethereum-testnet", "ethereum-mainnet");
399
400 let result = child.merge_with_parent(&parent);
401
402 assert_eq!(result.common.network, "ethereum-testnet");
404 assert_eq!(result.common.from, Some("ethereum-mainnet".to_string()));
405 assert_eq!(
406 result.common.rpc_urls,
407 Some(vec!["https://parent-rpc.example.com".to_string()])
408 );
409 assert_eq!(
410 result.common.explorer_urls,
411 Some(vec!["https://parent-explorer.example.com".to_string()])
412 );
413 assert_eq!(result.common.average_blocktime_ms, Some(10000));
414 assert_eq!(result.common.is_testnet, Some(true));
415 assert_eq!(result.common.tags, Some(vec!["parent-tag".to_string()]));
416 assert_eq!(result.chain_id, Some(1));
417 assert_eq!(result.required_confirmations, Some(6));
418 assert_eq!(result.features, Some(vec!["eip1559".to_string()]));
419 assert_eq!(result.symbol, Some("ETH".to_string()));
420 assert_eq!(
421 result.gas_price_cache,
422 Some(GasPriceCacheConfig {
423 enabled: true,
424 stale_after_ms: 20_000,
425 expire_after_ms: 100_000,
426 })
427 );
428 }
429
430 #[test]
431 fn test_merge_with_parent_mixed_inheritance() {
432 let parent = EvmNetworkConfig {
433 common: NetworkConfigCommon {
434 network: "parent".to_string(),
435 from: None,
436 rpc_urls: Some(vec!["https://parent-rpc.example.com".to_string()]),
437 explorer_urls: Some(vec!["https://parent-explorer.example.com".to_string()]),
438 average_blocktime_ms: Some(10000),
439 is_testnet: Some(true),
440 tags: Some(vec!["parent-tag1".to_string(), "parent-tag2".to_string()]),
441 },
442 chain_id: Some(1),
443 required_confirmations: Some(6),
444 features: Some(vec!["eip155".to_string(), "eip1559".to_string()]),
445 symbol: Some("ETH".to_string()),
446 gas_price_cache: Some(GasPriceCacheConfig {
447 enabled: true,
448 stale_after_ms: 20_000,
449 expire_after_ms: 100_000,
450 }),
451 };
452
453 let child = EvmNetworkConfig {
454 common: NetworkConfigCommon {
455 network: "child".to_string(),
456 from: Some("parent".to_string()),
457 rpc_urls: Some(vec!["https://child-rpc.example.com".to_string()]), 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()]), },
463 chain_id: Some(31337), required_confirmations: None, features: Some(vec!["eip2930".to_string()]), symbol: None, gas_price_cache: Some(GasPriceCacheConfig {
468 enabled: false,
469 stale_after_ms: 40_000,
470 expire_after_ms: 200_000,
471 }),
472 };
473
474 let result = child.merge_with_parent(&parent);
475
476 assert_eq!(result.common.network, "child");
477 assert_eq!(
478 result.common.rpc_urls,
479 Some(vec!["https://child-rpc.example.com".to_string()])
480 ); assert_eq!(
482 result.common.explorer_urls,
483 Some(vec!["https://child-explorer.example.com".to_string()])
484 ); assert_eq!(result.common.average_blocktime_ms, Some(10000)); assert_eq!(result.common.is_testnet, Some(false)); assert_eq!(
488 result.common.tags,
489 Some(vec![
490 "parent-tag1".to_string(),
491 "parent-tag2".to_string(),
492 "child-tag".to_string()
493 ])
494 ); assert_eq!(result.chain_id, Some(31337)); assert_eq!(result.required_confirmations, Some(6)); assert_eq!(
498 result.features,
499 Some(vec![
500 "eip155".to_string(),
501 "eip1559".to_string(),
502 "eip2930".to_string()
503 ])
504 ); assert_eq!(result.symbol, Some("ETH".to_string())); assert_eq!(
507 result.gas_price_cache,
508 Some(GasPriceCacheConfig {
509 enabled: false,
510 stale_after_ms: 40_000,
511 expire_after_ms: 200_000,
512 })
513 );
514 }
515
516 #[test]
517 fn test_merge_with_parent_both_empty() {
518 let parent = EvmNetworkConfig {
519 common: NetworkConfigCommon {
520 network: "parent".to_string(),
521 from: None,
522 rpc_urls: None,
523 explorer_urls: None,
524 average_blocktime_ms: None,
525 is_testnet: None,
526 tags: None,
527 },
528 chain_id: None,
529 required_confirmations: None,
530 features: None,
531 symbol: None,
532 gas_price_cache: None,
533 };
534
535 let child = EvmNetworkConfig {
536 common: NetworkConfigCommon {
537 network: "child".to_string(),
538 from: Some("parent".to_string()),
539 rpc_urls: None,
540 explorer_urls: None,
541 average_blocktime_ms: None,
542 is_testnet: None,
543 tags: None,
544 },
545 chain_id: None,
546 required_confirmations: None,
547 features: None,
548 symbol: None,
549 gas_price_cache: None,
550 };
551
552 let result = child.merge_with_parent(&parent);
553
554 assert_eq!(result.common.network, "child");
555 assert_eq!(result.common.from, Some("parent".to_string()));
556 assert_eq!(result.common.rpc_urls, None);
557 assert_eq!(result.common.average_blocktime_ms, None);
558 assert_eq!(result.common.is_testnet, None);
559 assert_eq!(result.common.tags, None);
560 assert_eq!(result.chain_id, None);
561 assert_eq!(result.required_confirmations, None);
562 assert_eq!(result.features, None);
563 assert_eq!(result.symbol, None);
564 assert_eq!(result.gas_price_cache, None);
565 }
566
567 #[test]
568 fn test_merge_with_parent_complex_features_merging() {
569 let parent = EvmNetworkConfig {
570 common: NetworkConfigCommon {
571 network: "parent".to_string(),
572 from: None,
573 rpc_urls: Some(vec!["https://rpc.example.com".to_string()]),
574 explorer_urls: Some(vec!["https://explorer.example.com".to_string()]),
575 average_blocktime_ms: Some(12000),
576 is_testnet: Some(false),
577 tags: None,
578 },
579 chain_id: Some(1),
580 required_confirmations: Some(12),
581 features: Some(vec![
582 "eip155".to_string(),
583 "eip1559".to_string(),
584 "shared".to_string(),
585 ]),
586 symbol: Some("ETH".to_string()),
587 gas_price_cache: Some(GasPriceCacheConfig {
588 enabled: true,
589 stale_after_ms: 20_000,
590 expire_after_ms: 100_000,
591 }),
592 };
593
594 let child = EvmNetworkConfig {
595 common: NetworkConfigCommon {
596 network: "child".to_string(),
597 from: Some("parent".to_string()),
598 rpc_urls: None,
599 explorer_urls: None,
600 average_blocktime_ms: None,
601 is_testnet: None,
602 tags: None,
603 },
604 chain_id: None,
605 required_confirmations: None,
606 features: Some(vec![
607 "shared".to_string(),
608 "eip2930".to_string(),
609 "custom".to_string(),
610 ]),
611 symbol: None,
612 gas_price_cache: None,
613 };
614
615 let result = child.merge_with_parent(&parent);
616
617 let expected_features = vec![
619 "eip155".to_string(),
620 "eip1559".to_string(),
621 "shared".to_string(), "eip2930".to_string(),
623 "custom".to_string(),
624 ];
625 assert_eq!(result.features, Some(expected_features));
626 assert_eq!(
627 result.gas_price_cache,
628 Some(GasPriceCacheConfig {
629 enabled: true,
630 stale_after_ms: 20_000,
631 expire_after_ms: 100_000,
632 })
633 );
634 }
635
636 #[test]
637 fn test_merge_with_parent_preserves_child_network_name() {
638 let parent = create_evm_network("ethereum-mainnet");
639 let mut child =
640 create_evm_network_for_inheritance_test("ethereum-testnet", "ethereum-mainnet");
641 child.common.network = "custom-child-name".to_string();
642
643 let result = child.merge_with_parent(&parent);
644
645 assert_eq!(result.common.network, "custom-child-name");
647 }
648
649 #[test]
650 fn test_merge_with_parent_preserves_child_from_field() {
651 let parent = EvmNetworkConfig {
652 common: NetworkConfigCommon {
653 network: "parent".to_string(),
654 from: Some("grandparent".to_string()),
655 rpc_urls: Some(vec!["https://parent.example.com".to_string()]),
656 explorer_urls: Some(vec!["https://parent-explorer.example.com".to_string()]),
657 average_blocktime_ms: Some(10000),
658 is_testnet: Some(true),
659 tags: None,
660 },
661 chain_id: Some(1),
662 required_confirmations: Some(6),
663 features: None,
664 symbol: Some("ETH".to_string()),
665 gas_price_cache: Some(GasPriceCacheConfig {
666 enabled: true,
667 stale_after_ms: 20_000,
668 expire_after_ms: 100_000,
669 }),
670 };
671
672 let child = EvmNetworkConfig {
673 common: NetworkConfigCommon {
674 network: "child".to_string(),
675 from: Some("parent".to_string()),
676 rpc_urls: None,
677 explorer_urls: None,
678 average_blocktime_ms: None,
679 is_testnet: None,
680 tags: None,
681 },
682 chain_id: None,
683 required_confirmations: None,
684 features: None,
685 symbol: None,
686 gas_price_cache: None,
687 };
688
689 let result = child.merge_with_parent(&parent);
690
691 assert_eq!(result.common.from, Some("parent".to_string()));
693 assert_eq!(
694 result.gas_price_cache,
695 Some(GasPriceCacheConfig {
696 enabled: true,
697 stale_after_ms: 20_000,
698 expire_after_ms: 100_000,
699 })
700 );
701 }
702
703 #[test]
704 fn test_validate_with_unicode_symbol() {
705 let mut config = create_evm_network("ethereum-mainnet");
706 config.symbol = Some("Ξ".to_string()); let result = config.validate();
709 assert!(result.is_ok());
710 }
711
712 #[test]
713 fn test_validate_with_unicode_features() {
714 let mut config = create_evm_network("ethereum-mainnet");
715 config.features = Some(vec!["eip1559".to_string(), "测试功能".to_string()]);
716
717 let result = config.validate();
718 assert!(result.is_ok());
719 }
720
721 #[test]
722 fn test_merge_with_parent_with_empty_features() {
723 let parent = EvmNetworkConfig {
724 common: NetworkConfigCommon {
725 network: "parent".to_string(),
726 from: None,
727 rpc_urls: Some(vec!["https://rpc.example.com".to_string()]),
728 explorer_urls: Some(vec!["https://explorer.example.com".to_string()]),
729 average_blocktime_ms: Some(12000),
730 is_testnet: Some(false),
731 tags: None,
732 },
733 chain_id: Some(1),
734 required_confirmations: Some(12),
735 features: Some(vec![]),
736 symbol: Some("ETH".to_string()),
737 gas_price_cache: Some(GasPriceCacheConfig {
738 enabled: true,
739 stale_after_ms: 20_000,
740 expire_after_ms: 100_000,
741 }),
742 };
743
744 let child = EvmNetworkConfig {
745 common: NetworkConfigCommon {
746 network: "child".to_string(),
747 from: Some("parent".to_string()),
748 rpc_urls: None,
749 explorer_urls: None,
750 average_blocktime_ms: None,
751 is_testnet: None,
752 tags: None,
753 },
754 chain_id: None,
755 required_confirmations: None,
756 features: Some(vec!["eip1559".to_string()]),
757 symbol: None,
758 gas_price_cache: None,
759 };
760
761 let result = child.merge_with_parent(&parent);
762
763 assert_eq!(result.features, Some(vec!["eip1559".to_string()]));
765 assert_eq!(
766 result.gas_price_cache,
767 Some(GasPriceCacheConfig {
768 enabled: true,
769 stale_after_ms: 20_000,
770 expire_after_ms: 100_000,
771 })
772 );
773 }
774
775 #[test]
776 fn test_validate_with_very_large_confirmations() {
777 let mut config = create_evm_network("ethereum-mainnet");
778 config.required_confirmations = Some(u64::MAX);
779
780 let result = config.validate();
781 assert!(result.is_ok());
782 }
783
784 #[test]
785 fn test_merge_with_parent_identical_configs() {
786 let config = create_evm_network("ethereum-mainnet");
787 let result = config.merge_with_parent(&config);
788
789 assert_eq!(result.common.network, config.common.network);
791 assert_eq!(result.chain_id, config.chain_id);
792 assert_eq!(result.required_confirmations, config.required_confirmations);
793 assert_eq!(result.features, config.features);
794 assert_eq!(result.symbol, config.symbol);
795 assert_eq!(result.gas_price_cache, config.gas_price_cache);
796 }
797
798 #[test]
799 fn test_validate_propagates_common_validation_errors() {
800 let mut config = create_evm_network("ethereum-mainnet");
801 config.common.rpc_urls = None; let result = config.validate();
804 assert!(result.is_err());
805 assert!(matches!(
806 result.unwrap_err(),
807 ConfigFileError::MissingField(_)
808 ));
809 }
810
811 #[test]
812 fn test_gas_price_cache_validation_zero_stale_after() {
813 let mut config = create_evm_network("ethereum-mainnet");
814 config.gas_price_cache = Some(GasPriceCacheConfig {
815 enabled: true,
816 stale_after_ms: 0, expire_after_ms: 45_000,
818 });
819
820 let result = config.validate();
821 assert!(result.is_err());
822 assert!(matches!(
823 result.unwrap_err(),
824 ConfigFileError::InvalidFormat(_)
825 ));
826 }
827
828 #[test]
829 fn test_gas_price_cache_validation_zero_expire_after() {
830 let mut config = create_evm_network("ethereum-mainnet");
831 config.gas_price_cache = Some(GasPriceCacheConfig {
832 enabled: true,
833 stale_after_ms: 20_000,
834 expire_after_ms: 0, });
836
837 let result = config.validate();
838 assert!(result.is_err());
839 assert!(matches!(
840 result.unwrap_err(),
841 ConfigFileError::InvalidFormat(_)
842 ));
843 }
844
845 #[test]
846 fn test_gas_price_cache_validation_expire_less_than_stale() {
847 let mut config = create_evm_network("ethereum-mainnet");
848 config.gas_price_cache = Some(GasPriceCacheConfig {
849 enabled: true,
850 stale_after_ms: 45_000,
851 expire_after_ms: 20_000, });
853
854 let result = config.validate();
855 assert!(result.is_err());
856 assert!(matches!(
857 result.unwrap_err(),
858 ConfigFileError::InvalidFormat(_)
859 ));
860 }
861
862 #[test]
863 fn test_gas_price_cache_validation_expire_equal_to_stale() {
864 let mut config = create_evm_network("ethereum-mainnet");
865 config.gas_price_cache = Some(GasPriceCacheConfig {
866 enabled: true,
867 stale_after_ms: 20_000,
868 expire_after_ms: 20_000, });
870
871 let result = config.validate();
872 assert!(result.is_err());
873 assert!(matches!(
874 result.unwrap_err(),
875 ConfigFileError::InvalidFormat(_)
876 ));
877 }
878
879 #[test]
880 fn test_gas_price_cache_validation_valid_config() {
881 let mut config = create_evm_network("ethereum-mainnet");
882 config.gas_price_cache = Some(GasPriceCacheConfig {
883 enabled: true,
884 stale_after_ms: 20_000,
885 expire_after_ms: 45_000, });
887
888 let result = config.validate();
889 assert!(result.is_ok());
890 }
891
892 #[test]
893 fn test_gas_price_cache_default_values() {
894 let config = GasPriceCacheConfig::default();
895
896 assert!(!config.enabled);
897 assert_eq!(config.stale_after_ms, 20_000);
898 assert_eq!(config.expire_after_ms, 45_000);
899
900 assert!(config.validate().is_ok());
902 }
903}