"""Deployment tests for Wall Construction API. Tests that verify the full stack is properly deployed and functional: - Database migrations have run successfully - API endpoints are accessible and return correct status codes - CRUD operations work end-to-end - Data persistence works correctly """ from __future__ import annotations import pytest from django.urls import reverse from rest_framework import status from rest_framework.test import APIClient @pytest.mark.django_db @pytest.mark.deployment class TestDeploymentHealth: """Test deployment health and basic API functionality.""" def test_api_root_accessible(self, api_client: APIClient) -> None: """Test API root endpoint is accessible.""" url = "/" response = api_client.get(url) assert response.status_code in ( status.HTTP_200_OK, status.HTTP_301_MOVED_PERMANENTLY, status.HTTP_302_FOUND, ) def test_profiles_endpoint_accessible(self, api_client: APIClient) -> None: """Test profiles list endpoint returns 200 with empty database.""" url = reverse("profile-list") response = api_client.get(url) assert response.status_code == status.HTTP_200_OK assert "results" in response.data assert "count" in response.data assert isinstance(response.data["results"], list) def test_wallsections_endpoint_accessible(self, api_client: APIClient) -> None: """Test wallsections list endpoint returns 200 with empty database.""" url = reverse("wallsection-list") response = api_client.get(url) assert response.status_code == status.HTTP_200_OK assert "results" in response.data assert "count" in response.data assert isinstance(response.data["results"], list) @pytest.mark.django_db @pytest.mark.deployment class TestDeploymentEndToEnd: """Test end-to-end workflows to verify deployment integrity.""" def test_create_profile_and_retrieve(self, api_client: APIClient) -> None: """Test creating a profile and retrieving it verifies database works.""" list_url = reverse("profile-list") # Create profile create_payload = { "name": "Deployment Test Profile", "team_lead": "Test Lead", "is_active": True, } create_response = api_client.post(list_url, create_payload, format="json") assert create_response.status_code == status.HTTP_201_CREATED assert create_response.data["name"] == "Deployment Test Profile" assert "id" in create_response.data profile_id = create_response.data["id"] # Retrieve profile detail_url = reverse("profile-detail", kwargs={"pk": profile_id}) retrieve_response = api_client.get(detail_url) assert retrieve_response.status_code == status.HTTP_200_OK assert retrieve_response.data["id"] == profile_id assert retrieve_response.data["name"] == "Deployment Test Profile" assert retrieve_response.data["team_lead"] == "Test Lead" def test_create_wallsection_with_profile(self, api_client: APIClient) -> None: """Test creating wallsection with profile verifies foreign keys work.""" # Create profile first profile_url = reverse("profile-list") profile_payload = {"name": "Wall Builder", "team_lead": "Builder Lead"} profile_response = api_client.post( profile_url, profile_payload, format="json", ) assert profile_response.status_code == status.HTTP_201_CREATED profile_id = profile_response.data["id"] # Create wallsection wallsection_url = reverse("wallsection-list") wallsection_payload = { "profile": profile_id, "section_name": "Section 1", } wallsection_response = api_client.post( wallsection_url, wallsection_payload, format="json", ) assert wallsection_response.status_code == status.HTTP_201_CREATED assert wallsection_response.data["profile"] == profile_id assert wallsection_response.data["section_name"] == "Section 1" assert "id" in wallsection_response.data def test_list_profiles_after_creation(self, api_client: APIClient) -> None: """Test listing profiles returns created profiles.""" url = reverse("profile-list") # Create multiple profiles for i in range(3): payload = { "name": f"Profile {i}", "team_lead": f"Lead {i}", } create_response = api_client.post(url, payload, format="json") assert create_response.status_code == status.HTTP_201_CREATED # List all profiles list_response = api_client.get(url) assert list_response.status_code == status.HTTP_200_OK assert list_response.data["count"] == 3 assert len(list_response.data["results"]) == 3 def test_update_profile(self, api_client: APIClient) -> None: """Test updating a profile verifies write operations work.""" # Create profile list_url = reverse("profile-list") create_payload = {"name": "Original Name", "team_lead": "Original Lead"} create_response = api_client.post(list_url, create_payload, format="json") assert create_response.status_code == status.HTTP_201_CREATED profile_id = create_response.data["id"] # Update profile detail_url = reverse("profile-detail", kwargs={"pk": profile_id}) update_payload = { "name": "Updated Name", "team_lead": "Updated Lead", "is_active": False, } update_response = api_client.put(detail_url, update_payload, format="json") assert update_response.status_code == status.HTTP_200_OK assert update_response.data["name"] == "Updated Name" assert update_response.data["team_lead"] == "Updated Lead" assert update_response.data["is_active"] is False def test_delete_profile(self, api_client: APIClient) -> None: """Test deleting a profile verifies delete operations work.""" # Create profile list_url = reverse("profile-list") create_payload = {"name": "To Delete", "team_lead": "Delete Lead"} create_response = api_client.post(list_url, create_payload, format="json") assert create_response.status_code == status.HTTP_201_CREATED profile_id = create_response.data["id"] # Delete profile detail_url = reverse("profile-detail", kwargs={"pk": profile_id}) delete_response = api_client.delete(detail_url) assert delete_response.status_code == status.HTTP_204_NO_CONTENT # Verify deletion retrieve_response = api_client.get(detail_url) assert retrieve_response.status_code == status.HTTP_404_NOT_FOUND @pytest.mark.django_db @pytest.mark.deployment class TestDatabaseMigrations: """Test that database migrations have been applied correctly.""" def test_profile_model_exists(self, api_client: APIClient) -> None: """Test Profile model table exists and is accessible.""" url = reverse("profile-list") response = api_client.get(url) assert response.status_code == status.HTTP_200_OK def test_wallsection_model_exists(self, api_client: APIClient) -> None: """Test WallSection model table exists and is accessible.""" url = reverse("wallsection-list") response = api_client.get(url) assert response.status_code == status.HTTP_200_OK def test_profile_fields_exist(self, api_client: APIClient) -> None: """Test Profile model has all required fields.""" url = reverse("profile-list") payload = { "name": "Field Test", "team_lead": "Test Lead", "is_active": True, } response = api_client.post(url, payload, format="json") assert response.status_code == status.HTTP_201_CREATED assert "id" in response.data assert "name" in response.data assert "team_lead" in response.data assert "is_active" in response.data assert "created_at" in response.data assert "updated_at" in response.data def test_wallsection_fields_exist(self, api_client: APIClient) -> None: """Test WallSection model has all required fields.""" # Create profile first profile_url = reverse("profile-list") profile_response = api_client.post( profile_url, {"name": "Test", "team_lead": "Lead"}, format="json", ) profile_id = profile_response.data["id"] # Create wallsection url = reverse("wallsection-list") payload = { "profile": profile_id, "section_name": "Test Section", } response = api_client.post(url, payload, format="json") assert response.status_code == status.HTTP_201_CREATED assert "id" in response.data assert "profile" in response.data assert "section_name" in response.data assert "created_at" in response.data