Files
api/tests/test_routes/test_purchases.py
CartSnitch Engineer Bot bbbf97d027 fix(api): revert auth/type regressions from standalone sync, keep email-in feature only
- Revert auth/dependencies.py to cookie+Bearer dual auth with str user IDs
- Add GET /auth/me/email-in-address endpoint for receipt email routing
- Update User model: add email_inbound_token, change id/store_id/user_id to str
- Update AuthService and UserResponse to use str user IDs
- Update route count test: 33 -> 34 routes
- Restore e2e test for email-in-address endpoint

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-03 09:40:39 +00:00

115 lines
3.6 KiB
Python

"""Integration tests for purchase endpoints."""
import secrets
import uuid
from datetime import UTC, date, datetime, timedelta
from decimal import Decimal
import pytest
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
from cartsnitch_api.models import Purchase, PurchaseItem, Store, User
@pytest.fixture
async def purchase_data(db_engine):
"""Seed a user, store, purchase, items, and a valid session."""
factory = async_sessionmaker(db_engine, class_=AsyncSession, expire_on_commit=False)
async with factory() as session:
user = User(
email="buyer@example.com",
hashed_password="not-used-with-better-auth",
display_name="Buyer",
)
store = Store(name="Kroger", slug="kroger")
session.add_all([user, store])
await session.commit()
await session.refresh(user)
await session.refresh(store)
purchase = Purchase(
user_id=user.id,
store_id=store.id,
receipt_id="receipt-001",
purchase_date=date(2026, 3, 10),
total=Decimal("42.50"),
)
session.add(purchase)
await session.commit()
await session.refresh(purchase)
item = PurchaseItem(
purchase_id=purchase.id,
product_name_raw="Organic Milk 1gal",
quantity=Decimal("1"),
unit_price=Decimal("5.99"),
extended_price=Decimal("5.99"),
)
session.add(item)
await session.commit()
# Create a session token directly in the sessions table
session_token = secrets.token_urlsafe(32)
now = datetime.now(UTC).isoformat()
expires = (datetime.now(UTC) + timedelta(days=7)).isoformat()
async with db_engine.begin() as conn:
await conn.execute(
text(
"INSERT INTO sessions (id, token, user_id, expires_at, created_at, updated_at) "
"VALUES (:id, :token, :user_id, :expires_at, :created_at, :updated_at)"
),
{
"id": str(uuid.uuid4()),
"token": session_token,
"user_id": str(user.id),
"expires_at": expires,
"created_at": now,
"updated_at": now,
},
)
return {
"user": user,
"store": store,
"purchase": purchase,
"headers": {"Cookie": f"better-auth.session_token={session_token}"},
}
@pytest.mark.asyncio
async def test_list_purchases(client, purchase_data):
resp = await client.get("/purchases", headers=purchase_data["headers"])
assert resp.status_code == 200
data = resp.json()
assert len(data) == 1
assert data[0]["store_name"] == "Kroger"
assert data[0]["total"] == 42.50
@pytest.mark.asyncio
async def test_get_purchase_detail(client, purchase_data):
pid = str(purchase_data["purchase"].id)
resp = await client.get(f"/purchases/{pid}", headers=purchase_data["headers"])
assert resp.status_code == 200
data = resp.json()
assert len(data["line_items"]) == 1
assert data["line_items"][0]["name"] == "Organic Milk 1gal"
@pytest.mark.asyncio
async def test_get_purchase_not_found(client, auth_headers):
resp = await client.get(f"/purchases/{uuid.uuid4()}", headers=auth_headers)
assert resp.status_code == 404
@pytest.mark.asyncio
async def test_purchase_stats(client, purchase_data):
resp = await client.get("/purchases/stats", headers=purchase_data["headers"])
assert resp.status_code == 200
data = resp.json()
assert data["total_spent"] == 42.50
assert data["purchase_count"] == 1
assert "Kroger" in data["by_store"]