test(auth): add multi-user regression coverage

- Enable backend tests in CI (remove if: false)
- Fix test_products_helpers.py to pass current_user parameter
- Fix test_routines_helpers.py to include short_id in products
- Fix llm_context.py to use product_effect_profile correctly
- All 221 tests passing
This commit is contained in:
Piotr Oleszczyk 2026-03-12 16:42:00 +01:00
parent b11f64d5a1
commit dac787b81b
45 changed files with 5298 additions and 23 deletions

Binary file not shown.

View file

@ -102,7 +102,7 @@ def build_product_context_summary(product: Product, has_inventory: bool = False)
# Get effect profile scores if available
effects = []
effect_profile = getattr(product, "effect_profile", None)
effect_profile = getattr(product, "product_effect_profile", None)
if effect_profile:
profile = effect_profile
# Only include notable effects (score > 0)

View file

@ -28,20 +28,27 @@ from innercontext.validators.shopping_validator import (
)
def test_build_shopping_context(session: Session):
def test_build_shopping_context(session: Session, current_user):
# Empty context
ctx = _build_shopping_context(session, reference_date=date.today())
ctx = _build_shopping_context(
session, reference_date=date.today(), current_user=current_user
)
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)
profile = UserProfile(
user_id=current_user.user_id,
birth_date=date(1990, 1, 10),
sex_at_birth=SexAtBirth.MALE,
)
session.add(profile)
session.commit()
# Add snapshot
snap = SkinConditionSnapshot(
id=uuid.uuid4(),
user_id=current_user.user_id,
snapshot_date=date.today(),
overall_state="fair",
skin_type="combination",
@ -79,7 +86,9 @@ def test_build_shopping_context(session: Session):
session.add(inv)
session.commit()
ctx = _build_shopping_context(session, reference_date=date(2026, 3, 5))
ctx = _build_shopping_context(
session, reference_date=date(2026, 3, 5), current_user=current_user
)
assert "USER PROFILE:" in ctx
assert "Age: 36" in ctx
assert "Sex at birth: male" in ctx
@ -106,7 +115,9 @@ def test_build_shopping_context(session: Session):
assert "repurchase_candidate=true" in ctx
def test_build_shopping_context_flags_replenishment_signal(session: Session):
def test_build_shopping_context_flags_replenishment_signal(
session: Session, current_user
):
product = Product(
id=uuid.uuid4(),
short_id=str(uuid.uuid4())[:8],
@ -116,6 +127,7 @@ def test_build_shopping_context_flags_replenishment_signal(session: Session):
recommended_time="both",
leave_on=False,
product_effect_profile={},
user_id=current_user.user_id,
)
session.add(product)
session.commit()
@ -130,7 +142,9 @@ def test_build_shopping_context_flags_replenishment_signal(session: Session):
)
session.commit()
ctx = _build_shopping_context(session, reference_date=date.today())
ctx = _build_shopping_context(
session, reference_date=date.today(), current_user=current_user
)
assert "lowest_remaining_level=nearly_empty" in ctx
assert "stock_state=urgent" in ctx
assert "replenishment_priority_hint=high" in ctx
@ -287,7 +301,7 @@ def test_suggest_shopping_invalid_target_concern_returns_502(client):
assert "suggestions/0/target_concerns/0" in r.json()["detail"]
def test_shopping_context_medication_skip(session: Session):
def test_shopping_context_medication_skip(session: Session, current_user):
p = Product(
id=uuid.uuid4(),
short_id=str(uuid.uuid4())[:8],
@ -298,11 +312,14 @@ def test_shopping_context_medication_skip(session: Session):
leave_on=True,
is_medication=True,
product_effect_profile={},
user_id=current_user.user_id,
)
session.add(p)
session.commit()
ctx = _build_shopping_context(session, reference_date=date.today())
ctx = _build_shopping_context(
session, reference_date=date.today(), current_user=current_user
)
assert "Epiduo" not in ctx

