1use crate::core::types::{TrackingError, TrackingResult};
7use serde::{Deserialize, Serialize};
8use serde_json::{Map, Value};
9use std::collections::HashMap;
10use std::time::{SystemTime, UNIX_EPOCH};
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct ValidationResult {
15 pub is_valid: bool,
17 pub errors: Vec<ValidationError>,
19 pub warnings: Vec<ValidationWarning>,
21 pub validation_metrics: ValidationMetrics,
23 pub integrity_hash: String,
25 pub validation_timestamp: u128,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct ValidationError {
32 pub code: String,
34 pub message: String,
36 pub path: String,
38 pub severity: ErrorSeverity,
40 pub suggested_fix: Option<String>,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct ValidationWarning {
47 pub warning_code: String,
49 pub message: String,
51 pub path: String,
53 pub suggestion: Option<String>,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
59pub enum ErrorSeverity {
60 Critical,
62 Warning,
64 Info,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct SchemaVersion {
71 pub version: String,
73 pub components: (u32, u32, u32),
75 pub is_supported: bool,
77 pub compatibility: CompatibilityLevel,
79 pub backward_compatible_with: Vec<String>,
81 pub forward_compatible_with: Vec<String>,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
87pub enum CompatibilityLevel {
88 FullyCompatible,
90 BackwardCompatible,
92 ForwardCompatible,
94 Incompatible,
96}
97
98#[derive(Debug, Clone)]
100pub struct SchemaValidatorConfig {
101 pub strict_mode: bool,
103 pub enable_integrity_check: bool,
105 pub enable_backward_compatibility: bool,
107 pub max_schema_version: String,
109 pub custom_rules: Vec<CustomValidationRule>,
111}
112
113#[derive(Debug, Clone)]
115pub struct CustomValidationRule {
116 pub name: String,
118 pub path_pattern: String,
120 pub validator: fn(&Value) -> Result<(), String>,
122}
123
124impl Default for SchemaValidatorConfig {
125 fn default() -> Self {
126 Self {
127 strict_mode: false,
128 enable_integrity_check: true,
129 enable_backward_compatibility: true,
130 max_schema_version: "2.0".to_string(),
131 custom_rules: Vec::new(),
132 }
133 }
134}
135
136pub struct SchemaValidator {
138 config: SchemaValidatorConfig,
140 supported_versions: HashMap<String, SchemaVersion>,
142 schema_definitions: HashMap<String, Value>,
144}
145
146impl SchemaValidator {
147 pub fn new() -> Self {
149 Self::with_config(SchemaValidatorConfig::default())
150 }
151
152 pub fn with_config(config: SchemaValidatorConfig) -> Self {
154 let mut validator = Self {
155 config,
156 supported_versions: HashMap::new(),
157 schema_definitions: HashMap::new(),
158 };
159
160 validator.initialize_schemas();
161 validator
162 }
163
164 pub fn validate_unsafe_ffi_analysis(&self, data: &Value) -> TrackingResult<ValidationResult> {
166 let mut errors = Vec::new();
167 let mut warnings = Vec::new();
168 let mut validation_metrics = ValidationMetrics::default();
169
170 let metadata = self.extract_metadata(data, &mut errors)?;
172
173 let schema_version = metadata
174 .get("schema_version")
175 .and_then(|v| v.as_str())
176 .unwrap_or("1.0");
177
178 self.validate_schema_version(schema_version, &mut errors, &mut warnings)?;
180
181 self.validate_main_structure(data, &mut errors, &mut warnings, &mut validation_metrics)?;
183
184 if let Some(unsafe_analysis) = data.get("unsafe_analysis") {
186 self.validate_unsafe_analysis(unsafe_analysis, &mut errors, &mut warnings)?;
187 }
188
189 if let Some(ffi_analysis) = data.get("ffi_analysis") {
191 self.validate_ffi_analysis(ffi_analysis, &mut errors, &mut warnings)?;
192 }
193
194 if let Some(boundary_analysis) = data.get("boundary_analysis") {
196 self.validate_boundary_analysis(boundary_analysis, &mut errors, &mut warnings)?;
197 }
198
199 if let Some(safety_violations) = data.get("safety_violations") {
201 self.validate_safety_violations(safety_violations, &mut errors, &mut warnings)?;
202 }
203
204 self.apply_custom_rules(data, &mut errors, &mut warnings)?;
206
207 let integrity_hash = if self.config.enable_integrity_check {
209 self.calculate_integrity_hash(data)?
210 } else {
211 "disabled".to_string()
212 };
213
214 let is_valid = errors.is_empty() && (!self.config.strict_mode || warnings.is_empty());
216
217 Ok(ValidationResult {
218 is_valid,
219 errors,
220 warnings,
221 validation_metrics,
222 integrity_hash,
223 validation_timestamp: SystemTime::now()
224 .duration_since(UNIX_EPOCH)
225 .unwrap_or_default()
226 .as_nanos(),
227 })
228 }
229
230 fn extract_metadata<'a>(
232 &self,
233 data: &'a Value,
234 errors: &mut Vec<ValidationError>,
235 ) -> TrackingResult<&'a Map<String, Value>> {
236 match data.get("metadata") {
237 Some(Value::Object(metadata)) => Ok(metadata),
238 Some(_) => {
239 errors.push(ValidationError {
240 code: "INVALID_METADATA_TYPE".to_string(),
241 message: "Metadata must be an object".to_string(),
242 path: "metadata".to_string(),
243 severity: ErrorSeverity::Critical,
244 suggested_fix: Some("Ensure metadata is a JSON object".to_string()),
245 });
246 Err(TrackingError::ValidationError(
247 "Invalid metadata type".to_string(),
248 ))
249 }
250 None => {
251 errors.push(ValidationError {
252 code: "MISSING_METADATA".to_string(),
253 message: "Required metadata section is missing".to_string(),
254 path: "metadata".to_string(),
255 severity: ErrorSeverity::Critical,
256 suggested_fix: Some("Add metadata section with required fields".to_string()),
257 });
258 Err(TrackingError::ValidationError(
259 "Missing metadata".to_string(),
260 ))
261 }
262 }
263 }
264
265 fn validate_schema_version(
267 &self,
268 version: &str,
269 errors: &mut Vec<ValidationError>,
270 warnings: &mut Vec<ValidationWarning>,
271 ) -> TrackingResult<()> {
272 if let Some(schema_version) = self.supported_versions.get(version) {
273 if !schema_version.is_supported {
274 errors.push(ValidationError {
275 code: "UNSUPPORTED_SCHEMA_VERSION".to_string(),
276 message: format!("Schema version {version} is not supported"),
277 path: "metadata.schema_version".to_string(),
278 severity: ErrorSeverity::Critical,
279 suggested_fix: Some("Use a supported schema version".to_string()),
280 });
281 }
282 } else {
283 errors.push(ValidationError {
284 code: "UNKNOWN_SCHEMA_VERSION".to_string(),
285 message: format!("Unknown schema version: {version}"),
286 path: "metadata.schema_version".to_string(),
287 severity: ErrorSeverity::Critical,
288 suggested_fix: Some("Use a supported schema version".to_string()),
289 });
290 }
291
292 if self.config.enable_backward_compatibility {
294 self.check_backward_compatibility(version, warnings)?;
295 }
296
297 Ok(())
298 }
299
300 fn calculate_integrity_hash(&self, data: &Value) -> TrackingResult<String> {
302 let canonical_json = serde_json::to_string(data)
304 .map_err(|e| TrackingError::SerializationError(e.to_string()))?;
305
306 let hash = self.simple_hash(&canonical_json);
307 Ok(format!("{hash:x}"))
308 }
309
310 pub fn verify_integrity(&self, data: &Value, expected_hash: &str) -> TrackingResult<bool> {
312 let calculated_hash = self.calculate_integrity_hash(data)?;
313 Ok(calculated_hash == expected_hash)
314 }
315
316 pub fn get_supported_versions(&self) -> &HashMap<String, SchemaVersion> {
318 &self.supported_versions
319 }
320
321 pub fn add_custom_rule(&mut self, rule: CustomValidationRule) {
323 self.config.custom_rules.push(rule);
324 }
325}
326
327impl SchemaValidator {
329 fn initialize_schemas(&mut self) {
331 self.supported_versions.insert(
333 "1.0".to_string(),
334 SchemaVersion {
335 version: "1.0".to_string(),
336 components: (1, 0, 0),
337 is_supported: true,
338 compatibility: CompatibilityLevel::BackwardCompatible,
339 backward_compatible_with: vec![],
340 forward_compatible_with: vec!["2.0".to_string()],
341 },
342 );
343
344 self.supported_versions.insert(
345 "2.0".to_string(),
346 SchemaVersion {
347 version: "2.0".to_string(),
348 components: (2, 0, 0),
349 is_supported: true,
350 compatibility: CompatibilityLevel::FullyCompatible,
351 backward_compatible_with: vec!["1.0".to_string()],
352 forward_compatible_with: vec![],
353 },
354 );
355
356 self.initialize_v2_schema();
358 }
359
360 fn initialize_v2_schema(&mut self) {
362 let schema = serde_json::json!({
363 "$schema": "http://json-schema.org/draft-07/schema#",
364 "title": "Unsafe/FFI Memory Analysis Schema v2.0",
365 "type": "object",
366 "required": ["metadata", "unsafe_analysis", "ffi_analysis"],
367 "properties": {
368 "metadata": {
369 "type": "object",
370 "required": ["analysis_type", "schema_version", "export_timestamp"],
371 "properties": {
372 "analysis_type": {"const": "unsafe_ffi_analysis_optimized"},
373 "schema_version": {"const": "2.0"},
374 "export_timestamp": {"type": "integer", "minimum": 0},
375 "optimization_level": {"enum": ["low", "medium", "high"]},
376 "processing_mode": {"enum": ["sequential", "parallel", "streaming"]},
377 "data_integrity_hash": {"type": "string"}
378 }
379 },
380 "unsafe_analysis": {
381 "type": "object",
382 "required": ["summary", "allocations"],
383 "properties": {
384 "summary": {"$ref": "#/definitions/RiskDistribution"},
385 "allocations": {"type": "array"},
386 "performance_metrics": {"type": "object"}
387 }
388 },
389 "ffi_analysis": {
390 "type": "object",
391 "required": ["summary", "allocations"],
392 "properties": {
393 "summary": {"$ref": "#/definitions/RiskDistribution"},
394 "allocations": {"type": "array"},
395 "performance_metrics": {"type": "object"}
396 }
397 },
398 "boundary_analysis": {
399 "type": "object",
400 "properties": {
401 "cross_boundary_transfers": {"type": "integer", "minimum": 0},
402 "events": {"type": "array"},
403 "performance_impact": {"type": "object"}
404 }
405 },
406 "safety_violations": {
407 "type": "object",
408 "properties": {
409 "severity_breakdown": {"type": "object"},
410 "violations": {"type": "array"}
411 }
412 }
413 },
414 "definitions": {
415 "RiskDistribution": {
416 "type": "object",
417 "properties": {
418 "low_risk": {"type": "integer", "minimum": 0},
419 "medium_risk": {"type": "integer", "minimum": 0},
420 "high_risk": {"type": "integer", "minimum": 0},
421 "critical_risk": {"type": "integer", "minimum": 0},
422 "overall_risk_score": {"type": "number", "minimum": 0.0, "maximum": 10.0}
423 }
424 }
425 }
426 });
427
428 self.schema_definitions.insert("2.0".to_string(), schema);
429 }
430
431 fn validate_main_structure(
433 &self,
434 data: &Value,
435 errors: &mut Vec<ValidationError>,
436 warnings: &mut Vec<ValidationWarning>,
437 metrics: &mut ValidationMetrics,
438 ) -> TrackingResult<()> {
439 if !data.is_object() {
441 errors.push(ValidationError {
442 code: "INVALID_ROOT_TYPE".to_string(),
443 message: "Root document must be a JSON object".to_string(),
444 path: "$".to_string(),
445 severity: ErrorSeverity::Critical,
446 suggested_fix: Some("Ensure root document is a JSON object".to_string()),
447 });
448 return Ok(());
449 }
450
451 metrics.sections_validated += 1;
452
453 let known_sections = [
455 "metadata",
456 "unsafe_analysis",
457 "ffi_analysis",
458 "boundary_analysis",
459 "safety_violations",
460 ];
461
462 if let Some(obj) = data.as_object() {
463 for key in obj.keys() {
464 if !known_sections.contains(&key.as_str()) {
465 warnings.push(ValidationWarning {
466 warning_code: "UNKNOWN_SECTION".to_string(),
467 message: format!("Unknown section: {key}"),
468 path: key.clone(),
469 suggestion: Some("Remove unknown sections or update schema".to_string()),
470 });
471 }
472 }
473 }
474
475 Ok(())
476 }
477
478 fn validate_unsafe_analysis(
480 &self,
481 unsafe_analysis: &Value,
482 errors: &mut Vec<ValidationError>,
483 _warnings: &mut [ValidationWarning],
484 ) -> TrackingResult<()> {
485 if !unsafe_analysis.is_object() {
486 errors.push(ValidationError {
487 code: "INVALID_UNSAFE_ANALYSIS_TYPE".to_string(),
488 message: "unsafe_analysis must be an object".to_string(),
489 path: "unsafe_analysis".to_string(),
490 severity: ErrorSeverity::Critical,
491 suggested_fix: Some("Ensure unsafe_analysis is a JSON object".to_string()),
492 });
493 return Ok(());
494 }
495
496 let required_fields = vec!["summary", "allocations"];
498 for field in required_fields {
499 if unsafe_analysis.get(field).is_none() {
500 errors.push(ValidationError {
501 code: "MISSING_REQUIRED_FIELD".to_string(),
502 message: format!("Required field '{field}' is missing in unsafe_analysis"),
503 path: format!("unsafe_analysis.{field}"),
504 severity: ErrorSeverity::Critical,
505 suggested_fix: Some(format!("Add required field '{field}'")),
506 });
507 }
508 }
509
510 Ok(())
511 }
512
513 fn validate_ffi_analysis(
515 &self,
516 ffi_analysis: &Value,
517 errors: &mut Vec<ValidationError>,
518 _warnings: &mut [ValidationWarning],
519 ) -> TrackingResult<()> {
520 if !ffi_analysis.is_object() {
521 errors.push(ValidationError {
522 code: "INVALID_FFI_ANALYSIS_TYPE".to_string(),
523 message: "ffi_analysis must be an object".to_string(),
524 path: "ffi_analysis".to_string(),
525 severity: ErrorSeverity::Critical,
526 suggested_fix: Some("Ensure ffi_analysis is a JSON object".to_string()),
527 });
528 return Ok(());
529 }
530
531 let required_fields = vec!["summary", "allocations"];
533 for field in required_fields {
534 if ffi_analysis.get(field).is_none() {
535 errors.push(ValidationError {
536 code: "MISSING_REQUIRED_FIELD".to_string(),
537 message: format!("Required field '{field}' is missing in ffi_analysis"),
538 path: format!("ffi_analysis.{field}"),
539 severity: ErrorSeverity::Critical,
540 suggested_fix: Some(format!("Add required field '{field}'")),
541 });
542 }
543 }
544
545 Ok(())
546 }
547
548 fn validate_boundary_analysis(
550 &self,
551 boundary_analysis: &Value,
552 errors: &mut Vec<ValidationError>,
553 _warnings: &mut [ValidationWarning],
554 ) -> TrackingResult<()> {
555 if !boundary_analysis.is_object() {
556 errors.push(ValidationError {
557 code: "INVALID_BOUNDARY_ANALYSIS_TYPE".to_string(),
558 message: "boundary_analysis must be an object".to_string(),
559 path: "boundary_analysis".to_string(),
560 severity: ErrorSeverity::Critical,
561 suggested_fix: Some("Ensure boundary_analysis is a JSON object".to_string()),
562 });
563 return Ok(());
564 }
565
566 if let Some(transfers) = boundary_analysis.get("cross_boundary_transfers") {
568 if !transfers.is_number() {
569 errors.push(ValidationError {
570 code: "INVALID_FIELD_TYPE".to_string(),
571 message: "cross_boundary_transfers must be a number".to_string(),
572 path: "boundary_analysis.cross_boundary_transfers".to_string(),
573 severity: ErrorSeverity::Warning,
574 suggested_fix: Some("Ensure cross_boundary_transfers is a number".to_string()),
575 });
576 }
577 }
578
579 Ok(())
580 }
581
582 fn validate_safety_violations(
584 &self,
585 safety_violations: &Value,
586 errors: &mut Vec<ValidationError>,
587 _warnings: &mut [ValidationWarning],
588 ) -> TrackingResult<()> {
589 if !safety_violations.is_object() {
590 errors.push(ValidationError {
591 code: "INVALID_SAFETY_VIOLATIONS_TYPE".to_string(),
592 message: "safety_violations must be an object".to_string(),
593 path: "safety_violations".to_string(),
594 severity: ErrorSeverity::Critical,
595 suggested_fix: Some("Ensure safety_violations is a JSON object".to_string()),
596 });
597 return Ok(());
598 }
599
600 if let Some(violations) = safety_violations.get("violations") {
602 if !violations.is_array() {
603 errors.push(ValidationError {
604 code: "INVALID_FIELD_TYPE".to_string(),
605 message: "violations must be an array".to_string(),
606 path: "safety_violations.violations".to_string(),
607 severity: ErrorSeverity::Critical,
608 suggested_fix: Some("Ensure violations is an array".to_string()),
609 });
610 }
611 }
612
613 Ok(())
614 }
615
616 fn apply_custom_rules(
618 &self,
619 data: &Value,
620 errors: &mut Vec<ValidationError>,
621 _warnings: &mut [ValidationWarning],
622 ) -> TrackingResult<()> {
623 for rule in &self.config.custom_rules {
624 if let Some(target_value) = self.get_value_by_path(data, &rule.path_pattern) {
626 if let Err(error_msg) = (rule.validator)(target_value) {
627 errors.push(ValidationError {
628 code: "CUSTOM_RULE_VIOLATION".to_string(),
629 message: format!("Custom rule '{}' failed: {}", rule.name, error_msg),
630 path: rule.path_pattern.clone(),
631 severity: ErrorSeverity::Warning,
632 suggested_fix: None,
633 });
634 }
635 }
636 }
637 Ok(())
638 }
639
640 fn check_backward_compatibility(
642 &self,
643 version: &str,
644 warnings: &mut Vec<ValidationWarning>,
645 ) -> TrackingResult<()> {
646 if let Some(schema_version) = self.supported_versions.get(version) {
647 if schema_version.backward_compatible_with.is_empty() {
648 warnings.push(ValidationWarning {
649 warning_code: "NO_BACKWARD_COMPATIBILITY".to_string(),
650 message: format!("Schema version {version} has no backward compatibility"),
651 path: "metadata.schema_version".to_string(),
652 suggestion: Some("Consider using a more recent schema version".to_string()),
653 });
654 }
655 }
656 Ok(())
657 }
658
659 fn get_value_by_path<'a>(&self, data: &'a Value, path: &str) -> Option<&'a Value> {
661 let parts: Vec<&str> = path.split('.').collect();
662 let mut current = data;
663
664 for part in parts {
665 current = current.get(part)?;
666 }
667
668 Some(current)
669 }
670
671 fn simple_hash(&self, data: &str) -> u64 {
673 let mut hash = 0u64;
674 for byte in data.bytes() {
675 hash = hash.wrapping_mul(31).wrapping_add(byte as u64);
676 }
677 hash
678 }
679}
680
681impl Default for SchemaValidator {
682 fn default() -> Self {
683 Self::new()
684 }
685}
686
687#[derive(Debug, Clone, Default, Serialize, Deserialize)]
689pub struct ValidationMetrics {
690 pub sections_validated: u32,
692 pub fields_validated: u32,
694 pub validation_duration_ns: u128,
696}
697
698impl SchemaValidator {
700 pub fn strict() -> Self {
702 Self::with_config(SchemaValidatorConfig {
703 strict_mode: true,
704 ..Default::default()
705 })
706 }
707
708 pub fn without_integrity_check() -> Self {
710 Self::with_config(SchemaValidatorConfig {
711 enable_integrity_check: false,
712 ..Default::default()
713 })
714 }
715}
716
717pub struct SchemaValidatorConfigBuilder {
719 config: SchemaValidatorConfig,
720}
721
722impl SchemaValidatorConfigBuilder {
723 pub fn new() -> Self {
725 Self {
726 config: SchemaValidatorConfig::default(),
727 }
728 }
729
730 pub fn strict_mode(mut self, enabled: bool) -> Self {
732 self.config.strict_mode = enabled;
733 self
734 }
735
736 pub fn integrity_check(mut self, enabled: bool) -> Self {
738 self.config.enable_integrity_check = enabled;
739 self
740 }
741
742 pub fn backward_compatibility(mut self, enabled: bool) -> Self {
744 self.config.enable_backward_compatibility = enabled;
745 self
746 }
747
748 pub fn max_schema_version(mut self, version: String) -> Self {
750 self.config.max_schema_version = version;
751 self
752 }
753
754 pub fn add_custom_rule(mut self, rule: CustomValidationRule) -> Self {
756 self.config.custom_rules.push(rule);
757 self
758 }
759
760 pub fn build(self) -> SchemaValidatorConfig {
762 self.config
763 }
764}
765
766impl Default for SchemaValidatorConfigBuilder {
767 fn default() -> Self {
768 Self::new()
769 }
770}
771
772#[cfg(test)]
773mod tests {
774 use super::*;
775 use serde_json::json;
776
777 #[test]
778 fn test_validator_creation() {
779 let validator = SchemaValidator::new();
780 assert!(!validator.supported_versions.is_empty());
781 }
782
783 #[test]
784 fn test_valid_minimal_document() {
785 let validator = SchemaValidator::new();
786 let data = json!({
787 "metadata": {
788 "analysis_type": "unsafe_ffi_analysis_optimized",
789 "schema_version": "2.0",
790 "export_timestamp": 1234567890
791 },
792 "unsafe_analysis": {
793 "summary": {"low_risk": 0, "medium_risk": 0, "high_risk": 0, "critical_risk": 0},
794 "allocations": []
795 },
796 "ffi_analysis": {
797 "summary": {"low_risk": 0, "medium_risk": 0, "high_risk": 0, "critical_risk": 0},
798 "allocations": []
799 }
800 });
801
802 let result = validator
803 .validate_unsafe_ffi_analysis(&data)
804 .expect("Failed to validate FFI analysis");
805 assert!(result.is_valid);
806 assert!(result.errors.is_empty());
807 }
808
809 #[test]
810 fn test_missing_metadata() {
811 let validator = SchemaValidator::new();
812 let data = json!({
813 "unsafe_analysis": {},
814 "ffi_analysis": {}
815 });
816
817 let result = validator.validate_unsafe_ffi_analysis(&data);
818 assert!(result.is_err());
819 }
820
821 #[test]
822 fn test_invalid_schema_version() {
823 let validator = SchemaValidator::new();
824 let data = json!({
825 "metadata": {
826 "analysis_type": "unsafe_ffi_analysis_optimized",
827 "schema_version": "999.0",
828 "export_timestamp": 1234567890
829 },
830 "unsafe_analysis": {"summary": {}, "allocations": []},
831 "ffi_analysis": {"summary": {}, "allocations": []}
832 });
833
834 let result = validator
835 .validate_unsafe_ffi_analysis(&data)
836 .expect("Test operation failed");
837 assert!(!result.is_valid);
838 assert!(!result.errors.is_empty());
839 }
840
841 #[test]
842 fn test_integrity_hash_calculation() {
843 let validator = SchemaValidator::new();
844 let data = json!({"test": "data"});
845
846 let hash1 = validator
847 .calculate_integrity_hash(&data)
848 .expect("Failed to calculate hash 1");
849 let hash2 = validator
850 .calculate_integrity_hash(&data)
851 .expect("Failed to calculate hash 2");
852
853 assert_eq!(hash1, hash2);
854 assert!(validator
855 .verify_integrity(&data, &hash1)
856 .expect("Failed to verify integrity"));
857 }
858
859 #[test]
860 fn test_config_builder() {
861 let config = SchemaValidatorConfigBuilder::new()
862 .strict_mode(true)
863 .integrity_check(false)
864 .max_schema_version("2.0".to_string())
865 .build();
866
867 assert!(config.strict_mode);
868 assert!(!config.enable_integrity_check);
869 assert_eq!(config.max_schema_version, "2.0");
870 }
871
872 #[test]
873 fn test_convenience_methods() {
874 let validator = SchemaValidator::new();
875 let valid_data = json!({
876 "metadata": {
877 "analysis_type": "unsafe_ffi_analysis_optimized",
878 "schema_version": "2.0",
879 "export_timestamp": 1234567890
880 },
881 "unsafe_analysis": {"summary": {}, "allocations": []},
882 "ffi_analysis": {"summary": {}, "allocations": []}
883 });
884
885 let result = validator
886 .validate_unsafe_ffi_analysis(&valid_data)
887 .expect("Failed to validate valid data");
888 assert!(result.is_valid);
889
890 let strict_validator = SchemaValidator::strict();
891 let result = strict_validator
892 .validate_unsafe_ffi_analysis(&valid_data)
893 .expect("Failed to validate with strict mode");
894 assert!(result.is_valid);
896
897 let no_integrity_validator = SchemaValidator::without_integrity_check();
898 let result = no_integrity_validator
899 .validate_unsafe_ffi_analysis(&valid_data)
900 .expect("Failed to validate without integrity check");
901 assert_eq!(result.integrity_hash, "disabled");
902 }
903}