test: update tests for multi-language translation support
- Update marketplace_product_fixtures to create translations - Update test_marketplace_product.py for translation-based titles - Update test_product.py for effective property tests - Update test_order.py to use get_title() method - Add comprehensive CSV processor tests for translations - Update stats service tests for new flat response structure - Fix product schema tests with required marketplace_product_id field - Add helper function create_marketplace_product_with_translation() 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
74
tests/fixtures/marketplace_product_fixtures.py
vendored
74
tests/fixtures/marketplace_product_fixtures.py
vendored
@@ -2,7 +2,10 @@
|
||||
"""
|
||||
Marketplace product test fixtures.
|
||||
|
||||
Note: Fixtures should NOT use db.expunge() as it breaks lazy loading.
|
||||
Note: Since title/description are now in translations table,
|
||||
we create the translation alongside the marketplace product.
|
||||
|
||||
Fixtures should NOT use db.expunge() as it breaks lazy loading.
|
||||
See tests/conftest.py for details on fixture best practices.
|
||||
"""
|
||||
import uuid
|
||||
@@ -10,12 +13,42 @@ import uuid
|
||||
import pytest
|
||||
|
||||
from models.database.marketplace_product import MarketplaceProduct
|
||||
from models.database.marketplace_product_translation import MarketplaceProductTranslation
|
||||
|
||||
|
||||
def _create_marketplace_product_with_translation(
|
||||
db,
|
||||
marketplace_product_id: str,
|
||||
title: str,
|
||||
description: str | None = None,
|
||||
**kwargs,
|
||||
) -> MarketplaceProduct:
|
||||
"""Helper to create a MarketplaceProduct with its English translation."""
|
||||
marketplace_product = MarketplaceProduct(
|
||||
marketplace_product_id=marketplace_product_id,
|
||||
**kwargs,
|
||||
)
|
||||
db.add(marketplace_product)
|
||||
db.flush() # Get the ID
|
||||
|
||||
# Create the English translation
|
||||
translation = MarketplaceProductTranslation(
|
||||
marketplace_product_id=marketplace_product.id,
|
||||
language="en",
|
||||
title=title,
|
||||
description=description,
|
||||
)
|
||||
db.add(translation)
|
||||
db.commit()
|
||||
db.refresh(marketplace_product)
|
||||
return marketplace_product
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def test_marketplace_product(db):
|
||||
"""Create a test product."""
|
||||
marketplace_product = MarketplaceProduct(
|
||||
"""Create a test product with translation."""
|
||||
return _create_marketplace_product_with_translation(
|
||||
db,
|
||||
marketplace_product_id="TEST001",
|
||||
title="Test MarketplaceProduct",
|
||||
description="A test product",
|
||||
@@ -27,17 +60,14 @@ def test_marketplace_product(db):
|
||||
marketplace="Letzshop",
|
||||
vendor_name="TestVendor",
|
||||
)
|
||||
db.add(marketplace_product)
|
||||
db.commit()
|
||||
db.refresh(marketplace_product)
|
||||
return marketplace_product
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def unique_product(db):
|
||||
"""Create a unique product for tests that need isolated product data."""
|
||||
unique_id = str(uuid.uuid4())[:8]
|
||||
marketplace_product = MarketplaceProduct(
|
||||
return _create_marketplace_product_with_translation(
|
||||
db,
|
||||
marketplace_product_id=f"UNIQUE_{unique_id}",
|
||||
title=f"Unique MarketplaceProduct {unique_id}",
|
||||
description=f"A unique test product {unique_id}",
|
||||
@@ -50,10 +80,6 @@ def unique_product(db):
|
||||
vendor_name=f"UniqueVendor_{unique_id}",
|
||||
google_product_category=f"UniqueCategory_{unique_id}",
|
||||
)
|
||||
db.add(marketplace_product)
|
||||
db.commit()
|
||||
db.refresh(marketplace_product)
|
||||
return marketplace_product
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -63,7 +89,8 @@ def multiple_products(db):
|
||||
marketplace_products = []
|
||||
|
||||
for i in range(5):
|
||||
marketplace_product = MarketplaceProduct(
|
||||
marketplace_product = _create_marketplace_product_with_translation(
|
||||
db,
|
||||
marketplace_product_id=f"MULTI_{unique_id}_{i}",
|
||||
title=f"Multi MarketplaceProduct {i} {unique_id}",
|
||||
description=f"Multi test product {i}",
|
||||
@@ -77,33 +104,30 @@ def multiple_products(db):
|
||||
)
|
||||
marketplace_products.append(marketplace_product)
|
||||
|
||||
db.add_all(marketplace_products)
|
||||
db.commit()
|
||||
for product in marketplace_products:
|
||||
db.refresh(product)
|
||||
return marketplace_products
|
||||
|
||||
|
||||
def create_unique_marketplace_product_factory():
|
||||
"""Factory function to create unique products in tests."""
|
||||
|
||||
def _marketplace_create_product(db, **kwargs):
|
||||
def _marketplace_create_product(db, title: str | None = None, **kwargs):
|
||||
unique_id = str(uuid.uuid4())[:8]
|
||||
defaults = {
|
||||
"marketplace_product_id": f"FACTORY_{unique_id}",
|
||||
"title": f"Factory MarketplaceProduct {unique_id}",
|
||||
"price": "15.99",
|
||||
"currency": "EUR",
|
||||
"marketplace": "TestMarket",
|
||||
"name": "TestVendor",
|
||||
"vendor_name": "TestVendor",
|
||||
}
|
||||
defaults.update(kwargs)
|
||||
|
||||
marketplace_product = MarketplaceProduct(**defaults)
|
||||
db.add(marketplace_product)
|
||||
db.commit()
|
||||
db.refresh(marketplace_product)
|
||||
return marketplace_product
|
||||
title = title or f"Factory MarketplaceProduct {unique_id}"
|
||||
|
||||
return _create_marketplace_product_with_translation(
|
||||
db,
|
||||
title=title,
|
||||
**defaults,
|
||||
)
|
||||
|
||||
return _marketplace_create_product
|
||||
|
||||
|
||||
@@ -4,6 +4,24 @@ import pytest
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
|
||||
from models.database.marketplace_product import MarketplaceProduct
|
||||
from models.database.marketplace_product_translation import MarketplaceProductTranslation
|
||||
|
||||
|
||||
def _create_with_translation(db, marketplace_product_id, title, **kwargs):
|
||||
"""Helper to create MarketplaceProduct with translation."""
|
||||
mp = MarketplaceProduct(marketplace_product_id=marketplace_product_id, **kwargs)
|
||||
db.add(mp)
|
||||
db.flush()
|
||||
|
||||
translation = MarketplaceProductTranslation(
|
||||
marketplace_product_id=mp.id,
|
||||
language="en",
|
||||
title=title,
|
||||
)
|
||||
db.add(translation)
|
||||
db.commit()
|
||||
db.refresh(mp)
|
||||
return mp
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -13,10 +31,10 @@ class TestMarketplaceProductModel:
|
||||
|
||||
def test_marketplace_product_creation(self, db):
|
||||
"""Test MarketplaceProduct model creation."""
|
||||
marketplace_product = MarketplaceProduct(
|
||||
marketplace_product = _create_with_translation(
|
||||
db,
|
||||
marketplace_product_id="DB_TEST_001",
|
||||
title="Database Test Product",
|
||||
description="Testing product model",
|
||||
price="25.99",
|
||||
currency="USD",
|
||||
brand="DBTest",
|
||||
@@ -26,42 +44,36 @@ class TestMarketplaceProductModel:
|
||||
vendor_name="Test Vendor",
|
||||
)
|
||||
|
||||
db.add(marketplace_product)
|
||||
db.commit()
|
||||
db.refresh(marketplace_product)
|
||||
|
||||
assert marketplace_product.id is not None
|
||||
assert marketplace_product.marketplace_product_id == "DB_TEST_001"
|
||||
assert marketplace_product.title == "Database Test Product"
|
||||
assert marketplace_product.get_title("en") == "Database Test Product"
|
||||
assert marketplace_product.marketplace == "Letzshop"
|
||||
assert marketplace_product.created_at is not None
|
||||
|
||||
def test_marketplace_product_id_uniqueness(self, db):
|
||||
"""Test unique marketplace_product_id constraint."""
|
||||
product1 = MarketplaceProduct(
|
||||
_create_with_translation(
|
||||
db,
|
||||
marketplace_product_id="UNIQUE_001",
|
||||
title="Product 1",
|
||||
marketplace="Letzshop",
|
||||
)
|
||||
db.add(product1)
|
||||
db.commit()
|
||||
|
||||
# Duplicate marketplace_product_id should raise error
|
||||
with pytest.raises(IntegrityError):
|
||||
product2 = MarketplaceProduct(
|
||||
_create_with_translation(
|
||||
db,
|
||||
marketplace_product_id="UNIQUE_001",
|
||||
title="Product 2",
|
||||
marketplace="Letzshop",
|
||||
)
|
||||
db.add(product2)
|
||||
db.commit()
|
||||
|
||||
def test_marketplace_product_all_fields(self, db):
|
||||
"""Test MarketplaceProduct with all optional fields."""
|
||||
marketplace_product = MarketplaceProduct(
|
||||
marketplace_product = _create_with_translation(
|
||||
db,
|
||||
marketplace_product_id="FULL_001",
|
||||
title="Full Product",
|
||||
description="Complete product description",
|
||||
link="https://example.com/product",
|
||||
image_link="https://example.com/image.jpg",
|
||||
availability="in stock",
|
||||
@@ -78,16 +90,12 @@ class TestMarketplaceProductModel:
|
||||
pattern="solid",
|
||||
size="M",
|
||||
google_product_category="Apparel & Accessories",
|
||||
product_type="Clothing",
|
||||
product_type_raw="Clothing",
|
||||
currency="EUR",
|
||||
marketplace="Letzshop",
|
||||
vendor_name="Full Vendor",
|
||||
)
|
||||
|
||||
db.add(marketplace_product)
|
||||
db.commit()
|
||||
db.refresh(marketplace_product)
|
||||
|
||||
assert marketplace_product.brand == "TestBrand"
|
||||
assert marketplace_product.gtin == "9876543210123"
|
||||
assert marketplace_product.color == "blue"
|
||||
@@ -95,7 +103,8 @@ class TestMarketplaceProductModel:
|
||||
|
||||
def test_marketplace_product_custom_labels(self, db):
|
||||
"""Test MarketplaceProduct with custom labels."""
|
||||
marketplace_product = MarketplaceProduct(
|
||||
marketplace_product = _create_with_translation(
|
||||
db,
|
||||
marketplace_product_id="LABELS_001",
|
||||
title="Labeled Product",
|
||||
marketplace="Letzshop",
|
||||
@@ -106,26 +115,127 @@ class TestMarketplaceProductModel:
|
||||
custom_label_4="Label4",
|
||||
)
|
||||
|
||||
db.add(marketplace_product)
|
||||
db.commit()
|
||||
db.refresh(marketplace_product)
|
||||
|
||||
assert marketplace_product.custom_label_0 == "Label0"
|
||||
assert marketplace_product.custom_label_4 == "Label4"
|
||||
|
||||
def test_marketplace_product_minimal_fields(self, db):
|
||||
"""Test MarketplaceProduct with only required fields."""
|
||||
marketplace_product = MarketplaceProduct(
|
||||
marketplace_product = _create_with_translation(
|
||||
db,
|
||||
marketplace_product_id="MINIMAL_001",
|
||||
title="Minimal Product",
|
||||
)
|
||||
|
||||
db.add(marketplace_product)
|
||||
db.commit()
|
||||
db.refresh(marketplace_product)
|
||||
|
||||
assert marketplace_product.id is not None
|
||||
assert marketplace_product.marketplace_product_id == "MINIMAL_001"
|
||||
assert marketplace_product.title == "Minimal Product"
|
||||
assert marketplace_product.description is None
|
||||
assert marketplace_product.get_title("en") == "Minimal Product"
|
||||
assert marketplace_product.get_description("en") is None
|
||||
assert marketplace_product.price is None
|
||||
|
||||
def test_marketplace_product_digital_fields(self, db):
|
||||
"""Test MarketplaceProduct with digital product fields."""
|
||||
marketplace_product = _create_with_translation(
|
||||
db,
|
||||
marketplace_product_id="DIGITAL_001",
|
||||
title="Digital Product",
|
||||
product_type_enum="digital",
|
||||
is_digital=True,
|
||||
digital_delivery_method="license_key",
|
||||
platform="steam",
|
||||
region_restrictions=["EU", "US"],
|
||||
license_type="single_use",
|
||||
)
|
||||
|
||||
assert marketplace_product.product_type_enum == "digital"
|
||||
assert marketplace_product.is_digital is True
|
||||
assert marketplace_product.digital_delivery_method == "license_key"
|
||||
assert marketplace_product.platform == "steam"
|
||||
assert marketplace_product.region_restrictions == ["EU", "US"]
|
||||
assert marketplace_product.license_type == "single_use"
|
||||
|
||||
def test_marketplace_product_translation_methods(self, db):
|
||||
"""Test translation helper methods."""
|
||||
mp = MarketplaceProduct(marketplace_product_id="TRANS_001")
|
||||
db.add(mp)
|
||||
db.flush()
|
||||
|
||||
# Add English translation
|
||||
en_trans = MarketplaceProductTranslation(
|
||||
marketplace_product_id=mp.id,
|
||||
language="en",
|
||||
title="English Title",
|
||||
description="English Description",
|
||||
)
|
||||
db.add(en_trans)
|
||||
|
||||
# Add French translation
|
||||
fr_trans = MarketplaceProductTranslation(
|
||||
marketplace_product_id=mp.id,
|
||||
language="fr",
|
||||
title="Titre Français",
|
||||
description="Description Française",
|
||||
)
|
||||
db.add(fr_trans)
|
||||
db.commit()
|
||||
db.refresh(mp)
|
||||
|
||||
assert mp.get_title("en") == "English Title"
|
||||
assert mp.get_title("fr") == "Titre Français"
|
||||
assert mp.get_description("en") == "English Description"
|
||||
assert mp.get_description("fr") == "Description Française"
|
||||
|
||||
# Test fallback to English for unknown language
|
||||
assert mp.get_title("de") == "English Title" # Falls back to 'en'
|
||||
assert mp.get_description("de") == "English Description"
|
||||
|
||||
def test_marketplace_product_numeric_prices(self, db):
|
||||
"""Test numeric price fields."""
|
||||
marketplace_product = _create_with_translation(
|
||||
db,
|
||||
marketplace_product_id="PRICES_001",
|
||||
title="Priced Product",
|
||||
price="99.99 EUR",
|
||||
price_numeric=99.99,
|
||||
sale_price="79.99 EUR",
|
||||
sale_price_numeric=79.99,
|
||||
)
|
||||
|
||||
assert marketplace_product.price == "99.99 EUR"
|
||||
assert marketplace_product.price_numeric == 99.99
|
||||
assert marketplace_product.sale_price_numeric == 79.99
|
||||
assert marketplace_product.effective_price == 99.99
|
||||
assert marketplace_product.effective_sale_price == 79.99
|
||||
|
||||
def test_marketplace_product_attributes_json(self, db):
|
||||
"""Test flexible attributes JSON field."""
|
||||
marketplace_product = _create_with_translation(
|
||||
db,
|
||||
marketplace_product_id="ATTRS_001",
|
||||
title="Product with Attributes",
|
||||
attributes={
|
||||
"custom_field": "custom_value",
|
||||
"nested": {"key": "value"},
|
||||
},
|
||||
)
|
||||
|
||||
assert marketplace_product.attributes["custom_field"] == "custom_value"
|
||||
assert marketplace_product.attributes["nested"]["key"] == "value"
|
||||
|
||||
def test_marketplace_product_all_images_property(self, db):
|
||||
"""Test all_images property."""
|
||||
marketplace_product = _create_with_translation(
|
||||
db,
|
||||
marketplace_product_id="IMAGES_001",
|
||||
title="Product with Images",
|
||||
image_link="https://example.com/main.jpg",
|
||||
additional_images=[
|
||||
"https://example.com/img1.jpg",
|
||||
"https://example.com/img2.jpg",
|
||||
],
|
||||
)
|
||||
|
||||
images = marketplace_product.all_images
|
||||
assert len(images) == 3
|
||||
assert images[0] == "https://example.com/main.jpg"
|
||||
assert "https://example.com/img1.jpg" in images
|
||||
assert "https://example.com/img2.jpg" in images
|
||||
|
||||
@@ -152,11 +152,14 @@ class TestOrderItemModel:
|
||||
|
||||
def test_order_item_creation(self, db, test_order, test_product):
|
||||
"""Test OrderItem model."""
|
||||
# Get title from translation
|
||||
product_title = test_product.marketplace_product.get_title("en")
|
||||
|
||||
order_item = OrderItem(
|
||||
order_id=test_order.id,
|
||||
product_id=test_product.id,
|
||||
product_name=test_product.marketplace_product.title,
|
||||
product_sku=test_product.product_id or "SKU001",
|
||||
product_name=product_title,
|
||||
product_sku=test_product.vendor_sku or "SKU001",
|
||||
quantity=2,
|
||||
unit_price=49.99,
|
||||
total_price=99.98,
|
||||
|
||||
@@ -16,7 +16,7 @@ class TestProductModel:
|
||||
product = Product(
|
||||
vendor_id=test_vendor.id,
|
||||
marketplace_product_id=test_marketplace_product.id,
|
||||
product_id="VENDOR_PROD_001",
|
||||
vendor_sku="VENDOR_PROD_001",
|
||||
price=89.99,
|
||||
currency="EUR",
|
||||
availability="in stock",
|
||||
@@ -34,7 +34,11 @@ class TestProductModel:
|
||||
assert product.price == 89.99
|
||||
assert product.is_featured is True
|
||||
assert product.vendor.vendor_code == test_vendor.vendor_code
|
||||
assert product.marketplace_product.title == test_marketplace_product.title
|
||||
# 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."""
|
||||
@@ -76,7 +80,7 @@ class TestProductModel:
|
||||
product = Product(
|
||||
vendor_id=test_vendor.id,
|
||||
marketplace_product_id=test_marketplace_product.id,
|
||||
product_id="CUSTOM_SKU_001",
|
||||
vendor_sku="CUSTOM_SKU_001",
|
||||
price=49.99,
|
||||
sale_price=39.99,
|
||||
currency="USD",
|
||||
@@ -87,7 +91,7 @@ class TestProductModel:
|
||||
db.commit()
|
||||
db.refresh(product)
|
||||
|
||||
assert product.product_id == "CUSTOM_SKU_001"
|
||||
assert product.vendor_sku == "CUSTOM_SKU_001"
|
||||
assert product.price == 49.99
|
||||
assert product.sale_price == 39.99
|
||||
assert product.currency == "USD"
|
||||
@@ -121,3 +125,99 @@ class TestProductModel:
|
||||
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"
|
||||
|
||||
@@ -157,12 +157,13 @@ class TestProductResponseSchema:
|
||||
"vendor_id": 1,
|
||||
"marketplace_product": {
|
||||
"id": 1,
|
||||
"marketplace_product_id": "TEST001", # Required field
|
||||
"gtin": "1234567890123",
|
||||
"title": "Test Product",
|
||||
"description": "A test product",
|
||||
"brand": "Test Brand",
|
||||
"category": "Electronics",
|
||||
"image_url": "https://example.com/image.jpg",
|
||||
"google_product_category": "Electronics",
|
||||
"image_link": "https://example.com/image.jpg",
|
||||
"created_at": datetime.now(),
|
||||
"updated_at": datetime.now(),
|
||||
},
|
||||
@@ -194,12 +195,13 @@ class TestProductResponseSchema:
|
||||
"vendor_id": 1,
|
||||
"marketplace_product": {
|
||||
"id": 1,
|
||||
"marketplace_product_id": "TEST002", # Required field
|
||||
"gtin": "1234567890123",
|
||||
"title": "Test Product",
|
||||
"description": None,
|
||||
"brand": None,
|
||||
"category": None,
|
||||
"image_url": None,
|
||||
"google_product_category": None,
|
||||
"image_link": None,
|
||||
"created_at": datetime.now(),
|
||||
"updated_at": datetime.now(),
|
||||
},
|
||||
|
||||
@@ -11,10 +11,31 @@ from app.services.stats_service import StatsService
|
||||
from models.database.inventory import Inventory
|
||||
from models.database.marketplace_import_job import MarketplaceImportJob
|
||||
from models.database.marketplace_product import MarketplaceProduct
|
||||
from models.database.marketplace_product_translation import MarketplaceProductTranslation
|
||||
from models.database.product import Product
|
||||
from models.database.vendor import Vendor
|
||||
|
||||
|
||||
def create_marketplace_product_with_translation(
|
||||
db, marketplace_product_id, title, language="en", **kwargs
|
||||
):
|
||||
"""Helper to create a MarketplaceProduct with its translation."""
|
||||
product = MarketplaceProduct(
|
||||
marketplace_product_id=marketplace_product_id,
|
||||
**kwargs
|
||||
)
|
||||
db.add(product)
|
||||
db.flush() # Get the product ID
|
||||
|
||||
translation = MarketplaceProductTranslation(
|
||||
marketplace_product_id=product.id,
|
||||
language=language,
|
||||
title=title,
|
||||
)
|
||||
db.add(translation)
|
||||
return product
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.stats
|
||||
class TestStatsService:
|
||||
@@ -58,29 +79,28 @@ class TestStatsService:
|
||||
):
|
||||
"""Test comprehensive stats with multiple marketplaces."""
|
||||
unique_id = str(uuid.uuid4())[:8]
|
||||
additional_products = [
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"PROD002_{unique_id}",
|
||||
title="MarketplaceProduct 2",
|
||||
brand="DifferentBrand",
|
||||
google_product_category="Different Category",
|
||||
marketplace="Amazon",
|
||||
vendor_name="AmazonVendor",
|
||||
price="15.99",
|
||||
currency="EUR",
|
||||
),
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"PROD003_{unique_id}",
|
||||
title="MarketplaceProduct 3",
|
||||
brand="ThirdBrand",
|
||||
google_product_category="Third Category",
|
||||
marketplace="eBay",
|
||||
vendor_name="eBayVendor",
|
||||
price="25.99",
|
||||
currency="USD",
|
||||
),
|
||||
]
|
||||
db.add_all(additional_products)
|
||||
create_marketplace_product_with_translation(
|
||||
db,
|
||||
marketplace_product_id=f"PROD002_{unique_id}",
|
||||
title="MarketplaceProduct 2",
|
||||
brand="DifferentBrand",
|
||||
google_product_category="Different Category",
|
||||
marketplace="Amazon",
|
||||
vendor_name="AmazonVendor",
|
||||
price="15.99",
|
||||
currency="EUR",
|
||||
)
|
||||
create_marketplace_product_with_translation(
|
||||
db,
|
||||
marketplace_product_id=f"PROD003_{unique_id}",
|
||||
title="MarketplaceProduct 3",
|
||||
brand="ThirdBrand",
|
||||
google_product_category="Third Category",
|
||||
marketplace="eBay",
|
||||
vendor_name="eBayVendor",
|
||||
price="25.99",
|
||||
currency="USD",
|
||||
)
|
||||
db.commit()
|
||||
|
||||
stats = self.service.get_comprehensive_stats(db)
|
||||
@@ -91,29 +111,28 @@ class TestStatsService:
|
||||
def test_get_comprehensive_stats_handles_nulls(self, db):
|
||||
"""Test comprehensive stats handles null/empty values correctly."""
|
||||
unique_id = str(uuid.uuid4())[:8]
|
||||
products_with_nulls = [
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"NULL001_{unique_id}",
|
||||
title="MarketplaceProduct with Nulls",
|
||||
brand=None,
|
||||
google_product_category=None,
|
||||
marketplace=None,
|
||||
vendor_name=None,
|
||||
price="10.00",
|
||||
currency="EUR",
|
||||
),
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"EMPTY001_{unique_id}",
|
||||
title="MarketplaceProduct with Empty Values",
|
||||
brand="",
|
||||
google_product_category="",
|
||||
marketplace="",
|
||||
vendor_name="",
|
||||
price="15.00",
|
||||
currency="EUR",
|
||||
),
|
||||
]
|
||||
db.add_all(products_with_nulls)
|
||||
create_marketplace_product_with_translation(
|
||||
db,
|
||||
marketplace_product_id=f"NULL001_{unique_id}",
|
||||
title="MarketplaceProduct with Nulls",
|
||||
brand=None,
|
||||
google_product_category=None,
|
||||
marketplace=None,
|
||||
vendor_name=None,
|
||||
price="10.00",
|
||||
currency="EUR",
|
||||
)
|
||||
create_marketplace_product_with_translation(
|
||||
db,
|
||||
marketplace_product_id=f"EMPTY001_{unique_id}",
|
||||
title="MarketplaceProduct with Empty Values",
|
||||
brand="",
|
||||
google_product_category="",
|
||||
marketplace="",
|
||||
vendor_name="",
|
||||
price="15.00",
|
||||
currency="EUR",
|
||||
)
|
||||
db.commit()
|
||||
|
||||
stats = self.service.get_comprehensive_stats(db)
|
||||
@@ -170,36 +189,36 @@ class TestStatsService:
|
||||
):
|
||||
"""Test marketplace breakdown with multiple marketplaces."""
|
||||
unique_id = str(uuid.uuid4())[:8]
|
||||
marketplace_products = [
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"AMAZON001_{unique_id}",
|
||||
title="Amazon MarketplaceProduct 1",
|
||||
brand="AmazonBrand1",
|
||||
marketplace="Amazon",
|
||||
vendor_name="AmazonVendor1",
|
||||
price="20.00",
|
||||
currency="EUR",
|
||||
),
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"AMAZON002_{unique_id}",
|
||||
title="Amazon MarketplaceProduct 2",
|
||||
brand="AmazonBrand2",
|
||||
marketplace="Amazon",
|
||||
vendor_name="AmazonVendor2",
|
||||
price="25.00",
|
||||
currency="EUR",
|
||||
),
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"EBAY001_{unique_id}",
|
||||
title="eBay MarketplaceProduct",
|
||||
brand="eBayBrand",
|
||||
marketplace="eBay",
|
||||
vendor_name="eBayVendor",
|
||||
price="30.00",
|
||||
currency="USD",
|
||||
),
|
||||
]
|
||||
db.add_all(marketplace_products)
|
||||
create_marketplace_product_with_translation(
|
||||
db,
|
||||
marketplace_product_id=f"AMAZON001_{unique_id}",
|
||||
title="Amazon MarketplaceProduct 1",
|
||||
brand="AmazonBrand1",
|
||||
marketplace="Amazon",
|
||||
vendor_name="AmazonVendor1",
|
||||
price="20.00",
|
||||
currency="EUR",
|
||||
)
|
||||
create_marketplace_product_with_translation(
|
||||
db,
|
||||
marketplace_product_id=f"AMAZON002_{unique_id}",
|
||||
title="Amazon MarketplaceProduct 2",
|
||||
brand="AmazonBrand2",
|
||||
marketplace="Amazon",
|
||||
vendor_name="AmazonVendor2",
|
||||
price="25.00",
|
||||
currency="EUR",
|
||||
)
|
||||
create_marketplace_product_with_translation(
|
||||
db,
|
||||
marketplace_product_id=f"EBAY001_{unique_id}",
|
||||
title="eBay MarketplaceProduct",
|
||||
brand="eBayBrand",
|
||||
marketplace="eBay",
|
||||
vendor_name="eBayVendor",
|
||||
price="30.00",
|
||||
currency="USD",
|
||||
)
|
||||
db.commit()
|
||||
|
||||
stats = self.service.get_marketplace_breakdown_stats(db)
|
||||
@@ -223,7 +242,8 @@ class TestStatsService:
|
||||
def test_get_marketplace_breakdown_stats_excludes_nulls(self, db):
|
||||
"""Test marketplace breakdown excludes products with null marketplaces."""
|
||||
unique_id = str(uuid.uuid4())[:8]
|
||||
null_marketplace_product = MarketplaceProduct(
|
||||
create_marketplace_product_with_translation(
|
||||
db,
|
||||
marketplace_product_id=f"NULLMARKET001_{unique_id}",
|
||||
title="MarketplaceProduct without marketplace",
|
||||
marketplace=None,
|
||||
@@ -232,7 +252,6 @@ class TestStatsService:
|
||||
price="10.00",
|
||||
currency="EUR",
|
||||
)
|
||||
db.add(null_marketplace_product)
|
||||
db.commit()
|
||||
|
||||
stats = self.service.get_marketplace_breakdown_stats(db)
|
||||
@@ -264,15 +283,16 @@ class TestStatsService:
|
||||
"""Test getting vendor statistics successfully."""
|
||||
stats = self.service.get_vendor_stats(db, test_vendor.id)
|
||||
|
||||
assert "catalog" in stats
|
||||
assert "staging" in stats
|
||||
assert "inventory" in stats
|
||||
assert "imports" in stats
|
||||
assert "orders" in stats
|
||||
assert "customers" in stats
|
||||
# New flat structure compatible with VendorDashboardStatsResponse
|
||||
assert "total_products" in stats
|
||||
assert "active_products" in stats
|
||||
assert "total_orders" in stats
|
||||
assert "total_customers" in stats
|
||||
assert "total_revenue" in stats
|
||||
assert "total_inventory_quantity" in stats
|
||||
|
||||
assert stats["catalog"]["total_products"] >= 0
|
||||
assert stats["inventory"]["total_quantity"] >= 0
|
||||
assert stats["total_products"] >= 0
|
||||
assert stats["total_inventory_quantity"] >= 0
|
||||
|
||||
def test_get_vendor_stats_vendor_not_found(self, db):
|
||||
"""Test vendor stats with non-existent vendor."""
|
||||
@@ -285,8 +305,8 @@ class TestStatsService:
|
||||
"""Test vendor stats includes inventory data."""
|
||||
stats = self.service.get_vendor_stats(db, test_vendor.id)
|
||||
|
||||
assert stats["inventory"]["total_quantity"] >= test_inventory.quantity
|
||||
assert stats["inventory"]["reserved_quantity"] >= 0
|
||||
assert stats["total_inventory_quantity"] >= test_inventory.quantity
|
||||
assert stats["reserved_inventory_quantity"] >= 0
|
||||
|
||||
def test_get_vendor_stats_database_error(self, db, test_vendor):
|
||||
"""Test vendor stats handles database errors after vendor check."""
|
||||
@@ -453,27 +473,26 @@ class TestStatsService:
|
||||
def test_get_unique_brands_count(self, db, test_marketplace_product):
|
||||
"""Test getting unique brands count."""
|
||||
unique_id = str(uuid.uuid4())[:8]
|
||||
brand_products = [
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"BRAND001_{unique_id}",
|
||||
title="Brand MarketplaceProduct 1",
|
||||
brand="BrandA",
|
||||
marketplace="Test",
|
||||
vendor_name="TestVendor",
|
||||
price="10.00",
|
||||
currency="EUR",
|
||||
),
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"BRAND002_{unique_id}",
|
||||
title="Brand MarketplaceProduct 2",
|
||||
brand="BrandB",
|
||||
marketplace="Test",
|
||||
vendor_name="TestVendor",
|
||||
price="15.00",
|
||||
currency="EUR",
|
||||
),
|
||||
]
|
||||
db.add_all(brand_products)
|
||||
create_marketplace_product_with_translation(
|
||||
db,
|
||||
marketplace_product_id=f"BRAND001_{unique_id}",
|
||||
title="Brand MarketplaceProduct 1",
|
||||
brand="BrandA",
|
||||
marketplace="Test",
|
||||
vendor_name="TestVendor",
|
||||
price="10.00",
|
||||
currency="EUR",
|
||||
)
|
||||
create_marketplace_product_with_translation(
|
||||
db,
|
||||
marketplace_product_id=f"BRAND002_{unique_id}",
|
||||
title="Brand MarketplaceProduct 2",
|
||||
brand="BrandB",
|
||||
marketplace="Test",
|
||||
vendor_name="TestVendor",
|
||||
price="15.00",
|
||||
currency="EUR",
|
||||
)
|
||||
db.commit()
|
||||
|
||||
count = self.service._get_unique_brands_count(db)
|
||||
@@ -484,27 +503,26 @@ class TestStatsService:
|
||||
def test_get_unique_categories_count(self, db, test_marketplace_product):
|
||||
"""Test getting unique categories count."""
|
||||
unique_id = str(uuid.uuid4())[:8]
|
||||
category_products = [
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"CAT001_{unique_id}",
|
||||
title="Category MarketplaceProduct 1",
|
||||
google_product_category="Electronics",
|
||||
marketplace="Test",
|
||||
vendor_name="TestVendor",
|
||||
price="10.00",
|
||||
currency="EUR",
|
||||
),
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"CAT002_{unique_id}",
|
||||
title="Category MarketplaceProduct 2",
|
||||
google_product_category="Books",
|
||||
marketplace="Test",
|
||||
vendor_name="TestVendor",
|
||||
price="15.00",
|
||||
currency="EUR",
|
||||
),
|
||||
]
|
||||
db.add_all(category_products)
|
||||
create_marketplace_product_with_translation(
|
||||
db,
|
||||
marketplace_product_id=f"CAT001_{unique_id}",
|
||||
title="Category MarketplaceProduct 1",
|
||||
google_product_category="Electronics",
|
||||
marketplace="Test",
|
||||
vendor_name="TestVendor",
|
||||
price="10.00",
|
||||
currency="EUR",
|
||||
)
|
||||
create_marketplace_product_with_translation(
|
||||
db,
|
||||
marketplace_product_id=f"CAT002_{unique_id}",
|
||||
title="Category MarketplaceProduct 2",
|
||||
google_product_category="Books",
|
||||
marketplace="Test",
|
||||
vendor_name="TestVendor",
|
||||
price="15.00",
|
||||
currency="EUR",
|
||||
)
|
||||
db.commit()
|
||||
|
||||
count = self.service._get_unique_categories_count(db)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# tests/test_csv_processor.py
|
||||
# tests/unit/utils/test_csv_processor.py
|
||||
"""Unit tests for CSV processor with translation support."""
|
||||
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pandas as pd
|
||||
@@ -7,6 +9,8 @@ import requests
|
||||
import requests.exceptions
|
||||
|
||||
from app.utils.csv_processor import CSVProcessor
|
||||
from models.database.marketplace_product import MarketplaceProduct
|
||||
from models.database.marketplace_product_translation import MarketplaceProductTranslation
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -102,6 +106,74 @@ TEST002,Test MarketplaceProduct 2,15.99,TestMarket"""
|
||||
assert df.iloc[0]["marketplace_product_id"] == "TEST001"
|
||||
assert df.iloc[1]["price"] == 15.99
|
||||
|
||||
def test_normalize_columns_google_shopping(self):
|
||||
"""Test column normalization for Google Shopping feed format"""
|
||||
csv_content = """g:id,g:title,g:description,g:price,g:brand,g:product_type
|
||||
TEST001,Product 1,Description 1,19.99 EUR,Brand1,Category1"""
|
||||
|
||||
df = self.processor.parse_csv(csv_content)
|
||||
df = self.processor.normalize_columns(df)
|
||||
|
||||
assert "marketplace_product_id" in df.columns
|
||||
assert "title" in df.columns
|
||||
assert "description" in df.columns
|
||||
assert "product_type_raw" in df.columns # Renamed from product_type
|
||||
assert df.iloc[0]["marketplace_product_id"] == "TEST001"
|
||||
assert df.iloc[0]["title"] == "Product 1"
|
||||
|
||||
def test_extract_translation_data(self):
|
||||
"""Test extraction of translation fields from product data"""
|
||||
product_data = {
|
||||
"marketplace_product_id": "TEST001",
|
||||
"title": "Test Product",
|
||||
"description": "Test Description",
|
||||
"short_description": "Short desc",
|
||||
"price": "19.99",
|
||||
"brand": "TestBrand",
|
||||
}
|
||||
|
||||
translation_data = self.processor._extract_translation_data(product_data)
|
||||
|
||||
# Translation fields should be extracted
|
||||
assert translation_data["title"] == "Test Product"
|
||||
assert translation_data["description"] == "Test Description"
|
||||
assert translation_data["short_description"] == "Short desc"
|
||||
|
||||
# Product data should no longer have translation fields
|
||||
assert "title" not in product_data
|
||||
assert "description" not in product_data
|
||||
assert "short_description" not in product_data
|
||||
|
||||
# Non-translation fields should remain
|
||||
assert product_data["marketplace_product_id"] == "TEST001"
|
||||
assert product_data["price"] == "19.99"
|
||||
assert product_data["brand"] == "TestBrand"
|
||||
|
||||
def test_parse_price_to_numeric(self):
|
||||
"""Test price string to numeric conversion"""
|
||||
assert self.processor._parse_price_to_numeric("19.99 EUR") == 19.99
|
||||
assert self.processor._parse_price_to_numeric("19,99 EUR") == 19.99
|
||||
assert self.processor._parse_price_to_numeric("$29.99") == 29.99
|
||||
assert self.processor._parse_price_to_numeric("100") == 100.0
|
||||
assert self.processor._parse_price_to_numeric(None) is None
|
||||
assert self.processor._parse_price_to_numeric("") is None
|
||||
|
||||
def test_clean_row_data_with_prices(self):
|
||||
"""Test row data cleaning with price parsing"""
|
||||
row_data = {
|
||||
"marketplace_product_id": "TEST001",
|
||||
"title": "Test Product",
|
||||
"price": "19.99 EUR",
|
||||
"sale_price": "14.99 EUR",
|
||||
"gtin": "1234567890123",
|
||||
}
|
||||
|
||||
cleaned = self.processor._clean_row_data(row_data)
|
||||
|
||||
assert cleaned["price_numeric"] == 19.99
|
||||
assert cleaned["sale_price_numeric"] == 14.99
|
||||
assert cleaned["currency"] == "EUR"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_process_marketplace_csv_from_url(self, db):
|
||||
"""Test complete marketplace CSV processing"""
|
||||
@@ -117,15 +189,275 @@ TEST002,Test MarketplaceProduct 2,15.99,TestMarket"""
|
||||
"title": ["MarketplaceProduct 1", "MarketplaceProduct 2"],
|
||||
"price": ["10.99", "15.99"],
|
||||
"marketplace": ["TestMarket", "TestMarket"],
|
||||
"name": ["TestVendor", "TestVendor"],
|
||||
"vendor_name": ["TestVendor", "TestVendor"],
|
||||
}
|
||||
)
|
||||
mock_parse.return_value = mock_df
|
||||
|
||||
result = await self.processor.process_marketplace_csv_from_url(
|
||||
"http://example.com/test.csv", "TestMarket", "TestVendor", 1000, db
|
||||
"http://example.com/test.csv",
|
||||
"TestMarket",
|
||||
"TestVendor",
|
||||
1000,
|
||||
db,
|
||||
language="en",
|
||||
)
|
||||
|
||||
assert "imported" in result
|
||||
assert "updated" in result
|
||||
assert "total_processed" in result
|
||||
|
||||
assert "language" in result
|
||||
assert result["language"] == "en"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_process_batch_creates_translations(self, db):
|
||||
"""Test that batch processing creates translation records"""
|
||||
# Clean up any existing test data
|
||||
existing = (
|
||||
db.query(MarketplaceProduct)
|
||||
.filter(
|
||||
MarketplaceProduct.marketplace_product_id.in_(
|
||||
["TRANS_TEST_001", "TRANS_TEST_002"]
|
||||
)
|
||||
)
|
||||
.all()
|
||||
)
|
||||
for p in existing:
|
||||
db.delete(p)
|
||||
db.commit()
|
||||
|
||||
# Create test DataFrame
|
||||
batch_df = pd.DataFrame(
|
||||
{
|
||||
"marketplace_product_id": ["TRANS_TEST_001", "TRANS_TEST_002"],
|
||||
"title": ["Product One", "Product Two"],
|
||||
"description": ["Description One", "Description Two"],
|
||||
"price": ["19.99 EUR", "29.99 EUR"],
|
||||
"brand": ["Brand1", "Brand2"],
|
||||
}
|
||||
)
|
||||
|
||||
result = await self.processor._process_marketplace_batch(
|
||||
batch_df,
|
||||
"TestMarket",
|
||||
"TestVendor",
|
||||
db,
|
||||
batch_num=1,
|
||||
language="en",
|
||||
source_file="test.csv",
|
||||
)
|
||||
|
||||
assert result["imported"] == 2
|
||||
assert result["errors"] == 0
|
||||
|
||||
# Verify products were created
|
||||
products = (
|
||||
db.query(MarketplaceProduct)
|
||||
.filter(
|
||||
MarketplaceProduct.marketplace_product_id.in_(
|
||||
["TRANS_TEST_001", "TRANS_TEST_002"]
|
||||
)
|
||||
)
|
||||
.all()
|
||||
)
|
||||
assert len(products) == 2
|
||||
|
||||
# Verify translations were created
|
||||
for product in products:
|
||||
assert len(product.translations) == 1
|
||||
translation = product.translations[0]
|
||||
assert translation.language == "en"
|
||||
assert translation.title is not None
|
||||
assert translation.source_file == "test.csv"
|
||||
|
||||
# Verify get_title method works
|
||||
product1 = next(
|
||||
p for p in products if p.marketplace_product_id == "TRANS_TEST_001"
|
||||
)
|
||||
assert product1.get_title("en") == "Product One"
|
||||
assert product1.get_description("en") == "Description One"
|
||||
|
||||
# Clean up
|
||||
for p in products:
|
||||
db.delete(p)
|
||||
db.commit()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_process_batch_updates_existing_translations(self, db):
|
||||
"""Test that batch processing updates existing translation records"""
|
||||
# Clean up any existing test data
|
||||
existing = (
|
||||
db.query(MarketplaceProduct)
|
||||
.filter(MarketplaceProduct.marketplace_product_id == "UPDATE_TEST_001")
|
||||
.first()
|
||||
)
|
||||
if existing:
|
||||
db.delete(existing)
|
||||
db.commit()
|
||||
|
||||
# Create initial product with translation
|
||||
batch_df = pd.DataFrame(
|
||||
{
|
||||
"marketplace_product_id": ["UPDATE_TEST_001"],
|
||||
"title": ["Original Title"],
|
||||
"description": ["Original Description"],
|
||||
"price": ["19.99 EUR"],
|
||||
}
|
||||
)
|
||||
|
||||
await self.processor._process_marketplace_batch(
|
||||
batch_df, "TestMarket", "TestVendor", db, 1, language="en"
|
||||
)
|
||||
|
||||
# Update with new data
|
||||
update_df = pd.DataFrame(
|
||||
{
|
||||
"marketplace_product_id": ["UPDATE_TEST_001"],
|
||||
"title": ["Updated Title"],
|
||||
"description": ["Updated Description"],
|
||||
"price": ["24.99 EUR"],
|
||||
}
|
||||
)
|
||||
|
||||
result = await self.processor._process_marketplace_batch(
|
||||
update_df, "TestMarket", "TestVendor", db, 1, language="en"
|
||||
)
|
||||
|
||||
assert result["updated"] == 1
|
||||
assert result["imported"] == 0
|
||||
|
||||
# Verify translation was updated
|
||||
product = (
|
||||
db.query(MarketplaceProduct)
|
||||
.filter(MarketplaceProduct.marketplace_product_id == "UPDATE_TEST_001")
|
||||
.first()
|
||||
)
|
||||
assert product.get_title("en") == "Updated Title"
|
||||
assert product.get_description("en") == "Updated Description"
|
||||
|
||||
# Clean up
|
||||
db.delete(product)
|
||||
db.commit()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_process_batch_multi_language(self, db):
|
||||
"""Test importing same product in multiple languages"""
|
||||
# Clean up
|
||||
existing = (
|
||||
db.query(MarketplaceProduct)
|
||||
.filter(MarketplaceProduct.marketplace_product_id == "MULTI_LANG_001")
|
||||
.first()
|
||||
)
|
||||
if existing:
|
||||
db.delete(existing)
|
||||
db.commit()
|
||||
|
||||
# Import English version
|
||||
en_df = pd.DataFrame(
|
||||
{
|
||||
"marketplace_product_id": ["MULTI_LANG_001"],
|
||||
"title": ["English Title"],
|
||||
"description": ["English Description"],
|
||||
"price": ["19.99 EUR"],
|
||||
"brand": ["TestBrand"],
|
||||
}
|
||||
)
|
||||
|
||||
await self.processor._process_marketplace_batch(
|
||||
en_df, "TestMarket", "TestVendor", db, 1, language="en"
|
||||
)
|
||||
|
||||
# Import French version (same product, different language)
|
||||
fr_df = pd.DataFrame(
|
||||
{
|
||||
"marketplace_product_id": ["MULTI_LANG_001"],
|
||||
"title": ["Titre Français"],
|
||||
"description": ["Description Française"],
|
||||
"price": ["19.99 EUR"],
|
||||
"brand": ["TestBrand"],
|
||||
}
|
||||
)
|
||||
|
||||
result = await self.processor._process_marketplace_batch(
|
||||
fr_df, "TestMarket", "TestVendor", db, 1, language="fr"
|
||||
)
|
||||
|
||||
assert result["updated"] == 1 # Product existed, so it's an update
|
||||
|
||||
# Verify both translations exist
|
||||
product = (
|
||||
db.query(MarketplaceProduct)
|
||||
.filter(MarketplaceProduct.marketplace_product_id == "MULTI_LANG_001")
|
||||
.first()
|
||||
)
|
||||
assert len(product.translations) == 2
|
||||
|
||||
# Verify each language
|
||||
assert product.get_title("en") == "English Title"
|
||||
assert product.get_title("fr") == "Titre Français"
|
||||
assert product.get_description("en") == "English Description"
|
||||
assert product.get_description("fr") == "Description Française"
|
||||
|
||||
# Test fallback to English for unknown language
|
||||
assert product.get_title("de") == "English Title"
|
||||
|
||||
# Clean up
|
||||
db.delete(product)
|
||||
db.commit()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_process_batch_skips_missing_title(self, db):
|
||||
"""Test that rows without title are skipped"""
|
||||
batch_df = pd.DataFrame(
|
||||
{
|
||||
"marketplace_product_id": ["NO_TITLE_001", "HAS_TITLE_001"],
|
||||
"title": [None, "Has Title"],
|
||||
"price": ["19.99", "29.99"],
|
||||
}
|
||||
)
|
||||
|
||||
result = await self.processor._process_marketplace_batch(
|
||||
batch_df, "TestMarket", "TestVendor", db, 1, language="en"
|
||||
)
|
||||
|
||||
assert result["imported"] == 1
|
||||
assert result["errors"] == 1 # Missing title is an error
|
||||
|
||||
# Clean up
|
||||
product = (
|
||||
db.query(MarketplaceProduct)
|
||||
.filter(MarketplaceProduct.marketplace_product_id == "HAS_TITLE_001")
|
||||
.first()
|
||||
)
|
||||
if product:
|
||||
db.delete(product)
|
||||
db.commit()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_process_batch_skips_missing_product_id(self, db):
|
||||
"""Test that rows without marketplace_product_id are skipped"""
|
||||
batch_df = pd.DataFrame(
|
||||
{
|
||||
"marketplace_product_id": [None, "HAS_ID_001"],
|
||||
"title": ["No ID Product", "Has ID Product"],
|
||||
"price": ["19.99", "29.99"],
|
||||
}
|
||||
)
|
||||
|
||||
result = await self.processor._process_marketplace_batch(
|
||||
batch_df, "TestMarket", "TestVendor", db, 1, language="en"
|
||||
)
|
||||
|
||||
assert result["imported"] == 1
|
||||
assert result["errors"] == 1 # Missing ID is an error
|
||||
|
||||
# Clean up
|
||||
product = (
|
||||
db.query(MarketplaceProduct)
|
||||
.filter(MarketplaceProduct.marketplace_product_id == "HAS_ID_001")
|
||||
.first()
|
||||
)
|
||||
if product:
|
||||
db.delete(product)
|
||||
db.commit()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# tests/test_data_validation.py
|
||||
# tests/unit/utils/test_data_validation.py
|
||||
import pytest
|
||||
|
||||
from app.utils.data_processing import GTINProcessor, PriceProcessor
|
||||
|
||||
Reference in New Issue
Block a user