Files
orion/tests/unit/services/test_product_service.py
Samir Boulahtit 238c1ec9b8 refactor: modernize code quality tooling with Ruff
- Replace black, isort, and flake8 with Ruff (all-in-one linter and formatter)
- Add comprehensive pyproject.toml configuration
- Simplify Makefile code quality targets
- Configure exclusions for venv/.venv in pyproject.toml
- Auto-fix 1,359 linting issues across codebase

Benefits:
- Much faster builds (Ruff is written in Rust)
- Single tool replaces multiple tools
- More comprehensive rule set (UP, B, C4, SIM, PIE, RET, Q)
- All configuration centralized in pyproject.toml
- Better import sorting and formatting consistency

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 19:37:38 +01:00

329 lines
14 KiB
Python

# tests/test_product_service.py
import pytest
from app.exceptions import (
InvalidMarketplaceProductDataException,
MarketplaceProductAlreadyExistsException,
MarketplaceProductNotFoundException,
MarketplaceProductValidationException,
)
from app.services.marketplace_product_service import MarketplaceProductService
from models.schema.marketplace_product import (
MarketplaceProductCreate,
MarketplaceProductUpdate,
)
@pytest.mark.unit
@pytest.mark.products
class TestProductService:
def setup_method(self):
self.service = MarketplaceProductService()
def test_create_product_success(self, db):
"""Test successful product creation with valid data"""
product_data = MarketplaceProductCreate(
marketplace_product_id="SVC001",
title="Service Test MarketplaceProduct",
gtin="1234567890123",
price="19.99",
marketplace="TestMarket",
)
product = self.service.create_product(db, product_data)
assert product.marketplace_product_id == "SVC001"
assert product.title == "Service Test MarketplaceProduct"
assert product.gtin == "1234567890123"
assert product.marketplace == "TestMarket"
assert product.price == "19.99" # Price is stored as string after processing
def test_create_product_invalid_gtin(self, db):
"""Test product creation with invalid GTIN raises InvalidMarketplaceProductDataException"""
product_data = MarketplaceProductCreate(
marketplace_product_id="SVC002",
title="Service Test MarketplaceProduct",
gtin="invalid_gtin",
price="19.99",
)
with pytest.raises(InvalidMarketplaceProductDataException) as exc_info:
self.service.create_product(db, product_data)
assert exc_info.value.error_code == "INVALID_PRODUCT_DATA"
assert "Invalid GTIN format" in str(exc_info.value)
assert exc_info.value.status_code == 422
assert exc_info.value.details.get("field") == "gtin"
def test_create_product_missing_product_id(self, db):
"""Test product creation without marketplace_product_id raises MarketplaceProductValidationException"""
product_data = MarketplaceProductCreate(
marketplace_product_id="", # Empty product ID
title="Service Test MarketplaceProduct",
price="19.99",
)
with pytest.raises(MarketplaceProductValidationException) as exc_info:
self.service.create_product(db, product_data)
assert exc_info.value.error_code == "PRODUCT_VALIDATION_FAILED"
assert "MarketplaceProduct ID is required" in str(exc_info.value)
assert exc_info.value.details.get("field") == "marketplace_product_id"
def test_create_product_missing_title(self, db):
"""Test product creation without title raises MarketplaceProductValidationException"""
product_data = MarketplaceProductCreate(
marketplace_product_id="SVC003",
title="", # Empty title
price="19.99",
)
with pytest.raises(MarketplaceProductValidationException) as exc_info:
self.service.create_product(db, product_data)
assert exc_info.value.error_code == "PRODUCT_VALIDATION_FAILED"
assert "MarketplaceProduct title is required" in str(exc_info.value)
assert exc_info.value.details.get("field") == "title"
def test_create_product_already_exists(self, db, test_marketplace_product):
"""Test creating product with existing ID raises MarketplaceProductAlreadyExistsException"""
product_data = MarketplaceProductCreate(
marketplace_product_id=test_marketplace_product.marketplace_product_id, # Use existing product ID
title="Duplicate MarketplaceProduct",
price="29.99",
)
with pytest.raises(MarketplaceProductAlreadyExistsException) as exc_info:
self.service.create_product(db, product_data)
assert exc_info.value.error_code == "PRODUCT_ALREADY_EXISTS"
assert test_marketplace_product.marketplace_product_id in str(exc_info.value)
assert exc_info.value.status_code == 409
assert (
exc_info.value.details.get("marketplace_product_id")
== test_marketplace_product.marketplace_product_id
)
def test_create_product_invalid_price(self, db):
"""Test product creation with invalid price raises InvalidMarketplaceProductDataException"""
product_data = MarketplaceProductCreate(
marketplace_product_id="SVC004",
title="Service Test MarketplaceProduct",
price="invalid_price",
)
with pytest.raises(InvalidMarketplaceProductDataException) as exc_info:
self.service.create_product(db, product_data)
assert exc_info.value.error_code == "INVALID_PRODUCT_DATA"
assert "Invalid price format" in str(exc_info.value)
assert exc_info.value.details.get("field") == "price"
def test_get_product_by_id_or_raise_success(self, db, test_marketplace_product):
"""Test successful product retrieval by ID"""
product = self.service.get_product_by_id_or_raise(
db, test_marketplace_product.marketplace_product_id
)
assert (
product.marketplace_product_id
== test_marketplace_product.marketplace_product_id
)
assert product.title == test_marketplace_product.title
def test_get_product_by_id_or_raise_not_found(self, db):
"""Test product retrieval with non-existent ID raises MarketplaceProductNotFoundException"""
with pytest.raises(MarketplaceProductNotFoundException) as exc_info:
self.service.get_product_by_id_or_raise(db, "NONEXISTENT")
assert exc_info.value.error_code == "PRODUCT_NOT_FOUND"
assert "NONEXISTENT" in str(exc_info.value)
assert exc_info.value.status_code == 404
assert exc_info.value.details.get("resource_type") == "MarketplaceProduct"
assert exc_info.value.details.get("identifier") == "NONEXISTENT"
def test_get_products_with_filters_success(self, db, test_marketplace_product):
"""Test getting products with various filters"""
products, total = self.service.get_products_with_filters(
db, brand=test_marketplace_product.brand
)
assert total == 1
assert len(products) == 1
assert products[0].brand == test_marketplace_product.brand
def test_get_products_with_search(self, db, test_marketplace_product):
"""Test getting products with search term"""
products, total = self.service.get_products_with_filters(
db, search="Test MarketplaceProduct"
)
assert total >= 1
assert len(products) >= 1
# Verify search worked by checking that title contains search term
found_product = next(
(
p
for p in products
if p.marketplace_product_id
== test_marketplace_product.marketplace_product_id
),
None,
)
assert found_product is not None
def test_update_product_success(self, db, test_marketplace_product):
"""Test successful product update"""
update_data = MarketplaceProductUpdate(
title="Updated MarketplaceProduct Title", price="39.99"
)
updated_product = self.service.update_product(
db, test_marketplace_product.marketplace_product_id, update_data
)
assert updated_product.title == "Updated MarketplaceProduct Title"
assert (
updated_product.price == "39.99"
) # Price is stored as string after processing
assert (
updated_product.marketplace_product_id
== test_marketplace_product.marketplace_product_id
) # ID unchanged
def test_update_product_not_found(self, db):
"""Test updating non-existent product raises MarketplaceProductNotFoundException"""
update_data = MarketplaceProductUpdate(title="Updated Title")
with pytest.raises(MarketplaceProductNotFoundException) as exc_info:
self.service.update_product(db, "NONEXISTENT", update_data)
assert exc_info.value.error_code == "PRODUCT_NOT_FOUND"
assert "NONEXISTENT" in str(exc_info.value)
def test_update_product_invalid_gtin(self, db, test_marketplace_product):
"""Test updating product with invalid GTIN raises InvalidMarketplaceProductDataException"""
update_data = MarketplaceProductUpdate(gtin="invalid_gtin")
with pytest.raises(InvalidMarketplaceProductDataException) as exc_info:
self.service.update_product(
db, test_marketplace_product.marketplace_product_id, update_data
)
assert exc_info.value.error_code == "INVALID_PRODUCT_DATA"
assert "Invalid GTIN format" in str(exc_info.value)
assert exc_info.value.details.get("field") == "gtin"
def test_update_product_empty_title(self, db, test_marketplace_product):
"""Test updating product with empty title raises MarketplaceProductValidationException"""
update_data = MarketplaceProductUpdate(title="")
with pytest.raises(MarketplaceProductValidationException) as exc_info:
self.service.update_product(
db, test_marketplace_product.marketplace_product_id, update_data
)
assert exc_info.value.error_code == "PRODUCT_VALIDATION_FAILED"
assert "MarketplaceProduct title cannot be empty" in str(exc_info.value)
assert exc_info.value.details.get("field") == "title"
def test_update_product_invalid_price(self, db, test_marketplace_product):
"""Test updating product with invalid price raises InvalidMarketplaceProductDataException"""
update_data = MarketplaceProductUpdate(price="invalid_price")
with pytest.raises(InvalidMarketplaceProductDataException) as exc_info:
self.service.update_product(
db, test_marketplace_product.marketplace_product_id, update_data
)
assert exc_info.value.error_code == "INVALID_PRODUCT_DATA"
assert "Invalid price format" in str(exc_info.value)
assert exc_info.value.details.get("field") == "price"
def test_delete_product_success(self, db, test_marketplace_product):
"""Test successful product deletion"""
result = self.service.delete_product(
db, test_marketplace_product.marketplace_product_id
)
assert result is True
# Verify product is deleted
deleted_product = self.service.get_product_by_id(
db, test_marketplace_product.marketplace_product_id
)
assert deleted_product is None
def test_delete_product_not_found(self, db):
"""Test deleting non-existent product raises MarketplaceProductNotFoundException"""
with pytest.raises(MarketplaceProductNotFoundException) as exc_info:
self.service.delete_product(db, "NONEXISTENT")
assert exc_info.value.error_code == "PRODUCT_NOT_FOUND"
assert "NONEXISTENT" in str(exc_info.value)
def test_get_inventory_info_success(
self, db, test_marketplace_product_with_inventory
):
"""Test getting inventory info for product with inventory"""
# Extract the product from the dictionary
marketplace_product = test_marketplace_product_with_inventory[
"marketplace_product"
]
inventory_info = self.service.get_inventory_info(db, marketplace_product.gtin)
assert inventory_info is not None
assert inventory_info.gtin == marketplace_product.gtin
assert inventory_info.total_quantity > 0
assert len(inventory_info.locations) > 0
def test_get_inventory_info_no_inventory(self, db, test_marketplace_product):
"""Test getting inventory info for product without inventory"""
inventory_info = self.service.get_inventory_info(
db, test_marketplace_product.gtin or "1234567890123"
)
assert inventory_info is None
def test_product_exists_true(self, db, test_marketplace_product):
"""Test product_exists returns True for existing product"""
exists = self.service.product_exists(
db, test_marketplace_product.marketplace_product_id
)
assert exists is True
def test_product_exists_false(self, db):
"""Test product_exists returns False for non-existent product"""
exists = self.service.product_exists(db, "NONEXISTENT")
assert exists is False
def test_generate_csv_export_success(self, db, test_marketplace_product):
"""Test CSV export generation"""
csv_generator = self.service.generate_csv_export(db)
# Convert generator to list to test content
csv_lines = list(csv_generator)
assert len(csv_lines) > 1 # Header + at least one data row
assert csv_lines[0].startswith(
"marketplace_product_id,title,description"
) # Check header
# Check that test product appears in CSV
csv_content = "".join(csv_lines)
assert test_marketplace_product.marketplace_product_id in csv_content
def test_generate_csv_export_with_filters(self, db, test_marketplace_product):
"""Test CSV export with marketplace filter"""
csv_generator = self.service.generate_csv_export(
db, marketplace=test_marketplace_product.marketplace
)
csv_lines = list(csv_generator)
assert len(csv_lines) >= 1 # At least header
if len(csv_lines) > 1: # If there's data
csv_content = "".join(csv_lines)
assert test_marketplace_product.marketplace in csv_content