forked from cleveragents/cleveragents-core
2f69358dcf
All 3 simulations tested with the fixed generate prompt that passes the original DoD to the LLM. Results: - SIM12 (Rate Limiter): 7 files — algorithms.py, store.py, middleware.py, config.py, example_app.py, requirements.txt, __init__.py - SIM14 (JSON Validator): 6 files — validator.py, types.py, errors.py, cli.py, requirements.txt, __init__.py - SIM17 (API Tester): 7 files — runner.py, assertions.py, variables.py, reporter.py, cli.py, requirements.txt, test_suite_example.yaml All Python files pass compile() syntax check. All DoD files present. Files written directly via --output-dir, no manual cleanup needed.
152 lines
5.6 KiB
Python
152 lines
5.6 KiB
Python
import json
|
|
from typing import Any, Dict, List, Optional, Union
|
|
from .errors import ValidationError
|
|
from .types import validate_string, validate_number, validate_integer, validate_boolean, validate_array, validate_object, validate_null
|
|
|
|
|
|
class SchemaValidator:
|
|
"""Main JSON schema validator class."""
|
|
|
|
def __init__(self, schema: Dict[str, Any]):
|
|
"""Initialize validator with a JSON schema.
|
|
|
|
Args:
|
|
schema: JSON schema dictionary
|
|
"""
|
|
self.schema = schema
|
|
|
|
def validate(self, data: Any) -> List[ValidationError]:
|
|
"""Validate data against the schema.
|
|
|
|
Args:
|
|
data: Data to validate
|
|
|
|
Returns:
|
|
List of validation errors (empty if valid)
|
|
"""
|
|
errors = []
|
|
self._validate_recursive(data, self.schema, "$", errors)
|
|
return errors
|
|
|
|
def _validate_recursive(self, data: Any, schema: Dict[str, Any], path: str, errors: List[ValidationError]) -> None:
|
|
"""Recursively validate data against schema.
|
|
|
|
Args:
|
|
data: Current data being validated
|
|
schema: Current schema definition
|
|
path: JSON path to current data
|
|
errors: List to accumulate errors
|
|
"""
|
|
# Handle type validation
|
|
schema_type = schema.get("type")
|
|
if schema_type:
|
|
if schema_type == "string":
|
|
self._validate_type(validate_string, data, schema, path, errors)
|
|
elif schema_type == "number":
|
|
self._validate_type(validate_number, data, schema, path, errors)
|
|
elif schema_type == "integer":
|
|
self._validate_type(validate_integer, data, schema, path, errors)
|
|
elif schema_type == "boolean":
|
|
self._validate_type(validate_boolean, data, schema, path, errors)
|
|
elif schema_type == "array":
|
|
self._validate_array(data, schema, path, errors)
|
|
elif schema_type == "object":
|
|
self._validate_object(data, schema, path, errors)
|
|
elif schema_type == "null":
|
|
self._validate_type(validate_null, data, schema, path, errors)
|
|
else:
|
|
errors.append(ValidationError(
|
|
path=path,
|
|
message=f"Unknown type '{schema_type}' in schema"
|
|
))
|
|
|
|
# Handle enum constraint (applies to all types)
|
|
if "enum" in schema:
|
|
if data not in schema["enum"]:
|
|
errors.append(ValidationError(
|
|
path=path,
|
|
message=f"Value must be one of {schema['enum']}, got {data}"
|
|
))
|
|
|
|
def _validate_type(self, validator_func, data: Any, schema: Dict[str, Any], path: str, errors: List[ValidationError]) -> None:
|
|
"""Validate data using a type-specific validator function.
|
|
|
|
Args:
|
|
validator_func: Type-specific validation function
|
|
data: Data to validate
|
|
schema: Schema definition
|
|
path: JSON path
|
|
errors: List to accumulate errors
|
|
"""
|
|
try:
|
|
validator_func(data, schema, path)
|
|
except ValidationError as e:
|
|
errors.append(e)
|
|
|
|
def _validate_array(self, data: Any, schema: Dict[str, Any], path: str, errors: List[ValidationError]) -> None:
|
|
"""Validate array data and its items.
|
|
|
|
Args:
|
|
data: Data to validate
|
|
schema: Schema definition
|
|
path: JSON path
|
|
errors: List to accumulate errors
|
|
"""
|
|
try:
|
|
validate_array(data, schema, path)
|
|
except ValidationError as e:
|
|
errors.append(e)
|
|
return
|
|
|
|
# Validate array items if schema is provided
|
|
items_schema = schema.get("items")
|
|
if items_schema and isinstance(data, list):
|
|
for i, item in enumerate(data):
|
|
item_path = f"{path}[{i}]"
|
|
self._validate_recursive(item, items_schema, item_path, errors)
|
|
|
|
def _validate_object(self, data: Any, schema: Dict[str, Any], path: str, errors: List[ValidationError]) -> None:
|
|
"""Validate object data and its properties.
|
|
|
|
Args:
|
|
data: Data to validate
|
|
schema: Schema definition
|
|
path: JSON path
|
|
errors: List to accumulate errors
|
|
"""
|
|
try:
|
|
validate_object(data, schema, path)
|
|
except ValidationError as e:
|
|
errors.append(e)
|
|
return
|
|
|
|
if not isinstance(data, dict):
|
|
return
|
|
|
|
# Check required fields
|
|
required = schema.get("required", [])
|
|
for field in required:
|
|
if field not in data:
|
|
field_path = f"{path}.{field}" if path != "$" else f"$.{field}"
|
|
errors.append(ValidationError(
|
|
path=field_path,
|
|
message=f"Required field '{field}' is missing"
|
|
))
|
|
|
|
# Validate object properties
|
|
properties = schema.get("properties", {})
|
|
for field, value in data.items():
|
|
if field in properties:
|
|
field_path = f"{path}.{field}" if path != "$" else f"$.{field}"
|
|
self._validate_recursive(value, properties[field], field_path, errors)
|
|
|
|
def is_valid(self, data: Any) -> bool:
|
|
"""Check if data is valid against the schema.
|
|
|
|
Args:
|
|
data: Data to validate
|
|
|
|
Returns:
|
|
True if valid, False otherwise
|
|
"""
|
|
return len(self.validate(data)) == 0 |