""" Comprehensive API testing with known positions. Tests tactical-vision and game-review endpoints. """ import requests import json import time BASE_URL = "https://anandu467-maia3-chess-api.hf.space" API_KEY = "sk-maia3-2026" HEADERS = { "Content-Type": "application/json", "x-api-key": API_KEY } def test_position(name, fen, player_color, expected_checks=None): """Test a position and verify expectations""" print(f"\n{'='*70}") print(f"TEST: {name}") print(f"{'='*70}") print(f"FEN: {fen}") print(f"Player: {player_color}") payload = { "fen": fen, "elo_self": 1500, "elo_oppo": 1500, "top_n": 5, "player_color": player_color, "compute_value_delta": True } t0 = time.time() r = requests.post(f"{BASE_URL}/api/tactical-vision", json=payload, headers=HEADERS, timeout=120) elapsed = (time.time() - t0) * 1000 print(f"Status: {r.status_code} ({elapsed:.0f}ms)") if r.status_code != 200: print(f"ERROR: {r.text[:200]}") return False data = r.json() # Display key information threats = data.get("threats", {}) alerts = data.get("tactical_alerts", []) top_moves = data.get("top_moves", []) position = data.get("position", {}) print(f"\nPosition Analysis:") print(f" Win probability: {position.get('win_probability', 0):.1%}") print(f" Eval: {position.get('eval_cp', 0)}cp") print(f" Confidence: {position.get('confidence', 'unknown')}") print(f" Danger level: {position.get('danger_level', 'unknown')}") print(f"\nTop 3 moves:") for i, move in enumerate(top_moves[:3], 1): tags = f" [{', '.join(move.get('tactical_tags', []))}]" if move.get('tactical_tags') else "" delta = f" (delta: {move.get('eval_delta_cp', 0):+d}cp)" if move.get('eval_delta_cp') is not None else "" print(f" {i}. {move['san']} - {move['probability']:.1%} - {move['classification']}{tags}{delta}") print(f"\nThreats:") print(f" Hanging pieces: {threats.get('hanging_pieces', [])}") print(f" Attacked pieces: {threats.get('attacked_pieces', [])}") print(f" Underdefended: {threats.get('underdefended_squares', [])}") print(f" King safety: {threats.get('king_safety_concern', False)}") print(f"\nTactical alerts: {len(alerts)}") for alert in alerts[:5]: # Show first 5 print(f" - {alert['type']}: {alert['description']}") if len(alerts) > 5: print(f" ... and {len(alerts) - 5} more") # Verify expectations if expected_checks: print(f"\nVerification:") all_pass = True for check_name, check_func in expected_checks.items(): try: result = check_func(data) status = "PASS" if result else "FAIL" print(f" {check_name}: {status}") if not result: all_pass = False except Exception as e: print(f" {check_name}: ERROR - {e}") all_pass = False return all_pass return True # Test 1: Starting position test_position( "Starting Position", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", "white", { "No hanging pieces": lambda d: len(d.get("threats", {}).get("hanging_pieces", [])) == 0, "No tactical alerts": lambda d: len(d.get("tactical_alerts", [])) == 0, "Has top moves": lambda d: len(d.get("top_moves", [])) >= 3, "First move is e4 or d4": lambda d: d["top_moves"][0]["san"] in ["e4", "d4", "Nf3", "c4"] } ) # Test 2: Scholar's mate setup (Qh5 threatening Qxf7#) test_position( "Scholar's Mate Setup (White to play Qxf7#)", "r1bqkbnr/pppp1ppp/2n5/4p3/2B1P3/5Q2/PPPP1PPP/RNB1K1NR w KQkq - 4 4", "white", { "Has checkmate move": lambda d: any(m.get("is_mate") for m in d.get("top_moves", [])), "Qxf7 is top move": lambda d: d["top_moves"][0]["uci"] == "f3f7", "High win probability": lambda d: d.get("position", {}).get("win_probability", 0) > 0.9 } ) # Test 3: Hanging piece (knight on e5 attacked by d6 pawn, no defenders) test_position( "Hanging Knight on e5", "rnbqkbnr/pppp1ppp/3p4/4N3/8/8/PPPPPPPP/RNBQKB1R w KQkq - 0 1", "black", { "Detects hanging piece": lambda d: "e5" in d.get("threats", {}).get("hanging_pieces", []), "Has hanging_piece alert": lambda d: any(a["type"] == "hanging_piece" and a["square"] == "e5" for a in d.get("tactical_alerts", [])) } ) # Test 4: Fork opportunity (knight can fork king and rook) test_position( "Knight Fork Opportunity (Nc7+ forking king and rook)", "r3k2r/ppp2ppp/2n1b3/3q4/3P4/2N1B3/PPP2PPP/R2QK2R w KQkq - 0 1", "white", { "Has tactical tags": lambda d: any(m.get("tactical_tags") for m in d.get("top_moves", [])), "Fork detected": lambda d: any("fork" in m.get("tactical_tags", []) for m in d.get("top_moves", [])) } ) # Test 5: Pin (bishop pins knight to king) test_position( "Absolute Pin (Bb5 pins Nc6 to king)", "r1bqkbnr/pppp1ppp/2n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R b KQkq - 3 3", "black", { "Has top moves": lambda d: len(d.get("top_moves", [])) >= 3, "Reasonable eval": lambda d: abs(d.get("position", {}).get("eval_cp", 0)) < 200 } ) # Test 6: Back rank mate threat test_position( "Back Rank Mate Threat", "6k1/5ppp/8/8/8/8/5PPP/R5K1 w - - 0 1", "white", { "Has top moves": lambda d: len(d.get("top_moves", [])) >= 3, "Ra8 might be suggested": lambda d: any(m["uci"] == "a1a8" for m in d.get("top_moves", [])) } ) # Test 7: Endgame position (king and pawn vs king) test_position( "King and Pawn Endgame", "8/8/8/8/8/5k2/5P2/6K1 w - - 0 1", "white", { "Has top moves": lambda d: len(d.get("top_moves", [])) >= 1, "Pawn push is good": lambda d: any(m["uci"].startswith("f2") for m in d.get("top_moves", [])) } ) # Test 8: Position with multiple tactical themes test_position( "Complex Tactical Position", "r1bq1rk1/pp2ppbp/2np1np1/8/3NP3/2N1B3/PPP1BPPP/R2Q1RK1 w - - 0 9", "white", { "Has top moves": lambda d: len(d.get("top_moves", [])) >= 3, "Reasonable confidence": lambda d: d.get("position", {}).get("confidence") in ["clear", "complex", "confusing"] } ) # Test 9: Game review with known game (Immortal Game excerpt) print(f"\n{'='*70}") print("TEST: Game Review (Short Tactical Game)") print(f"{'='*70}") moves = ["e2e4", "e7e5", "f2f4", "e5f4", "f1c4", "d8h4", "e1f1", "b7b5"] payload = { "moves": moves, "elo_white": 1800, "elo_black": 1800 } t0 = time.time() r = requests.post(f"{BASE_URL}/api/game-review", json=payload, headers=HEADERS, timeout=300) elapsed = (time.time() - t0) * 1000 print(f"Status: {r.status_code} ({elapsed:.0f}ms)") if r.status_code == 200: data = r.json() summary = data.get("summary", {}) print(f"\nGame Summary:") print(f" Consistency: {summary.get('consistency_score', 0):.2f}") print(f" Label: {summary.get('consistency_label', 'unknown')}") print(f" Blunders: {summary.get('blunder_count', 0)}") print(f" Mistakes: {summary.get('mistake_count', 0)}") print(f" Good moves: {summary.get('good_count', 0)}") print(f" Excellent moves: {summary.get('excellent_count', 0)}") print(f"\nPhase Breakdown:") for phase in ["opening", "middlegame", "endgame"]: stats = data.get("phase_breakdown", {}).get(phase, {}) if stats.get("moves", 0) > 0: print(f" {phase}: {stats['moves']} moves, consistency {stats['consistency_score']:.2f}") print(f"\nMove Analysis (first 4):") for move in data.get("moves", [])[:4]: print(f" {move['move_number']}. {move['side']} {move['san']} - {move['classification']} ({move['probability']:.1%})") print(f"\nRecommendations:") for rec in data.get("recommendations", [])[:3]: print(f" - {rec}") else: print(f"ERROR: {r.text[:200]}") print(f"\n{'='*70}") print("All tests completed!") print(f"{'='*70}")