from datetime import date, timedelta import uuid from sqlmodel import Session from innercontext.api.routines import ( _contains_minoxidil_text, _is_minoxidil_product, _ev, _build_skin_context, _build_grooming_context, _build_recent_history, _build_products_context, _build_objectives_context, _build_day_context, ) from innercontext.models import ( Product, SkinConditionSnapshot, GroomingSchedule, Routine, RoutineStep, ProductInventory, ) def test_contains_minoxidil_text(): assert _contains_minoxidil_text(None) is False assert _contains_minoxidil_text("") is False assert _contains_minoxidil_text("some random text") is False assert _contains_minoxidil_text("contains MINOXIDIL here") is True assert _contains_minoxidil_text("minoksydyl 5%") is True def test_is_minoxidil_product(): # Setup product p = Product(id=uuid.uuid4(), name="Test", brand="Brand", is_medication=True) assert _is_minoxidil_product(p) is False p.name = "Minoxidil 5%" assert _is_minoxidil_product(p) is True p.name = "Test" p.brand = "Brand with minoksydyl" assert _is_minoxidil_product(p) is True p.brand = "Brand" p.line_name = "Minoxidil Line" assert _is_minoxidil_product(p) is True p.line_name = None p.usage_notes = "Use minoxidil daily" assert _is_minoxidil_product(p) is True p.usage_notes = None p.inci = ["water", "minoxidil"] assert _is_minoxidil_product(p) is True p.inci = None p.actives = [{"name": "minoxidil", "strength": "5%"}] assert _is_minoxidil_product(p) is True # As Pydantic model representation isn't exactly a dict in db sometimes, we just test dict p.actives = [{"name": "Retinol", "strength": "1%"}] assert _is_minoxidil_product(p) is False def test_ev(): class DummyEnum: value = "dummy" assert _ev(None) == "" assert _ev(DummyEnum()) == "dummy" assert _ev("string") == "string" def test_build_skin_context(session: Session): # Empty assert _build_skin_context(session) == "SKIN CONDITION: no data\n" # With data snap = SkinConditionSnapshot( id=uuid.uuid4(), snapshot_date=date.today(), overall_state="good", hydration_level=4, barrier_state="intact", active_concerns=["acne", "dryness"], priorities=["hydration"], notes="Feeling good", ) session.add(snap) session.commit() ctx = _build_skin_context(session) assert "SKIN CONDITION (snapshot from" in ctx assert "Overall state: good" in ctx assert "Hydration: 4/5" in ctx assert "Barrier: intact" in ctx assert "Active concerns: acne, dryness" in ctx assert "Priorities: hydration" in ctx assert "Notes: Feeling good" in ctx def test_build_grooming_context(session: Session): assert _build_grooming_context(session) == "GROOMING SCHEDULE: none\n" sch = GroomingSchedule( id=uuid.uuid4(), day_of_week=0, action="shaving_oneblade", notes="Morning" ) session.add(sch) session.commit() ctx = _build_grooming_context(session) assert "GROOMING SCHEDULE:" in ctx assert "poniedziaƂek: shaving_oneblade (Morning)" in ctx # Test weekdays filter ctx2 = _build_grooming_context(session, weekdays=[1]) # not monday assert "(no entries for specified days)" in ctx2 def test_build_recent_history(session: Session): assert _build_recent_history(session) == "RECENT ROUTINES: none\n" r = Routine(id=uuid.uuid4(), routine_date=date.today(), part_of_day="am") session.add(r) p = Product( id=uuid.uuid4(), name="Cleanser", category="cleanser", brand="Test", recommended_time="both", leave_on=False, product_effect_profile={}, ) session.add(p) session.commit() s1 = RoutineStep(id=uuid.uuid4(), routine_id=r.id, order_index=1, product_id=p.id) s2 = RoutineStep( id=uuid.uuid4(), routine_id=r.id, order_index=2, action_type="shaving_razor" ) # Step with non-existent product s3 = RoutineStep( id=uuid.uuid4(), routine_id=r.id, order_index=3, product_id=uuid.uuid4() ) session.add_all([s1, s2, s3]) session.commit() ctx = _build_recent_history(session) assert "RECENT ROUTINES:" in ctx assert "AM:" in ctx assert "cleanser [" in ctx assert "action: shaving_razor" in ctx assert "unknown [" in ctx def test_build_products_context(session: Session): p1 = Product( id=uuid.uuid4(), name="Regaine", category="serum", is_medication=True, brand="J&J", recommended_time="both", leave_on=True, product_effect_profile={}, ) p2 = Product( id=uuid.uuid4(), name="Sunscreen", category="spf", brand="Test", leave_on=True, recommended_time="am", pao_months=6, product_effect_profile={"hydration_immediate": 2, "exfoliation_strength": 0}, incompatible_with=[{"target": "retinol", "scope": "same_routine"}], context_rules={"safe_after_shaving": False}, min_interval_hours=12, max_frequency_per_week=7, ) session.add_all([p1, p2]) session.commit() # Inventory inv1 = ProductInventory( id=uuid.uuid4(), product_id=p2.id, is_opened=True, opened_at=date.today() - timedelta(days=10), expiry_date=date.today() + timedelta(days=365), ) inv2 = ProductInventory(id=uuid.uuid4(), product_id=p2.id, is_opened=False) session.add_all([inv1, inv2]) session.commit() # Usage r = Routine(id=uuid.uuid4(), routine_date=date.today(), part_of_day="am") session.add(r) session.commit() s = RoutineStep(id=uuid.uuid4(), routine_id=r.id, order_index=1, product_id=p2.id) session.add(s) session.commit() ctx = _build_products_context( session, time_filter="am", reference_date=date.today() ) # p1 is medication but not minoxidil (wait, Regaine name doesn't contain minoxidil!) -> skipped assert "Regaine" not in ctx # Let's fix p1 to be minoxidil p1.name = "Regaine Minoxidil" session.add(p1) session.commit() ctx = _build_products_context( session, time_filter="am", reference_date=date.today() ) assert "Regaine Minoxidil" in ctx assert "Sunscreen" in ctx assert "inventory_status={active:2,opened:1,sealed:1}" in ctx assert "nearest_open_expiry=" in ctx assert "nearest_open_pao_deadline=" in ctx assert "pao_months=6" in ctx assert "effects={'hydration_immediate': 2}" in ctx assert "incompatible_with=['avoid retinol (same_routine)']" in ctx assert "context_rules={'safe_after_shaving': False}" in ctx assert "min_interval_hours=12" in ctx assert "max_frequency_per_week=7" in ctx assert "used_in_last_7_days=1" in ctx def test_build_objectives_context(): assert _build_objectives_context(False) == "" assert "improve beard" in _build_objectives_context(True) def test_build_day_context(): assert _build_day_context(None) == "" assert "Leaving home: yes" in _build_day_context(True) assert "Leaving home: no" in _build_day_context(False)