Spaces:
Running
Running
| from __future__ import annotations | |
| import os | |
| import tempfile | |
| import unittest | |
| from pathlib import Path | |
| from fastapi.testclient import TestClient | |
| os.environ["TOPOBENCH_SKIP_AUTOAPP"] = "1" | |
| from space_app.config import Settings | |
| from space_app.dataset import DatasetStore | |
| from space_app.db import SessionStore | |
| from space_app.main import create_app | |
| from space_app.models import DatasetRow | |
| class FakeVerifier: | |
| def verify(self, *, puzzle_type: str, problem_ascii: str, board_ascii: str, args: str): | |
| return { | |
| "board_exists": True, | |
| "board_valid": True, | |
| "board_modified": False, | |
| "correct": board_ascii == problem_ascii, | |
| "puzzle_type": puzzle_type, | |
| "args": args, | |
| } | |
| class ApiTests(unittest.TestCase): | |
| def setUp(self) -> None: | |
| self.tempdir = tempfile.TemporaryDirectory() | |
| data_dir = Path(self.tempdir.name) | |
| self.settings = Settings( | |
| data_dir=data_dir, | |
| database_path=data_dir / "test.sqlite", | |
| hf_cache_dir=data_dir / "hf-cache", | |
| admin_api_token="secret-token", | |
| frontend_dist_dir=data_dir / "frontend-dist", | |
| host="127.0.0.1", | |
| port=7860, | |
| ) | |
| self.settings.prepare() | |
| rows = [ | |
| DatasetRow( | |
| filename="bridges_1.txt", | |
| puzzlename="bridges", | |
| args="5x5de", | |
| problem="1.1\n...\n1.1", | |
| solution="1-1\n|.|\n1-1", | |
| difficulty="easy", | |
| ) | |
| ] | |
| self.app = create_app( | |
| settings=self.settings, | |
| dataset_store=DatasetStore(rows), | |
| session_store=SessionStore(self.settings.database_path), | |
| verifier=FakeVerifier(), | |
| ) | |
| self.client = TestClient(self.app) | |
| def tearDown(self) -> None: | |
| self.tempdir.cleanup() | |
| def test_session_lifecycle(self) -> None: | |
| list_response = self.client.get( | |
| "/api/puzzles", | |
| params={"puzzle_type": "bridges", "difficulty": "easy"}, | |
| ) | |
| self.assertEqual(list_response.status_code, 200) | |
| options = list_response.json() | |
| self.assertEqual(len(options), 1) | |
| self.assertEqual(options[0]["puzzle_id"], "bridges_1.txt") | |
| create_response = self.client.post( | |
| "/api/sessions", | |
| json={ | |
| "player_name": "Ada Lovelace", | |
| "puzzle_type": "bridges", | |
| "difficulty": "easy", | |
| "puzzle_id": "bridges_1.txt", | |
| }, | |
| ) | |
| self.assertEqual(create_response.status_code, 200) | |
| session = create_response.json() | |
| self.assertEqual(session["puzzle_type"], "bridges") | |
| self.assertEqual(session["payload"]["problem_ascii"], "1.1\n...\n1.1") | |
| def test_session_requires_matching_selected_puzzle(self) -> None: | |
| create_response = self.client.post( | |
| "/api/sessions", | |
| json={ | |
| "player_name": "Ada Lovelace", | |
| "puzzle_type": "flow_free", | |
| "difficulty": "easy", | |
| "puzzle_id": "bridges_1.txt", | |
| }, | |
| ) | |
| self.assertEqual(create_response.status_code, 400) | |
| def test_ready_and_submit_lifecycle(self) -> None: | |
| create_response = self.client.post( | |
| "/api/sessions", | |
| json={ | |
| "player_name": "Ada Lovelace", | |
| "puzzle_type": "bridges", | |
| "difficulty": "easy", | |
| "puzzle_id": "bridges_1.txt", | |
| }, | |
| ) | |
| session = create_response.json() | |
| ready_response = self.client.post(f"/api/sessions/{session['session_id']}/ready") | |
| self.assertEqual(ready_response.status_code, 200) | |
| self.assertIsNotNone(ready_response.json()["started_at"]) | |
| submit_response = self.client.post( | |
| f"/api/sessions/{session['session_id']}/submit", | |
| json={"board_ascii": "1.1\n...\n1.1"}, | |
| ) | |
| self.assertEqual(submit_response.status_code, 200) | |
| payload = submit_response.json() | |
| self.assertTrue(payload["solved"]) | |
| self.assertEqual(payload["status"], "solved") | |
| def test_admin_endpoints_require_token(self) -> None: | |
| unauthorized = self.client.get("/api/admin/solves") | |
| self.assertEqual(unauthorized.status_code, 401) | |
| authorized = self.client.get( | |
| "/api/admin/aggregates", | |
| headers={"Authorization": "Bearer secret-token"}, | |
| ) | |
| self.assertEqual(authorized.status_code, 200) | |