161 lines
5.9 KiB
Python
161 lines
5.9 KiB
Python
"""
|
|
End-to-end tests for the OpenAPI-first Veterinary Clinic example app.
|
|
|
|
These tests validate that all CRUD operations behave correctly
|
|
against the in-memory mock data store using Pydantic models.
|
|
"""
|
|
|
|
from fastapi.testclient import TestClient
|
|
|
|
from main import app
|
|
from openapi_first.loader import load_openapi
|
|
from openapi_first.client import OpenAPIClient
|
|
|
|
|
|
test_client = TestClient(app)
|
|
spec = load_openapi("openapi.yaml")
|
|
client = OpenAPIClient(
|
|
spec=spec,
|
|
base_url="http://testserver",
|
|
client=test_client,
|
|
)
|
|
|
|
# Bootstrap CSRF token via a safe GET request
|
|
_ = client.list_parents(query={"limit": 1})
|
|
_CSRF_TOKEN = client.client.cookies.get("csrftoken")
|
|
|
|
|
|
def _csrf_headers() -> dict:
|
|
return {"X-CSRFToken": _CSRF_TOKEN} if _CSRF_TOKEN else {}
|
|
|
|
|
|
def test_list_parents():
|
|
"""List parents returns paginated response."""
|
|
response = client.list_parents(query={"limit": 10, "offset": 0})
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "total" in data
|
|
assert "items" in data
|
|
|
|
|
|
def test_create_parent():
|
|
"""Creating a parent returns 201 with the created entity."""
|
|
payload = {"name": "Alice", "email": "alice@example.com"}
|
|
response = client.create_parent(body=payload, headers=_csrf_headers())
|
|
assert response.status_code == 201
|
|
parent = response.json()
|
|
assert parent["name"] == "Alice"
|
|
assert "id" in parent
|
|
|
|
|
|
def test_get_parent():
|
|
"""Get parent by ID returns the entity."""
|
|
parent = client.create_parent(body={"name": "Bob", "email": "bob@example.com"}, headers=_csrf_headers()).json()
|
|
response = client.get_parent(path_params={"id": parent["id"]})
|
|
assert response.status_code == 200
|
|
assert response.json()["name"] == "Bob"
|
|
|
|
|
|
def test_update_parent():
|
|
"""Update parent replaces its values."""
|
|
parent = client.create_parent(body={"name": "Carol", "email": "carol@example.com"}, headers=_csrf_headers()).json()
|
|
payload = {"name": "Carol Smith", "email": "carol.smith@example.com"}
|
|
response = client.update_parent(path_params={"id": parent["id"]}, body=payload, headers=_csrf_headers())
|
|
assert response.status_code == 200
|
|
assert response.json()["name"] == "Carol Smith"
|
|
|
|
|
|
def test_delete_parent():
|
|
"""Delete parent returns 204 and removes the entity."""
|
|
parent = client.create_parent(body={"name": "Dave", "email": "dave@example.com"}, headers=_csrf_headers()).json()
|
|
response = client.delete_parent(path_params={"id": parent["id"]}, headers=_csrf_headers())
|
|
assert response.status_code == 204
|
|
|
|
|
|
def test_list_vets():
|
|
"""List vets returns paginated response."""
|
|
response = client.list_vets(query={"limit": 10, "offset": 0})
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "total" in data
|
|
assert "items" in data
|
|
|
|
|
|
def test_create_vet():
|
|
"""Creating a vet returns 201."""
|
|
payload = {"name": "Dr. Smith", "specialty": "Surgery", "email": "smith@clinic.com"}
|
|
response = client.create_vet(body=payload, headers=_csrf_headers())
|
|
assert response.status_code == 201
|
|
assert response.json()["name"] == "Dr. Smith"
|
|
|
|
|
|
def test_list_treatments():
|
|
"""List treatments returns an array."""
|
|
response = client.list_treatments()
|
|
assert response.status_code == 200
|
|
assert isinstance(response.json(), list)
|
|
|
|
|
|
def test_create_treatment():
|
|
"""Creating a treatment returns 201."""
|
|
payload = {"label": "Vaccination", "description": "Annual vaccination"}
|
|
response = client.create_treatment(body=payload, headers=_csrf_headers())
|
|
assert response.status_code == 201
|
|
assert response.json()["label"] == "Vaccination"
|
|
|
|
|
|
def test_create_pet():
|
|
"""Creating a pet links FK references."""
|
|
parent = client.create_parent(body={"name": "Owner", "email": "owner@example.com"}, headers=_csrf_headers()).json()
|
|
payload = {"name": "Fido", "species": "dog", "parent_ids": [parent["id"]]}
|
|
response = client.create_pet(body=payload, headers=_csrf_headers())
|
|
assert response.status_code == 201
|
|
assert response.json()["name"] == "Fido"
|
|
|
|
|
|
def test_upload_pet_photo():
|
|
"""Upload pet photo returns 200."""
|
|
pet = client.create_pet(body={"name": "PhotoPet", "species": "cat", "parent_ids": []}, headers=_csrf_headers()).json()
|
|
response = client.client.post(
|
|
f"http://testserver/pets/{pet['id']}",
|
|
files={"file": ("test.jpg", b"fake-image-data", "image/jpeg")},
|
|
headers=_csrf_headers(),
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
|
|
def test_list_appointments():
|
|
"""List appointments returns paginated response with filter params."""
|
|
response = client.list_appointments(query={"limit": 10, "offset": 0})
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "total" in data
|
|
assert "items" in data
|
|
|
|
|
|
def test_full_appointment_lifecycle():
|
|
"""Create a parent, vet, treatment, pet, then an appointment."""
|
|
parent = client.create_parent(body={"name": "Eve", "email": "eve@example.com"}, headers=_csrf_headers()).json()
|
|
vet = client.create_vet(body={"name": "Dr. Jones", "specialty": "Dentistry", "email": "jones@clinic.com"}, headers=_csrf_headers()).json()
|
|
treatment = client.create_treatment(body={"label": "Cleaning", "description": "Teeth cleaning"}, headers=_csrf_headers()).json()
|
|
pet = client.create_pet(body={"name": "Max", "species": "dog", "parent_ids": [parent["id"]]}, headers=_csrf_headers()).json()
|
|
|
|
payload = {
|
|
"date": "2025-06-01T10:00:00",
|
|
"pet_id": pet["id"],
|
|
"vet_id": vet["id"],
|
|
"treatment_id": treatment["id"],
|
|
}
|
|
response = client.create_appointment(body=payload, headers=_csrf_headers())
|
|
assert response.status_code == 201
|
|
appointment = response.json()
|
|
assert appointment["pet_id"] == pet["id"]
|
|
|
|
# Fetch it back
|
|
get_resp = client.get_appointment(path_params={"id": appointment["id"]})
|
|
assert get_resp.status_code == 200
|
|
|
|
# Delete it
|
|
del_resp = client.delete_appointment(path_params={"id": appointment["id"]}, headers=_csrf_headers())
|
|
assert del_resp.status_code == 204
|