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", "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 = { "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", "recommended_time": "both", "leave_on": True, "category": "serum", } client.post("/products", json={**base, "name": "Normal", "is_medication": False}) client.post( "/products", json={ **base, "name": "Med", "is_medication": True, }, ) 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", "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 def test_parse_text_accepts_numeric_strength_levels(client, monkeypatch): from innercontext.api import products as products_api class _FakeResponse: text = ( '{"name":"Test Serum","actives":[{"name":"Niacinamide","percent":10,' '"functions":["niacinamide"],"strength_level":2,"irritation_potential":1}]}' ) monkeypatch.setattr(products_api, "call_gemini", lambda **kwargs: _FakeResponse()) r = client.post("/products/parse-text", json={"text": "dummy input"}) assert r.status_code == 200 data = r.json() assert data["name"] == "Test Serum" assert data["actives"][0]["strength_level"] == 2 assert data["actives"][0]["irritation_potential"] == 1