//! Tests for configuration parsing. use entropyk_cli::config::{ComponentConfig, ScenarioConfig, SolverConfig}; use entropyk_cli::error::CliError; use std::path::PathBuf; use tempfile::tempdir; #[test] fn test_parse_minimal_config() { let json = r#"{ "fluid": "R134a" }"#; let config = ScenarioConfig::from_json(json).unwrap(); assert_eq!(config.fluid, "R134a"); assert!(config.circuits.is_empty()); assert_eq!(config.solver.strategy, "newton"); assert!(config.controls.is_empty()); } #[test] fn test_parse_saturated_control_loop() { let json = r#" { "fluid": "R134a", "controls": [ { "type": "SaturatedController", "id": "lwt_capacity", "measure": { "component": "evap_0", "output": "capacity" }, "actuator": { "component": "screw_0", "factor": "f_m", "initial": 1.0, "min": 0.5, "max": 1.5 }, "target": 350000.0, "gain": 0.01, "band": 1.0, "smooth_eps": 0.001 } ] }"#; let config = ScenarioConfig::from_json(json).unwrap(); assert_eq!(config.controls.len(), 1); let c = &config.controls[0]; assert_eq!(c.control_type, "SaturatedController"); assert_eq!(c.id, "lwt_capacity"); assert_eq!(c.measure.component, "evap_0"); assert_eq!(c.measure.output, "capacity"); assert_eq!(c.actuator.factor, "f_m"); assert_eq!(c.actuator.min, 0.5); assert_eq!(c.actuator.max, 1.5); assert_eq!(c.target, 350000.0); assert_eq!(c.gain, 0.01); assert_eq!(c.smooth_eps, Some(0.001)); } #[test] fn test_control_defaults_apply() { let json = r#" { "fluid": "R134a", "controls": [ { "id": "cap", "measure": { "component": "evap", "output": "capacity" }, "actuator": { "component": "comp", "factor": "f_m", "min": 0.5, "max": 1.5 }, "target": 1000.0 } ] }"#; let config = ScenarioConfig::from_json(json).unwrap(); let c = &config.controls[0]; assert_eq!(c.control_type, "SaturatedController"); assert_eq!(c.gain, 1.0); assert_eq!(c.band, 1.0); assert_eq!(c.actuator.initial, 1.0); assert_eq!(c.smooth_eps, None); } #[test] fn test_parse_full_config() { let json = r#" { "fluid": "R410A", "circuits": [{ "id": 0, "components": [ { "type": "Condenser", "name": "cond1", "ua": 5000.0 }, { "type": "Evaporator", "name": "evap1", "ua": 4000.0 } ], "edges": [ { "from": "cond1:outlet", "to": "evap1:inlet" } ] }], "solver": { "strategy": "newton", "max_iterations": 50, "tolerance": 1e-8 } }"#; let config = ScenarioConfig::from_json(json).unwrap(); assert_eq!(config.fluid, "R410A"); assert_eq!(config.circuits.len(), 1); assert_eq!(config.circuits[0].components.len(), 2); assert_eq!(config.solver.strategy, "newton"); assert_eq!(config.solver.max_iterations, 50); } #[test] fn test_validate_missing_fluid() { let json = r#"{ "fluid": "" }"#; let result = ScenarioConfig::from_json(json); assert!(result.is_err()); if let Err(CliError::Config(msg)) = result { assert!(msg.contains("fluid")); } } #[test] fn test_validate_empty_circuit() { let json = r#" { "fluid": "R134a", "circuits": [{ "id": 0, "components": [] }] }"#; let result = ScenarioConfig::from_json(json); assert!(result.is_err()); } #[test] fn test_validate_invalid_edge_format() { let json = r#" { "fluid": "R134a", "circuits": [{ "id": 0, "components": [{ "type": "Condenser", "name": "cond1", "ua": 5000.0 }], "edges": [{ "from": "invalid", "to": "also_invalid" }] }] }"#; let result = ScenarioConfig::from_json(json); assert!(result.is_err()); if let Err(CliError::Config(msg)) = result { assert!(msg.contains("edge format")); } } #[test] fn test_load_config_from_file() { let dir = tempdir().unwrap(); let config_path = dir.path().join("test.json"); let json = r#"{ "fluid": "R744" }"#; std::fs::write(&config_path, json).unwrap(); let config = ScenarioConfig::from_file(&config_path).unwrap(); assert_eq!(config.fluid, "R744"); } #[test] fn test_load_config_file_not_found() { let result = ScenarioConfig::from_file(PathBuf::from("/nonexistent/path.json").as_path()); assert!(result.is_err()); if let Err(CliError::ConfigNotFound(path)) = result { assert!(path.to_str().unwrap().contains("nonexistent")); } } #[test] fn test_solver_config_defaults() { let config = SolverConfig::default(); assert_eq!(config.strategy, "newton"); assert_eq!(config.max_iterations, 100); assert_eq!(config.tolerance, 1e-6); assert_eq!(config.timeout_ms, 0); assert!(!config.verbose); } #[test] fn test_component_config_params() { let json = r#" { "type": "Evaporator", "name": "evap1", "ua": 4000.0, "t_sat_k": 278.15, "superheat_k": 5.0 }"#; let component: ComponentConfig = serde_json::from_str(json).unwrap(); assert_eq!(component.component_type, "Evaporator"); assert_eq!(component.name, "evap1"); assert_eq!( component.params.get("ua").unwrap().as_f64().unwrap(), 4000.0 ); assert_eq!( component.params.get("t_sat_k").unwrap().as_f64().unwrap(), 278.15 ); assert_eq!( component .params .get("superheat_k") .unwrap() .as_f64() .unwrap(), 5.0 ); } #[test] fn test_validate_edge_unknown_component() { let json = r#" { "fluid": "R134a", "circuits": [{ "id": 0, "components": [{ "type": "Condenser", "name": "cond1", "ua": 5000.0 }], "edges": [{ "from": "cond1:outlet", "to": "nonexistent:inlet" }] }] }"#; let result = ScenarioConfig::from_json(json); assert!(result.is_err()); if let Err(CliError::Config(msg)) = result { assert!(msg.contains("unknown component")); assert!(msg.contains("nonexistent")); } }