| """Integration tests for simulation API endpoints.""" |
|
|
| from __future__ import annotations |
|
|
| import pytest |
| from rest_framework import status |
| from rest_framework.test import APIClient |
|
|
| from apps.profiles.models import DailyProgress, Profile, WallSection |
|
|
|
|
| @pytest.mark.django_db |
| class TestSimulationAPI: |
| """Test cases for simulation API endpoints.""" |
|
|
| def test_simulate_endpoint_creates_profiles_and_sections( |
| self, |
| api_client: APIClient, |
| ) -> None: |
| """Test that simulation endpoint creates profiles and sections.""" |
| config_data = { |
| "config": "21 25 28\n17\n17 22 17 19 17", |
| "num_teams": 4, |
| "start_date": "2025-10-20", |
| } |
|
|
| response = api_client.post("/api/profiles/simulate/", config_data, format="json") |
|
|
| assert response.status_code == status.HTTP_201_CREATED |
| assert Profile.objects.count() == 3 |
| assert WallSection.objects.count() == 9 |
|
|
| def test_simulate_endpoint_runs_simulation( |
| self, |
| api_client: APIClient, |
| ) -> None: |
| """Test that simulation endpoint creates daily progress records.""" |
| config_data = { |
| "config": "28 29", |
| "num_teams": 2, |
| "start_date": "2025-10-20", |
| } |
|
|
| response = api_client.post("/api/profiles/simulate/", config_data, format="json") |
|
|
| assert response.status_code == status.HTTP_201_CREATED |
| assert DailyProgress.objects.count() > 0 |
|
|
| assert response.data["total_sections"] == 2 |
| assert int(response.data["total_days"]) > 0 |
|
|
| def test_simulate_endpoint_validates_config( |
| self, |
| api_client: APIClient, |
| ) -> None: |
| """Test that simulation endpoint validates config.""" |
| config_data = { |
| "config": "21 abc 28", |
| "num_teams": 2, |
| } |
|
|
| response = api_client.post("/api/profiles/simulate/", config_data, format="json") |
|
|
| assert response.status_code == status.HTTP_400_BAD_REQUEST |
| assert "config" in response.data |
| assert "Invalid number format" in str(response.data["config"]) |
|
|
| def test_simulate_endpoint_requires_config( |
| self, |
| api_client: APIClient, |
| ) -> None: |
| """Test that simulation endpoint requires config.""" |
| config_data = { |
| "num_teams": 2, |
| } |
|
|
| response = api_client.post("/api/profiles/simulate/", config_data, format="json") |
|
|
| assert response.status_code == status.HTTP_400_BAD_REQUEST |
| assert "config" in response.data |
| assert "config parameter is required" in str(response.data["config"]) |
|
|
| def test_simulate_endpoint_validates_num_teams( |
| self, |
| api_client: APIClient, |
| ) -> None: |
| """Test that simulation endpoint validates num_teams.""" |
| config_data = { |
| "config": "21 25 28", |
| "num_teams": -1, |
| } |
|
|
| response = api_client.post("/api/profiles/simulate/", config_data, format="json") |
|
|
| assert response.status_code == status.HTTP_400_BAD_REQUEST |
| assert "num_teams" in response.data |
| assert "must be at least" in str(response.data["num_teams"]) |
|
|
| def test_days_endpoint_returns_ice_usage( |
| self, |
| api_client: APIClient, |
| ) -> None: |
| """Test GET /profiles/<id>/days/<day>/ endpoint.""" |
| config_data = { |
| "config": "28 29", |
| "num_teams": 2, |
| "start_date": "2025-10-20", |
| } |
|
|
| api_client.post("/api/profiles/simulate/", config_data, format="json") |
|
|
| profile = Profile.objects.first() |
| assert profile is not None |
|
|
| response = api_client.get(f"/api/profiles/{profile.id}/days/1/") |
|
|
| assert response.status_code == status.HTTP_200_OK |
| assert "day" in response.data |
| assert "total_feet_built" in response.data |
| assert "total_ice_cubic_yards" in response.data |
| assert "sections" in response.data |
| assert response.data["day"] == 1 |
| assert isinstance(response.data["sections"], list) |
| assert len(response.data["sections"]) == 2 |
|
|
| def test_days_endpoint_returns_404_for_no_data( |
| self, |
| api_client: APIClient, |
| ) -> None: |
| """Test days endpoint returns 404 when no simulation data.""" |
| profile = Profile.objects.create(name="Test", team_lead="Lead") |
|
|
| response = api_client.get(f"/api/profiles/{profile.id}/days/1/") |
|
|
| assert response.status_code == status.HTTP_404_NOT_FOUND |
|
|
| def test_overview_by_day_endpoint( |
| self, |
| api_client: APIClient, |
| ) -> None: |
| """Test GET /profiles/<id>/overview/<day>/ endpoint.""" |
| config_data = { |
| "config": "28 29", |
| "num_teams": 2, |
| "start_date": "2025-10-20", |
| } |
|
|
| api_client.post("/api/profiles/simulate/", config_data, format="json") |
|
|
| profile = Profile.objects.first() |
| assert profile is not None |
|
|
| response = api_client.get(f"/api/profiles/{profile.id}/overview/1/") |
|
|
| assert response.status_code == status.HTTP_200_OK |
| assert "day" in response.data |
| assert "cost" in response.data |
| assert response.data["day"] == "1" |
|
|
| def test_overview_all_by_day_endpoint( |
| self, |
| api_client: APIClient, |
| ) -> None: |
| """Test GET /profiles/overview/<day>/ endpoint.""" |
| config_data = { |
| "config": "28\n29", |
| "num_teams": 2, |
| "start_date": "2025-10-20", |
| } |
|
|
| api_client.post("/api/profiles/simulate/", config_data, format="json") |
|
|
| response = api_client.get("/api/profiles/overview/1/") |
|
|
| assert response.status_code == status.HTTP_200_OK |
| assert "day" in response.data |
| assert "cost" in response.data |
| assert response.data["day"] == "1" |
|
|
| def test_overview_total_endpoint( |
| self, |
| api_client: APIClient, |
| ) -> None: |
| """Test GET /profiles/overview/ endpoint returns total days. |
| |
| The endpoint now calculates and returns the actual number of |
| construction days from DailyProgress records instead of None. |
| """ |
| config_data = { |
| "config": "28 29", |
| "num_teams": 2, |
| "start_date": "2025-10-20", |
| } |
|
|
| api_client.post("/api/profiles/simulate/", config_data, format="json") |
|
|
| response = api_client.get("/api/profiles/overview/") |
|
|
| assert response.status_code == status.HTTP_200_OK |
| assert "day" in response.data |
| assert "cost" in response.data |
| |
| assert response.data["day"] > 0 |
| assert isinstance(response.data["day"], int) |
|
|
| def test_spec_example_simulation( |
| self, |
| api_client: APIClient, |
| ) -> None: |
| """Test simulation with spec example data.""" |
| config_data = { |
| "config": "21 25 28\n17\n17 22 17 19 17", |
| "num_teams": 20, |
| "start_date": "2025-10-20", |
| } |
|
|
| response = api_client.post("/api/profiles/simulate/", config_data, format="json") |
|
|
| assert response.status_code == status.HTTP_201_CREATED |
| assert response.data["total_sections"] == 9 |
|
|
| profile_1 = Profile.objects.get(name="Profile 1") |
|
|
| day_1_response = api_client.get(f"/api/profiles/{profile_1.id}/days/1/") |
| assert day_1_response.status_code == status.HTTP_200_OK |
| assert day_1_response.data["total_ice_cubic_yards"] == "585" |
|
|
| overview_response = api_client.get("/api/profiles/overview/") |
| assert overview_response.status_code == status.HTTP_200_OK |
|
|