| """ |
| 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) |
| |
| |
| raw_records = load_games_dataset("app/data/games_dataset.json") |
| |
| |
| tested_count = 0 |
| valid_count = 0 |
| invalid_games = [] |
| |
| for raw in raw_records[:3]: |
| try: |
| |
| output = raw.get('expected_output', {}) |
| input_data = raw.get('input', {}) |
| |
| |
| |
| tasks_transformed = [] |
| for task in output.get('tasks', []): |
| task_copy = task.copy() |
| |
| if 'title' not in task_copy: |
| task_copy['title'] = task_copy.get('description', 'Task')[:50] |
| |
| 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]: |
| 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", |
| "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" |
| |
| }, |
| "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") |
| |
| |
| schema_ok = test_schema_structure() |
| template_ok = test_minimal_template() |
| dataset_ok = test_dataset_games() |
| test_task_validation() |
| test_safety_validation() |
| |
| |
| 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() |
|
|