innercontext/backend/tests/test_products.py
Piotr Oleszczyk 8f7d893a63 Initial commit: backend API, data models, and test suite
FastAPI backend for personal health and skincare data with MCP export.
Includes SQLModel models for products, inventory, medications, lab results,
routines, and skin condition snapshots. Pytest suite with 111 tests running
on SQLite in-memory (no PostgreSQL required).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 15:10:24 +01:00

198 lines
5.9 KiB
Python

import uuid
def test_create_minimal(client, product_data):
r = client.post("/products/", json=product_data)
assert r.status_code == 201
data = r.json()
assert "id" in data
assert data["name"] == product_data["name"]
assert data["brand"] == product_data["brand"]
assert data["category"] == "moisturizer"
def test_create_with_actives(client, product_data):
product_data["actives"] = [
{"name": "Niacinamide", "percent": 10.0, "functions": ["niacinamide"]}
]
r = client.post("/products/", json=product_data)
assert r.status_code == 201
data = r.json()
assert len(data["actives"]) == 1
assert data["actives"][0]["name"] == "Niacinamide"
assert data["actives"][0]["percent"] == 10.0
def test_create_invalid_enum(client, product_data):
product_data["category"] = "not_a_category"
r = client.post("/products/", json=product_data)
assert r.status_code == 422
def test_create_missing_required(client, product_data):
del product_data["name"]
r = client.post("/products/", json=product_data)
assert r.status_code == 422
def test_list_empty(client):
r = client.get("/products/")
assert r.status_code == 200
assert r.json() == []
def test_list_returns_created(client, created_product):
r = client.get("/products/")
assert r.status_code == 200
ids = [p["id"] for p in r.json()]
assert created_product["id"] in ids
def test_list_filter_category(client, client_and_data=None):
# Create a moisturizer and a serum
base = {
"brand": "B",
"routine_role": "seal",
"recommended_time": "both",
"leave_on": True,
}
r1 = client.post("/products/", json={**base, "name": "Moist", "category": "moisturizer"})
r2 = client.post("/products/", json={**base, "name": "Ser", "category": "serum"})
assert r1.status_code == 201
assert r2.status_code == 201
r = client.get("/products/?category=moisturizer")
assert r.status_code == 200
data = r.json()
assert all(p["category"] == "moisturizer" for p in data)
names = [p["name"] for p in data]
assert "Moist" in names
assert "Ser" not in names
def test_list_filter_brand(client):
base = {
"routine_role": "seal",
"recommended_time": "both",
"leave_on": True,
"category": "serum",
}
client.post("/products/", json={**base, "name": "A1", "brand": "BrandA"})
client.post("/products/", json={**base, "name": "B1", "brand": "BrandB"})
r = client.get("/products/?brand=BrandA")
assert r.status_code == 200
data = r.json()
assert all(p["brand"] == "BrandA" for p in data)
assert len(data) == 1
def test_list_filter_is_medication(client):
base = {
"brand": "B",
"routine_role": "seal",
"recommended_time": "both",
"leave_on": True,
"category": "serum",
}
client.post("/products/", json={**base, "name": "Normal", "is_medication": False})
# is_medication=True requires usage_notes (model validator)
client.post(
"/products/",
json={**base, "name": "Med", "is_medication": True, "usage_notes": "Apply pea-sized amount"},
)
r = client.get("/products/?is_medication=true")
assert r.status_code == 200
data = r.json()
assert all(p["is_medication"] is True for p in data)
assert len(data) == 1
assert data[0]["name"] == "Med"
def test_list_filter_targets(client):
base = {
"brand": "B",
"routine_role": "seal",
"recommended_time": "both",
"leave_on": True,
"category": "serum",
}
client.post("/products/", json={**base, "name": "Acne", "targets": ["acne"]})
client.post("/products/", json={**base, "name": "Aging", "targets": ["aging"]})
r = client.get("/products/?targets=acne")
assert r.status_code == 200
data = r.json()
assert len(data) == 1
assert data[0]["name"] == "Acne"
def test_get_by_id(client, created_product):
pid = created_product["id"]
r = client.get(f"/products/{pid}")
assert r.status_code == 200
assert r.json()["id"] == pid
def test_get_not_found(client):
r = client.get(f"/products/{uuid.uuid4()}")
assert r.status_code == 404
def test_update_name(client, created_product):
pid = created_product["id"]
r = client.patch(f"/products/{pid}", json={"name": "New Name"})
assert r.status_code == 200
assert r.json()["name"] == "New Name"
def test_update_json_field(client, created_product):
pid = created_product["id"]
r = client.patch(f"/products/{pid}", json={"inci": ["Water", "Glycerin"]})
assert r.status_code == 200
assert r.json()["inci"] == ["Water", "Glycerin"]
def test_update_not_found(client):
r = client.patch(f"/products/{uuid.uuid4()}", json={"name": "x"})
assert r.status_code == 404
def test_delete(client, created_product):
pid = created_product["id"]
r = client.delete(f"/products/{pid}")
assert r.status_code == 204
r2 = client.get(f"/products/{pid}")
assert r2.status_code == 404
def test_delete_not_found(client):
r = client.delete(f"/products/{uuid.uuid4()}")
assert r.status_code == 404
def test_list_inventory_empty(client, created_product):
pid = created_product["id"]
r = client.get(f"/products/{pid}/inventory")
assert r.status_code == 200
assert r.json() == []
def test_list_inventory_product_not_found(client):
r = client.get(f"/products/{uuid.uuid4()}/inventory")
assert r.status_code == 404
def test_create_inventory(client, created_product):
pid = created_product["id"]
r = client.post(f"/products/{pid}/inventory", json={"is_opened": False})
assert r.status_code == 201
data = r.json()
assert data["product_id"] == pid
assert data["is_opened"] is False
def test_create_inventory_product_not_found(client):
r = client.post(f"/products/{uuid.uuid4()}/inventory", json={})
assert r.status_code == 404