import pytest from httpx import AsyncClient from app.models.contest import Contest from app.models.problem import Problem from app.models.test_case import TestCase class TestListProblems: """Tests for listing problems by contest.""" async def test_list_problems_empty( self, client: AsyncClient, auth_headers: dict, test_contest: Contest ): """Test listing problems when none exist.""" response = await client.get( f"/api/problems/contest/{test_contest.id}", headers=auth_headers ) assert response.status_code == 200 assert response.json() == [] async def test_list_problems_with_data( self, client: AsyncClient, auth_headers: dict, test_contest: Contest, test_problem: Problem, ): """Test listing problems with existing data.""" response = await client.get( f"/api/problems/contest/{test_contest.id}", headers=auth_headers ) assert response.status_code == 200 data = response.json() assert len(data) == 1 assert data[0]["title"] == test_problem.title class TestGetProblem: """Tests for getting a single problem.""" async def test_get_problem_success( self, client: AsyncClient, auth_headers: dict, test_problem: Problem ): """Test getting an existing problem.""" response = await client.get( f"/api/problems/{test_problem.id}", headers=auth_headers ) assert response.status_code == 200 data = response.json() assert data["id"] == test_problem.id assert data["title"] == test_problem.title assert data["description"] == test_problem.description async def test_get_problem_not_found(self, client: AsyncClient, auth_headers: dict): """Test getting a non-existent problem.""" response = await client.get("/api/problems/99999", headers=auth_headers) assert response.status_code == 404 async def test_get_problem_includes_sample_tests( self, client: AsyncClient, auth_headers: dict, test_problem: Problem, test_cases: list[TestCase], ): """Test that getting a problem includes sample tests.""" response = await client.get( f"/api/problems/{test_problem.id}", headers=auth_headers ) assert response.status_code == 200 data = response.json() # Should include sample tests assert "sample_tests" in data # Only sample tests should be visible (is_sample=True) sample_count = sum(1 for tc in test_cases if tc.is_sample) assert len(data["sample_tests"]) == sample_count class TestCreateProblem: """Tests for creating problems.""" async def test_create_problem_as_admin( self, client: AsyncClient, admin_headers: dict, test_contest: Contest ): """Test creating a problem as admin.""" response = await client.post( "/api/problems/", headers=admin_headers, json={ "contest_id": test_contest.id, "title": "New Problem", "description": "Solve this problem", "input_format": "One integer n", "output_format": "One integer", "constraints": "1 <= n <= 100", "time_limit_ms": 2000, "memory_limit_kb": 131072, "total_points": 50, }, ) assert response.status_code == 201 data = response.json() assert data["title"] == "New Problem" assert data["total_points"] == 50 async def test_create_problem_as_participant( self, client: AsyncClient, auth_headers: dict, test_contest: Contest ): """Test that participants cannot create problems.""" response = await client.post( "/api/problems/", headers=auth_headers, json={ "contest_id": test_contest.id, "title": "New Problem", "description": "Solve this problem", }, ) assert response.status_code == 403 async def test_create_problem_with_test_cases( self, client: AsyncClient, admin_headers: dict, test_contest: Contest ): """Test creating a problem with test cases.""" response = await client.post( "/api/problems/", headers=admin_headers, json={ "contest_id": test_contest.id, "title": "Problem with Tests", "description": "A problem with test cases", "test_cases": [ { "input": "1 2", "expected_output": "3", "is_sample": True, "points": 25, }, { "input": "5 5", "expected_output": "10", "is_sample": False, "points": 75, }, ], }, ) assert response.status_code == 201 data = response.json() assert len(data["sample_tests"]) == 1 # Only sample tests in response class TestUpdateProblem: """Tests for updating problems.""" async def test_update_problem_as_admin( self, client: AsyncClient, admin_headers: dict, test_problem: Problem ): """Test updating a problem as admin.""" response = await client.put( f"/api/problems/{test_problem.id}", headers=admin_headers, json={"title": "Updated Problem Title"}, ) assert response.status_code == 200 data = response.json() assert data["title"] == "Updated Problem Title" async def test_update_problem_as_participant( self, client: AsyncClient, auth_headers: dict, test_problem: Problem ): """Test that participants cannot update problems.""" response = await client.put( f"/api/problems/{test_problem.id}", headers=auth_headers, json={"title": "Updated Title"}, ) assert response.status_code == 403 class TestDeleteProblem: """Tests for deleting problems.""" async def test_delete_problem_as_admin( self, client: AsyncClient, admin_headers: dict, test_problem: Problem ): """Test deleting a problem as admin.""" response = await client.delete( f"/api/problems/{test_problem.id}", headers=admin_headers ) assert response.status_code == 204 # Verify it's deleted response = await client.get( f"/api/problems/{test_problem.id}", headers=admin_headers ) assert response.status_code == 404 class TestTestCases: """Tests for test case management.""" async def test_get_test_cases_as_admin( self, client: AsyncClient, admin_headers: dict, test_problem: Problem, test_cases: list[TestCase], ): """Test getting all test cases as admin.""" response = await client.get( f"/api/problems/{test_problem.id}/test-cases", headers=admin_headers ) assert response.status_code == 200 data = response.json() assert len(data) == len(test_cases) async def test_add_test_case_as_admin( self, client: AsyncClient, admin_headers: dict, test_problem: Problem ): """Test adding a test case as admin.""" response = await client.post( f"/api/problems/{test_problem.id}/test-cases", headers=admin_headers, json={ "input": "10 20", "expected_output": "30", "is_sample": True, "points": 25, }, ) assert response.status_code == 201 data = response.json() assert data["input"] == "10 20" assert data["expected_output"] == "30" async def test_delete_test_case_as_admin( self, client: AsyncClient, admin_headers: dict, test_cases: list[TestCase], ): """Test deleting a test case as admin.""" test_case = test_cases[0] response = await client.delete( f"/api/problems/test-cases/{test_case.id}", headers=admin_headers ) assert response.status_code == 204