1use schemars::schema_for;
8
9pub fn generate_config_schema() -> serde_json::Value {
30 let schema = schema_for!(mockforge_core::ServerConfig);
33
34 let mut schema_value = serde_json::to_value(schema).expect("Failed to serialize schema");
35
36 if let Some(obj) = schema_value.as_object_mut() {
38 obj.insert(
39 "$schema".to_string(),
40 serde_json::json!("http://json-schema.org/draft-07/schema#"),
41 );
42 obj.insert("title".to_string(), serde_json::json!("MockForge Server Configuration"));
43 obj.insert(
44 "description".to_string(),
45 serde_json::json!(
46 "Complete configuration schema for MockForge mock server. \
47 This schema provides autocomplete and validation for mockforge.yaml files."
48 ),
49 );
50 }
51
52 schema_value
53}
54
55pub fn generate_reality_schema() -> serde_json::Value {
60 let schema = schema_for!(mockforge_core::config::RealitySliderConfig);
61
62 let mut schema_value =
63 serde_json::to_value(schema).expect("Failed to serialize reality schema");
64
65 if let Some(obj) = schema_value.as_object_mut() {
67 obj.insert(
68 "$schema".to_string(),
69 serde_json::json!("http://json-schema.org/draft-07/schema#"),
70 );
71 obj.insert("title".to_string(), serde_json::json!("MockForge Reality Configuration"));
72 obj.insert(
73 "description".to_string(),
74 serde_json::json!(
75 "Reality slider configuration for controlling mock environment realism. \
76 Maps reality levels (1-5) to specific subsystem settings."
77 ),
78 );
79 }
80
81 schema_value
82}
83
84pub fn generate_persona_schema() -> serde_json::Value {
89 let schema = schema_for!(mockforge_core::config::PersonaRegistryConfig);
91
92 let mut schema_value =
93 serde_json::to_value(schema).expect("Failed to serialize persona schema");
94
95 if let Some(obj) = schema_value.as_object_mut() {
97 obj.insert(
98 "$schema".to_string(),
99 serde_json::json!("http://json-schema.org/draft-07/schema#"),
100 );
101 obj.insert("title".to_string(), serde_json::json!("MockForge Persona Configuration"));
102 obj.insert(
103 "description".to_string(),
104 serde_json::json!(
105 "Persona configuration for consistent, personality-driven data generation. \
106 Defines personas with unique IDs, domains, traits, and deterministic seeds."
107 ),
108 );
109 }
110
111 schema_value
112}
113
114pub fn generate_blueprint_schema() -> serde_json::Value {
120 serde_json::json!({
123 "$schema": "http://json-schema.org/draft-07/schema#",
124 "title": "MockForge Blueprint Configuration",
125 "description": "Blueprint metadata schema for predefined app archetypes. \
126 Blueprints provide pre-configured personas, reality defaults, \
127 flows, scenarios, and playground collections.",
128 "type": "object",
129 "required": ["manifest_version", "name", "version", "title", "description", "author", "category"],
130 "properties": {
131 "manifest_version": {
132 "type": "string",
133 "description": "Blueprint manifest version (e.g., '1.0')",
134 "example": "1.0"
135 },
136 "name": {
137 "type": "string",
138 "description": "Unique blueprint identifier (e.g., 'b2c-saas', 'ecommerce')",
139 "pattern": "^[a-z0-9-]+$"
140 },
141 "version": {
142 "type": "string",
143 "description": "Blueprint version (semver)",
144 "pattern": "^\\d+\\.\\d+\\.\\d+$"
145 },
146 "title": {
147 "type": "string",
148 "description": "Human-readable blueprint title"
149 },
150 "description": {
151 "type": "string",
152 "description": "Detailed description of what this blueprint provides"
153 },
154 "author": {
155 "type": "string",
156 "description": "Blueprint author name"
157 },
158 "author_email": {
159 "type": "string",
160 "format": "email",
161 "description": "Blueprint author email (optional)"
162 },
163 "category": {
164 "type": "string",
165 "description": "Blueprint category (e.g., 'saas', 'ecommerce', 'banking')",
166 "enum": ["saas", "ecommerce", "banking", "fintech", "healthcare", "other"]
167 },
168 "tags": {
169 "type": "array",
170 "items": {
171 "type": "string"
172 },
173 "description": "Tags for categorizing and searching blueprints"
174 },
175 "setup": {
176 "type": "object",
177 "description": "What this blueprint sets up",
178 "properties": {
179 "personas": {
180 "type": "array",
181 "items": {
182 "type": "object",
183 "required": ["id", "name"],
184 "properties": {
185 "id": {
186 "type": "string",
187 "description": "Persona identifier"
188 },
189 "name": {
190 "type": "string",
191 "description": "Persona display name"
192 },
193 "description": {
194 "type": "string",
195 "description": "Persona description (optional)"
196 }
197 }
198 }
199 },
200 "reality": {
201 "type": "object",
202 "properties": {
203 "level": {
204 "type": "string",
205 "enum": ["static", "light", "moderate", "high", "chaos"],
206 "description": "Default reality level for this blueprint"
207 },
208 "description": {
209 "type": "string",
210 "description": "Why this reality level is chosen"
211 }
212 }
213 },
214 "flows": {
215 "type": "array",
216 "items": {
217 "type": "object",
218 "required": ["id", "name"],
219 "properties": {
220 "id": {
221 "type": "string"
222 },
223 "name": {
224 "type": "string"
225 },
226 "description": {
227 "type": "string"
228 }
229 }
230 }
231 },
232 "scenarios": {
233 "type": "array",
234 "items": {
235 "type": "object",
236 "required": ["id", "name", "type", "file"],
237 "properties": {
238 "id": {
239 "type": "string",
240 "description": "Scenario identifier"
241 },
242 "name": {
243 "type": "string",
244 "description": "Scenario display name"
245 },
246 "type": {
247 "type": "string",
248 "enum": ["happy_path", "known_failure", "slow_path"],
249 "description": "Scenario type"
250 },
251 "description": {
252 "type": "string",
253 "description": "Scenario description (optional)"
254 },
255 "file": {
256 "type": "string",
257 "description": "Path to scenario YAML file"
258 }
259 }
260 }
261 },
262 "playground": {
263 "type": "object",
264 "properties": {
265 "enabled": {
266 "type": "boolean",
267 "default": true
268 },
269 "collection_file": {
270 "type": "string",
271 "description": "Path to playground collection file"
272 }
273 }
274 }
275 }
276 },
277 "compatibility": {
278 "type": "object",
279 "properties": {
280 "min_version": {
281 "type": "string",
282 "description": "Minimum MockForge version required"
283 },
284 "max_version": {
285 "type": "string",
286 "description": "Maximum MockForge version (null for latest)"
287 },
288 "required_features": {
289 "type": "array",
290 "items": {
291 "type": "string"
292 }
293 },
294 "protocols": {
295 "type": "array",
296 "items": {
297 "type": "string",
298 "enum": ["http", "websocket", "grpc", "graphql", "mqtt"]
299 }
300 }
301 }
302 },
303 "files": {
304 "type": "array",
305 "items": {
306 "type": "string"
307 },
308 "description": "List of files included in this blueprint"
309 },
310 "readme": {
311 "type": "string",
312 "description": "Path to README file (optional)"
313 },
314 "contracts": {
315 "type": "array",
316 "items": {
317 "type": "object",
318 "required": ["file"],
319 "properties": {
320 "file": {
321 "type": "string",
322 "description": "Path to contract schema file"
323 },
324 "description": {
325 "type": "string",
326 "description": "Contract description (optional)"
327 }
328 }
329 }
330 }
331 }
332 })
333}
334
335pub fn generate_config_schema_json() -> String {
341 let schema = generate_config_schema();
342 serde_json::to_string_pretty(&schema).expect("Failed to format schema as JSON")
343}
344
345pub fn generate_all_schemas() -> std::collections::HashMap<String, serde_json::Value> {
349 let mut schemas = std::collections::HashMap::new();
350
351 schemas.insert("mockforge-config".to_string(), generate_config_schema());
352 schemas.insert("reality-config".to_string(), generate_reality_schema());
353 schemas.insert("persona-config".to_string(), generate_persona_schema());
354 schemas.insert("blueprint-config".to_string(), generate_blueprint_schema());
355
356 schemas
357}
358
359#[derive(Debug, Clone)]
361pub struct ValidationResult {
362 pub valid: bool,
364 pub file_path: String,
366 pub schema_type: String,
368 pub errors: Vec<String>,
370}
371
372impl ValidationResult {
373 pub fn success(file_path: String, schema_type: String) -> Self {
375 Self {
376 valid: true,
377 file_path,
378 schema_type,
379 errors: Vec::new(),
380 }
381 }
382
383 pub fn failure(file_path: String, schema_type: String, errors: Vec<String>) -> Self {
385 Self {
386 valid: false,
387 file_path,
388 schema_type,
389 errors,
390 }
391 }
392}
393
394pub fn validate_config_file(
406 file_path: &std::path::Path,
407 schema_type: &str,
408 schema: &serde_json::Value,
409) -> Result<ValidationResult, Box<dyn std::error::Error>> {
410 use jsonschema::{Draft, Validator as SchemaValidator};
411 use std::fs;
412
413 let content = fs::read_to_string(file_path)?;
415 let config_value: serde_json::Value = if file_path
416 .extension()
417 .and_then(|ext| ext.to_str())
418 .map(|ext| ext.eq_ignore_ascii_case("yaml") || ext.eq_ignore_ascii_case("yml"))
419 .unwrap_or(false)
420 {
421 serde_yaml::from_str(&content).map_err(|e| format!("Failed to parse YAML: {}", e))?
423 } else {
424 serde_json::from_str(&content).map_err(|e| format!("Failed to parse JSON: {}", e))?
426 };
427
428 let compiled_schema = SchemaValidator::options()
430 .with_draft(Draft::Draft7)
431 .build(schema)
432 .map_err(|e| format!("Failed to compile schema: {}", e))?;
433
434 let mut errors = Vec::new();
436 for error in compiled_schema.iter_errors(&config_value) {
437 errors.push(format!("{}: {}", error.instance_path, error));
438 }
439
440 if errors.is_empty() {
441 Ok(ValidationResult::success(
442 file_path.to_string_lossy().to_string(),
443 schema_type.to_string(),
444 ))
445 } else {
446 Ok(ValidationResult::failure(
447 file_path.to_string_lossy().to_string(),
448 schema_type.to_string(),
449 errors,
450 ))
451 }
452}
453
454pub fn detect_schema_type(file_path: &std::path::Path) -> Option<String> {
459 let file_name = file_path.file_name()?.to_string_lossy().to_lowercase();
460 let path_str = file_path.to_string_lossy().to_lowercase();
461
462 if file_name == "mockforge.yaml"
464 || file_name == "mockforge.yml"
465 || file_name == "mockforge.json"
466 {
467 return Some("mockforge-config".to_string());
468 }
469
470 if file_name == "blueprint.yaml" || file_name == "blueprint.yml" {
471 return Some("blueprint-config".to_string());
472 }
473
474 if path_str.contains("reality") {
475 return Some("reality-config".to_string());
476 }
477
478 if path_str.contains("persona") {
479 return Some("persona-config".to_string());
480 }
481
482 Some("mockforge-config".to_string())
484}
485
486#[cfg(test)]
487mod tests {
488 use super::*;
489 use std::io::Write;
490 use std::path::PathBuf;
491
492 #[test]
495 fn test_generate_config_schema() {
496 let schema = generate_config_schema();
497 assert!(schema.is_object());
498
499 let obj = schema.as_object().unwrap();
500
501 assert!(obj.contains_key("$schema"));
503 assert_eq!(
504 obj.get("$schema").unwrap(),
505 &serde_json::json!("http://json-schema.org/draft-07/schema#")
506 );
507 assert!(obj.contains_key("title"));
508 assert_eq!(obj.get("title").unwrap(), &serde_json::json!("MockForge Server Configuration"));
509 assert!(obj.contains_key("description"));
510 }
511
512 #[test]
513 fn test_generate_reality_schema() {
514 let schema = generate_reality_schema();
515 assert!(schema.is_object());
516
517 let obj = schema.as_object().unwrap();
518
519 assert!(obj.contains_key("$schema"));
521 assert!(obj.contains_key("title"));
522 assert_eq!(
523 obj.get("title").unwrap(),
524 &serde_json::json!("MockForge Reality Configuration")
525 );
526 assert!(obj.contains_key("description"));
527 }
528
529 #[test]
530 fn test_generate_persona_schema() {
531 let schema = generate_persona_schema();
532 assert!(schema.is_object());
533
534 let obj = schema.as_object().unwrap();
535
536 assert!(obj.contains_key("$schema"));
538 assert!(obj.contains_key("title"));
539 assert_eq!(
540 obj.get("title").unwrap(),
541 &serde_json::json!("MockForge Persona Configuration")
542 );
543 assert!(obj.contains_key("description"));
544 }
545
546 #[test]
547 fn test_generate_blueprint_schema() {
548 let schema = generate_blueprint_schema();
549 assert!(schema.is_object());
550
551 let obj = schema.as_object().unwrap();
552
553 assert!(obj.contains_key("$schema"));
555 assert!(obj.contains_key("title"));
556 assert_eq!(
557 obj.get("title").unwrap(),
558 &serde_json::json!("MockForge Blueprint Configuration")
559 );
560
561 assert_eq!(obj.get("type").unwrap(), &serde_json::json!("object"));
563
564 assert!(obj.contains_key("required"));
566 let required = obj.get("required").unwrap().as_array().unwrap();
567 assert!(required.contains(&serde_json::json!("name")));
568 assert!(required.contains(&serde_json::json!("version")));
569 assert!(required.contains(&serde_json::json!("title")));
570
571 assert!(obj.contains_key("properties"));
573 let props = obj.get("properties").unwrap().as_object().unwrap();
574 assert!(props.contains_key("manifest_version"));
575 assert!(props.contains_key("name"));
576 assert!(props.contains_key("version"));
577 assert!(props.contains_key("category"));
578 assert!(props.contains_key("setup"));
579 assert!(props.contains_key("compatibility"));
580 }
581
582 #[test]
583 fn test_generate_blueprint_schema_category_enum() {
584 let schema = generate_blueprint_schema();
585 let obj = schema.as_object().unwrap();
586 let props = obj.get("properties").unwrap().as_object().unwrap();
587 let category = props.get("category").unwrap().as_object().unwrap();
588
589 assert!(category.contains_key("enum"));
591 let enum_values = category.get("enum").unwrap().as_array().unwrap();
592 assert!(enum_values.contains(&serde_json::json!("saas")));
593 assert!(enum_values.contains(&serde_json::json!("ecommerce")));
594 assert!(enum_values.contains(&serde_json::json!("banking")));
595 }
596
597 #[test]
598 fn test_generate_config_schema_json() {
599 let json = generate_config_schema_json();
600 assert!(!json.is_empty());
601
602 let parsed: Result<serde_json::Value, _> = serde_json::from_str(&json);
604 assert!(parsed.is_ok());
605
606 let schema = generate_config_schema();
608 let reparsed = serde_json::from_str::<serde_json::Value>(&json).unwrap();
609
610 assert_eq!(reparsed.get("title").unwrap(), schema.get("title").unwrap());
612 }
613
614 #[test]
615 fn test_generate_all_schemas() {
616 let schemas = generate_all_schemas();
617
618 assert_eq!(schemas.len(), 4);
620 assert!(schemas.contains_key("mockforge-config"));
621 assert!(schemas.contains_key("reality-config"));
622 assert!(schemas.contains_key("persona-config"));
623 assert!(schemas.contains_key("blueprint-config"));
624
625 for (name, schema) in &schemas {
627 assert!(schema.is_object(), "Schema {} should be an object", name);
628 }
629 }
630
631 #[test]
634 fn test_validation_result_success() {
635 let result = ValidationResult::success(
636 "/path/to/config.yaml".to_string(),
637 "mockforge-config".to_string(),
638 );
639
640 assert!(result.valid);
641 assert_eq!(result.file_path, "/path/to/config.yaml");
642 assert_eq!(result.schema_type, "mockforge-config");
643 assert!(result.errors.is_empty());
644 }
645
646 #[test]
647 fn test_validation_result_failure() {
648 let errors = vec![
649 "Missing required field: name".to_string(),
650 "Invalid type for port".to_string(),
651 ];
652
653 let result = ValidationResult::failure(
654 "/path/to/invalid.yaml".to_string(),
655 "mockforge-config".to_string(),
656 errors.clone(),
657 );
658
659 assert!(!result.valid);
660 assert_eq!(result.file_path, "/path/to/invalid.yaml");
661 assert_eq!(result.schema_type, "mockforge-config");
662 assert_eq!(result.errors.len(), 2);
663 assert_eq!(result.errors, errors);
664 }
665
666 #[test]
667 fn test_validation_result_debug() {
668 let result = ValidationResult::success("test.yaml".to_string(), "config".to_string());
669 let debug_str = format!("{:?}", result);
670 assert!(debug_str.contains("ValidationResult"));
671 assert!(debug_str.contains("valid"));
672 assert!(debug_str.contains("test.yaml"));
673 }
674
675 #[test]
676 fn test_validation_result_clone() {
677 let result = ValidationResult::failure(
678 "test.yaml".to_string(),
679 "config".to_string(),
680 vec!["error1".to_string()],
681 );
682 let cloned = result.clone();
683
684 assert_eq!(cloned.valid, result.valid);
685 assert_eq!(cloned.file_path, result.file_path);
686 assert_eq!(cloned.schema_type, result.schema_type);
687 assert_eq!(cloned.errors, result.errors);
688 }
689
690 #[test]
693 fn test_detect_schema_type_mockforge_yaml() {
694 let path = PathBuf::from("/project/mockforge.yaml");
695 assert_eq!(detect_schema_type(&path), Some("mockforge-config".to_string()));
696 }
697
698 #[test]
699 fn test_detect_schema_type_mockforge_yml() {
700 let path = PathBuf::from("/project/mockforge.yml");
701 assert_eq!(detect_schema_type(&path), Some("mockforge-config".to_string()));
702 }
703
704 #[test]
705 fn test_detect_schema_type_mockforge_json() {
706 let path = PathBuf::from("/project/mockforge.json");
707 assert_eq!(detect_schema_type(&path), Some("mockforge-config".to_string()));
708 }
709
710 #[test]
711 fn test_detect_schema_type_blueprint_yaml() {
712 let path = PathBuf::from("/blueprints/saas/blueprint.yaml");
713 assert_eq!(detect_schema_type(&path), Some("blueprint-config".to_string()));
714 }
715
716 #[test]
717 fn test_detect_schema_type_blueprint_yml() {
718 let path = PathBuf::from("/blueprints/ecommerce/blueprint.yml");
719 assert_eq!(detect_schema_type(&path), Some("blueprint-config".to_string()));
720 }
721
722 #[test]
723 fn test_detect_schema_type_reality_path() {
724 let path = PathBuf::from("/config/reality/settings.yaml");
725 assert_eq!(detect_schema_type(&path), Some("reality-config".to_string()));
726 }
727
728 #[test]
729 fn test_detect_schema_type_persona_path() {
730 let path = PathBuf::from("/config/persona/developer.yaml");
731 assert_eq!(detect_schema_type(&path), Some("persona-config".to_string()));
732 }
733
734 #[test]
735 fn test_detect_schema_type_default() {
736 let path = PathBuf::from("/some/other/config.yaml");
737 assert_eq!(detect_schema_type(&path), Some("mockforge-config".to_string()));
738 }
739
740 #[test]
741 fn test_detect_schema_type_case_insensitive() {
742 let path = PathBuf::from("/Project/MOCKFORGE.YAML");
743 assert_eq!(detect_schema_type(&path), Some("mockforge-config".to_string()));
744
745 let path2 = PathBuf::from("/blueprints/Blueprint.YML");
746 assert_eq!(detect_schema_type(&path2), Some("blueprint-config".to_string()));
747 }
748
749 #[test]
752 fn test_validate_yaml_file_valid() {
753 let schema = serde_json::json!({
755 "$schema": "http://json-schema.org/draft-07/schema#",
756 "type": "object",
757 "properties": {
758 "name": { "type": "string" },
759 "port": { "type": "integer" }
760 },
761 "required": ["name"]
762 });
763
764 let temp_dir = std::env::temp_dir();
766 let file_path = temp_dir.join("test_valid_config.yaml");
767 let mut file = std::fs::File::create(&file_path).unwrap();
768 writeln!(file, "name: test-service\nport: 8080").unwrap();
769
770 let result = validate_config_file(&file_path, "test-config", &schema).unwrap();
771
772 assert!(result.valid);
773 assert!(result.errors.is_empty());
774
775 std::fs::remove_file(&file_path).ok();
777 }
778
779 #[test]
780 fn test_validate_json_file_valid() {
781 let schema = serde_json::json!({
782 "$schema": "http://json-schema.org/draft-07/schema#",
783 "type": "object",
784 "properties": {
785 "name": { "type": "string" }
786 },
787 "required": ["name"]
788 });
789
790 let temp_dir = std::env::temp_dir();
791 let file_path = temp_dir.join("test_valid_config.json");
792 let content = r#"{"name": "test-service"}"#;
793 std::fs::write(&file_path, content).unwrap();
794
795 let result = validate_config_file(&file_path, "test-config", &schema).unwrap();
796
797 assert!(result.valid);
798 assert!(result.errors.is_empty());
799
800 std::fs::remove_file(&file_path).ok();
801 }
802
803 #[test]
804 fn test_validate_yaml_file_missing_required() {
805 let schema = serde_json::json!({
806 "$schema": "http://json-schema.org/draft-07/schema#",
807 "type": "object",
808 "properties": {
809 "name": { "type": "string" },
810 "port": { "type": "integer" }
811 },
812 "required": ["name", "port"]
813 });
814
815 let temp_dir = std::env::temp_dir();
816 let file_path = temp_dir.join("test_missing_required.yaml");
817 let mut file = std::fs::File::create(&file_path).unwrap();
818 writeln!(file, "name: test-service").unwrap(); let result = validate_config_file(&file_path, "test-config", &schema).unwrap();
821
822 assert!(!result.valid);
823 assert!(!result.errors.is_empty());
824 let has_port_error = result.errors.iter().any(|e| e.contains("port"));
826 assert!(has_port_error, "Expected error about missing 'port'");
827
828 std::fs::remove_file(&file_path).ok();
829 }
830
831 #[test]
832 fn test_validate_yaml_file_wrong_type() {
833 let schema = serde_json::json!({
834 "$schema": "http://json-schema.org/draft-07/schema#",
835 "type": "object",
836 "properties": {
837 "port": { "type": "integer" }
838 }
839 });
840
841 let temp_dir = std::env::temp_dir();
842 let file_path = temp_dir.join("test_wrong_type.yaml");
843 let mut file = std::fs::File::create(&file_path).unwrap();
844 writeln!(file, "port: not-a-number").unwrap(); let result = validate_config_file(&file_path, "test-config", &schema).unwrap();
847
848 assert!(!result.valid);
849 assert!(!result.errors.is_empty());
850
851 std::fs::remove_file(&file_path).ok();
852 }
853
854 #[test]
855 fn test_validate_file_not_found() {
856 let schema = serde_json::json!({
857 "type": "object"
858 });
859
860 let file_path = PathBuf::from("/nonexistent/path/config.yaml");
861 let result = validate_config_file(&file_path, "test-config", &schema);
862
863 assert!(result.is_err());
864 }
865
866 #[test]
867 fn test_validate_invalid_yaml_syntax() {
868 let schema = serde_json::json!({
869 "type": "object"
870 });
871
872 let temp_dir = std::env::temp_dir();
873 let file_path = temp_dir.join("test_invalid_yaml.yaml");
874 let mut file = std::fs::File::create(&file_path).unwrap();
875 writeln!(file, "invalid: yaml: syntax: [unclosed").unwrap();
876
877 let result = validate_config_file(&file_path, "test-config", &schema);
878
879 assert!(result.is_err());
880
881 std::fs::remove_file(&file_path).ok();
882 }
883
884 #[test]
885 fn test_validate_invalid_json_syntax() {
886 let schema = serde_json::json!({
887 "type": "object"
888 });
889
890 let temp_dir = std::env::temp_dir();
891 let file_path = temp_dir.join("test_invalid.json");
892 let content = r#"{"unclosed": "#;
893 std::fs::write(&file_path, content).unwrap();
894
895 let result = validate_config_file(&file_path, "test-config", &schema);
896
897 assert!(result.is_err());
898
899 std::fs::remove_file(&file_path).ok();
900 }
901
902 #[test]
903 fn test_validate_yml_extension() {
904 let schema = serde_json::json!({
905 "type": "object"
906 });
907
908 let temp_dir = std::env::temp_dir();
909 let file_path = temp_dir.join("test_config.yml"); let mut file = std::fs::File::create(&file_path).unwrap();
911 writeln!(file, "key: value").unwrap();
912
913 let result = validate_config_file(&file_path, "test-config", &schema).unwrap();
914
915 assert!(result.valid);
916
917 std::fs::remove_file(&file_path).ok();
918 }
919
920 #[test]
923 fn test_empty_yaml_file() {
924 let schema = serde_json::json!({
925 "type": "object"
926 });
927
928 let temp_dir = std::env::temp_dir();
929 let file_path = temp_dir.join("test_empty.yaml");
930 std::fs::File::create(&file_path).unwrap();
931
932 let result = validate_config_file(&file_path, "test-config", &schema);
934 assert!(result.is_ok() || result.is_err());
936
937 std::fs::remove_file(&file_path).ok();
938 }
939
940 #[test]
941 fn test_schema_with_additional_properties_false() {
942 let schema = serde_json::json!({
943 "$schema": "http://json-schema.org/draft-07/schema#",
944 "type": "object",
945 "properties": {
946 "name": { "type": "string" }
947 },
948 "additionalProperties": false
949 });
950
951 let temp_dir = std::env::temp_dir();
952 let file_path = temp_dir.join("test_extra_props.yaml");
953 let mut file = std::fs::File::create(&file_path).unwrap();
954 writeln!(file, "name: test\nextra: not-allowed").unwrap();
955
956 let result = validate_config_file(&file_path, "test-config", &schema).unwrap();
957
958 assert!(!result.valid);
959 assert!(!result.errors.is_empty());
960
961 std::fs::remove_file(&file_path).ok();
962 }
963
964 #[test]
965 fn test_nested_validation_error() {
966 let schema = serde_json::json!({
967 "$schema": "http://json-schema.org/draft-07/schema#",
968 "type": "object",
969 "properties": {
970 "server": {
971 "type": "object",
972 "properties": {
973 "port": { "type": "integer", "minimum": 1, "maximum": 65535 }
974 }
975 }
976 }
977 });
978
979 let temp_dir = std::env::temp_dir();
980 let file_path = temp_dir.join("test_nested.yaml");
981 let mut file = std::fs::File::create(&file_path).unwrap();
982 writeln!(file, "server:\n port: 99999").unwrap(); let result = validate_config_file(&file_path, "test-config", &schema).unwrap();
985
986 assert!(!result.valid);
987 let has_port_error = result.errors.iter().any(|e| e.contains("port"));
989 assert!(has_port_error);
990
991 std::fs::remove_file(&file_path).ok();
992 }
993
994 #[test]
995 fn test_validation_result_empty_errors() {
996 let result =
997 ValidationResult::failure("test.yaml".to_string(), "config".to_string(), vec![]);
998
999 assert!(!result.valid);
1001 assert!(result.errors.is_empty());
1002 }
1003
1004 #[test]
1005 fn test_blueprint_schema_setup_structure() {
1006 let schema = generate_blueprint_schema();
1007 let obj = schema.as_object().unwrap();
1008 let props = obj.get("properties").unwrap().as_object().unwrap();
1009 let setup = props.get("setup").unwrap().as_object().unwrap();
1010
1011 assert_eq!(setup.get("type").unwrap(), &serde_json::json!("object"));
1012
1013 let setup_props = setup.get("properties").unwrap().as_object().unwrap();
1014 assert!(setup_props.contains_key("personas"));
1015 assert!(setup_props.contains_key("reality"));
1016 assert!(setup_props.contains_key("flows"));
1017 assert!(setup_props.contains_key("scenarios"));
1018 assert!(setup_props.contains_key("playground"));
1019 }
1020
1021 #[test]
1024 fn test_config_schema_has_required_properties() {
1025 let schema = generate_config_schema();
1026 let obj = schema.as_object().unwrap();
1027
1028 assert!(obj.contains_key("properties"));
1030 assert!(obj.contains_key("$schema"));
1031 assert!(obj.contains_key("title"));
1032 assert!(obj.contains_key("description"));
1033 }
1034
1035 #[test]
1036 fn test_reality_schema_structure() {
1037 let schema = generate_reality_schema();
1038 let obj = schema.as_object().unwrap();
1039
1040 assert_eq!(
1042 obj.get("$schema").unwrap(),
1043 &serde_json::json!("http://json-schema.org/draft-07/schema#")
1044 );
1045 assert!(obj.contains_key("properties") || obj.contains_key("definitions"));
1046 }
1047
1048 #[test]
1049 fn test_persona_schema_structure() {
1050 let schema = generate_persona_schema();
1051 let obj = schema.as_object().unwrap();
1052
1053 assert_eq!(
1055 obj.get("$schema").unwrap(),
1056 &serde_json::json!("http://json-schema.org/draft-07/schema#")
1057 );
1058 assert!(obj.contains_key("properties") || obj.contains_key("definitions"));
1059 }
1060
1061 #[test]
1062 fn test_blueprint_schema_compatibility_structure() {
1063 let schema = generate_blueprint_schema();
1064 let obj = schema.as_object().unwrap();
1065 let props = obj.get("properties").unwrap().as_object().unwrap();
1066
1067 assert!(props.contains_key("compatibility"));
1068 let compatibility = props.get("compatibility").unwrap().as_object().unwrap();
1069 assert_eq!(compatibility.get("type").unwrap(), &serde_json::json!("object"));
1070
1071 let compat_props = compatibility.get("properties").unwrap().as_object().unwrap();
1072 assert!(compat_props.contains_key("min_version"));
1073 assert!(compat_props.contains_key("max_version"));
1074 assert!(compat_props.contains_key("required_features"));
1075 assert!(compat_props.contains_key("protocols"));
1076 }
1077
1078 #[test]
1079 fn test_blueprint_schema_protocols_enum() {
1080 let schema = generate_blueprint_schema();
1081 let obj = schema.as_object().unwrap();
1082 let props = obj.get("properties").unwrap().as_object().unwrap();
1083 let compatibility = props.get("compatibility").unwrap().as_object().unwrap();
1084 let compat_props = compatibility.get("properties").unwrap().as_object().unwrap();
1085 let protocols = compat_props.get("protocols").unwrap().as_object().unwrap();
1086
1087 assert_eq!(protocols.get("type").unwrap(), &serde_json::json!("array"));
1089
1090 let items = protocols.get("items").unwrap().as_object().unwrap();
1092 assert!(items.contains_key("enum"));
1093 let enum_values = items.get("enum").unwrap().as_array().unwrap();
1094 assert!(enum_values.contains(&serde_json::json!("http")));
1095 assert!(enum_values.contains(&serde_json::json!("websocket")));
1096 assert!(enum_values.contains(&serde_json::json!("grpc")));
1097 }
1098
1099 #[test]
1100 fn test_blueprint_schema_name_pattern() {
1101 let schema = generate_blueprint_schema();
1102 let obj = schema.as_object().unwrap();
1103 let props = obj.get("properties").unwrap().as_object().unwrap();
1104 let name = props.get("name").unwrap().as_object().unwrap();
1105
1106 assert!(name.contains_key("pattern"));
1108 assert_eq!(name.get("pattern").unwrap(), &serde_json::json!("^[a-z0-9-]+$"));
1109 }
1110
1111 #[test]
1112 fn test_blueprint_schema_version_pattern() {
1113 let schema = generate_blueprint_schema();
1114 let obj = schema.as_object().unwrap();
1115 let props = obj.get("properties").unwrap().as_object().unwrap();
1116 let version = props.get("version").unwrap().as_object().unwrap();
1117
1118 assert!(version.contains_key("pattern"));
1120 let pattern = version.get("pattern").unwrap().as_str().unwrap();
1121 assert!(pattern.contains("\\d+"));
1122 }
1123
1124 #[test]
1125 fn test_all_schemas_are_valid_json() {
1126 let schemas = generate_all_schemas();
1127
1128 for (name, schema) in schemas {
1129 let json_str = serde_json::to_string(&schema).unwrap();
1131 assert!(!json_str.is_empty(), "Schema {} should serialize to non-empty JSON", name);
1132
1133 let reparsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
1135 assert_eq!(reparsed, schema, "Schema {} should round-trip correctly", name);
1136 }
1137 }
1138
1139 #[test]
1142 fn test_validate_with_multiple_validation_errors() {
1143 let schema = serde_json::json!({
1144 "$schema": "http://json-schema.org/draft-07/schema#",
1145 "type": "object",
1146 "properties": {
1147 "name": { "type": "string" },
1148 "port": { "type": "integer", "minimum": 1, "maximum": 65535 },
1149 "enabled": { "type": "boolean" }
1150 },
1151 "required": ["name", "port", "enabled"]
1152 });
1153
1154 let temp_dir = std::env::temp_dir();
1155 let file_path = temp_dir.join("test_multi_errors.yaml");
1156 let mut file = std::fs::File::create(&file_path).unwrap();
1157 writeln!(file, "port: not-a-number").unwrap(); let result = validate_config_file(&file_path, "test-config", &schema).unwrap();
1160
1161 assert!(!result.valid);
1162 assert!(
1164 result.errors.len() >= 2,
1165 "Expected at least 2 errors, got {}",
1166 result.errors.len()
1167 );
1168
1169 std::fs::remove_file(&file_path).ok();
1170 }
1171
1172 #[test]
1173 fn test_validate_array_schema() {
1174 let schema = serde_json::json!({
1175 "$schema": "http://json-schema.org/draft-07/schema#",
1176 "type": "object",
1177 "properties": {
1178 "items": {
1179 "type": "array",
1180 "items": { "type": "string" },
1181 "minItems": 1
1182 }
1183 },
1184 "required": ["items"]
1185 });
1186
1187 let temp_dir = std::env::temp_dir();
1188 let file_path = temp_dir.join("test_array.yaml");
1189 let mut file = std::fs::File::create(&file_path).unwrap();
1190 writeln!(file, "items:\n - item1\n - item2").unwrap();
1191
1192 let result = validate_config_file(&file_path, "test-config", &schema).unwrap();
1193
1194 assert!(result.valid);
1195 assert!(result.errors.is_empty());
1196
1197 std::fs::remove_file(&file_path).ok();
1198 }
1199
1200 #[test]
1201 fn test_validate_array_schema_empty_array() {
1202 let schema = serde_json::json!({
1203 "$schema": "http://json-schema.org/draft-07/schema#",
1204 "type": "object",
1205 "properties": {
1206 "items": {
1207 "type": "array",
1208 "items": { "type": "string" },
1209 "minItems": 1
1210 }
1211 }
1212 });
1213
1214 let temp_dir = std::env::temp_dir();
1215 let file_path = temp_dir.join("test_empty_array.yaml");
1216 let mut file = std::fs::File::create(&file_path).unwrap();
1217 writeln!(file, "items: []").unwrap();
1218
1219 let result = validate_config_file(&file_path, "test-config", &schema).unwrap();
1220
1221 assert!(!result.valid);
1222 assert!(!result.errors.is_empty());
1223
1224 std::fs::remove_file(&file_path).ok();
1225 }
1226
1227 #[test]
1228 fn test_validate_enum_schema_valid() {
1229 let schema = serde_json::json!({
1230 "$schema": "http://json-schema.org/draft-07/schema#",
1231 "type": "object",
1232 "properties": {
1233 "level": {
1234 "type": "string",
1235 "enum": ["low", "medium", "high"]
1236 }
1237 }
1238 });
1239
1240 let temp_dir = std::env::temp_dir();
1241 let file_path = temp_dir.join("test_enum_valid.yaml");
1242 let mut file = std::fs::File::create(&file_path).unwrap();
1243 writeln!(file, "level: medium").unwrap();
1244
1245 let result = validate_config_file(&file_path, "test-config", &schema).unwrap();
1246
1247 assert!(result.valid);
1248
1249 std::fs::remove_file(&file_path).ok();
1250 }
1251
1252 #[test]
1253 fn test_validate_enum_schema_invalid() {
1254 let schema = serde_json::json!({
1255 "$schema": "http://json-schema.org/draft-07/schema#",
1256 "type": "object",
1257 "properties": {
1258 "level": {
1259 "type": "string",
1260 "enum": ["low", "medium", "high"]
1261 }
1262 }
1263 });
1264
1265 let temp_dir = std::env::temp_dir();
1266 let file_path = temp_dir.join("test_enum_invalid.yaml");
1267 let mut file = std::fs::File::create(&file_path).unwrap();
1268 writeln!(file, "level: invalid").unwrap();
1269
1270 let result = validate_config_file(&file_path, "test-config", &schema).unwrap();
1271
1272 assert!(!result.valid);
1273 assert!(!result.errors.is_empty());
1274
1275 std::fs::remove_file(&file_path).ok();
1276 }
1277
1278 #[test]
1279 fn test_validate_pattern_string_valid() {
1280 let schema = serde_json::json!({
1281 "$schema": "http://json-schema.org/draft-07/schema#",
1282 "type": "object",
1283 "properties": {
1284 "email": {
1285 "type": "string",
1286 "pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
1287 }
1288 }
1289 });
1290
1291 let temp_dir = std::env::temp_dir();
1292 let file_path = temp_dir.join("test_pattern_valid.yaml");
1293 let mut file = std::fs::File::create(&file_path).unwrap();
1294 writeln!(file, "email: test@example.com").unwrap();
1295
1296 let result = validate_config_file(&file_path, "test-config", &schema).unwrap();
1297
1298 assert!(result.valid);
1299
1300 std::fs::remove_file(&file_path).ok();
1301 }
1302
1303 #[test]
1304 fn test_validate_pattern_string_invalid() {
1305 let schema = serde_json::json!({
1306 "$schema": "http://json-schema.org/draft-07/schema#",
1307 "type": "object",
1308 "properties": {
1309 "email": {
1310 "type": "string",
1311 "pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
1312 }
1313 }
1314 });
1315
1316 let temp_dir = std::env::temp_dir();
1317 let file_path = temp_dir.join("test_pattern_invalid.yaml");
1318 let mut file = std::fs::File::create(&file_path).unwrap();
1319 writeln!(file, "email: not-an-email").unwrap();
1320
1321 let result = validate_config_file(&file_path, "test-config", &schema).unwrap();
1322
1323 assert!(!result.valid);
1324
1325 std::fs::remove_file(&file_path).ok();
1326 }
1327
1328 #[test]
1329 fn test_validate_number_constraints() {
1330 let schema = serde_json::json!({
1331 "$schema": "http://json-schema.org/draft-07/schema#",
1332 "type": "object",
1333 "properties": {
1334 "percentage": {
1335 "type": "number",
1336 "minimum": 0,
1337 "maximum": 100
1338 }
1339 }
1340 });
1341
1342 let temp_dir = std::env::temp_dir();
1343
1344 let file_path = temp_dir.join("test_number_valid.yaml");
1346 let mut file = std::fs::File::create(&file_path).unwrap();
1347 writeln!(file, "percentage: 50.5").unwrap();
1348 let result = validate_config_file(&file_path, "test-config", &schema).unwrap();
1349 assert!(result.valid);
1350 std::fs::remove_file(&file_path).ok();
1351
1352 let file_path = temp_dir.join("test_number_invalid.yaml");
1354 let mut file = std::fs::File::create(&file_path).unwrap();
1355 writeln!(file, "percentage: 150").unwrap();
1356 let result = validate_config_file(&file_path, "test-config", &schema).unwrap();
1357 assert!(!result.valid);
1358 std::fs::remove_file(&file_path).ok();
1359 }
1360
1361 #[test]
1364 fn test_detect_schema_type_no_extension() {
1365 let path = PathBuf::from("/config/mockforge");
1366 assert_eq!(detect_schema_type(&path), Some("mockforge-config".to_string()));
1368 }
1369
1370 #[test]
1371 fn test_detect_schema_type_nested_persona_path() {
1372 let path = PathBuf::from("/very/deep/path/with/persona/in/middle/config.yaml");
1373 assert_eq!(detect_schema_type(&path), Some("persona-config".to_string()));
1374 }
1375
1376 #[test]
1377 fn test_detect_schema_type_nested_reality_path() {
1378 let path = PathBuf::from("/config/reality-slider/settings.yml");
1379 assert_eq!(detect_schema_type(&path), Some("reality-config".to_string()));
1380 }
1381
1382 #[test]
1383 fn test_detect_schema_type_mixed_case_path() {
1384 let path = PathBuf::from("/Config/REALITY/Settings.YML");
1385 assert_eq!(detect_schema_type(&path), Some("reality-config".to_string()));
1386 }
1387
1388 #[test]
1391 fn test_generate_config_schema_json_pretty_printed() {
1392 let json = generate_config_schema_json();
1393
1394 assert!(json.contains('\n'));
1396
1397 assert!(json.contains(" ") || json.contains(" "));
1399 }
1400
1401 #[test]
1402 fn test_generate_config_schema_json_has_schema_url() {
1403 let json = generate_config_schema_json();
1404 assert!(json.contains("http://json-schema.org/draft-07/schema#"));
1405 }
1406
1407 #[test]
1410 fn test_validate_with_generated_config_schema() {
1411 let schema = generate_config_schema();
1412
1413 let temp_dir = std::env::temp_dir();
1415 let file_path = temp_dir.join("test_real_config.yaml");
1416 let mut file = std::fs::File::create(&file_path).unwrap();
1417 writeln!(file, "port: 8080").unwrap();
1419
1420 let result = validate_config_file(&file_path, "mockforge-config", &schema);
1423 assert!(result.is_ok() || result.is_err()); std::fs::remove_file(&file_path).ok();
1426 }
1427
1428 #[test]
1429 fn test_validate_complex_json_structure() {
1430 let schema = serde_json::json!({
1431 "$schema": "http://json-schema.org/draft-07/schema#",
1432 "type": "object",
1433 "properties": {
1434 "server": {
1435 "type": "object",
1436 "properties": {
1437 "host": { "type": "string" },
1438 "port": { "type": "integer" },
1439 "ssl": {
1440 "type": "object",
1441 "properties": {
1442 "enabled": { "type": "boolean" },
1443 "cert_path": { "type": "string" }
1444 }
1445 }
1446 }
1447 }
1448 }
1449 });
1450
1451 let temp_dir = std::env::temp_dir();
1452 let file_path = temp_dir.join("test_complex.json");
1453 let content = r#"{
1454 "server": {
1455 "host": "localhost",
1456 "port": 8080,
1457 "ssl": {
1458 "enabled": true,
1459 "cert_path": "/path/to/cert"
1460 }
1461 }
1462 }"#;
1463 std::fs::write(&file_path, content).unwrap();
1464
1465 let result = validate_config_file(&file_path, "test-config", &schema).unwrap();
1466 assert!(result.valid);
1467
1468 std::fs::remove_file(&file_path).ok();
1469 }
1470
1471 #[test]
1474 fn test_validate_uppercase_yaml_extension() {
1475 let schema = serde_json::json!({
1476 "type": "object"
1477 });
1478
1479 let temp_dir = std::env::temp_dir();
1480 let file_path = temp_dir.join("test_config.YAML");
1481 let mut file = std::fs::File::create(&file_path).unwrap();
1482 writeln!(file, "key: value").unwrap();
1483
1484 let result = validate_config_file(&file_path, "test-config", &schema).unwrap();
1485 assert!(result.valid);
1486
1487 std::fs::remove_file(&file_path).ok();
1488 }
1489
1490 #[test]
1491 fn test_validate_uppercase_json_extension() {
1492 let schema = serde_json::json!({
1493 "type": "object"
1494 });
1495
1496 let temp_dir = std::env::temp_dir();
1497 let file_path = temp_dir.join("test_config.JSON");
1498 let content = r#"{"key": "value"}"#;
1499 std::fs::write(&file_path, content).unwrap();
1500
1501 let result = validate_config_file(&file_path, "test-config", &schema).unwrap();
1502 assert!(result.valid);
1503
1504 std::fs::remove_file(&file_path).ok();
1505 }
1506
1507 #[test]
1508 fn test_validate_no_extension_treats_as_json() {
1509 let schema = serde_json::json!({
1510 "type": "object"
1511 });
1512
1513 let temp_dir = std::env::temp_dir();
1514 let file_path = temp_dir.join("test_config_no_ext");
1515 let content = r#"{"key": "value"}"#;
1516 std::fs::write(&file_path, content).unwrap();
1517
1518 let result = validate_config_file(&file_path, "test-config", &schema).unwrap();
1519 assert!(result.valid);
1520
1521 std::fs::remove_file(&file_path).ok();
1522 }
1523
1524 #[test]
1527 fn test_validation_result_with_long_error_messages() {
1528 let errors = vec![
1529 "Error at /path/to/nested/property: expected integer but got string 'invalid'"
1530 .to_string(),
1531 "Error at /another/path: value must be between 1 and 100 but got 150".to_string(),
1532 "Error at /required/field: this field is required but was not provided".to_string(),
1533 ];
1534
1535 let result = ValidationResult::failure(
1536 "/path/to/config.yaml".to_string(),
1537 "test-config".to_string(),
1538 errors.clone(),
1539 );
1540
1541 assert!(!result.valid);
1542 assert_eq!(result.errors.len(), 3);
1543 assert_eq!(result.errors, errors);
1544 }
1545
1546 #[test]
1547 fn test_validation_result_with_special_characters_in_path() {
1548 let result = ValidationResult::success(
1549 "/path/with spaces/and-special_chars/config.yaml".to_string(),
1550 "mockforge-config".to_string(),
1551 );
1552
1553 assert!(result.valid);
1554 assert_eq!(result.file_path, "/path/with spaces/and-special_chars/config.yaml");
1555 }
1556
1557 #[test]
1560 fn test_all_schemas_have_schema_key() {
1561 let schemas = generate_all_schemas();
1562
1563 for (name, schema) in schemas {
1564 let obj = schema.as_object().unwrap();
1565 assert!(obj.contains_key("$schema"), "Schema {} should have $schema key", name);
1566 }
1567 }
1568
1569 #[test]
1570 fn test_all_schemas_have_title() {
1571 let schemas = generate_all_schemas();
1572
1573 for (name, schema) in schemas {
1574 let obj = schema.as_object().unwrap();
1575 assert!(obj.contains_key("title"), "Schema {} should have title", name);
1576 }
1577 }
1578
1579 #[test]
1580 fn test_all_schemas_have_description() {
1581 let schemas = generate_all_schemas();
1582
1583 for (name, schema) in schemas {
1584 let obj = schema.as_object().unwrap();
1585 assert!(obj.contains_key("description"), "Schema {} should have description", name);
1586 }
1587 }
1588
1589 #[test]
1590 fn test_schema_titles_are_unique() {
1591 let schemas = generate_all_schemas();
1592 let mut titles = std::collections::HashSet::new();
1593
1594 for (_name, schema) in schemas {
1595 let obj = schema.as_object().unwrap();
1596 let title = obj.get("title").unwrap().as_str().unwrap();
1597 assert!(titles.insert(title.to_string()), "Duplicate title found: {}", title);
1598 }
1599 }
1600
1601 #[test]
1604 fn test_end_to_end_config_validation_workflow() {
1605 let schemas = generate_all_schemas();
1607 let config_schema = schemas.get("mockforge-config").unwrap();
1608
1609 let temp_dir = std::env::temp_dir();
1611 let file_path = temp_dir.join("mockforge.yaml");
1612 let mut file = std::fs::File::create(&file_path).unwrap();
1613 writeln!(file, "port: 8080").unwrap();
1614
1615 let detected_type = detect_schema_type(&file_path);
1617 assert_eq!(detected_type, Some("mockforge-config".to_string()));
1618
1619 let result = validate_config_file(&file_path, "mockforge-config", config_schema);
1621 assert!(result.is_ok());
1622
1623 std::fs::remove_file(&file_path).ok();
1624 }
1625
1626 #[test]
1627 fn test_blueprint_validation_workflow() {
1628 let schemas = generate_all_schemas();
1629 let blueprint_schema = schemas.get("blueprint-config").unwrap();
1630
1631 let temp_dir = std::env::temp_dir();
1632 let file_path = temp_dir.join("blueprint.yaml");
1633 let mut file = std::fs::File::create(&file_path).unwrap();
1634 writeln!(file, "manifest_version: '1.0'").unwrap();
1635 writeln!(file, "name: test-blueprint").unwrap();
1636 writeln!(file, "version: 1.0.0").unwrap();
1637 writeln!(file, "title: Test Blueprint").unwrap();
1638 writeln!(file, "description: A test blueprint").unwrap();
1639 writeln!(file, "author: Test Author").unwrap();
1640 writeln!(file, "category: saas").unwrap();
1641
1642 let detected_type = detect_schema_type(&file_path);
1643 assert_eq!(detected_type, Some("blueprint-config".to_string()));
1644
1645 let result =
1646 validate_config_file(&file_path, "blueprint-config", blueprint_schema).unwrap();
1647 assert!(result.valid, "Blueprint validation should succeed");
1648
1649 std::fs::remove_file(&file_path).ok();
1650 }
1651
1652 #[test]
1653 fn test_validate_yaml_with_comments() {
1654 let schema = serde_json::json!({
1655 "type": "object",
1656 "properties": {
1657 "name": { "type": "string" }
1658 }
1659 });
1660
1661 let temp_dir = std::env::temp_dir();
1662 let file_path = temp_dir.join("test_with_comments.yaml");
1663 let mut file = std::fs::File::create(&file_path).unwrap();
1664 writeln!(file, "# This is a comment").unwrap();
1665 writeln!(file, "name: test-service # inline comment").unwrap();
1666
1667 let result = validate_config_file(&file_path, "test-config", &schema).unwrap();
1668 assert!(result.valid);
1669
1670 std::fs::remove_file(&file_path).ok();
1671 }
1672
1673 #[test]
1674 fn test_validate_yaml_with_anchors_and_aliases() {
1675 let schema = serde_json::json!({
1676 "type": "object",
1677 "properties": {
1678 "config1": { "type": "object" },
1679 "config2": { "type": "object" }
1680 }
1681 });
1682
1683 let temp_dir = std::env::temp_dir();
1684 let file_path = temp_dir.join("test_anchors.yaml");
1685 let mut file = std::fs::File::create(&file_path).unwrap();
1686 writeln!(file, "config1: &defaults").unwrap();
1687 writeln!(file, " key: value").unwrap();
1688 writeln!(file, "config2: *defaults").unwrap();
1689
1690 let result = validate_config_file(&file_path, "test-config", &schema).unwrap();
1691 assert!(result.valid);
1692
1693 std::fs::remove_file(&file_path).ok();
1694 }
1695}