2008robocode-crypto/code-generation-system
0
1"""2Validation engine for detecting and reporting issues in generated schemas.3Checks for: invalid JSON, missing keys, type mismatches, cross-layer inconsistencies.4"""5 6import json7from typing import Any, Dict, List, Optional, Tuple8from schemas import ValidationResult, GeneratedConfig, DBTable, APIEndpoint, UIPage, Role, FieldType9 10 11class Validator:12 """Comprehensive validation for generated configurations."""13 14 def __init__(self):15 self.result = ValidationResult()16 17 def validate_json(self, json_str: str) -> Tuple[bool, Optional[Dict[str, Any]]]:18 """Validate if string is valid JSON."""19 try:20 data = json.loads(json_str)21 return True, data22 except json.JSONDecodeError as e:23 self.result.add_error(f"Invalid JSON: {str(e)}")24 return False, None25 26 def validate_required_fields(self, data: Dict[str, Any], required_fields: List[str]) -> bool:27 """Check if all required fields are present."""28 missing = [f for f in required_fields if f not in data or data[f] is None]29 if missing:30 self.result.add_error(f"Missing required fields: {', '.join(missing)}")31 return False32 return True33 34 def validate_generated_config_structure(self, config_dict: Dict[str, Any]) -> bool:35 """Validate top-level structure of GeneratedConfig."""36 required_fields = [37 "app_name", "app_description", "database_schema", 38 "api_schema", "ui_schema", "auth_config", "roles", "business_logic"39 ]40 return self.validate_required_fields(config_dict, required_fields)41 42 def validate_database_schema(self, db_schema: List[Dict[str, Any]]) -> bool:43 """Validate database schema integrity."""44 if not isinstance(db_schema, list):45 self.result.add_error("database_schema must be a list")46 return False47 48 table_names = set()49 is_valid = True50 51 for i, table in enumerate(db_schema):52 if not isinstance(table, dict):53 self.result.add_error(f"Table {i} is not a dict")54 is_valid = False55 continue56 57 # Check required fields58 if "name" not in table or "fields" not in table or "primary_key" not in table:59 self.result.add_error(f"Table {i}: missing name, fields, or primary_key")60 is_valid = False61 continue62 63 table_names.add(table["name"])64 65 # Validate fields66 if not isinstance(table["fields"], list):67 self.result.add_error(f"Table '{table['name']}': fields must be a list")68 is_valid = False69 continue70 71 # Validate field types72 for field in table["fields"]:73 if not self._validate_field(field, table["name"]):74 is_valid = False75 76 # Validate foreign key relations77 for table in db_schema:78 if "relations" in table and table["relations"]:79 for field, related_table in table["relations"].items():80 if related_table not in table_names:81 self.result.add_warning(82 f"Foreign key in {table['name']}.{field} references non-existent table: {related_table}"83 )84 85 return is_valid86 87 def _validate_field(self, field: Dict[str, Any], table_name: str) -> bool:88 """Validate a single field."""89 if not isinstance(field, dict):90 self.result.add_error(f"Field in {table_name} is not a dict")91 return False92 93 required = ["name", "type"]94 if not all(k in field for k in required):95 self.result.add_error(f"Field in {table_name}: missing name or type")96 return False97 98 field_type = field["type"]99 valid_types = [t.value for t in FieldType]100 if field_type not in valid_types:101 self.result.add_error(102 f"Field '{field['name']}' in {table_name}: invalid type '{field_type}'"103 )104 return False105 106 return True107 108 def validate_api_schema(self, api_schema: List[Dict[str, Any]]) -> bool:109 """Validate API schema structure."""110 if not isinstance(api_schema, list):111 self.result.add_error("api_schema must be a list")112 return False113 114 valid_methods = ["GET", "POST", "PUT", "DELETE", "PATCH"]115 is_valid = True116 117 for i, endpoint in enumerate(api_schema):118 if not isinstance(endpoint, dict):119 self.result.add_error(f"Endpoint {i} is not a dict")120 is_valid = False121 continue122 123 # Check required fields124 if "path" not in endpoint or "method" not in endpoint:125 self.result.add_error(f"Endpoint {i}: missing path or method")126 is_valid = False127 continue128 129 # Validate method130 if endpoint["method"] not in valid_methods:131 self.result.add_error(132 f"Endpoint {endpoint['path']}: invalid method '{endpoint['method']}'"133 )134 is_valid = False135 136 return is_valid137 138 def validate_ui_schema(self, ui_schema: List[Dict[str, Any]]) -> bool:139 """Validate UI schema structure."""140 if not isinstance(ui_schema, list):141 self.result.add_error("ui_schema must be a list")142 return False143 144 is_valid = True145 146 for i, page in enumerate(ui_schema):147 if not isinstance(page, dict):148 self.result.add_error(f"Page {i} is not a dict")149 is_valid = False150 continue151 152 required = ["path", "title", "components"]153 if not all(k in page for k in required):154 self.result.add_error(f"Page {i}: missing path, title, or components")155 is_valid = False156 continue157 158 if not isinstance(page["components"], list):159 self.result.add_error(f"Page {i}: components must be a list")160 is_valid = False161 162 return is_valid163 164 def validate_cross_layer_consistency(self, config_dict: Dict[str, Any]) -> bool:165 """Validate consistency between API, DB, and UI layers."""166 is_valid = True167 168 # Extract API fields169 api_fields = set()170 for endpoint in config_dict.get("api_schema", []):171 if endpoint.get("request_body"):172 api_fields.update(endpoint["request_body"].keys())173 if endpoint.get("response_body"):174 api_fields.update(endpoint["response_body"].keys())175 176 # Extract DB fields177 db_fields = set()178 for table in config_dict.get("database_schema", []):179 for field in table.get("fields", []):180 if isinstance(field, dict):181 db_fields.add(field.get("name"))182 elif hasattr(field, "name"):183 db_fields.add(field.name)184 185 # Extract UI fields186 ui_fields = set()187 for page in config_dict.get("ui_schema", []):188 for component in page.get("components", []):189 if isinstance(component, dict):190 ui_fields.update(component.get("fields", {}).keys() if isinstance(component.get("fields"), dict) else [])191 192 # Check consistency193 if api_fields and db_fields:194 # API fields should reference DB fields (not strict - API can have computed fields)195 missing_db_fields = api_fields - db_fields196 if missing_db_fields and len(missing_db_fields) > 3: # Allow some computed fields197 self.result.add_warning(198 f"API references fields not in DB: {missing_db_fields}"199 )200 201 return is_valid202 203 def validate_roles_and_permissions(self, roles: List[Dict[str, Any]], 204 api_schema: List[Dict[str, Any]]) -> bool:205 """Validate roles match API requirements."""206 is_valid = True207 208 if not isinstance(roles, list):209 self.result.add_error("roles must be a list")210 return False211 212 role_names = set()213 for role in roles:214 if not isinstance(role, dict) or "name" not in role:215 self.result.add_error("Each role must be a dict with 'name'")216 is_valid = False217 continue218 role_names.add(role["name"])219 220 # Check if API endpoints reference existing roles221 for endpoint in api_schema:222 required_role = endpoint.get("required_role")223 if required_role and required_role not in role_names:224 self.result.add_warning(225 f"API endpoint {endpoint.get('path')} requires role '{required_role}' which doesn't exist"226 )227 228 return is_valid229 230 def validate_no_hallucinations(self, config_dict: Dict[str, Any]) -> bool:231 """Detect hallucinated or nonsensical fields."""232 is_valid = True233 234 # Check for common hallucination patterns235 for key, value in config_dict.items():236 if isinstance(value, str):237 # Check for placeholder text238 if any(p in value.lower() for p in ["todo", "placeholder", "tbd", "fix me"]):239 self.result.add_warning(f"Potential placeholder in {key}: {value}")240 241 # Check for values that don't match their keys semantically242 if key == "business_logic" and not isinstance(value, dict):243 self.result.add_error(f"{key} should be a dict, got {type(value)}")244 is_valid = False245 246 return is_valid247 248 def validate_complete(self, config_dict: Dict[str, Any]) -> ValidationResult:249 """Run complete validation suite."""250 self.result = ValidationResult()251 252 # Structure validation253 if not self.validate_generated_config_structure(config_dict):254 return self.result255 256 # Layer-specific validation257 self.validate_database_schema(config_dict.get("database_schema", []))258 self.validate_api_schema(config_dict.get("api_schema", []))259 self.validate_ui_schema(config_dict.get("ui_schema", []))260 261 # Cross-layer validation262 self.validate_cross_layer_consistency(config_dict)263 self.validate_roles_and_permissions(264 config_dict.get("roles", []),265 config_dict.get("api_schema", [])266 )267 268 # Hallucination detection269 self.validate_no_hallucinations(config_dict)270 271 return self.result272 