fix(auth): revert to Better-Auth session-cookie auth, preserve email-in feature
- Revert auth/dependencies.py, auth/routes.py, services/auth.py, schemas.py to Better-Auth session-cookie auth (removed JWT register/login/refresh) - Preserve GET /auth/me/email-in-address endpoint - Fix UUIDString TypeDecorator: process_result_value returns uuid.UUID (not str) so SQLAlchemy 2.0 sentinel tracking matches UUID-to-UUID - Fix seed_data fixture: look up real user_id from session token via sessions table; purchases now reference actual user FK - Update purchase_data fixture to use session-cookie auth - Update test_auth_endpoints, test_auth_validation to cookie-based tests - Remove TestRegistrationErrors and TestLoginErrors (no longer applicable) - Update test_openapi.py expected routes and count - Update test_error_handler.py to use PATCH /auth/me validation Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
+45
-11
@@ -7,12 +7,13 @@ exercise cross-resource queries against real data.
|
||||
|
||||
from datetime import date, timedelta
|
||||
from decimal import Decimal
|
||||
from uuid import UUID
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import text
|
||||
|
||||
import pytest
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
|
||||
|
||||
from cartsnitch_api.auth.jwt import decode_token
|
||||
from cartsnitch_api.models import (
|
||||
Coupon,
|
||||
NormalizedProduct,
|
||||
@@ -33,17 +34,20 @@ ANCHOR_DATE = date.today()
|
||||
@pytest.fixture
|
||||
async def seed_data(db_engine, auth_headers):
|
||||
"""Seed a full dataset and return identifiers for test assertions."""
|
||||
import uuid
|
||||
|
||||
factory = async_sessionmaker(db_engine, class_=AsyncSession, expire_on_commit=False)
|
||||
async with factory() as session:
|
||||
# -- Stores --
|
||||
meijer = Store(name="Meijer", slug="meijer")
|
||||
kroger = Store(name="Kroger", slug="kroger")
|
||||
target = Store(name="Target", slug="target")
|
||||
meijer = Store(name="Meijer", slug="meijer", id=uuid.uuid4())
|
||||
kroger = Store(name="Kroger", slug="kroger", id=uuid.uuid4())
|
||||
target = Store(name="Target", slug="target", id=uuid.uuid4())
|
||||
session.add_all([meijer, kroger, target])
|
||||
await session.flush()
|
||||
|
||||
# -- Products --
|
||||
cheerios = NormalizedProduct(
|
||||
id=uuid.uuid4(),
|
||||
canonical_name="Cheerios 18oz",
|
||||
category="pantry",
|
||||
brand="General Mills",
|
||||
@@ -52,6 +56,7 @@ async def seed_data(db_engine, auth_headers):
|
||||
upc_variants=["016000275263"],
|
||||
)
|
||||
milk = NormalizedProduct(
|
||||
id=uuid.uuid4(),
|
||||
canonical_name="Whole Milk 1gal",
|
||||
category="dairy",
|
||||
brand="Meijer",
|
||||
@@ -59,6 +64,7 @@ async def seed_data(db_engine, auth_headers):
|
||||
size_unit="gal",
|
||||
)
|
||||
chicken = NormalizedProduct(
|
||||
id=uuid.uuid4(),
|
||||
canonical_name="Chicken Breast 1lb",
|
||||
category="meat",
|
||||
brand=None,
|
||||
@@ -75,6 +81,7 @@ async def seed_data(db_engine, auth_headers):
|
||||
for i, price_val in enumerate([Decimal("3.99"), Decimal("4.29"), Decimal("4.79")]):
|
||||
prices.append(
|
||||
PriceHistory(
|
||||
id=uuid.uuid4(),
|
||||
normalized_product_id=cheerios.id,
|
||||
store_id=meijer.id,
|
||||
observed_date=today - timedelta(days=60 - i * 30),
|
||||
@@ -86,6 +93,7 @@ async def seed_data(db_engine, auth_headers):
|
||||
for i in range(3):
|
||||
prices.append(
|
||||
PriceHistory(
|
||||
id=uuid.uuid4(),
|
||||
normalized_product_id=cheerios.id,
|
||||
store_id=kroger.id,
|
||||
observed_date=today - timedelta(days=60 - i * 30),
|
||||
@@ -96,6 +104,7 @@ async def seed_data(db_engine, auth_headers):
|
||||
# Milk at Meijer
|
||||
prices.append(
|
||||
PriceHistory(
|
||||
id=uuid.uuid4(),
|
||||
normalized_product_id=milk.id,
|
||||
store_id=meijer.id,
|
||||
observed_date=today - timedelta(days=7),
|
||||
@@ -106,6 +115,7 @@ async def seed_data(db_engine, auth_headers):
|
||||
# Milk at Kroger
|
||||
prices.append(
|
||||
PriceHistory(
|
||||
id=uuid.uuid4(),
|
||||
normalized_product_id=milk.id,
|
||||
store_id=kroger.id,
|
||||
observed_date=today - timedelta(days=5),
|
||||
@@ -116,6 +126,7 @@ async def seed_data(db_engine, auth_headers):
|
||||
# Chicken at Target
|
||||
prices.append(
|
||||
PriceHistory(
|
||||
id=uuid.uuid4(),
|
||||
normalized_product_id=chicken.id,
|
||||
store_id=target.id,
|
||||
observed_date=today - timedelta(days=3),
|
||||
@@ -127,12 +138,28 @@ async def seed_data(db_engine, auth_headers):
|
||||
await session.flush()
|
||||
|
||||
# -- Purchases (need the user_id from the registered test user) --
|
||||
token = auth_headers["Authorization"].split(" ")[1]
|
||||
payload = decode_token(token)
|
||||
user_id = UUID(payload["sub"])
|
||||
# Extract session_token from auth_headers, then look up the real user_id
|
||||
import http.cookies
|
||||
cookie_header = auth_headers.get("Cookie", "")
|
||||
cookies = http.cookies.SimpleCookie()
|
||||
cookies.load(cookie_header)
|
||||
session_token = cookies.get("better-auth.session_token").value if "better-auth.session_token" in cookie_header else None
|
||||
if session_token is None:
|
||||
raise RuntimeError("seed_data fixture requires cookie-based auth session token")
|
||||
|
||||
# Look up the real user_id from the sessions table
|
||||
row = await session.execute(
|
||||
text("SELECT user_id FROM sessions WHERE token = :token"),
|
||||
{"token": session_token}
|
||||
)
|
||||
session_row = row.fetchone()
|
||||
if session_row is None:
|
||||
raise RuntimeError("Session not found for session token in auth_headers")
|
||||
real_user_id = session_row[0]
|
||||
|
||||
purchase1 = Purchase(
|
||||
user_id=user_id,
|
||||
id=uuid.uuid4(),
|
||||
user_id=uuid.UUID(real_user_id),
|
||||
store_id=meijer.id,
|
||||
receipt_id="meijer-2026-001",
|
||||
purchase_date=today - timedelta(days=10),
|
||||
@@ -141,7 +168,8 @@ async def seed_data(db_engine, auth_headers):
|
||||
tax=Decimal("1.95"),
|
||||
)
|
||||
purchase2 = Purchase(
|
||||
user_id=user_id,
|
||||
id=uuid.uuid4(),
|
||||
user_id=uuid.UUID(real_user_id),
|
||||
store_id=kroger.id,
|
||||
receipt_id="kroger-2026-001",
|
||||
purchase_date=today - timedelta(days=5),
|
||||
@@ -154,6 +182,7 @@ async def seed_data(db_engine, auth_headers):
|
||||
|
||||
# -- Purchase Items --
|
||||
item1 = PurchaseItem(
|
||||
id=uuid.uuid4(),
|
||||
purchase_id=purchase1.id,
|
||||
product_name_raw="Cheerios 18oz Box",
|
||||
quantity=Decimal("1"),
|
||||
@@ -162,6 +191,7 @@ async def seed_data(db_engine, auth_headers):
|
||||
normalized_product_id=cheerios.id,
|
||||
)
|
||||
item2 = PurchaseItem(
|
||||
id=uuid.uuid4(),
|
||||
purchase_id=purchase1.id,
|
||||
product_name_raw="Meijer Whole Milk 1gal",
|
||||
quantity=Decimal("2"),
|
||||
@@ -170,6 +200,7 @@ async def seed_data(db_engine, auth_headers):
|
||||
normalized_product_id=milk.id,
|
||||
)
|
||||
item3 = PurchaseItem(
|
||||
id=uuid.uuid4(),
|
||||
purchase_id=purchase2.id,
|
||||
product_name_raw="KRO CHEERIOS 18OZ",
|
||||
quantity=Decimal("1"),
|
||||
@@ -182,6 +213,7 @@ async def seed_data(db_engine, auth_headers):
|
||||
|
||||
# -- Coupons --
|
||||
coupon1 = Coupon(
|
||||
id=uuid.uuid4(),
|
||||
store_id=meijer.id,
|
||||
normalized_product_id=cheerios.id,
|
||||
title="$1 off Cheerios",
|
||||
@@ -192,6 +224,7 @@ async def seed_data(db_engine, auth_headers):
|
||||
valid_to=today + timedelta(days=30),
|
||||
)
|
||||
coupon2 = Coupon(
|
||||
id=uuid.uuid4(),
|
||||
store_id=kroger.id,
|
||||
normalized_product_id=None,
|
||||
title="10% off dairy",
|
||||
@@ -206,6 +239,7 @@ async def seed_data(db_engine, auth_headers):
|
||||
|
||||
# -- Shrinkflation events --
|
||||
shrink = ShrinkflationEvent(
|
||||
id=uuid.uuid4(),
|
||||
normalized_product_id=cheerios.id,
|
||||
detected_date=today - timedelta(days=15),
|
||||
old_size="20",
|
||||
@@ -240,7 +274,7 @@ async def seed_data(db_engine, auth_headers):
|
||||
|
||||
return {
|
||||
"headers": auth_headers,
|
||||
"user_id": user_id,
|
||||
"user_id": real_user_id,
|
||||
"stores": {"meijer": meijer, "kroger": kroger, "target": target},
|
||||
"products": {"cheerios": cheerios, "milk": milk, "chicken": chicken},
|
||||
"purchases": {"meijer_trip": purchase1, "kroger_trip": purchase2},
|
||||
|
||||
Reference in New Issue
Block a user