Public Access
feat: recipe CRUD endpoints with canonical ingredient validation
POST/PATCH validate every ingredient_id against the ingredient table and return 422 with the missing list when refs don't resolve. Replaces the prior recipes.py stub. Public read routes + admin write routes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import pytest
|
||||
|
||||
pytestmark = pytest.mark.requires_postgres
|
||||
|
||||
|
||||
def _admin_headers() -> dict:
|
||||
return {"Authorization": "Bearer test-admin-token"}
|
||||
|
||||
|
||||
def _new_ingredient(client, name: str) -> str:
|
||||
r = client.post(
|
||||
"/api/admin/ingredients",
|
||||
json={"name": name, "aliases": [], "aisle": "pantry", "unit": "ea"},
|
||||
headers=_admin_headers(),
|
||||
)
|
||||
assert r.status_code == 201, r.text
|
||||
return r.json()["id"]
|
||||
|
||||
|
||||
def test_create_recipe_with_canonical_ingredients(client):
|
||||
chicken = _new_ingredient(client, "Test Chicken Thighs Recipe1")
|
||||
olive_oil = _new_ingredient(client, "Test Olive Oil Recipe1")
|
||||
body = {
|
||||
"name": "Test Sheet-Pan Chicken",
|
||||
"prep_time_minutes": 10,
|
||||
"cook_time_minutes": 30,
|
||||
"servings": 4,
|
||||
"cuisine_tags": ["american"],
|
||||
"dietary_tags": [],
|
||||
"protein_type": "chicken",
|
||||
"calories_per_serving": 520,
|
||||
"ingredients": [
|
||||
{"ingredient_id": chicken, "qty": 2.0, "unit": "lb"},
|
||||
{"ingredient_id": olive_oil, "qty": 2.0, "unit": "tbsp"},
|
||||
],
|
||||
"instructions": ["Preheat oven to 425", "Roast 30 min"],
|
||||
}
|
||||
r = client.post("/api/admin/recipes", json=body, headers=_admin_headers())
|
||||
assert r.status_code == 201, r.text
|
||||
data = r.json()
|
||||
assert data["id"]
|
||||
assert len(data["ingredients"]) == 2
|
||||
|
||||
|
||||
def test_create_recipe_rejects_unknown_ingredient_id(client):
|
||||
body = {
|
||||
"name": "Test Bogus Recipe",
|
||||
"prep_time_minutes": 5,
|
||||
"cook_time_minutes": 5,
|
||||
"servings": 4,
|
||||
"cuisine_tags": [],
|
||||
"dietary_tags": [],
|
||||
"protein_type": "vegetarian",
|
||||
"ingredients": [
|
||||
{"ingredient_id": "00000000-0000-0000-0000-000000000000", "qty": 1, "unit": "ea"}
|
||||
],
|
||||
"instructions": ["nope"],
|
||||
}
|
||||
r = client.post("/api/admin/recipes", json=body, headers=_admin_headers())
|
||||
assert r.status_code == 422
|
||||
|
||||
|
||||
def test_list_recipes_returns_seeded_data(client):
|
||||
chicken = _new_ingredient(client, "Test Chicken Thighs Recipe2")
|
||||
client.post(
|
||||
"/api/admin/recipes",
|
||||
json={
|
||||
"name": "Test Listable Recipe",
|
||||
"prep_time_minutes": 5,
|
||||
"cook_time_minutes": 25,
|
||||
"servings": 4,
|
||||
"cuisine_tags": ["american"],
|
||||
"dietary_tags": [],
|
||||
"protein_type": "chicken",
|
||||
"ingredients": [{"ingredient_id": chicken, "qty": 1, "unit": "lb"}],
|
||||
"instructions": ["cook"],
|
||||
},
|
||||
headers=_admin_headers(),
|
||||
)
|
||||
r = client.get("/api/recipes")
|
||||
assert r.status_code == 200
|
||||
names = {row["name"] for row in r.json()}
|
||||
assert "Test Listable Recipe" in names
|
||||
Reference in New Issue
Block a user