Files

226 lines
6.5 KiB
Python

"""Tests for Pydantic v2 schemas."""
import uuid
from datetime import UTC, date, datetime
from decimal import Decimal
import pytest
from pydantic import ValidationError
from cartsnitch_common.constants import (
AccountStatus,
DiscountType,
EventType,
PriceSource,
ProductCategory,
SizeUnit,
StoreSlug,
)
from cartsnitch_common.schemas import (
CouponCreate,
EventEnvelope,
NormalizedProductCreate,
PriceHistoryCreate,
PurchaseCreate,
PurchaseItemCreate,
ShrinkflationEventCreate,
StoreCreate,
StoreLocationCreate,
StoreRead,
UserCreate,
UserStoreAccountCreate,
)
class TestStoreSchemas:
def test_store_create_valid(self):
s = StoreCreate(name="Meijer", slug=StoreSlug.MEIJER)
assert s.slug == StoreSlug.MEIJER
def test_store_create_invalid_slug(self):
with pytest.raises(ValidationError):
StoreCreate(name="Walmart", slug="walmart")
def test_store_read_from_attributes(self):
data = {
"id": uuid.uuid4(),
"name": "Kroger",
"slug": StoreSlug.KROGER,
"logo_url": None,
"website_url": None,
"created_at": datetime.now(UTC),
"updated_at": datetime.now(UTC),
}
s = StoreRead(**data)
assert s.slug == StoreSlug.KROGER
class TestStoreLocationSchemas:
def test_location_create(self):
loc = StoreLocationCreate(
store_id=uuid.uuid4(),
address="456 Oak Ave",
city="Detroit",
state="MI",
zip="48201",
)
assert loc.city == "Detroit"
class TestUserSchemas:
def test_user_create_valid(self):
u = UserCreate(email="test@example.com", password="secret123")
assert u.email == "test@example.com"
def test_user_create_invalid_email(self):
with pytest.raises(ValidationError):
UserCreate(email="not-an-email", password="secret123")
class TestUserStoreAccountSchemas:
def test_account_create_with_status(self):
a = UserStoreAccountCreate(
user_id=uuid.uuid4(),
store_id=uuid.uuid4(),
status=AccountStatus.EXPIRED,
)
assert a.status == AccountStatus.EXPIRED
def test_account_create_default_status(self):
a = UserStoreAccountCreate(
user_id=uuid.uuid4(),
store_id=uuid.uuid4(),
)
assert a.status == AccountStatus.ACTIVE
def test_account_create_invalid_status(self):
with pytest.raises(ValidationError):
UserStoreAccountCreate(
user_id=uuid.uuid4(),
store_id=uuid.uuid4(),
status="invalid_status",
)
class TestPurchaseSchemas:
def test_purchase_create_with_items(self):
p = PurchaseCreate(
user_id=uuid.uuid4(),
store_id=uuid.uuid4(),
receipt_id="RCP-001",
purchase_date=date(2026, 3, 15),
total=Decimal("42.50"),
items=[
PurchaseItemCreate(
product_name_raw="Milk",
unit_price=Decimal("3.49"),
extended_price=Decimal("3.49"),
),
],
)
assert len(p.items) == 1
assert p.items[0].quantity == Decimal("1")
class TestNormalizedProductSchemas:
def test_product_create_with_enums(self):
p = NormalizedProductCreate(
canonical_name="Whole Milk, 1 Gallon",
category=ProductCategory.DAIRY,
size_unit=SizeUnit.FL_OZ,
upc_variants=["0041250000001"],
)
assert p.category == ProductCategory.DAIRY
def test_product_create_invalid_category(self):
with pytest.raises(ValidationError):
NormalizedProductCreate(
canonical_name="Test",
category="invalid_category",
)
class TestPriceHistorySchemas:
def test_price_create(self):
p = PriceHistoryCreate(
normalized_product_id=uuid.uuid4(),
store_id=uuid.uuid4(),
observed_date=date(2026, 3, 15),
regular_price=Decimal("4.99"),
source=PriceSource.RECEIPT,
)
assert p.source == PriceSource.RECEIPT
def test_price_create_invalid_source(self):
with pytest.raises(ValidationError):
PriceHistoryCreate(
normalized_product_id=uuid.uuid4(),
store_id=uuid.uuid4(),
observed_date=date(2026, 3, 15),
regular_price=Decimal("4.99"),
source="invalid_source",
)
class TestCouponSchemas:
def test_coupon_create(self):
c = CouponCreate(
store_id=uuid.uuid4(),
title="BOGO Chips",
discount_type=DiscountType.BOGO,
)
assert c.discount_type == DiscountType.BOGO
def test_coupon_create_invalid_discount_type(self):
with pytest.raises(ValidationError):
CouponCreate(
store_id=uuid.uuid4(),
title="Test",
discount_type="free_stuff",
)
class TestShrinkflationEventSchemas:
def test_shrinkflation_create(self):
s = ShrinkflationEventCreate(
normalized_product_id=uuid.uuid4(),
detected_date=date(2026, 3, 10),
old_size="18",
new_size="15.4",
old_unit=SizeUnit.OZ,
new_unit=SizeUnit.OZ,
confidence=Decimal("0.95"),
)
assert s.old_unit == SizeUnit.OZ
def test_shrinkflation_create_invalid_unit(self):
with pytest.raises(ValidationError):
ShrinkflationEventCreate(
normalized_product_id=uuid.uuid4(),
detected_date=date(2026, 3, 10),
old_size="18",
new_size="15.4",
old_unit="bushels",
new_unit=SizeUnit.OZ,
)
class TestEventEnvelope:
def test_valid_event(self):
e = EventEnvelope(
event_type=EventType.RECEIPTS_INGESTED,
timestamp=datetime.now(UTC),
service="receiptwitness",
payload={"receipt_id": "RCP-001"},
)
assert e.event_type == EventType.RECEIPTS_INGESTED
def test_invalid_event_type(self):
with pytest.raises(ValidationError):
EventEnvelope(
event_type="invalid.event",
timestamp=datetime.now(UTC),
service="test",
payload={},
)