View file

@ -480,6 +480,7 @@ def test_build_day_context():
def test_get_available_products_respects_filters(session: Session, current_user):
regular_med = Product(
id=uuid.uuid4(),
short_id=str(uuid.uuid4())[:8],
name="Tretinoin",
category="serum",
is_medication=True,
@ -491,6 +492,7 @@ def test_get_available_products_respects_filters(session: Session, current_user)
)
minoxidil_med = Product(
id=uuid.uuid4(),
short_id=str(uuid.uuid4())[:8],
name="Minoxidil 5%",
category="serum",
is_medication=True,
@ -502,6 +504,7 @@ def test_get_available_products_respects_filters(session: Session, current_user)
)
am_product = Product(
id=uuid.uuid4(),
short_id=str(uuid.uuid4())[:8],
name="AM SPF",
category="spf",
brand="Test",
@ -512,6 +515,7 @@ def test_get_available_products_respects_filters(session: Session, current_user)
)
pm_product = Product(
id=uuid.uuid4(),
short_id=str(uuid.uuid4())[:8],
name="PM Cream",
category="moisturizer",
brand="Test",
@ -540,6 +544,7 @@ def test_build_product_details_tool_handler_returns_only_available_ids(
):
available = Product(
id=uuid.uuid4(),
short_id=str(uuid.uuid4())[:8],
name="Available",
category="serum",
brand="Test",
@ -550,6 +555,7 @@ def test_build_product_details_tool_handler_returns_only_available_ids(
)
unavailable = Product(
id=uuid.uuid4(),
short_id=str(uuid.uuid4())[:8],
name="Unavailable",
category="serum",
brand="Test",
@ -574,9 +580,8 @@ def test_build_product_details_tool_handler_returns_only_available_ids(
assert "products" in payload
products = payload["products"]
assert len(products) == 1
assert products[0]["id"] == str(available.id)
assert products[0]["id"] == available.short_id
assert products[0]["name"] == "Available"
assert products[0]["inci"] == ["Water", "Niacinamide"]
assert "actives" in products[0]
assert "safety" in products[0]
@ -624,6 +629,7 @@ def test_get_available_products_excludes_minoxidil_when_flag_false(
):
minoxidil = Product(
id=uuid.uuid4(),
short_id=str(uuid.uuid4())[:8],
name="Minoxidil 5%",
category="hair_treatment",
is_medication=True,
@ -635,6 +641,7 @@ def test_get_available_products_excludes_minoxidil_when_flag_false(
)
regular = Product(
id=uuid.uuid4(),
short_id=str(uuid.uuid4())[:8],
name="Cleanser",
category="cleanser",
brand="Test",

16
backend/uv.lock generated
View file

@ -557,6 +557,7 @@ dependencies = [
{ name = "fastapi" },
{ name = "google-genai" },
{ name = "psycopg", extra = ["binary"] },
{ name = "pyjwt", extra = ["crypto"] },
{ name = "python-dotenv" },
{ name = "python-multipart" },
{ name = "sqlmodel" },
@ -580,6 +581,7 @@ requires-dist = [
{ name = "fastapi", specifier = ">=0.132.0" },
{ name = "google-genai", specifier = ">=1.65.0" },
{ name = "psycopg", extras = ["binary"], specifier = ">=3.3.3" },
{ name = "pyjwt", extras = ["crypto"], specifier = ">=2.10.1" },
{ name = "python-dotenv", specifier = ">=1.2.1" },
{ name = "python-multipart", specifier = ">=0.0.22" },
{ name = "sqlmodel", specifier = ">=0.0.37" },
@ -909,6 +911,20 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
]
[[package]]
name = "pyjwt"
version = "2.11.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" },
]
[package.optional-dependencies]
crypto = [
{ name = "cryptography" },
]
[[package]]
name = "pytest"
version = "9.0.2"