- 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>
554 lines
24 KiB
Python
554 lines
24 KiB
Python
# tests/test_inventory_service.py
|
|
import uuid
|
|
|
|
import pytest
|
|
|
|
from app.exceptions import (
|
|
InsufficientInventoryException,
|
|
InvalidQuantityException,
|
|
InventoryNotFoundException,
|
|
InventoryValidationException,
|
|
)
|
|
from app.services.inventory_service import InventoryService
|
|
from models.database.inventory import Inventory
|
|
from models.database.marketplace_product import MarketplaceProduct
|
|
from models.schema.inventory import InventoryAdd, InventoryCreate, InventoryUpdate
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.inventory
|
|
class TestInventoryService:
|
|
def setup_method(self):
|
|
self.service = InventoryService()
|
|
|
|
def test_normalize_gtin_invalid(self):
|
|
"""Test GTIN normalization with invalid GTINs."""
|
|
# Completely invalid values that should return None
|
|
assert self.service._normalize_gtin("invalid") is None
|
|
assert self.service._normalize_gtin("abcdef") is None
|
|
assert self.service._normalize_gtin("") is None
|
|
assert self.service._normalize_gtin(None) is None
|
|
assert self.service._normalize_gtin(" ") is None # Only whitespace
|
|
assert self.service._normalize_gtin("!@#$%") is None # Only special characters
|
|
|
|
# Mixed invalid characters that become empty after filtering
|
|
assert self.service._normalize_gtin("abc-def-ghi") is None # No digits
|
|
|
|
def test_normalize_gtin_valid(self):
|
|
"""Test GTIN normalization with valid GTINs."""
|
|
# Test various valid GTIN formats - these should remain unchanged
|
|
assert (
|
|
self.service._normalize_gtin("1234567890123") == "1234567890123"
|
|
) # EAN-13
|
|
assert self.service._normalize_gtin("123456789012") == "123456789012" # UPC-A
|
|
assert self.service._normalize_gtin("12345678") == "12345678" # EAN-8
|
|
assert (
|
|
self.service._normalize_gtin("12345678901234") == "12345678901234"
|
|
) # GTIN-14
|
|
|
|
# Test with decimal points (should be removed)
|
|
assert self.service._normalize_gtin("1234567890123.0") == "1234567890123"
|
|
|
|
# Test with whitespace (should be trimmed)
|
|
assert self.service._normalize_gtin(" 1234567890123 ") == "1234567890123"
|
|
|
|
# Test short GTINs being padded
|
|
assert (
|
|
self.service._normalize_gtin("123") == "0000000000123"
|
|
) # Padded to EAN-13
|
|
assert (
|
|
self.service._normalize_gtin("12345") == "0000000012345"
|
|
) # Padded to EAN-13
|
|
|
|
# Test long GTINs being truncated
|
|
assert (
|
|
self.service._normalize_gtin("123456789012345") == "3456789012345"
|
|
) # Truncated to 13
|
|
|
|
def test_normalize_gtin_edge_cases(self):
|
|
"""Test GTIN normalization edge cases."""
|
|
# Test numeric inputs
|
|
assert self.service._normalize_gtin(1234567890123) == "1234567890123"
|
|
assert self.service._normalize_gtin(123) == "0000000000123"
|
|
|
|
# Test mixed valid/invalid characters
|
|
assert (
|
|
self.service._normalize_gtin("123-456-789-012") == "123456789012"
|
|
) # Dashes removed
|
|
assert (
|
|
self.service._normalize_gtin("123 456 789 012") == "123456789012"
|
|
) # Spaces removed
|
|
assert (
|
|
self.service._normalize_gtin("ABC123456789012DEF") == "123456789012"
|
|
) # Letters removed
|
|
|
|
def test_set_inventory_new_entry_success(self, db):
|
|
"""Test setting inventory for a new GTIN/location combination successfully."""
|
|
unique_id = str(uuid.uuid4())[:8]
|
|
inventory_data = InventoryCreate(
|
|
gtin="1234567890123", location=f"WAREHOUSE_A_{unique_id}", quantity=100
|
|
)
|
|
|
|
result = self.service.set_inventory(db, inventory_data)
|
|
|
|
assert result.gtin == "1234567890123"
|
|
assert result.location.upper() == f"WAREHOUSE_A_{unique_id}".upper()
|
|
assert result.quantity == 100
|
|
|
|
def test_set_inventory_existing_entry_success(self, db, test_inventory):
|
|
"""Test setting inventory for an existing GTIN/location combination successfully."""
|
|
inventory_data = InventoryCreate(
|
|
gtin=test_inventory.gtin,
|
|
location=test_inventory.location, # Use exact same location as test_inventory
|
|
quantity=200,
|
|
)
|
|
|
|
result = self.service.set_inventory(db, inventory_data)
|
|
|
|
assert result.gtin == test_inventory.gtin
|
|
assert result.location == test_inventory.location
|
|
assert result.quantity == 200 # Should replace the original quantity
|
|
|
|
def test_set_inventory_invalid_gtin_validation_error(self, db):
|
|
"""Test setting inventory with invalid GTIN returns InventoryValidationException."""
|
|
inventory_data = InventoryCreate(
|
|
gtin="invalid_gtin", location="WAREHOUSE_A", quantity=100
|
|
)
|
|
|
|
with pytest.raises(InventoryValidationException) as exc_info:
|
|
self.service.set_inventory(db, inventory_data)
|
|
|
|
assert exc_info.value.error_code == "INVENTORY_VALIDATION_FAILED"
|
|
assert "Invalid GTIN format" in str(exc_info.value)
|
|
assert exc_info.value.details.get("field") == "gtin"
|
|
|
|
def test_set_inventory_invalid_quantity_error(self, db):
|
|
"""Test setting inventory with invalid quantity through service validation."""
|
|
|
|
# Test the service validation directly instead of going through Pydantic schema
|
|
# This bypasses the Pydantic validation to test service layer validation
|
|
|
|
# Create a mock inventory data object that bypasses Pydantic validation
|
|
class MockInventoryData:
|
|
def __init__(self, gtin, location, quantity):
|
|
self.gtin = gtin
|
|
self.location = location
|
|
self.quantity = quantity
|
|
|
|
mock_inventory_data = MockInventoryData("1234567890123", "WAREHOUSE_A", -10)
|
|
|
|
# Test the internal validation method directly
|
|
with pytest.raises(InvalidQuantityException) as exc_info:
|
|
self.service._validate_quantity(-10, allow_zero=True)
|
|
|
|
assert exc_info.value.error_code == "INVALID_QUANTITY"
|
|
assert "Quantity cannot be negative" in str(exc_info.value)
|
|
assert exc_info.value.details.get("quantity") == -10
|
|
|
|
def test_add_inventory_new_entry_success(self, db):
|
|
"""Test adding inventory for a new GTIN/location combination successfully."""
|
|
unique_id = str(uuid.uuid4())[:8]
|
|
inventory_data = InventoryAdd(
|
|
gtin="1234567890123", location=f"WAREHOUSE_B_{unique_id}", quantity=50
|
|
)
|
|
|
|
result = self.service.add_inventory(db, inventory_data)
|
|
|
|
assert result.gtin == "1234567890123"
|
|
assert result.location.upper() == f"WAREHOUSE_B_{unique_id}".upper()
|
|
assert result.quantity == 50
|
|
|
|
def test_add_inventory_existing_entry_success(self, db, test_inventory):
|
|
"""Test adding inventory to an existing GTIN/location combination successfully."""
|
|
original_quantity = test_inventory.quantity
|
|
inventory_data = InventoryAdd(
|
|
gtin=test_inventory.gtin,
|
|
location=test_inventory.location, # Use exact same location as test_inventory
|
|
quantity=25,
|
|
)
|
|
|
|
result = self.service.add_inventory(db, inventory_data)
|
|
|
|
assert result.gtin == test_inventory.gtin
|
|
assert result.location == test_inventory.location
|
|
assert result.quantity == original_quantity + 25
|
|
|
|
def test_add_inventory_invalid_gtin_validation_error(self, db):
|
|
"""Test adding inventory with invalid GTIN returns InventoryValidationException."""
|
|
inventory_data = InventoryAdd(
|
|
gtin="invalid_gtin", location="WAREHOUSE_A", quantity=50
|
|
)
|
|
|
|
with pytest.raises(InventoryValidationException) as exc_info:
|
|
self.service.add_inventory(db, inventory_data)
|
|
|
|
assert exc_info.value.error_code == "INVENTORY_VALIDATION_FAILED"
|
|
assert "Invalid GTIN format" in str(exc_info.value)
|
|
|
|
def test_add_inventory_invalid_quantity_error(self, db):
|
|
"""Test adding inventory with invalid quantity through service validation."""
|
|
# Test zero quantity which should fail for add_inventory (doesn't allow zero)
|
|
# This tests the service validation: allow_zero=False for add operations
|
|
with pytest.raises(InvalidQuantityException) as exc_info:
|
|
self.service._validate_quantity(0, allow_zero=False)
|
|
|
|
assert exc_info.value.error_code == "INVALID_QUANTITY"
|
|
assert "Quantity must be positive" in str(exc_info.value)
|
|
|
|
def test_remove_inventory_success(self, db, test_inventory):
|
|
"""Test removing inventory successfully."""
|
|
original_quantity = test_inventory.quantity
|
|
remove_quantity = min(
|
|
10, original_quantity
|
|
) # Ensure we don't remove more than available
|
|
|
|
inventory_data = InventoryAdd(
|
|
gtin=test_inventory.gtin,
|
|
location=test_inventory.location, # Use exact same location as test_inventory
|
|
quantity=remove_quantity,
|
|
)
|
|
|
|
result = self.service.remove_inventory(db, inventory_data)
|
|
|
|
assert result.gtin == test_inventory.gtin
|
|
assert result.location == test_inventory.location
|
|
assert result.quantity == original_quantity - remove_quantity
|
|
|
|
def test_remove_inventory_insufficient_inventory_error(self, db, test_inventory):
|
|
"""Test removing more inventory than available returns InsufficientInventoryException."""
|
|
inventory_data = InventoryAdd(
|
|
gtin=test_inventory.gtin,
|
|
location=test_inventory.location, # Use exact same location as test_inventory
|
|
quantity=test_inventory.quantity + 10, # More than available
|
|
)
|
|
|
|
with pytest.raises(InsufficientInventoryException) as exc_info:
|
|
self.service.remove_inventory(db, inventory_data)
|
|
|
|
assert exc_info.value.error_code == "INSUFFICIENT_INVENTORY"
|
|
assert exc_info.value.details["gtin"] == test_inventory.gtin
|
|
assert exc_info.value.details["location"] == test_inventory.location
|
|
assert (
|
|
exc_info.value.details["requested_quantity"] == test_inventory.quantity + 10
|
|
)
|
|
assert exc_info.value.details["available_quantity"] == test_inventory.quantity
|
|
|
|
def test_remove_inventory_nonexistent_entry_not_found(self, db):
|
|
"""Test removing inventory from non-existent GTIN/location returns InventoryNotFoundException."""
|
|
unique_id = str(uuid.uuid4())[:8]
|
|
inventory_data = InventoryAdd(
|
|
gtin="9999999999999", location=f"NONEXISTENT_{unique_id}", quantity=10
|
|
)
|
|
|
|
with pytest.raises(InventoryNotFoundException) as exc_info:
|
|
self.service.remove_inventory(db, inventory_data)
|
|
|
|
assert exc_info.value.error_code == "INVENTORY_NOT_FOUND"
|
|
assert "9999999999999" in str(exc_info.value)
|
|
assert exc_info.value.details["resource_type"] == "Inventory"
|
|
|
|
def test_remove_inventory_invalid_gtin_validation_error(self, db):
|
|
"""Test removing inventory with invalid GTIN returns InventoryValidationException."""
|
|
inventory_data = InventoryAdd(
|
|
gtin="invalid_gtin", location="WAREHOUSE_A", quantity=10
|
|
)
|
|
|
|
with pytest.raises(InventoryValidationException) as exc_info:
|
|
self.service.remove_inventory(db, inventory_data)
|
|
|
|
assert exc_info.value.error_code == "INVENTORY_VALIDATION_FAILED"
|
|
assert "Invalid GTIN format" in str(exc_info.value)
|
|
|
|
def test_remove_inventory_negative_result_error(self, db, test_inventory):
|
|
"""Test removing inventory that would result in negative quantity returns NegativeInventoryException."""
|
|
# This is handled by InsufficientInventoryException, but test the logic
|
|
inventory_data = InventoryAdd(
|
|
gtin=test_inventory.gtin,
|
|
location=test_inventory.location,
|
|
quantity=test_inventory.quantity + 1, # One more than available
|
|
)
|
|
|
|
with pytest.raises(InsufficientInventoryException) as exc_info:
|
|
self.service.remove_inventory(db, inventory_data)
|
|
|
|
# The service prevents negative inventory through InsufficientInventoryException
|
|
assert exc_info.value.error_code == "INSUFFICIENT_INVENTORY"
|
|
|
|
def test_get_inventory_by_gtin_success(
|
|
self, db, test_inventory, test_marketplace_product
|
|
):
|
|
"""Test getting inventory summary by GTIN successfully."""
|
|
result = self.service.get_inventory_by_gtin(db, test_inventory.gtin)
|
|
|
|
assert result.gtin == test_inventory.gtin
|
|
assert result.total_quantity == test_inventory.quantity
|
|
assert len(result.locations) == 1
|
|
assert result.locations[0].location == test_inventory.location
|
|
assert result.locations[0].quantity == test_inventory.quantity
|
|
assert result.product_title == test_marketplace_product.title
|
|
|
|
def test_get_inventory_by_gtin_multiple_locations_success(
|
|
self, db, test_marketplace_product
|
|
):
|
|
"""Test getting inventory summary with multiple locations successfully."""
|
|
unique_gtin = test_marketplace_product.gtin
|
|
unique_id = str(uuid.uuid4())[:8]
|
|
|
|
# Create multiple inventory entries for the same GTIN with unique locations
|
|
inventory1 = Inventory(
|
|
gtin=unique_gtin, location=f"WAREHOUSE_A_{unique_id}", quantity=50
|
|
)
|
|
inventory2 = Inventory(
|
|
gtin=unique_gtin, location=f"WAREHOUSE_B_{unique_id}", quantity=30
|
|
)
|
|
|
|
db.add(inventory1)
|
|
db.add(inventory2)
|
|
db.commit()
|
|
|
|
result = self.service.get_inventory_by_gtin(db, unique_gtin)
|
|
|
|
assert result.gtin == unique_gtin
|
|
assert result.total_quantity == 80
|
|
assert len(result.locations) == 2
|
|
|
|
def test_get_inventory_by_gtin_not_found_error(self, db):
|
|
"""Test getting inventory for non-existent GTIN returns InventoryNotFoundException."""
|
|
with pytest.raises(InventoryNotFoundException) as exc_info:
|
|
self.service.get_inventory_by_gtin(db, "9999999999999")
|
|
|
|
assert exc_info.value.error_code == "INVENTORY_NOT_FOUND"
|
|
assert "9999999999999" in str(exc_info.value)
|
|
assert exc_info.value.details["resource_type"] == "Inventory"
|
|
|
|
def test_get_inventory_by_gtin_invalid_gtin_validation_error(self, db):
|
|
"""Test getting inventory with invalid GTIN returns InventoryValidationException."""
|
|
with pytest.raises(InventoryValidationException) as exc_info:
|
|
self.service.get_inventory_by_gtin(db, "invalid_gtin")
|
|
|
|
assert exc_info.value.error_code == "INVENTORY_VALIDATION_FAILED"
|
|
assert "Invalid GTIN format" in str(exc_info.value)
|
|
|
|
def test_get_total_inventory_success(
|
|
self, db, test_inventory, test_marketplace_product
|
|
):
|
|
"""Test getting total inventory for a GTIN successfully."""
|
|
result = self.service.get_total_inventory(db, test_inventory.gtin)
|
|
|
|
assert result["gtin"] == test_inventory.gtin
|
|
assert result["total_quantity"] == test_inventory.quantity
|
|
assert result["product_title"] == test_marketplace_product.title
|
|
assert result["locations_count"] == 1
|
|
|
|
def test_get_total_inventory_invalid_gtin_validation_error(self, db):
|
|
"""Test getting total inventory with invalid GTIN returns InventoryValidationException."""
|
|
with pytest.raises(InventoryValidationException) as exc_info:
|
|
self.service.get_total_inventory(db, "invalid_gtin")
|
|
|
|
assert exc_info.value.error_code == "INVENTORY_VALIDATION_FAILED"
|
|
assert "Invalid GTIN format" in str(exc_info.value)
|
|
|
|
def test_get_total_inventory_not_found_error(self, db):
|
|
"""Test getting total inventory for non-existent GTIN returns InventoryNotFoundException."""
|
|
with pytest.raises(InventoryNotFoundException) as exc_info:
|
|
self.service.get_total_inventory(db, "9999999999999")
|
|
|
|
assert exc_info.value.error_code == "INVENTORY_NOT_FOUND"
|
|
|
|
def test_get_all_inventory_no_filters_success(self, db, test_inventory):
|
|
"""Test getting all inventory without filters successfully."""
|
|
result = self.service.get_all_inventory(db)
|
|
|
|
assert len(result) >= 1
|
|
assert any(inventory.gtin == test_inventory.gtin for inventory in result)
|
|
|
|
def test_get_all_inventory_with_location_filter_success(self, db, test_inventory):
|
|
"""Test getting all inventory with location filter successfully."""
|
|
result = self.service.get_all_inventory(db, location=test_inventory.location)
|
|
|
|
assert len(result) >= 1
|
|
# Check that all returned inventory match the filter (case insensitive)
|
|
for inventory in result:
|
|
assert test_inventory.location.upper() in inventory.location.upper()
|
|
|
|
def test_get_all_inventory_with_gtin_filter_success(self, db, test_inventory):
|
|
"""Test getting all inventory with GTIN filter successfully."""
|
|
result = self.service.get_all_inventory(db, gtin=test_inventory.gtin)
|
|
|
|
assert len(result) >= 1
|
|
assert all(inventory.gtin == test_inventory.gtin for inventory in result)
|
|
|
|
def test_get_all_inventory_with_pagination_success(self, db):
|
|
"""Test getting all inventory with pagination successfully."""
|
|
unique_prefix = str(uuid.uuid4())[:8]
|
|
|
|
# Create multiple inventory entries with unique GTINs and locations
|
|
for i in range(5):
|
|
inventory = Inventory(
|
|
gtin=f"1234567890{i:03d}", # Creates valid 13-digit GTINs
|
|
location=f"WAREHOUSE_{unique_prefix}_{i}",
|
|
quantity=10,
|
|
)
|
|
db.add(inventory)
|
|
db.commit()
|
|
|
|
result = self.service.get_all_inventory(db, skip=2, limit=2)
|
|
|
|
assert (
|
|
len(result) <= 2
|
|
) # Should be at most 2, might be less if other records exist
|
|
|
|
def test_update_inventory_success(self, db, test_inventory):
|
|
"""Test updating inventory quantity successfully."""
|
|
inventory_update = InventoryUpdate(quantity=150)
|
|
|
|
result = self.service.update_inventory(db, test_inventory.id, inventory_update)
|
|
|
|
assert result.id == test_inventory.id
|
|
assert result.quantity == 150
|
|
|
|
def test_update_inventory_not_found_error(self, db):
|
|
"""Test updating non-existent inventory entry returns InventoryNotFoundException."""
|
|
inventory_update = InventoryUpdate(quantity=150)
|
|
|
|
with pytest.raises(InventoryNotFoundException) as exc_info:
|
|
self.service.update_inventory(db, 99999, inventory_update)
|
|
|
|
assert exc_info.value.error_code == "INVENTORY_NOT_FOUND"
|
|
assert "99999" in str(exc_info.value)
|
|
|
|
def test_update_inventory_invalid_quantity_error(self, db, test_inventory):
|
|
"""Test updating inventory with invalid quantity returns InvalidQuantityException."""
|
|
inventory_update = InventoryUpdate(quantity=-10)
|
|
|
|
with pytest.raises(InvalidQuantityException) as exc_info:
|
|
self.service.update_inventory(db, test_inventory.id, inventory_update)
|
|
|
|
assert exc_info.value.error_code == "INVALID_QUANTITY"
|
|
assert "Quantity cannot be negative" in str(exc_info.value)
|
|
|
|
def test_delete_inventory_success(self, db, test_inventory):
|
|
"""Test deleting inventory entry successfully."""
|
|
inventory_id = test_inventory.id
|
|
|
|
result = self.service.delete_inventory(db, inventory_id)
|
|
|
|
assert result is True
|
|
|
|
# Verify the inventory is actually deleted
|
|
deleted_inventory = (
|
|
db.query(Inventory).filter(Inventory.id == inventory_id).first()
|
|
)
|
|
assert deleted_inventory is None
|
|
|
|
def test_delete_inventory_not_found_error(self, db):
|
|
"""Test deleting non-existent inventory entry returns InventoryNotFoundException."""
|
|
with pytest.raises(InventoryNotFoundException) as exc_info:
|
|
self.service.delete_inventory(db, 99999)
|
|
|
|
assert exc_info.value.error_code == "INVENTORY_NOT_FOUND"
|
|
assert "99999" in str(exc_info.value)
|
|
|
|
def test_get_low_inventory_items_success(
|
|
self, db, test_inventory, test_marketplace_product
|
|
):
|
|
"""Test getting low inventory items successfully."""
|
|
# Set inventory to a low value
|
|
test_inventory.quantity = 5
|
|
db.commit()
|
|
|
|
result = self.service.get_low_inventory_items(db, threshold=10)
|
|
|
|
assert len(result) >= 1
|
|
low_inventory_item = next(
|
|
(item for item in result if item["gtin"] == test_inventory.gtin), None
|
|
)
|
|
assert low_inventory_item is not None
|
|
assert low_inventory_item["current_quantity"] == 5
|
|
assert low_inventory_item["location"] == test_inventory.location
|
|
assert low_inventory_item["product_title"] == test_marketplace_product.title
|
|
|
|
def test_get_low_inventory_items_invalid_threshold_error(self, db):
|
|
"""Test getting low inventory items with invalid threshold returns InvalidQuantityException."""
|
|
with pytest.raises(InvalidQuantityException) as exc_info:
|
|
self.service.get_low_inventory_items(db, threshold=-5)
|
|
|
|
assert exc_info.value.error_code == "INVALID_QUANTITY"
|
|
assert "Threshold must be non-negative" in str(exc_info.value)
|
|
|
|
def test_get_inventory_summary_by_location_success(self, db, test_inventory):
|
|
"""Test getting inventory summary by location successfully."""
|
|
result = self.service.get_inventory_summary_by_location(
|
|
db, test_inventory.location
|
|
)
|
|
|
|
assert (
|
|
result["location"] == test_inventory.location.upper()
|
|
) # Service normalizes to uppercase
|
|
assert result["total_items"] >= 1
|
|
assert result["total_quantity"] >= test_inventory.quantity
|
|
assert result["unique_gtins"] >= 1
|
|
|
|
def test_get_inventory_summary_by_location_empty_result(self, db):
|
|
"""Test getting inventory summary for location with no inventory."""
|
|
unique_id = str(uuid.uuid4())[:8]
|
|
result = self.service.get_inventory_summary_by_location(
|
|
db, f"EMPTY_LOCATION_{unique_id}"
|
|
)
|
|
|
|
assert result["total_items"] == 0
|
|
assert result["total_quantity"] == 0
|
|
assert result["unique_gtins"] == 0
|
|
|
|
def test_validate_quantity_edge_cases(self, db):
|
|
"""Test quantity validation with edge cases."""
|
|
# Test zero quantity with allow_zero=True (should succeed)
|
|
inventory_data = InventoryCreate(
|
|
gtin="1234567890123", location="WAREHOUSE_A", quantity=0
|
|
)
|
|
result = self.service.set_inventory(db, inventory_data)
|
|
assert result.quantity == 0
|
|
|
|
# Test zero quantity with add_inventory (should fail - doesn't allow zero)
|
|
inventory_data_add = InventoryAdd(
|
|
gtin="1234567890123", location="WAREHOUSE_B", quantity=0
|
|
)
|
|
with pytest.raises(InvalidQuantityException):
|
|
self.service.add_inventory(db, inventory_data_add)
|
|
|
|
def test_exception_structure_consistency(self, db):
|
|
"""Test that all exceptions follow the consistent WizamartException structure."""
|
|
# Test with a known error case
|
|
with pytest.raises(InventoryNotFoundException) as exc_info:
|
|
self.service.get_inventory_by_gtin(db, "9999999999999")
|
|
|
|
exception = exc_info.value
|
|
|
|
# Verify exception structure matches WizamartException.to_dict()
|
|
assert hasattr(exception, "error_code")
|
|
assert hasattr(exception, "message")
|
|
assert hasattr(exception, "status_code")
|
|
assert hasattr(exception, "details")
|
|
|
|
assert isinstance(exception.error_code, str)
|
|
assert isinstance(exception.message, str)
|
|
assert isinstance(exception.status_code, int)
|
|
assert isinstance(exception.details, dict)
|
|
|
|
|
|
@pytest.fixture
|
|
def test_product_with_inventory(db, test_inventory):
|
|
"""Create a test product that corresponds to the test inventory."""
|
|
product = MarketplaceProduct(
|
|
marketplace_product_id="MP_TEST_001",
|
|
title="Inventory Test MarketplaceProduct",
|
|
gtin=test_inventory.gtin,
|
|
price="29.99",
|
|
brand="TestBrand",
|
|
marketplace="Letzshop",
|
|
)
|
|
db.add(product)
|
|
db.commit()
|
|
db.refresh(product)
|
|
return product
|