Skip to main content

mockforge_grpc/reflection/
validation_framework.rs

1//! Cross-endpoint validation framework
2//!
3//! This module provides validation capabilities to ensure data coherence
4//! across different endpoints, maintaining referential integrity and
5//! business logic constraints in generated mock data.
6
7use crate::reflection::schema_graph::{ForeignKeyMapping, Relationship, SchemaGraph};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use tracing::{debug, info};
11
12/// Configuration for cross-endpoint validation
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct ValidationConfig {
15    /// Enable cross-endpoint validation
16    pub enabled: bool,
17    /// Strict mode - fail on any validation error
18    pub strict_mode: bool,
19    /// Maximum validation depth for nested relationships
20    pub max_validation_depth: usize,
21    /// Custom validation rules
22    pub custom_rules: Vec<CustomValidationRule>,
23    /// Cache validation results for performance
24    pub cache_results: bool,
25}
26
27impl Default for ValidationConfig {
28    fn default() -> Self {
29        Self {
30            enabled: true,
31            strict_mode: false,
32            max_validation_depth: 3,
33            custom_rules: vec![],
34            cache_results: true,
35        }
36    }
37}
38
39/// Custom validation rule definition
40#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct CustomValidationRule {
42    /// Rule name/identifier
43    pub name: String,
44    /// Entity types this rule applies to
45    pub applies_to_entities: Vec<String>,
46    /// Fields this rule validates
47    pub validates_fields: Vec<String>,
48    /// Rule type
49    pub rule_type: ValidationRuleType,
50    /// Rule parameters
51    pub parameters: HashMap<String, String>,
52    /// Error message template
53    pub error_message: String,
54}
55
56/// Types of validation rules
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub enum ValidationRuleType {
59    /// Foreign key existence validation
60    ForeignKeyExists,
61    /// Field format validation
62    FieldFormat,
63    /// Range validation
64    Range,
65    /// Unique constraint validation
66    Unique,
67    /// Business logic validation
68    BusinessLogic,
69    /// Custom validation function
70    Custom,
71}
72
73/// Result of a validation operation
74#[derive(Debug, Clone)]
75pub struct ValidationResult {
76    /// Whether validation passed
77    pub is_valid: bool,
78    /// Validation errors found
79    pub errors: Vec<ValidationError>,
80    /// Warnings (non-fatal issues)
81    pub warnings: Vec<ValidationWarning>,
82    /// Entities that were validated
83    pub validated_entities: Vec<String>,
84}
85
86/// A validation error
87#[derive(Debug, Clone)]
88pub struct ValidationError {
89    /// Error type
90    pub error_type: ValidationErrorType,
91    /// Entity where error occurred
92    pub entity_name: String,
93    /// Field where error occurred
94    pub field_name: String,
95    /// Error message
96    pub message: String,
97    /// Value that caused the error
98    pub invalid_value: String,
99    /// Suggested fix (if available)
100    pub suggested_fix: Option<String>,
101}
102
103/// A validation warning
104#[derive(Debug, Clone)]
105pub struct ValidationWarning {
106    /// Warning type
107    pub warning_type: ValidationWarningType,
108    /// Entity where warning occurred
109    pub entity_name: String,
110    /// Field where warning occurred (optional)
111    pub field_name: Option<String>,
112    /// Warning message
113    pub message: String,
114}
115
116/// Types of validation errors
117#[derive(Debug, Clone, PartialEq)]
118pub enum ValidationErrorType {
119    /// Foreign key references non-existent entity
120    ForeignKeyNotFound,
121    /// Field format is invalid
122    InvalidFormat,
123    /// Value is outside allowed range
124    OutOfRange,
125    /// Unique constraint violation
126    DuplicateValue,
127    /// Business logic constraint violation
128    BusinessRuleViolation,
129    /// Circular reference detected
130    CircularReference,
131}
132
133/// Types of validation warnings
134#[derive(Debug, Clone)]
135pub enum ValidationWarningType {
136    /// Potential data inconsistency
137    DataInconsistency,
138    /// Performance concern
139    PerformanceConcern,
140    /// Best practice violation
141    BestPracticeViolation,
142}
143
144/// Data store for validation - tracks generated entities
145#[derive(Debug, Default)]
146pub struct ValidationDataStore {
147    /// Generated entities by type
148    entities: HashMap<String, Vec<GeneratedEntity>>,
149    /// Index for fast foreign key lookups
150    foreign_key_index: HashMap<String, HashMap<String, Vec<usize>>>,
151}
152
153/// A generated entity for validation
154#[derive(Debug, Clone)]
155pub struct GeneratedEntity {
156    /// Entity type name
157    pub entity_type: String,
158    /// Primary key value (if available)
159    pub primary_key: Option<String>,
160    /// All field values
161    pub field_values: HashMap<String, String>,
162    /// Endpoint this entity was generated for
163    pub endpoint: String,
164    /// Generation timestamp
165    pub generated_at: std::time::SystemTime,
166}
167
168/// Cross-endpoint validation framework
169pub struct ValidationFramework {
170    /// Configuration
171    config: ValidationConfig,
172    /// Schema graph for relationship validation
173    schema_graph: Option<SchemaGraph>,
174    /// Data store for tracking generated entities
175    data_store: ValidationDataStore,
176    /// Validation cache
177    validation_cache: HashMap<String, ValidationResult>,
178}
179
180impl ValidationFramework {
181    /// Create a new validation framework
182    pub fn new(config: ValidationConfig) -> Self {
183        Self {
184            config,
185            schema_graph: None,
186            data_store: ValidationDataStore::default(),
187            validation_cache: HashMap::new(),
188        }
189    }
190
191    /// Set the schema graph for relationship validation
192    pub fn set_schema_graph(&mut self, schema_graph: SchemaGraph) {
193        info!(
194            "Setting schema graph with {} entities for validation",
195            schema_graph.entities.len()
196        );
197        self.schema_graph = Some(schema_graph);
198    }
199
200    /// Register a generated entity for validation
201    pub fn register_entity(
202        &mut self,
203        entity: GeneratedEntity,
204    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
205        debug!("Registering entity {} for endpoint {}", entity.entity_type, entity.endpoint);
206
207        let entity_type = entity.entity_type.clone();
208        let primary_key = entity.primary_key.clone();
209
210        // Add to entities list
211        let entities_list = self.data_store.entities.entry(entity_type.clone()).or_default();
212        let entity_index = entities_list.len();
213        entities_list.push(entity);
214
215        // Update foreign key index if primary key exists
216        if let Some(pk) = primary_key {
217            let type_index = self.data_store.foreign_key_index.entry(entity_type).or_default();
218            let pk_index = type_index.entry(pk).or_default();
219            pk_index.push(entity_index);
220        }
221
222        Ok(())
223    }
224
225    /// Validate all registered entities for cross-endpoint consistency
226    pub fn validate_all_entities(&mut self) -> ValidationResult {
227        if !self.config.enabled {
228            return ValidationResult {
229                is_valid: true,
230                errors: vec![],
231                warnings: vec![],
232                validated_entities: vec![],
233            };
234        }
235
236        info!(
237            "Starting cross-endpoint validation of {} entity types",
238            self.data_store.entities.len()
239        );
240
241        let mut result = ValidationResult {
242            is_valid: true,
243            errors: vec![],
244            warnings: vec![],
245            validated_entities: vec![],
246        };
247
248        // Validate foreign key relationships
249        self.validate_foreign_key_relationships(&mut result);
250
251        // Validate custom rules
252        self.validate_custom_rules(&mut result);
253
254        // Validate referential integrity
255        self.validate_referential_integrity(&mut result);
256
257        // Check for potential issues
258        self.check_data_consistency(&mut result);
259
260        result.is_valid = result.errors.is_empty() || !self.config.strict_mode;
261
262        info!(
263            "Validation completed: {} errors, {} warnings",
264            result.errors.len(),
265            result.warnings.len()
266        );
267
268        result
269    }
270
271    /// Validate foreign key relationships
272    fn validate_foreign_key_relationships(&self, result: &mut ValidationResult) {
273        if let Some(schema_graph) = &self.schema_graph {
274            for (entity_type, entities) in &self.data_store.entities {
275                result.validated_entities.push(entity_type.clone());
276
277                // Get foreign key mappings for this entity
278                if let Some(fk_mappings) = schema_graph.foreign_keys.get(entity_type) {
279                    for entity in entities {
280                        self.validate_entity_foreign_keys(entity, fk_mappings, result);
281                    }
282                }
283            }
284        }
285    }
286
287    /// Validate foreign keys for a specific entity
288    fn validate_entity_foreign_keys(
289        &self,
290        entity: &GeneratedEntity,
291        fk_mappings: &[ForeignKeyMapping],
292        result: &mut ValidationResult,
293    ) {
294        for mapping in fk_mappings {
295            if let Some(fk_value) = entity.field_values.get(&mapping.field_name) {
296                // Check if the referenced entity exists
297                if !self.foreign_key_exists(&mapping.target_entity, fk_value) {
298                    result.errors.push(ValidationError {
299                        error_type: ValidationErrorType::ForeignKeyNotFound,
300                        entity_name: entity.entity_type.clone(),
301                        field_name: mapping.field_name.clone(),
302                        message: format!(
303                            "Foreign key '{}' references non-existent {} with value '{}'",
304                            mapping.field_name, mapping.target_entity, fk_value
305                        ),
306                        invalid_value: fk_value.clone(),
307                        suggested_fix: Some(format!(
308                            "Create a {} entity with primary key '{}'",
309                            mapping.target_entity, fk_value
310                        )),
311                    });
312                }
313            }
314        }
315    }
316
317    /// Check if a foreign key value exists
318    fn foreign_key_exists(&self, target_entity: &str, key_value: &str) -> bool {
319        if let Some(type_index) = self.data_store.foreign_key_index.get(target_entity) {
320            type_index.contains_key(key_value)
321        } else {
322            false
323        }
324    }
325
326    /// Validate custom validation rules
327    fn validate_custom_rules(&self, result: &mut ValidationResult) {
328        for rule in &self.config.custom_rules {
329            for entity_type in &rule.applies_to_entities {
330                if let Some(entities) = self.data_store.entities.get(entity_type) {
331                    for entity in entities {
332                        self.validate_entity_against_rule(entity, rule, result);
333                    }
334                }
335            }
336        }
337    }
338
339    /// Validate an entity against a custom rule
340    fn validate_entity_against_rule(
341        &self,
342        entity: &GeneratedEntity,
343        rule: &CustomValidationRule,
344        result: &mut ValidationResult,
345    ) {
346        match &rule.rule_type {
347            ValidationRuleType::ForeignKeyExists => {
348                self.validate_foreign_key_rule(entity, rule, result);
349            }
350            ValidationRuleType::FieldFormat => {
351                self.validate_field_format(entity, rule, result);
352            }
353            ValidationRuleType::Range => {
354                self.validate_field_range(entity, rule, result);
355            }
356            ValidationRuleType::Unique => {
357                self.validate_field_uniqueness(entity, rule, result);
358            }
359            ValidationRuleType::BusinessLogic | ValidationRuleType::Custom => {
360                // Evaluate simple expression-style rules if configured: field/operator/value.
361                // Unknown operators/rules are surfaced as best-practice warnings.
362                self.validate_business_logic_rule(entity, rule, result);
363            }
364        }
365    }
366
367    /// Validate explicit foreign key existence rule.
368    /// Parameters:
369    /// - target_entity: referenced entity type
370    fn validate_foreign_key_rule(
371        &self,
372        entity: &GeneratedEntity,
373        rule: &CustomValidationRule,
374        result: &mut ValidationResult,
375    ) {
376        let Some(target_entity) = rule.parameters.get("target_entity") else {
377            result.warnings.push(ValidationWarning {
378                warning_type: ValidationWarningType::BestPracticeViolation,
379                entity_name: entity.entity_type.clone(),
380                field_name: None,
381                message: format!(
382                    "Rule '{}' is missing required parameter 'target_entity'",
383                    rule.name
384                ),
385            });
386            return;
387        };
388
389        for field_name in &rule.validates_fields {
390            if let Some(fk_value) = entity.field_values.get(field_name) {
391                if !self.foreign_key_exists(target_entity, fk_value) {
392                    result.errors.push(ValidationError {
393                        error_type: ValidationErrorType::ForeignKeyNotFound,
394                        entity_name: entity.entity_type.clone(),
395                        field_name: field_name.clone(),
396                        message: rule.error_message.clone(),
397                        invalid_value: fk_value.clone(),
398                        suggested_fix: Some(format!(
399                            "Create a {} entity with primary key '{}'",
400                            target_entity, fk_value
401                        )),
402                    });
403                }
404            }
405        }
406    }
407
408    /// Validate basic business logic/custom rule expressions.
409    /// Supported parameters:
410    /// - field: field name to validate
411    /// - operator: eq|ne|contains|starts_with|ends_with
412    /// - value: expected value
413    fn validate_business_logic_rule(
414        &self,
415        entity: &GeneratedEntity,
416        rule: &CustomValidationRule,
417        result: &mut ValidationResult,
418    ) {
419        let Some(field) = rule.parameters.get("field") else {
420            result.warnings.push(ValidationWarning {
421                warning_type: ValidationWarningType::BestPracticeViolation,
422                entity_name: entity.entity_type.clone(),
423                field_name: None,
424                message: format!(
425                    "Rule '{}' skipped: missing 'field' parameter for {:?}",
426                    rule.name, rule.rule_type
427                ),
428            });
429            return;
430        };
431        let Some(operator) = rule.parameters.get("operator") else {
432            result.warnings.push(ValidationWarning {
433                warning_type: ValidationWarningType::BestPracticeViolation,
434                entity_name: entity.entity_type.clone(),
435                field_name: Some(field.clone()),
436                message: format!(
437                    "Rule '{}' skipped: missing 'operator' parameter for {:?}",
438                    rule.name, rule.rule_type
439                ),
440            });
441            return;
442        };
443        let Some(expected) = rule.parameters.get("value") else {
444            result.warnings.push(ValidationWarning {
445                warning_type: ValidationWarningType::BestPracticeViolation,
446                entity_name: entity.entity_type.clone(),
447                field_name: Some(field.clone()),
448                message: format!(
449                    "Rule '{}' skipped: missing 'value' parameter for {:?}",
450                    rule.name, rule.rule_type
451                ),
452            });
453            return;
454        };
455
456        if let Some(actual) = entity.field_values.get(field) {
457            let passed = match operator.as_str() {
458                "eq" => actual == expected,
459                "ne" => actual != expected,
460                "contains" => actual.contains(expected),
461                "starts_with" => actual.starts_with(expected),
462                "ends_with" => actual.ends_with(expected),
463                _ => {
464                    result.warnings.push(ValidationWarning {
465                        warning_type: ValidationWarningType::BestPracticeViolation,
466                        entity_name: entity.entity_type.clone(),
467                        field_name: Some(field.clone()),
468                        message: format!(
469                            "Rule '{}' uses unsupported operator '{}'",
470                            rule.name, operator
471                        ),
472                    });
473                    return;
474                }
475            };
476
477            if !passed {
478                result.errors.push(ValidationError {
479                    error_type: ValidationErrorType::BusinessRuleViolation,
480                    entity_name: entity.entity_type.clone(),
481                    field_name: field.clone(),
482                    message: rule.error_message.clone(),
483                    invalid_value: actual.clone(),
484                    suggested_fix: Some(format!(
485                        "Expected '{}' {} '{}'",
486                        field, operator, expected
487                    )),
488                });
489            }
490        }
491    }
492
493    /// Validate field format
494    fn validate_field_format(
495        &self,
496        entity: &GeneratedEntity,
497        rule: &CustomValidationRule,
498        result: &mut ValidationResult,
499    ) {
500        for field_name in &rule.validates_fields {
501            if let Some(field_value) = entity.field_values.get(field_name) {
502                if let Some(pattern) = rule.parameters.get("pattern") {
503                    if let Ok(regex) = regex::Regex::new(pattern) {
504                        if !regex.is_match(field_value) {
505                            result.errors.push(ValidationError {
506                                error_type: ValidationErrorType::InvalidFormat,
507                                entity_name: entity.entity_type.clone(),
508                                field_name: field_name.clone(),
509                                message: rule.error_message.clone(),
510                                invalid_value: field_value.clone(),
511                                suggested_fix: Some(format!(
512                                    "Value should match pattern: {}",
513                                    pattern
514                                )),
515                            });
516                        }
517                    }
518                }
519            }
520        }
521    }
522
523    /// Validate field range constraints
524    fn validate_field_range(
525        &self,
526        entity: &GeneratedEntity,
527        rule: &CustomValidationRule,
528        result: &mut ValidationResult,
529    ) {
530        for field_name in &rule.validates_fields {
531            if let Some(field_value) = entity.field_values.get(field_name) {
532                if let Ok(value) = field_value.parse::<f64>() {
533                    let min = rule.parameters.get("min").and_then(|s| s.parse::<f64>().ok());
534                    let max = rule.parameters.get("max").and_then(|s| s.parse::<f64>().ok());
535
536                    let out_of_range = (min.is_some() && value < min.unwrap())
537                        || (max.is_some() && value > max.unwrap());
538
539                    if out_of_range {
540                        result.errors.push(ValidationError {
541                            error_type: ValidationErrorType::OutOfRange,
542                            entity_name: entity.entity_type.clone(),
543                            field_name: field_name.clone(),
544                            message: rule.error_message.clone(),
545                            invalid_value: field_value.clone(),
546                            suggested_fix: Some(format!(
547                                "Value should be between {} and {}",
548                                min.map_or("any".to_string(), |v| v.to_string()),
549                                max.map_or("any".to_string(), |v| v.to_string())
550                            )),
551                        });
552                    }
553                }
554            }
555        }
556    }
557
558    /// Validate field uniqueness constraints
559    fn validate_field_uniqueness(
560        &self,
561        entity: &GeneratedEntity,
562        rule: &CustomValidationRule,
563        result: &mut ValidationResult,
564    ) {
565        for field_name in &rule.validates_fields {
566            if let Some(field_value) = entity.field_values.get(field_name) {
567                // Check if this value appears in other entities
568                let mut duplicate_count = 0;
569
570                if let Some(entities) = self.data_store.entities.get(&entity.entity_type) {
571                    for other_entity in entities {
572                        if let Some(other_value) = other_entity.field_values.get(field_name) {
573                            if other_value == field_value {
574                                duplicate_count += 1;
575                            }
576                        }
577                    }
578                }
579
580                if duplicate_count > 1 {
581                    result.errors.push(ValidationError {
582                        error_type: ValidationErrorType::DuplicateValue,
583                        entity_name: entity.entity_type.clone(),
584                        field_name: field_name.clone(),
585                        message: rule.error_message.clone(),
586                        invalid_value: field_value.clone(),
587                        suggested_fix: Some("Generate unique values for this field".to_string()),
588                    });
589                }
590            }
591        }
592    }
593
594    /// Validate referential integrity across endpoints
595    fn validate_referential_integrity(&self, result: &mut ValidationResult) {
596        if let Some(schema_graph) = &self.schema_graph {
597            for relationship in &schema_graph.relationships {
598                self.validate_relationship_integrity(relationship, result);
599            }
600        }
601    }
602
603    /// Validate a specific relationship's integrity
604    fn validate_relationship_integrity(
605        &self,
606        relationship: &Relationship,
607        result: &mut ValidationResult,
608    ) {
609        if let (Some(from_entities), Some(to_entities)) = (
610            self.data_store.entities.get(&relationship.from_entity),
611            self.data_store.entities.get(&relationship.to_entity),
612        ) {
613            for from_entity in from_entities {
614                if let Some(ref_value) = from_entity.field_values.get(&relationship.field_name) {
615                    let target_exists = to_entities
616                        .iter()
617                        .any(|to_entity| to_entity.primary_key.as_ref() == Some(ref_value));
618
619                    if !target_exists && relationship.is_required {
620                        result.warnings.push(ValidationWarning {
621                            warning_type: ValidationWarningType::DataInconsistency,
622                            entity_name: from_entity.entity_type.clone(),
623                            field_name: Some(relationship.field_name.clone()),
624                            message: format!(
625                                "Required relationship from {} to {} not satisfied - referenced {} '{}' does not exist",
626                                relationship.from_entity, relationship.to_entity,
627                                relationship.to_entity, ref_value
628                            ),
629                        });
630                    }
631                }
632            }
633        }
634    }
635
636    /// Check for general data consistency issues
637    fn check_data_consistency(&self, result: &mut ValidationResult) {
638        // Check for entities that are never referenced
639        self.check_orphaned_entities(result);
640
641        // Check for potential performance issues
642        self.check_performance_concerns(result);
643    }
644
645    /// Check for entities that might be orphaned
646    fn check_orphaned_entities(&self, result: &mut ValidationResult) {
647        if let Some(schema_graph) = &self.schema_graph {
648            for (entity_type, entity_node) in &schema_graph.entities {
649                if entity_node.referenced_by.is_empty() && !entity_node.is_root {
650                    if let Some(entities) = self.data_store.entities.get(entity_type) {
651                        if !entities.is_empty() {
652                            result.warnings.push(ValidationWarning {
653                                warning_type: ValidationWarningType::DataInconsistency,
654                                entity_name: entity_type.clone(),
655                                field_name: None,
656                                message: format!(
657                                    "Entity type {} is not referenced by any other entities but {} instances were generated",
658                                    entity_type, entities.len()
659                                ),
660                            });
661                        }
662                    }
663                }
664            }
665        }
666    }
667
668    /// Check for potential performance concerns
669    fn check_performance_concerns(&self, result: &mut ValidationResult) {
670        for (entity_type, entities) in &self.data_store.entities {
671            if entities.len() > 10000 {
672                result.warnings.push(ValidationWarning {
673                    warning_type: ValidationWarningType::PerformanceConcern,
674                    entity_name: entity_type.clone(),
675                    field_name: None,
676                    message: format!(
677                        "Large number of {} entities ({}) may impact performance",
678                        entity_type,
679                        entities.len()
680                    ),
681                });
682            }
683        }
684    }
685
686    /// Clear all validation data
687    pub fn clear(&mut self) {
688        self.data_store.entities.clear();
689        self.data_store.foreign_key_index.clear();
690        self.validation_cache.clear();
691        info!("Validation framework data cleared");
692    }
693
694    /// Get validation statistics
695    pub fn get_statistics(&self) -> ValidationStatistics {
696        let total_entities: usize = self.data_store.entities.values().map(|v| v.len()).sum();
697        let entity_type_count = self.data_store.entities.len();
698        let indexed_keys: usize = self
699            .data_store
700            .foreign_key_index
701            .values()
702            .map(|type_index| type_index.len())
703            .sum();
704
705        ValidationStatistics {
706            total_entities,
707            entity_type_count,
708            indexed_foreign_keys: indexed_keys,
709            cache_size: self.validation_cache.len(),
710        }
711    }
712}
713
714/// Validation framework statistics
715#[derive(Debug, Clone)]
716pub struct ValidationStatistics {
717    /// Total number of entities tracked
718    pub total_entities: usize,
719    /// Number of different entity types
720    pub entity_type_count: usize,
721    /// Number of indexed foreign key values
722    pub indexed_foreign_keys: usize,
723    /// Size of validation cache
724    pub cache_size: usize,
725}
726
727#[cfg(test)]
728mod tests {
729    use super::*;
730    use std::time::SystemTime;
731
732    #[test]
733    fn test_validation_framework_creation() {
734        let config = ValidationConfig::default();
735        let framework = ValidationFramework::new(config);
736        assert!(framework.config.enabled);
737        assert!(!framework.config.strict_mode);
738    }
739
740    #[test]
741    fn test_entity_registration() {
742        let config = ValidationConfig::default();
743        let mut framework = ValidationFramework::new(config);
744
745        let entity = GeneratedEntity {
746            entity_type: "User".to_string(),
747            primary_key: Some("user_123".to_string()),
748            field_values: HashMap::from([
749                ("id".to_string(), "user_123".to_string()),
750                ("name".to_string(), "John Doe".to_string()),
751            ]),
752            endpoint: "/users".to_string(),
753            generated_at: SystemTime::now(),
754        };
755
756        framework.register_entity(entity).expect("Should register entity successfully");
757
758        let stats = framework.get_statistics();
759        assert_eq!(stats.total_entities, 1);
760        assert_eq!(stats.entity_type_count, 1);
761    }
762
763    #[test]
764    fn test_validation_with_no_schema() {
765        let config = ValidationConfig::default();
766        let mut framework = ValidationFramework::new(config);
767
768        // Add some entities
769        let entity1 = GeneratedEntity {
770            entity_type: "User".to_string(),
771            primary_key: Some("1".to_string()),
772            field_values: HashMap::from([("id".to_string(), "1".to_string())]),
773            endpoint: "/users".to_string(),
774            generated_at: SystemTime::now(),
775        };
776
777        framework.register_entity(entity1).unwrap();
778
779        let result = framework.validate_all_entities();
780        assert!(result.is_valid);
781        assert!(result.errors.is_empty());
782    }
783
784    #[test]
785    fn test_custom_validation_rule() {
786        let mut config = ValidationConfig::default();
787        config.custom_rules.push(CustomValidationRule {
788            name: "email_format".to_string(),
789            applies_to_entities: vec!["User".to_string()],
790            validates_fields: vec!["email".to_string()],
791            rule_type: ValidationRuleType::FieldFormat,
792            parameters: HashMap::from([(
793                "pattern".to_string(),
794                r"^[^@]+@[^@]+\.[^@]+$".to_string(),
795            )]),
796            error_message: "Invalid email format".to_string(),
797        });
798
799        let mut framework = ValidationFramework::new(config);
800
801        let entity = GeneratedEntity {
802            entity_type: "User".to_string(),
803            primary_key: Some("1".to_string()),
804            field_values: HashMap::from([
805                ("id".to_string(), "1".to_string()),
806                ("email".to_string(), "invalid-email".to_string()),
807            ]),
808            endpoint: "/users".to_string(),
809            generated_at: SystemTime::now(),
810        };
811
812        framework.register_entity(entity).unwrap();
813
814        let result = framework.validate_all_entities();
815        assert!(!result.errors.is_empty());
816        assert_eq!(result.errors[0].error_type, ValidationErrorType::InvalidFormat);
817    }
818}