- Add comprehensive module and function docstrings to crud_app and model_app templates - Document OpenAPI-first guarantees, non-goals, and usage patterns in templates - Add template-level __init__.py files with CLI and client usage examples - Update mkdocs.yml to include CRUD and model-based CRUD template documentation - Ensure template documentation follows strict package-bound mkdocstrings rules
125 lines
3.1 KiB
Python
125 lines
3.1 KiB
Python
"""
|
|
End-to-end tests for the OpenAPI-first model CRUD example app.
|
|
|
|
These tests validate that all CRUD operations behave correctly
|
|
against the in-memory mock data store using Pydantic models.
|
|
- OpenAPI specification loading
|
|
- OperationId-driven route binding on the server
|
|
- OperationId-driven client invocation
|
|
- Pydantic model-based request and response handling
|
|
|
|
All CRUD operations are exercised against an in-memory mock data store
|
|
backed by Pydantic domain models.
|
|
|
|
The tests assume:
|
|
- OpenAPI-first route binding
|
|
- Pydantic model validation
|
|
- In-memory storage (no persistence guarantees)
|
|
- Deterministic behavior in a single process
|
|
"""
|
|
|
|
from fastapi.testclient import TestClient
|
|
|
|
from main import app
|
|
from openapi_first.loader import load_openapi
|
|
from openapi_first.client import OpenAPIClient
|
|
|
|
|
|
client = TestClient(app)
|
|
spec = load_openapi("openapi.yaml")
|
|
client = OpenAPIClient(
|
|
spec=spec,
|
|
base_url="http://testserver",
|
|
client=client,
|
|
)
|
|
|
|
|
|
def test_list_items_initial():
|
|
"""Initial items should be present."""
|
|
response = client.list_items()
|
|
assert response.status_code == 200
|
|
|
|
data = response.json()
|
|
assert isinstance(data, list)
|
|
assert len(data) >= 2
|
|
|
|
ids = {item["id"] for item in data}
|
|
assert 1 in ids
|
|
assert 2 in ids
|
|
|
|
|
|
def test_get_item():
|
|
"""Existing item should be retrievable by ID."""
|
|
response = client.get_item(
|
|
path_params={"item_id": 1}
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
item = response.json()
|
|
assert item["id"] == 1
|
|
assert isinstance(item["name"], str)
|
|
assert isinstance(item["price"], float)
|
|
|
|
|
|
def test_create_item():
|
|
"""Creating a new item should return the created entity."""
|
|
payload = {
|
|
"name": "Orange",
|
|
"price": 0.8,
|
|
}
|
|
|
|
response = client.create_item(
|
|
body=payload
|
|
)
|
|
assert response.status_code == 201
|
|
|
|
item = response.json()
|
|
assert "id" in item
|
|
assert item["name"] == payload["name"]
|
|
assert item["price"] == payload["price"]
|
|
|
|
# Verify it appears in list
|
|
list_response = client.list_items()
|
|
ids = {i["id"] for i in list_response.json()}
|
|
assert item["id"] in ids
|
|
|
|
|
|
def test_update_item():
|
|
"""Updating an item should replace its values."""
|
|
payload = {
|
|
"name": "Green Apple",
|
|
"price": 0.6,
|
|
}
|
|
|
|
response = client.update_item(
|
|
path_params={"item_id": 1},
|
|
body=payload,
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
item = response.json()
|
|
assert item["id"] == 1
|
|
assert item["name"] == payload["name"]
|
|
assert item["price"] == payload["price"]
|
|
|
|
# Verify persisted update
|
|
get_response = client.get_item(
|
|
path_params={"item_id": 1}
|
|
)
|
|
updated = get_response.json()
|
|
assert updated["name"] == payload["name"]
|
|
assert updated["price"] == payload["price"]
|
|
|
|
|
|
def test_delete_item():
|
|
"""Deleting an item should remove it from the store."""
|
|
response = client.delete_item(
|
|
path_params={"item_id": 2}
|
|
)
|
|
assert response.status_code == 204
|
|
|
|
# Verify deletion
|
|
list_response = client.list_items()
|
|
ids = {item["id"] for item in list_response.json()}
|
|
assert 2 not in ids
|