Spaces:
Running on Zero
Running on Zero
| """ | |
| Test the game JSON schema and validation utilities. | |
| Run this script to: | |
| 1. Test the schema against example games from the dataset | |
| 2. Validate schema structure | |
| 3. Test minimal game templates | |
| 4. Display validation results | |
| """ | |
| import json | |
| from app.services.retrieval import load_games_dataset, normalize_game_record | |
| from app.services.schema_validator import ( | |
| load_schema, | |
| validate_game_schema, | |
| validate_task_structure, | |
| validate_safety_structure, | |
| create_minimal_game_template | |
| ) | |
| def test_schema_structure(): | |
| """Test that the schema itself is valid.""" | |
| print("=" * 80) | |
| print("SCHEMA STRUCTURE TEST") | |
| print("=" * 80) | |
| try: | |
| schema = load_schema("game_schema.json") | |
| print("β Game schema loaded successfully") | |
| print(f" Title: {schema.get('title')}") | |
| print(f" Type: {schema.get('type')}") | |
| print(f" Required fields: {', '.join(schema.get('required', []))}") | |
| return True | |
| except Exception as e: | |
| print(f"β Failed to load schema: {e}") | |
| return False | |
| def test_minimal_template(): | |
| """Test validation of a minimal game template.""" | |
| print("\n" + "=" * 80) | |
| print("MINIMAL GAME TEMPLATE TEST") | |
| print("=" * 80) | |
| template = create_minimal_game_template() | |
| is_valid, errors = validate_game_schema(template) | |
| if is_valid: | |
| print("β Minimal template is valid against schema") | |
| print(f" Game ID: {template['game_id']}") | |
| print(f" Title: {template['title']}") | |
| print(f" Tasks: {len(template['tasks'])}") | |
| print(f" Rules: {len(template['rules'])}") | |
| return True | |
| else: | |
| print(f"β Minimal template validation failed:") | |
| for error in errors: | |
| print(f" - {error}") | |
| return False | |
| def test_dataset_games(): | |
| """Test validation of example games from the dataset.""" | |
| print("\n" + "=" * 80) | |
| print("DATASET GAMES VALIDATION TEST") | |
| print("=" * 80) | |
| # Load dataset | |
| raw_records = load_games_dataset("app/data/games_dataset.json") | |
| # Convert raw records to game schema format for testing | |
| tested_count = 0 | |
| valid_count = 0 | |
| invalid_games = [] | |
| for raw in raw_records[:3]: # Test first 3 as sample | |
| try: | |
| # Extract expected output as game structure | |
| output = raw.get('expected_output', {}) | |
| input_data = raw.get('input', {}) | |
| # Build game in schema format | |
| # Transform tasks to include required title field | |
| tasks_transformed = [] | |
| for task in output.get('tasks', []): | |
| task_copy = task.copy() | |
| # Add title if missing | |
| if 'title' not in task_copy: | |
| task_copy['title'] = task_copy.get('description', 'Task')[:50] | |
| # Ensure all required fields exist | |
| task_copy.setdefault('proof_type', 'observation') | |
| task_copy.setdefault('hint', 'See location hint above') | |
| task_copy.setdefault('safety_note', 'Follow general safety rules') | |
| tasks_transformed.append(task_copy) | |
| game = { | |
| "game_id": raw.get('id'), | |
| "title": f"Game {raw.get('id')}", | |
| "theme": "discovery", | |
| "setup": { | |
| "city": input_data.get('location', {}).get('city', 'Paris'), | |
| "area": input_data.get('location', {}).get('area', ''), | |
| "meeting_point": "Central meeting point", | |
| "duration_minutes": input_data.get('preferences', {}).get('duration_minutes', 45), | |
| "num_players": input_data.get('preferences', {}).get('num_players', 4) | |
| }, | |
| "rules": output.get('rules', []), | |
| "tasks": tasks_transformed, | |
| "global_hints": output.get('hints', [[]])[0] if output.get('hints') else [], | |
| "score_rules": ["Standard scoring"], | |
| "tie_breaker": "Most tasks completed", | |
| "safety": { | |
| "allowed_zone": "Public area", | |
| "forbidden_behaviors": [], | |
| "adult_supervision": input_data.get('preferences', {}).get('age_group') == 'kids', | |
| "stop_conditions": ["Emergency", "Weather"] | |
| }, | |
| "story_seed": { | |
| "tone": "playful", | |
| "motifs": [], | |
| "recap_style": "episode_recap" | |
| } | |
| } | |
| tested_count += 1 | |
| is_valid, errors = validate_game_schema(game) | |
| if is_valid: | |
| valid_count += 1 | |
| print(f"β {game['game_id']}: Valid") | |
| else: | |
| invalid_games.append({ | |
| 'id': game['game_id'], | |
| 'errors': errors | |
| }) | |
| print(f"β {game['game_id']}: Invalid") | |
| for error in errors[:2]: # Show first 2 errors | |
| print(f" {error}") | |
| except Exception as e: | |
| tested_count += 1 | |
| print(f"β {raw.get('id')}: Exception - {str(e)[:60]}") | |
| print(f"\nResults: {valid_count}/{tested_count} games valid") | |
| if invalid_games: | |
| print("\nInvalid games details:") | |
| for game_info in invalid_games: | |
| print(f" {game_info['id']}: {game_info['errors']}") | |
| return valid_count == tested_count | |
| def test_task_validation(): | |
| """Test task-level validation.""" | |
| print("\n" + "=" * 80) | |
| print("TASK VALIDATION TEST") | |
| print("=" * 80) | |
| test_cases = [ | |
| { | |
| "name": "Valid task", | |
| "task": { | |
| "task_id": "t1", | |
| "title": "Find landmark", | |
| "description": "Locate and photograph the fountain", | |
| "location_hint": "Look in the central square", | |
| "points": 25, | |
| "time_limit_minutes": 15, | |
| "proof_type": "photo", | |
| "hint": "It's in the middle", | |
| "safety_note": "Stay on paths" | |
| }, | |
| "expect_valid": True | |
| }, | |
| { | |
| "name": "Invalid proof_type", | |
| "task": { | |
| "task_id": "t2", | |
| "title": "Task", | |
| "description": "Do something", | |
| "location_hint": "Somewhere", | |
| "points": 10, | |
| "time_limit_minutes": 5, | |
| "proof_type": "video", # Invalid | |
| "hint": "Hint", | |
| "safety_note": "Safe" | |
| }, | |
| "expect_valid": False | |
| }, | |
| { | |
| "name": "Missing safety_note", | |
| "task": { | |
| "task_id": "t3", | |
| "title": "Task", | |
| "description": "Do something", | |
| "location_hint": "Somewhere", | |
| "points": 10, | |
| "time_limit_minutes": 5, | |
| "proof_type": "observation", | |
| "hint": "Hint" | |
| # Missing safety_note | |
| }, | |
| "expect_valid": False | |
| } | |
| ] | |
| for test in test_cases: | |
| is_valid, errors = validate_task_structure(test['task']) | |
| status = "β" if is_valid == test['expect_valid'] else "β" | |
| print(f"{status} {test['name']}: {is_valid}") | |
| if errors: | |
| for error in errors[:1]: | |
| print(f" {error}") | |
| def test_safety_validation(): | |
| """Test safety object validation.""" | |
| print("\n" + "=" * 80) | |
| print("SAFETY VALIDATION TEST") | |
| print("=" * 80) | |
| valid_safety = { | |
| "allowed_zone": "Public park and streets", | |
| "forbidden_behaviors": [ | |
| "Entering buildings", | |
| "Crossing roads unsafely" | |
| ], | |
| "adult_supervision": True, | |
| "stop_conditions": [ | |
| "Injury", | |
| "Emergency" | |
| ] | |
| } | |
| is_valid, errors = validate_safety_structure(valid_safety) | |
| print(f"{'β' if is_valid else 'β'} Valid safety object: {is_valid}") | |
| if errors: | |
| for error in errors: | |
| print(f" {error}") | |
| def main(): | |
| print("\nGAME SCHEMA AND VALIDATION TESTS\n") | |
| # Run all tests | |
| schema_ok = test_schema_structure() | |
| template_ok = test_minimal_template() | |
| dataset_ok = test_dataset_games() | |
| test_task_validation() | |
| test_safety_validation() | |
| # Summary | |
| print("\n" + "=" * 80) | |
| print("TEST SUMMARY") | |
| print("=" * 80) | |
| print(f"Schema structure: {'β PASS' if schema_ok else 'β FAIL'}") | |
| print(f"Minimal template: {'β PASS' if template_ok else 'β FAIL'}") | |
| print(f"Dataset games: {'β PASS' if dataset_ok else 'β FAIL'}") | |
| print("\nSchema validation is ready for use in game generation and validation.") | |
| if __name__ == "__main__": | |
| main() | |