import uuid from datetime import date from unittest.mock import patch from sqlmodel import Session from innercontext.api.products import ( _build_shopping_context, _extract_requested_product_ids, build_product_details_tool_handler, ) from innercontext.models import ( Product, ProductInventory, SexAtBirth, SkinConditionSnapshot, ) from innercontext.models.profile import UserProfile def test_build_shopping_context(session: Session): # Empty context ctx = _build_shopping_context(session, reference_date=date.today()) assert "USER PROFILE: no data" in ctx assert "(brak danych)" in ctx assert "POSIADANE PRODUKTY" in ctx profile = UserProfile(birth_date=date(1990, 1, 10), sex_at_birth=SexAtBirth.MALE) session.add(profile) session.commit() # Add snapshot snap = SkinConditionSnapshot( id=uuid.uuid4(), snapshot_date=date.today(), overall_state="fair", skin_type="combination", hydration_level=3, sensitivity_level=4, barrier_state="mildly_compromised", active_concerns=["redness"], priorities=["soothing"], ) session.add(snap) # Add product p = Product( id=uuid.uuid4(), name="Soothing Serum", brand="BrandX", category="serum", recommended_time="both", leave_on=True, targets=["redness"], product_effect_profile={"soothing_strength": 4, "hydration_immediate": 1}, actives=[{"name": "Centella"}], ) session.add(p) session.commit() # Add inventory inv = ProductInventory(id=uuid.uuid4(), product_id=p.id, is_opened=True) session.add(inv) session.commit() ctx = _build_shopping_context(session, reference_date=date(2026, 3, 5)) assert "USER PROFILE:" in ctx assert "Age: 36" in ctx assert "Sex at birth: male" in ctx assert "Typ skóry: combination" in ctx assert "Nawilżenie: 3/5" in ctx assert "Wrażliwość: 4/5" in ctx assert "Aktywne problemy: redness" in ctx assert "Priorytety: soothing" in ctx # Check product assert "[✓] id=" in ctx assert "Soothing Serum" in ctx assert f"id={p.id}" in ctx assert "BrandX" in ctx assert "targets: ['redness']" in ctx assert "actives: ['Centella']" in ctx assert "effects: {'soothing': 4}" in ctx def test_suggest_shopping(client, session): with patch( "innercontext.api.products.call_gemini_with_function_tools" ) as mock_gemini: mock_response = type( "Response", (), { "text": '{"suggestions": [{"category": "cleanser", "product_type": "cleanser", "priority": "high", "key_ingredients": [], "target_concerns": [], "why_needed": "reason", "recommended_time": "am", "frequency": "daily"}], "reasoning": "Test shopping"}' }, ) mock_gemini.return_value = mock_response r = client.post("/products/suggest") assert r.status_code == 200 data = r.json() assert len(data["suggestions"]) == 1 assert data["suggestions"][0]["product_type"] == "cleanser" assert data["reasoning"] == "Test shopping" kwargs = mock_gemini.call_args.kwargs assert "USER PROFILE:" in kwargs["contents"] assert "function_handlers" in kwargs assert "get_product_details" in kwargs["function_handlers"] def test_shopping_context_medication_skip(session: Session): p = Product( id=uuid.uuid4(), name="Epiduo", brand="Galderma", category="serum", recommended_time="pm", leave_on=True, is_medication=True, product_effect_profile={}, ) session.add(p) session.commit() ctx = _build_shopping_context(session, reference_date=date.today()) assert "Epiduo" not in ctx def test_extract_requested_product_ids_dedupes_and_limits(): ids = _extract_requested_product_ids( {"product_ids": ["a", "b", "a", 1, "c", "d"]}, max_ids=3, ) assert ids == ["a", "b", "c"] def test_shopping_tool_handlers_return_payloads(session: Session): product = Product( id=uuid.uuid4(), name="Test Product", brand="Brand", category="serum", recommended_time="both", leave_on=True, inci=["Water", "Niacinamide"], actives=[{"name": "Niacinamide", "percent": 5, "functions": ["niacinamide"]}], context_rules={"safe_after_shaving": True}, product_effect_profile={}, ) payload = {"product_ids": [str(product.id)]} details = build_product_details_tool_handler([product])(payload) assert details["products"][0]["inci"] == ["Water", "Niacinamide"] assert details["products"][0]["actives"][0]["name"] == "Niacinamide" assert "context_rules" in details["products"][0] assert details["products"][0]["last_used_on"] is None def test_shopping_tool_handler_includes_last_used_on_from_mapping(session: Session): product = Product( id=uuid.uuid4(), name="Mapped Product", brand="Brand", category="serum", recommended_time="both", leave_on=True, product_effect_profile={}, ) payload = {"product_ids": [str(product.id)]} details = build_product_details_tool_handler( [product], last_used_on_by_product={str(product.id): date(2026, 3, 1)}, )(payload) assert details["products"][0]["last_used_on"] == "2026-03-01"