openzeppelin_relayer/config/config_file/network/
mod.rs1pub mod collection;
22pub mod common;
23pub mod evm;
24pub mod file_loading;
25pub mod inheritance;
26pub mod solana;
27pub mod stellar;
28#[cfg(test)]
29pub mod test_utils;
30
31pub use collection::*;
32pub use common::*;
33pub use evm::*;
34pub use file_loading::*;
35pub use inheritance::*;
36pub use solana::*;
37pub use stellar::*;
38
39use super::ConfigFileNetworkType;
40use crate::config::ConfigFileError;
41use serde::{Deserialize, Serialize};
42
43#[derive(Debug, Serialize, Deserialize, Clone)]
47#[serde(tag = "type", rename_all = "lowercase")]
48pub enum NetworkFileConfig {
49 Evm(EvmNetworkConfig),
51 Solana(SolanaNetworkConfig),
53 Stellar(StellarNetworkConfig),
55}
56
57impl NetworkFileConfig {
58 pub fn validate(&self) -> Result<(), ConfigFileError> {
64 match self {
65 NetworkFileConfig::Evm(network) => network.validate(),
66 NetworkFileConfig::Solana(network) => network.validate(),
67 NetworkFileConfig::Stellar(network) => network.validate(),
68 }
69 }
70
71 pub fn network_name(&self) -> &str {
76 match self {
77 NetworkFileConfig::Evm(network) => &network.common.network,
78 NetworkFileConfig::Solana(network) => &network.common.network,
79 NetworkFileConfig::Stellar(network) => &network.common.network,
80 }
81 }
82
83 pub fn network_type(&self) -> ConfigFileNetworkType {
88 match self {
89 NetworkFileConfig::Evm(_) => ConfigFileNetworkType::Evm,
90 NetworkFileConfig::Solana(_) => ConfigFileNetworkType::Solana,
91 NetworkFileConfig::Stellar(_) => ConfigFileNetworkType::Stellar,
92 }
93 }
94
95 pub fn is_testnet(&self) -> bool {
101 match self {
102 NetworkFileConfig::Evm(network) => network.common.is_testnet.unwrap_or(false),
103 NetworkFileConfig::Solana(network) => network.common.is_testnet.unwrap_or(false),
104 NetworkFileConfig::Stellar(network) => network.common.is_testnet.unwrap_or(false),
105 }
106 }
107
108 pub fn inherits_from(&self) -> Option<&str> {
114 match self {
115 NetworkFileConfig::Evm(network) => network.common.from.as_deref(),
116 NetworkFileConfig::Solana(network) => network.common.from.as_deref(),
117 NetworkFileConfig::Stellar(network) => network.common.from.as_deref(),
118 }
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125 use crate::config::config_file::network::test_utils::*;
126
127 #[test]
128 fn test_validate_evm_network_success() {
129 let config = create_evm_network_wrapped("test-evm");
130 let result = config.validate();
131 assert!(result.is_ok());
132 }
133
134 #[test]
135 fn test_validate_solana_network_success() {
136 let config = create_solana_network_wrapped("test-solana");
137 let result = config.validate();
138 assert!(result.is_ok());
139 }
140
141 #[test]
142 fn test_validate_stellar_network_success() {
143 let config = create_stellar_network_wrapped("test-stellar");
144 let result = config.validate();
145 assert!(result.is_ok());
146 }
147
148 #[test]
149 fn test_validate_evm_network_failure() {
150 let mut config = create_evm_network_wrapped("test-evm");
151 if let NetworkFileConfig::Evm(ref mut evm_config) = config {
152 evm_config.common.network = "".to_string(); }
154
155 let result = config.validate();
156 assert!(result.is_err());
157 assert!(matches!(
158 result.unwrap_err(),
159 ConfigFileError::MissingField(_)
160 ));
161 }
162
163 #[test]
164 fn test_validate_solana_network_failure() {
165 let mut config = create_solana_network_wrapped("test-solana");
166 if let NetworkFileConfig::Solana(ref mut solana_config) = config {
167 solana_config.common.rpc_urls = None; }
169
170 let result = config.validate();
171 assert!(result.is_err());
172 assert!(matches!(
173 result.unwrap_err(),
174 ConfigFileError::MissingField(_)
175 ));
176 }
177
178 #[test]
179 fn test_validate_stellar_network_failure() {
180 let mut config = create_stellar_network_wrapped("test-stellar");
181 if let NetworkFileConfig::Stellar(ref mut stellar_config) = config {
182 stellar_config.common.network = "".to_string(); }
184
185 let result = config.validate();
186 assert!(result.is_err());
187 assert!(matches!(
188 result.unwrap_err(),
189 ConfigFileError::MissingField(_)
190 ));
191 }
192
193 #[test]
194 fn test_validate_evm_network_missing_chain_id() {
195 let mut config = create_evm_network_wrapped("test-evm");
196 if let NetworkFileConfig::Evm(ref mut evm_config) = config {
197 evm_config.chain_id = None; }
199
200 let result = config.validate();
201 assert!(result.is_err());
202 assert!(matches!(
203 result.unwrap_err(),
204 ConfigFileError::MissingField(_)
205 ));
206 }
207
208 #[test]
209 fn test_validate_evm_network_missing_confirmations() {
210 let mut config = create_evm_network_wrapped("test-evm");
211 if let NetworkFileConfig::Evm(ref mut evm_config) = config {
212 evm_config.required_confirmations = None; }
214
215 let result = config.validate();
216 assert!(result.is_err());
217 assert!(matches!(
218 result.unwrap_err(),
219 ConfigFileError::MissingField(_)
220 ));
221 }
222
223 #[test]
224 fn test_validate_evm_network_missing_symbol() {
225 let mut config = create_evm_network_wrapped("test-evm");
226 if let NetworkFileConfig::Evm(ref mut evm_config) = config {
227 evm_config.symbol = None; }
229
230 let result = config.validate();
231 assert!(result.is_err());
232 assert!(matches!(
233 result.unwrap_err(),
234 ConfigFileError::MissingField(_)
235 ));
236 }
237
238 #[test]
240 fn test_network_name_evm() {
241 let config = create_evm_network_wrapped("test-evm");
242 assert_eq!(config.network_name(), "test-evm");
243 }
244
245 #[test]
246 fn test_network_name_solana() {
247 let config = create_solana_network_wrapped("test-solana");
248 assert_eq!(config.network_name(), "test-solana");
249 }
250
251 #[test]
252 fn test_network_name_stellar() {
253 let config = create_stellar_network_wrapped("test-stellar");
254 assert_eq!(config.network_name(), "test-stellar");
255 }
256
257 #[test]
258 fn test_network_name_with_unicode() {
259 let mut config = create_evm_network_wrapped("test-evm");
260 if let NetworkFileConfig::Evm(ref mut evm_config) = config {
261 evm_config.common.network = "测试网络".to_string();
262 }
263 assert_eq!(config.network_name(), "测试网络");
264 }
265
266 #[test]
267 fn test_network_name_with_special_characters() {
268 let mut config = create_solana_network_wrapped("test-solana");
269 if let NetworkFileConfig::Solana(ref mut solana_config) = config {
270 solana_config.common.network = "test-network_123-dev".to_string();
271 }
272 assert_eq!(config.network_name(), "test-network_123-dev");
273 }
274
275 #[test]
276 fn test_network_name_empty_string() {
277 let mut config = create_stellar_network_wrapped("test-stellar");
278 if let NetworkFileConfig::Stellar(ref mut stellar_config) = config {
279 stellar_config.common.network = "".to_string();
280 }
281 assert_eq!(config.network_name(), "");
282 }
283
284 #[test]
285 fn test_network_type_evm() {
286 let config = create_evm_network_wrapped("test-evm");
287 assert_eq!(config.network_type(), ConfigFileNetworkType::Evm);
288 }
289
290 #[test]
291 fn test_network_type_solana() {
292 let config = create_solana_network_wrapped("test-solana");
293 assert_eq!(config.network_type(), ConfigFileNetworkType::Solana);
294 }
295
296 #[test]
297 fn test_network_type_stellar() {
298 let config = create_stellar_network_wrapped("test-stellar");
299 assert_eq!(config.network_type(), ConfigFileNetworkType::Stellar);
300 }
301
302 #[test]
303 fn test_network_type_consistency() {
304 let evm_config = create_evm_network_wrapped("test-evm");
305 let solana_config = create_solana_network_wrapped("test-solana");
306 let stellar_config = create_stellar_network_wrapped("test-stellar");
307
308 assert!(matches!(
310 evm_config.network_type(),
311 ConfigFileNetworkType::Evm
312 ));
313 assert!(matches!(
314 solana_config.network_type(),
315 ConfigFileNetworkType::Solana
316 ));
317 assert!(matches!(
318 stellar_config.network_type(),
319 ConfigFileNetworkType::Stellar
320 ));
321 }
322
323 #[test]
324 fn test_inherits_from_none() {
325 let config = create_evm_network_wrapped("test-evm");
326 assert_eq!(config.inherits_from(), None);
327 }
328
329 #[test]
330 fn test_inherits_from_some_evm() {
331 let config = create_evm_network_wrapped_with_parent("child-evm", "parent-evm");
332 assert_eq!(config.inherits_from(), Some("parent-evm"));
333 }
334
335 #[test]
336 fn test_inherits_from_some_solana() {
337 let mut config = create_solana_network_wrapped("test-solana");
338 if let NetworkFileConfig::Solana(ref mut solana_config) = config {
339 solana_config.common.from = Some("parent-solana".to_string());
340 }
341 assert_eq!(config.inherits_from(), Some("parent-solana"));
342 }
343
344 #[test]
345 fn test_inherits_from_some_stellar() {
346 let mut config = create_stellar_network_wrapped("test-stellar");
347 if let NetworkFileConfig::Stellar(ref mut stellar_config) = config {
348 stellar_config.common.from = Some("parent-stellar".to_string());
349 }
350 assert_eq!(config.inherits_from(), Some("parent-stellar"));
351 }
352
353 #[test]
354 fn test_inherits_from_empty_string() {
355 let mut config = create_evm_network_wrapped("test-evm");
356 if let NetworkFileConfig::Evm(ref mut evm_config) = config {
357 evm_config.common.from = Some("".to_string());
358 }
359 assert_eq!(config.inherits_from(), Some(""));
360 }
361
362 #[test]
363 fn test_inherits_from_with_unicode() {
364 let mut config = create_solana_network_wrapped("test-solana");
365 if let NetworkFileConfig::Solana(ref mut solana_config) = config {
366 solana_config.common.from = Some("父网络".to_string());
367 }
368 assert_eq!(config.inherits_from(), Some("父网络"));
369 }
370
371 #[test]
372 fn test_serialize_deserialize_evm() {
373 let original = create_evm_network_wrapped("test-evm");
374 let serialized = serde_json::to_string(&original).unwrap();
375 let deserialized: NetworkFileConfig = serde_json::from_str(&serialized).unwrap();
376
377 assert_eq!(original.network_name(), deserialized.network_name());
378 assert_eq!(original.network_type(), deserialized.network_type());
379 assert_eq!(original.inherits_from(), deserialized.inherits_from());
380 }
381
382 #[test]
383 fn test_serialize_deserialize_solana() {
384 let original = create_solana_network_wrapped("test-solana");
385 let serialized = serde_json::to_string(&original).unwrap();
386 let deserialized: NetworkFileConfig = serde_json::from_str(&serialized).unwrap();
387
388 assert_eq!(original.network_name(), deserialized.network_name());
389 assert_eq!(original.network_type(), deserialized.network_type());
390 assert_eq!(original.inherits_from(), deserialized.inherits_from());
391 }
392
393 #[test]
394 fn test_serialize_deserialize_stellar() {
395 let original = create_stellar_network_wrapped("test-stellar");
396 let serialized = serde_json::to_string(&original).unwrap();
397 let deserialized: NetworkFileConfig = serde_json::from_str(&serialized).unwrap();
398
399 assert_eq!(original.network_name(), deserialized.network_name());
400 assert_eq!(original.network_type(), deserialized.network_type());
401 assert_eq!(original.inherits_from(), deserialized.inherits_from());
402 }
403
404 #[test]
405 fn test_deserialize_evm_from_json() {
406 let json = r#"{
407 "type": "evm",
408 "network": "test-evm-json",
409 "chain_id": 1337,
410 "required_confirmations": 2,
411 "symbol": "ETH",
412 "rpc_urls": ["https://rpc.example.com"]
413 }"#;
414
415 let config: NetworkFileConfig = serde_json::from_str(json).unwrap();
416 assert_eq!(config.network_name(), "test-evm-json");
417 assert_eq!(config.network_type(), ConfigFileNetworkType::Evm);
418 assert_eq!(config.inherits_from(), None);
419 }
420
421 #[test]
422 fn test_deserialize_solana_from_json() {
423 let json = r#"{
424 "type": "solana",
425 "network": "test-solana-json",
426 "rpc_urls": ["https://api.devnet.solana.com"]
427 }"#;
428
429 let config: NetworkFileConfig = serde_json::from_str(json).unwrap();
430 assert_eq!(config.network_name(), "test-solana-json");
431 assert_eq!(config.network_type(), ConfigFileNetworkType::Solana);
432 assert_eq!(config.inherits_from(), None);
433 }
434
435 #[test]
436 fn test_deserialize_stellar_from_json() {
437 let json = r#"{
438 "type": "stellar",
439 "network": "test-stellar-json",
440 "rpc_urls": ["https://horizon-testnet.stellar.org"]
441 }"#;
442
443 let config: NetworkFileConfig = serde_json::from_str(json).unwrap();
444 assert_eq!(config.network_name(), "test-stellar-json");
445 assert_eq!(config.network_type(), ConfigFileNetworkType::Stellar);
446 assert_eq!(config.inherits_from(), None);
447 }
448
449 #[test]
450 fn test_deserialize_with_inheritance() {
451 let json = r#"{
452 "type": "evm",
453 "network": "child-network",
454 "from": "parent-network",
455 "chain_id": 1337,
456 "required_confirmations": 1,
457 "symbol": "ETH"
458 }"#;
459
460 let config: NetworkFileConfig = serde_json::from_str(json).unwrap();
461 assert_eq!(config.network_name(), "child-network");
462 assert_eq!(config.inherits_from(), Some("parent-network"));
463 }
464
465 #[test]
466 fn test_deserialize_invalid_type() {
467 let json = r#"{
468 "type": "invalid",
469 "network": "test-network"
470 }"#;
471
472 let result: Result<NetworkFileConfig, _> = serde_json::from_str(json);
473 assert!(result.is_err());
474 }
475
476 #[test]
477 fn test_deserialize_missing_type() {
478 let json = r#"{
479 "network": "test-network",
480 "chain_id": 1337
481 }"#;
482
483 let result: Result<NetworkFileConfig, _> = serde_json::from_str(json);
484 assert!(result.is_err());
485 }
486
487 #[test]
488 fn test_deserialize_missing_network_field() {
489 let json = r#"{
490 "type": "evm",
491 "chain_id": 1337
492 }"#;
493
494 let result: Result<NetworkFileConfig, _> = serde_json::from_str(json);
495 assert!(result.is_err());
496 }
497
498 #[test]
499 fn test_all_network_types_in_collection() {
500 let configs = vec![
501 create_evm_network_wrapped("test-evm"),
502 create_solana_network_wrapped("test-solana"),
503 create_stellar_network_wrapped("test-stellar"),
504 ];
505
506 let types: Vec<ConfigFileNetworkType> = configs.iter().map(|c| c.network_type()).collect();
507 assert!(types.contains(&ConfigFileNetworkType::Evm));
508 assert!(types.contains(&ConfigFileNetworkType::Solana));
509 assert!(types.contains(&ConfigFileNetworkType::Stellar));
510 }
511
512 #[test]
513 fn test_network_names_uniqueness() {
514 let configs = vec![
515 create_evm_network_wrapped("test-evm"),
516 create_solana_network_wrapped("test-solana"),
517 create_stellar_network_wrapped("test-stellar"),
518 ];
519
520 let names: Vec<&str> = configs.iter().map(|c| c.network_name()).collect();
521 assert_eq!(names.len(), 3);
522 assert!(names.contains(&"test-evm"));
523 assert!(names.contains(&"test-solana"));
524 assert!(names.contains(&"test-stellar"));
525 }
526
527 #[test]
528 fn test_inheritance_patterns() {
529 let mut configs = vec![
530 create_evm_network_wrapped("test-evm"),
531 create_evm_network_wrapped_with_parent("child-evm", "parent-evm"),
532 ];
533
534 let mut solana_with_inheritance = create_solana_network_wrapped("test-solana");
535 if let NetworkFileConfig::Solana(ref mut solana_config) = solana_with_inheritance {
536 solana_config.common.from = Some("parent-solana".to_string());
537 }
538 configs.push(solana_with_inheritance);
539
540 let inheritance_info: Vec<Option<&str>> =
541 configs.iter().map(|c| c.inherits_from()).collect();
542 assert_eq!(inheritance_info[0], None); assert_eq!(inheritance_info[1], Some("parent-evm")); assert_eq!(inheritance_info[2], Some("parent-solana")); }
546
547 #[test]
548 fn test_validation_error_propagation() {
549 let mut config = create_evm_network_wrapped("test-evm");
550 if let NetworkFileConfig::Evm(ref mut evm_config) = config {
551 evm_config.common.rpc_urls = Some(vec!["invalid-url".to_string()]);
552 }
553
554 let result = config.validate();
555 assert!(result.is_err());
556 assert!(matches!(
557 result.unwrap_err(),
558 ConfigFileError::InvalidFormat(_)
559 ));
560 }
561
562 #[test]
563 fn test_serialization_preserves_all_fields() {
564 let config = create_evm_network_wrapped("test-evm");
565 let serialized = serde_json::to_string(&config).unwrap();
566
567 assert!(serialized.contains("\"type\":\"evm\""));
569 assert!(serialized.contains("\"network\":\"test-evm\""));
570 assert!(serialized.contains("\"chain_id\":31337"));
571 assert!(serialized.contains("\"required_confirmations\":1"));
572 assert!(serialized.contains("\"symbol\":\"ETH\""));
573 }
574
575 #[test]
576 fn test_deserialization_with_extra_fields() {
577 let json = r#"{
578 "type": "evm",
579 "network": "test-network",
580 "chain_id": 1337,
581 "required_confirmations": 1,
582 "symbol": "ETH",
583 "rpc_urls": ["https://rpc.example.com"],
584 "extra_field": "should_be_ignored"
585 }"#;
586
587 let result: Result<NetworkFileConfig, _> = serde_json::from_str(json);
588 assert!(result.is_err());
589 }
590
591 #[test]
592 fn test_method_consistency_across_types() {
593 let configs = vec![
594 create_evm_network_wrapped("test-evm"),
595 create_solana_network_wrapped("test-solana"),
596 create_stellar_network_wrapped("test-stellar"),
597 ];
598
599 for config in configs {
601 assert!(!config.network_name().is_empty());
603
604 let network_type = config.network_type();
606 assert!(matches!(
607 network_type,
608 ConfigFileNetworkType::Evm
609 | ConfigFileNetworkType::Solana
610 | ConfigFileNetworkType::Stellar
611 ));
612
613 assert!(config.validate().is_ok());
615
616 assert_eq!(config.inherits_from(), None);
618 }
619 }
620}