Files
orion/tests/unit/models/database/test_product.py
Samir Boulahtit bb0aabb918 fix: add bin_location to inventory fixtures and service
- Add warehouse and bin_location fields to test fixtures
- Update InventoryService to include warehouse/bin_location when creating entries
- Fix all Inventory model instantiations in tests to include required fields
- All 119 inventory tests now pass

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 17:31:16 +01:00

338 lines
11 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_get_source_comparison_info(
self, db, test_vendor, test_marketplace_product
):
"""Test get_source_comparison_info method for 'view original source' feature.
Products are independent entities with all fields populated at creation.
Source values are kept for comparison only, not inheritance.
"""
# Set up marketplace product values
test_marketplace_product.price_cents = 10000 # €100.00
test_marketplace_product.brand = "SourceBrand"
db.commit()
# Create product with its own values (independent copy pattern)
product = Product(
vendor_id=test_vendor.id,
marketplace_product_id=test_marketplace_product.id,
price_cents=8999, # €89.99 - vendor's price
brand="VendorBrand", # Vendor's brand
)
db.add(product)
db.commit()
db.refresh(product)
info = product.get_source_comparison_info()
# Product has its own price
assert info["price"] == 89.99
assert info["price_cents"] == 8999
assert info["price_source"] == 100.00 # Original marketplace price
# Product has its own brand
assert info["brand"] == "VendorBrand"
assert info["brand_source"] == "SourceBrand" # Original marketplace brand
# No more *_overridden keys in the pattern
assert "price_overridden" not in info
assert "brand_overridden" not in info
def test_product_fields_are_independent(
self, db, test_vendor, test_marketplace_product
):
"""Test that product fields don't inherit from marketplace product.
Products are independent entities - NULL fields stay NULL,
no inheritance/fallback logic.
"""
# Set up marketplace product values
test_marketplace_product.price_cents = 10000
test_marketplace_product.brand = "SourceBrand"
db.commit()
# Create product without copying values
product = Product(
vendor_id=test_vendor.id,
marketplace_product_id=test_marketplace_product.id,
# Not copying price_cents or brand
)
db.add(product)
db.commit()
db.refresh(product)
# Fields should be NULL (not inherited)
assert product.price_cents is None
assert product.price is None
assert product.brand is None
# But we can still see the source values for comparison
info = product.get_source_comparison_info()
assert info["price_source"] == 100.00
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,
warehouse="strassen",
bin_location="SA-01-01",
location="WAREHOUSE_A",
quantity=100,
reserved_quantity=10,
)
inv2 = Inventory(
product_id=product.id,
vendor_id=test_vendor.id,
warehouse="strassen",
bin_location="SA-01-02",
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,
warehouse="strassen",
bin_location="DIG-01-01",
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