import os import uuid import pytest from fastapi.testclient import TestClient # Ensure required env vars for app import/config os.environ.setdefault("SECRET_KEY", "x" * 32) os.environ.setdefault("DATABASE_URL", "sqlite:////tmp/delphi_test.sqlite") from app.main import app # noqa: E402 from app.auth.security import get_current_user # noqa: E402 @pytest.fixture(scope="module") def client(): # Override auth to bypass JWT for these tests class _User: def __init__(self): self.id = "test" self.username = "tester" self.is_admin = True self.is_active = True app.dependency_overrides[get_current_user] = lambda: _User() try: yield TestClient(app) finally: app.dependency_overrides.pop(get_current_user, None) def _assert_validation_error(resp, field_name: str): assert resp.status_code == 422 body = resp.json() assert body.get("success") is False assert body.get("error", {}).get("code") == "validation_error" # Ensure correlation id is present and echoed in header cid = body.get("correlation_id") assert isinstance(cid, str) and cid assert resp.headers.get("X-Correlation-ID") == cid # Ensure the field appears in details details = body.get("error", {}).get("details", []) assert any(field_name in ":".join(map(str, err.get("loc", []))) for err in details) def _assert_http_error(resp, status_code: int, message_substr: str): assert resp.status_code == status_code body = resp.json() assert body.get("success") is False assert body.get("error", {}).get("code") == "http_error" assert message_substr in body.get("error", {}).get("message", "") cid = body.get("correlation_id") assert isinstance(cid, str) and cid assert resp.headers.get("X-Correlation-ID") == cid def test_create_customer_invalid_email_returns_422(client: TestClient): customer_id = f"SCHEMA-{uuid.uuid4().hex[:8]}" payload = { "id": customer_id, "last": "InvalidEmail", "email": "not-an-email", } resp = client.post("/api/customers/", json=payload) _assert_validation_error(resp, "email") def test_update_customer_invalid_email_returns_422(client: TestClient): customer_id = f"SCHEMA-UPD-{uuid.uuid4().hex[:8]}" # Create valid customer first create_payload = { "id": customer_id, "last": "Valid", "email": "ok@example.com", } resp = client.post("/api/customers/", json=create_payload) assert resp.status_code == 200 # Attempt invalid email on update resp = client.put(f"/api/customers/{customer_id}", json={"email": "bad"}) _assert_validation_error(resp, "email") # Cleanup resp = client.delete(f"/api/customers/{customer_id}") assert resp.status_code == 200 def test_create_customer_duplicate_id_returns_400(client: TestClient): customer_id = f"DUP-{uuid.uuid4().hex[:8]}" payload = { "id": customer_id, "last": "Doe", "email": "john.doe@example.com", } # First create OK resp = client.post("/api/customers/", json=payload) assert resp.status_code == 200 # Duplicate should be 400 with envelope resp = client.post("/api/customers/", json=payload) _assert_http_error(resp, 400, "Customer ID already exists") # Cleanup resp = client.delete(f"/api/customers/{customer_id}") assert resp.status_code == 200 def test_get_update_delete_nonexistent_customer_404(client: TestClient): missing_id = f"NOPE-{uuid.uuid4().hex[:8]}" resp = client.get(f"/api/customers/{missing_id}") _assert_http_error(resp, 404, "Customer not found") resp = client.put(f"/api/customers/{missing_id}", json={"last": "X"}) _assert_http_error(resp, 404, "Customer not found") resp = client.delete(f"/api/customers/{missing_id}") _assert_http_error(resp, 404, "Customer not found") def test_phones_endpoints_404_for_missing_customer_and_phone(client: TestClient): missing_id = f"NOPE-{uuid.uuid4().hex[:8]}" # Missing customer: get and add phone resp = client.get(f"/api/customers/{missing_id}/phones") _assert_http_error(resp, 404, "Customer not found") resp = client.post( f"/api/customers/{missing_id}/phones", json={"location": "Office", "phone": "(555) 000-0000"}, ) _assert_http_error(resp, 404, "Customer not found") # Create a real customer to test non-existent phone id real_id = f"PHONE-{uuid.uuid4().hex[:8]}" resp = client.post( "/api/customers/", json={"id": real_id, "last": "Phones", "email": "phones@example.com"}, ) assert resp.status_code == 200 # Update non-existent phone for this customer resp = client.put( f"/api/customers/{real_id}/phones/999999", json={"location": "Home", "phone": "(555) 111-2222"}, ) _assert_http_error(resp, 404, "Phone number not found") # Delete non-existent phone for this customer resp = client.delete(f"/api/customers/{real_id}/phones/999999") _assert_http_error(resp, 404, "Phone number not found") # Cleanup resp = client.delete(f"/api/customers/{real_id}") assert resp.status_code == 200 def test_list_customers_query_param_validation_422(client: TestClient): # limit must be >=1 and <=200 resp = client.get("/api/customers/?limit=0") _assert_validation_error(resp, "limit") # skip must be >=0 resp = client.get("/api/customers/?skip=-1") _assert_validation_error(resp, "skip")