| | """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_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"] |
| |
|
| | |
| | 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.""" |
| | |
| | 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"] |
| |
|
| | |
| | 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") |
| |
|
| | |
| | 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_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.""" |
| | |
| | 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"] |
| |
|
| | |
| | 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.""" |
| | |
| | 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"] |
| |
|
| | |
| | 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 |
| |
|
| | |
| | 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.""" |
| | |
| | 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"] |
| |
|
| | |
| | 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 |
| |
|