Files
orion/tests/unit/models/database/test_product.py
Samir Boulahtit 0ab10128ae feat: enhance Letzshop order import with EAN matching and stats
- Add historical order import with pagination support
- Add customer_locale, shipping_country_iso, billing_country_iso columns
- Add gtin/gtin_type columns to Product table for EAN matching
- Fix order stats to count all orders server-side (not just visible page)
- Add GraphQL introspection script with tracking workaround tests
- Enrich inventory units with EAN, MPN, SKU, product name
- Add LetzshopOrderStats schema for proper status counts

Migrations:
- a9a86cef6cca: Add locale and country fields to letzshop_orders
- cb88bc9b5f86: Add gtin columns to products table

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 21:04:33 +01:00

358 lines
12 KiB
Python

# tests/unit/models/database/test_product.py
"""Unit tests for Product (vendor catalog) database model."""
import pytest
from sqlalchemy.exc import IntegrityError
from models.database.product import Product
@pytest.mark.unit
@pytest.mark.database
class TestProductModel:
"""Test Product (vendor catalog) model."""
def test_product_creation(self, db, test_vendor, test_marketplace_product):
"""Test Product model linking vendor catalog to marketplace product."""
product = Product(
vendor_id=test_vendor.id,
marketplace_product_id=test_marketplace_product.id,
vendor_sku="VENDOR_PROD_001",
price=89.99,
currency="EUR",
availability="in stock",
is_featured=True,
is_active=True,
)
db.add(product)
db.commit()
db.refresh(product)
assert product.id is not None
assert product.vendor_id == test_vendor.id
assert product.marketplace_product_id == test_marketplace_product.id
assert product.price == 89.99
assert product.is_featured is True
assert product.vendor.vendor_code == test_vendor.vendor_code
# Use get_title() method instead of .title attribute
assert product.marketplace_product.get_title(
"en"
) == test_marketplace_product.get_title("en")
def test_product_unique_per_vendor(self, db, test_vendor, test_marketplace_product):
"""Test that same marketplace product can't be added twice to vendor catalog."""
product1 = Product(
vendor_id=test_vendor.id,
marketplace_product_id=test_marketplace_product.id,
is_active=True,
)
db.add(product1)
db.commit()
# Same marketplace product to same vendor should fail
with pytest.raises(IntegrityError):
product2 = Product(
vendor_id=test_vendor.id,
marketplace_product_id=test_marketplace_product.id,
is_active=True,
)
db.add(product2)
db.commit()
def test_product_default_values(self, db, test_vendor, test_marketplace_product):
"""Test Product model default values."""
product = Product(
vendor_id=test_vendor.id,
marketplace_product_id=test_marketplace_product.id,
)
db.add(product)
db.commit()
db.refresh(product)
assert product.is_active is True # Default
assert product.is_featured is False # Default
assert product.min_quantity == 1 # Default
assert product.display_order == 0 # Default
def test_product_vendor_override_fields(
self, db, test_vendor, test_marketplace_product
):
"""Test Product model vendor-specific override fields."""
product = Product(
vendor_id=test_vendor.id,
marketplace_product_id=test_marketplace_product.id,
vendor_sku="CUSTOM_SKU_001",
price=49.99,
sale_price=39.99,
currency="USD",
availability="limited",
condition="new",
)
db.add(product)
db.commit()
db.refresh(product)
assert product.vendor_sku == "CUSTOM_SKU_001"
assert product.price == 49.99
assert product.sale_price == 39.99
assert product.currency == "USD"
assert product.availability == "limited"
def test_product_inventory_settings(
self, db, test_vendor, test_marketplace_product
):
"""Test Product model inventory settings."""
product = Product(
vendor_id=test_vendor.id,
marketplace_product_id=test_marketplace_product.id,
min_quantity=2,
max_quantity=10,
)
db.add(product)
db.commit()
db.refresh(product)
assert product.min_quantity == 2
assert product.max_quantity == 10
def test_product_relationships(self, db, test_vendor, test_marketplace_product):
"""Test Product relationships."""
product = Product(
vendor_id=test_vendor.id,
marketplace_product_id=test_marketplace_product.id,
)
db.add(product)
db.commit()
db.refresh(product)
assert product.vendor is not None
assert product.marketplace_product is not None
assert product.inventory_entries == [] # No inventory yet
def test_product_effective_properties(
self, db, test_vendor, test_marketplace_product
):
"""Test Product effective properties with override pattern."""
# First, set some values on the marketplace product
test_marketplace_product.price_numeric = 100.00
test_marketplace_product.brand = "SourceBrand"
db.commit()
db.refresh(test_marketplace_product)
# Create product without overrides
product = Product(
vendor_id=test_vendor.id,
marketplace_product_id=test_marketplace_product.id,
)
db.add(product)
db.commit()
db.refresh(product)
# Should inherit from marketplace product
assert product.effective_price == 100.00
assert product.effective_brand == "SourceBrand"
# Now override the price
product.price = 89.99
db.commit()
db.refresh(product)
# Should use the override
assert product.effective_price == 89.99
# Brand still inherited
assert product.effective_brand == "SourceBrand"
def test_product_reset_to_source(self, db, test_vendor, test_marketplace_product):
"""Test reset_to_source methods."""
# Set up marketplace product values
test_marketplace_product.price_numeric = 100.00
test_marketplace_product.brand = "SourceBrand"
db.commit()
# Create product with overrides
product = Product(
vendor_id=test_vendor.id,
marketplace_product_id=test_marketplace_product.id,
price=89.99,
brand="OverrideBrand",
)
db.add(product)
db.commit()
db.refresh(product)
assert product.effective_price == 89.99
assert product.effective_brand == "OverrideBrand"
# Reset price to source
product.reset_field_to_source("price")
db.commit()
db.refresh(product)
assert product.price is None
assert product.effective_price == 100.00 # Now inherits
# Reset all fields
product.reset_all_to_source()
db.commit()
db.refresh(product)
assert product.brand is None
assert product.effective_brand == "SourceBrand" # Now inherits
def test_product_get_override_info(self, db, test_vendor, test_marketplace_product):
"""Test get_override_info method."""
test_marketplace_product.price_numeric = 100.00
test_marketplace_product.brand = "SourceBrand"
db.commit()
product = Product(
vendor_id=test_vendor.id,
marketplace_product_id=test_marketplace_product.id,
price=89.99, # Override
# brand not set - will inherit
)
db.add(product)
db.commit()
db.refresh(product)
info = product.get_override_info()
# Price is overridden
assert info["price"] == 89.99
assert info["price_overridden"] is True
assert info["price_source"] == 100.00
# Brand is inherited
assert info["brand"] == "SourceBrand"
assert info["brand_overridden"] is False
assert info["brand_source"] == "SourceBrand"
@pytest.mark.unit
@pytest.mark.database
@pytest.mark.inventory
class TestProductInventoryProperties:
"""Test Product inventory properties including digital product handling."""
def test_physical_product_no_inventory_returns_zero(
self, db, test_vendor, test_marketplace_product
):
"""Test physical product with no inventory entries returns 0."""
# Ensure product is physical
test_marketplace_product.is_digital = False
db.commit()
product = Product(
vendor_id=test_vendor.id,
marketplace_product_id=test_marketplace_product.id,
)
db.add(product)
db.commit()
db.refresh(product)
assert product.is_digital is False
assert product.has_unlimited_inventory is False
assert product.total_inventory == 0
assert product.available_inventory == 0
def test_physical_product_with_inventory(
self, db, test_vendor, test_marketplace_product
):
"""Test physical product calculates inventory from entries."""
from models.database.inventory import Inventory
test_marketplace_product.is_digital = False
db.commit()
product = Product(
vendor_id=test_vendor.id,
marketplace_product_id=test_marketplace_product.id,
)
db.add(product)
db.commit()
db.refresh(product)
# Add inventory entries
inv1 = Inventory(
product_id=product.id,
vendor_id=test_vendor.id,
location="WAREHOUSE_A",
quantity=100,
reserved_quantity=10,
)
inv2 = Inventory(
product_id=product.id,
vendor_id=test_vendor.id,
location="WAREHOUSE_B",
quantity=50,
reserved_quantity=5,
)
db.add_all([inv1, inv2])
db.commit()
db.refresh(product)
assert product.has_unlimited_inventory is False
assert product.total_inventory == 150 # 100 + 50
assert product.available_inventory == 135 # (100-10) + (50-5)
def test_digital_product_has_unlimited_inventory(
self, db, test_vendor, test_marketplace_product
):
"""Test digital product returns unlimited inventory."""
test_marketplace_product.is_digital = True
db.commit()
product = Product(
vendor_id=test_vendor.id,
marketplace_product_id=test_marketplace_product.id,
)
db.add(product)
db.commit()
db.refresh(product)
assert product.is_digital is True
assert product.has_unlimited_inventory is True
assert product.total_inventory == Product.UNLIMITED_INVENTORY
assert product.available_inventory == Product.UNLIMITED_INVENTORY
def test_digital_product_ignores_inventory_entries(
self, db, test_vendor, test_marketplace_product
):
"""Test digital product returns unlimited even with inventory entries."""
from models.database.inventory import Inventory
test_marketplace_product.is_digital = True
db.commit()
product = Product(
vendor_id=test_vendor.id,
marketplace_product_id=test_marketplace_product.id,
)
db.add(product)
db.commit()
db.refresh(product)
# Add inventory entries (e.g., for license keys)
inv = Inventory(
product_id=product.id,
vendor_id=test_vendor.id,
location="DIGITAL_LICENSES",
quantity=10,
reserved_quantity=2,
)
db.add(inv)
db.commit()
db.refresh(product)
# Digital product should still return unlimited
assert product.has_unlimited_inventory is True
assert product.total_inventory == Product.UNLIMITED_INVENTORY
assert product.available_inventory == Product.UNLIMITED_INVENTORY
def test_unlimited_inventory_constant(self):
"""Test UNLIMITED_INVENTORY constant value."""
assert Product.UNLIMITED_INVENTORY == 999999
# Should be large enough to never cause "insufficient inventory"
assert Product.UNLIMITED_INVENTORY > 100000