""" Tests for API enhancements. Tests cover: - Standardized response format - API versioning - Enhanced health check endpoints - Project templates """ import os os.environ["TESTING"] = "true" import pytest class TestStandardizedResponse: """Test standardized API response format.""" def test_success_response_structure(self, client, admin_token, db): """Test that success responses have standard structure.""" from app.models import Space space = Space(id="resp-space", name="Response Test", owner_id="00000000-0000-0000-0000-000000000001") db.add(space) db.commit() response = client.get( "/api/spaces", headers={"Authorization": f"Bearer {admin_token}"} ) assert response.status_code == 200 data = response.json() # Response should be either wrapped or direct data # Depending on implementation, check for standard fields assert data is not None # If wrapped: assert "success" in data and "data" in data # If direct: assert isinstance(data, (list, dict)) def test_error_response_structure(self, client, admin_token): """Test that error responses have standard structure.""" # Request non-existent resource response = client.get( "/api/spaces/non-existent-id", headers={"Authorization": f"Bearer {admin_token}"} ) assert response.status_code == 404 data = response.json() # Error response should have detail field assert "detail" in data or "message" in data or "error" in data class TestAPIVersioning: """Test API versioning with /api/v1 prefix.""" def test_v1_routes_accessible(self, client, admin_token, db): """Test that /api/v1 routes are accessible.""" from app.models import Space space = Space(id="v1-space", name="V1 Test", owner_id="00000000-0000-0000-0000-000000000001") db.add(space) db.commit() # Try v1 endpoint response = client.get( "/api/v1/spaces", headers={"Authorization": f"Bearer {admin_token}"} ) # Should be 200 if v1 routes exist, or 404 if not yet migrated assert response.status_code in [200, 404] def test_legacy_routes_still_work(self, client, admin_token, db): """Test that legacy /api routes still work during transition.""" from app.models import Space space = Space(id="legacy-space", name="Legacy Test", owner_id="00000000-0000-0000-0000-000000000001") db.add(space) db.commit() response = client.get( "/api/spaces", headers={"Authorization": f"Bearer {admin_token}"} ) assert response.status_code == 200 def test_deprecation_headers(self, client, admin_token, db): """Test that deprecated routes include deprecation headers.""" from app.models import Space space = Space(id="deprecation-space", name="Deprecation Test", owner_id="00000000-0000-0000-0000-000000000001") db.add(space) db.commit() response = client.get( "/api/spaces", headers={"Authorization": f"Bearer {admin_token}"} ) # Check for deprecation header (if implemented) # This is optional depending on implementation # assert "Deprecation" in response.headers or "Sunset" in response.headers class TestEnhancedHealthCheck: """Test enhanced health check endpoints.""" def test_health_endpoint_returns_status(self, client): """Test basic health endpoint.""" response = client.get("/health") assert response.status_code == 200 data = response.json() assert "status" in data or data == {"status": "healthy"} def test_health_live_endpoint(self, client): """Test /health/live endpoint for liveness probe.""" response = client.get("/health/live") assert response.status_code == 200 data = response.json() assert data.get("status") == "alive" or "live" in str(data).lower() or "healthy" in str(data).lower() def test_health_ready_endpoint(self, client, db): """Test /health/ready endpoint for readiness probe.""" response = client.get("/health/ready") assert response.status_code == 200 data = response.json() # Should include component checks assert "status" in data or "ready" in str(data).lower() def test_health_includes_database_check(self, client, db): """Test that health check includes database connectivity.""" response = client.get("/health/ready") if response.status_code == 200: data = response.json() # Check if database status is included if "checks" in data or "components" in data or "database" in data: checks = data.get("checks", data.get("components", data)) # Database should be checked assert "database" in str(checks).lower() or "db" in str(checks).lower() or data.get("status") == "ready" def test_health_includes_redis_check(self, client, mock_redis): """Test that health check includes Redis connectivity.""" response = client.get("/health/ready") if response.status_code == 200: data = response.json() # Redis check may or may not be included based on implementation class TestProjectTemplates: """Test project template functionality.""" def test_list_templates(self, client, admin_token, db): """Test listing available project templates.""" response = client.get( "/api/templates", headers={"Authorization": f"Bearer {admin_token}"} ) assert response.status_code == 200 data = response.json() # Should return list of templates assert "templates" in data or isinstance(data, list) def test_create_template(self, client, auth_headers, db): """Test creating a new project template.""" from app.models import Space space = Space(id="template-space", name="Template Space", owner_id="00000000-0000-0000-0000-000000000001") db.add(space) db.commit() response = client.post( "/api/templates", json={ "name": "Test Template", "description": "A test template", "default_statuses": [ {"name": "To Do", "color": "#808080"}, {"name": "In Progress", "color": "#0000FF"}, {"name": "Done", "color": "#00FF00"} ] }, headers=auth_headers ) assert response.status_code in [200, 201] data = response.json() assert data.get("name") == "Test Template" def test_create_project_from_template(self, client, auth_headers, db): """Test creating a project from a template.""" from app.models import Space, ProjectTemplate space = Space(id="from-template-space", name="From Template Space", owner_id="00000000-0000-0000-0000-000000000001") db.add(space) template = ProjectTemplate( id="test-template-id", name="Test Template", description="Test", default_statuses=[ {"name": "Backlog", "color": "#808080"}, {"name": "Active", "color": "#0000FF"}, {"name": "Complete", "color": "#00FF00"} ], created_by="00000000-0000-0000-0000-000000000001" ) db.add(template) db.commit() # Create project from template response = client.post( "/api/spaces/from-template-space/projects", json={ "name": "Project from Template", "description": "Created from template", "template_id": "test-template-id" }, headers=auth_headers ) assert response.status_code in [200, 201] data = response.json() assert data.get("name") == "Project from Template" def test_delete_template(self, client, auth_headers, db): """Test deleting a project template.""" from app.models import ProjectTemplate template = ProjectTemplate( id="delete-template-id", name="Template to Delete", description="Will be deleted", default_statuses=[], created_by="00000000-0000-0000-0000-000000000001" ) db.add(template) db.commit() response = client.delete( "/api/templates/delete-template-id", headers=auth_headers ) assert response.status_code in [200, 204]