Refactoring code for modular approach
This commit is contained in:
322
tests/test_stock_service.py
Normal file
322
tests/test_stock_service.py
Normal file
@@ -0,0 +1,322 @@
|
||||
# tests/test_stock_service.py
|
||||
import pytest
|
||||
from app.services.stock_service import StockService
|
||||
from models.api_models import StockCreate, StockAdd, StockUpdate
|
||||
from models.database_models import Stock, Product
|
||||
|
||||
|
||||
class TestStockService:
|
||||
def setup_method(self):
|
||||
self.service = StockService()
|
||||
|
||||
def test_normalize_gtin_valid(self):
|
||||
"""Test GTIN normalization with valid GTINs"""
|
||||
# Test various valid GTIN formats
|
||||
assert self.service.normalize_gtin("1234567890123") == "1234567890123"
|
||||
assert self.service.normalize_gtin("123456789012") == "123456789012"
|
||||
assert self.service.normalize_gtin("12345678") == "12345678"
|
||||
|
||||
def test_normalize_gtin_invalid(self):
|
||||
"""Test GTIN normalization with invalid GTINs"""
|
||||
assert self.service.normalize_gtin("invalid") is None
|
||||
assert self.service.normalize_gtin("123") is None
|
||||
assert self.service.normalize_gtin("") is None
|
||||
assert self.service.normalize_gtin(None) is None
|
||||
|
||||
def test_set_stock_new_entry(self, db):
|
||||
"""Test setting stock for a new GTIN/location combination"""
|
||||
stock_data = StockCreate(
|
||||
gtin="1234567890123",
|
||||
location="WAREHOUSE_A",
|
||||
quantity=100
|
||||
)
|
||||
|
||||
result = self.service.set_stock(db, stock_data)
|
||||
|
||||
assert result.gtin == "1234567890123"
|
||||
assert result.location == "WAREHOUSE_A"
|
||||
assert result.quantity == 100
|
||||
|
||||
def test_set_stock_existing_entry(self, db, test_stock):
|
||||
"""Test setting stock for an existing GTIN/location combination"""
|
||||
stock_data = StockCreate(
|
||||
gtin=test_stock.gtin,
|
||||
location=test_stock.location,
|
||||
quantity=200
|
||||
)
|
||||
|
||||
result = self.service.set_stock(db, stock_data)
|
||||
|
||||
assert result.gtin == test_stock.gtin
|
||||
assert result.location == test_stock.location
|
||||
assert result.quantity == 200 # Should replace the original quantity
|
||||
|
||||
def test_set_stock_invalid_gtin(self, db):
|
||||
"""Test setting stock with invalid GTIN"""
|
||||
stock_data = StockCreate(
|
||||
gtin="invalid_gtin",
|
||||
location="WAREHOUSE_A",
|
||||
quantity=100
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError, match="Invalid GTIN format"):
|
||||
self.service.set_stock(db, stock_data)
|
||||
|
||||
def test_add_stock_new_entry(self, db):
|
||||
"""Test adding stock for a new GTIN/location combination"""
|
||||
stock_data = StockAdd(
|
||||
gtin="1234567890123",
|
||||
location="WAREHOUSE_B",
|
||||
quantity=50
|
||||
)
|
||||
|
||||
result = self.service.add_stock(db, stock_data)
|
||||
|
||||
assert result.gtin == "1234567890123"
|
||||
assert result.location == "WAREHOUSE_B"
|
||||
assert result.quantity == 50
|
||||
|
||||
def test_add_stock_existing_entry(self, db, test_stock):
|
||||
"""Test adding stock to an existing GTIN/location combination"""
|
||||
original_quantity = test_stock.quantity
|
||||
stock_data = StockAdd(
|
||||
gtin=test_stock.gtin,
|
||||
location=test_stock.location,
|
||||
quantity=25
|
||||
)
|
||||
|
||||
result = self.service.add_stock(db, stock_data)
|
||||
|
||||
assert result.gtin == test_stock.gtin
|
||||
assert result.location == test_stock.location
|
||||
assert result.quantity == original_quantity + 25
|
||||
|
||||
def test_add_stock_invalid_gtin(self, db):
|
||||
"""Test adding stock with invalid GTIN"""
|
||||
stock_data = StockAdd(
|
||||
gtin="invalid_gtin",
|
||||
location="WAREHOUSE_A",
|
||||
quantity=50
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError, match="Invalid GTIN format"):
|
||||
self.service.add_stock(db, stock_data)
|
||||
|
||||
def test_remove_stock_success(self, db, test_stock):
|
||||
"""Test removing stock successfully"""
|
||||
original_quantity = test_stock.quantity
|
||||
remove_quantity = 10
|
||||
|
||||
stock_data = StockAdd(
|
||||
gtin=test_stock.gtin,
|
||||
location=test_stock.location,
|
||||
quantity=remove_quantity
|
||||
)
|
||||
|
||||
result = self.service.remove_stock(db, stock_data)
|
||||
|
||||
assert result.gtin == test_stock.gtin
|
||||
assert result.location == test_stock.location
|
||||
assert result.quantity == original_quantity - remove_quantity
|
||||
|
||||
def test_remove_stock_insufficient_stock(self, db, test_stock):
|
||||
"""Test removing more stock than available"""
|
||||
stock_data = StockAdd(
|
||||
gtin=test_stock.gtin,
|
||||
location=test_stock.location,
|
||||
quantity=test_stock.quantity + 10 # More than available
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError, match="Insufficient stock"):
|
||||
self.service.remove_stock(db, stock_data)
|
||||
|
||||
def test_remove_stock_nonexistent_entry(self, db):
|
||||
"""Test removing stock from non-existent GTIN/location"""
|
||||
stock_data = StockAdd(
|
||||
gtin="9999999999999",
|
||||
location="NONEXISTENT",
|
||||
quantity=10
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError, match="No stock found"):
|
||||
self.service.remove_stock(db, stock_data)
|
||||
|
||||
def test_remove_stock_invalid_gtin(self, db):
|
||||
"""Test removing stock with invalid GTIN"""
|
||||
stock_data = StockAdd(
|
||||
gtin="invalid_gtin",
|
||||
location="WAREHOUSE_A",
|
||||
quantity=10
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError, match="Invalid GTIN format"):
|
||||
self.service.remove_stock(db, stock_data)
|
||||
|
||||
def test_get_stock_by_gtin_success(self, db, test_stock, test_product):
|
||||
"""Test getting stock summary by GTIN"""
|
||||
result = self.service.get_stock_by_gtin(db, test_stock.gtin)
|
||||
|
||||
assert result.gtin == test_stock.gtin
|
||||
assert result.total_quantity == test_stock.quantity
|
||||
assert len(result.locations) == 1
|
||||
assert result.locations[0].location == test_stock.location
|
||||
assert result.locations[0].quantity == test_stock.quantity
|
||||
assert result.product_title == test_product.title
|
||||
|
||||
def test_get_stock_by_gtin_multiple_locations(self, db):
|
||||
"""Test getting stock summary with multiple locations"""
|
||||
gtin = "1234567890123"
|
||||
|
||||
# Create multiple stock entries for the same GTIN
|
||||
stock1 = Stock(gtin=gtin, location="WAREHOUSE_A", quantity=50)
|
||||
stock2 = Stock(gtin=gtin, location="WAREHOUSE_B", quantity=30)
|
||||
|
||||
db.add(stock1)
|
||||
db.add(stock2)
|
||||
db.commit()
|
||||
|
||||
result = self.service.get_stock_by_gtin(db, gtin)
|
||||
|
||||
assert result.gtin == gtin
|
||||
assert result.total_quantity == 80
|
||||
assert len(result.locations) == 2
|
||||
|
||||
def test_get_stock_by_gtin_not_found(self, db):
|
||||
"""Test getting stock for non-existent GTIN"""
|
||||
with pytest.raises(ValueError, match="No stock found"):
|
||||
self.service.get_stock_by_gtin(db, "9999999999999")
|
||||
|
||||
def test_get_stock_by_gtin_invalid_gtin(self, db):
|
||||
"""Test getting stock with invalid GTIN"""
|
||||
with pytest.raises(ValueError, match="Invalid GTIN format"):
|
||||
self.service.get_stock_by_gtin(db, "invalid_gtin")
|
||||
|
||||
def test_get_total_stock_success(self, db, test_stock, test_product):
|
||||
"""Test getting total stock for a GTIN"""
|
||||
result = self.service.get_total_stock(db, test_stock.gtin)
|
||||
|
||||
assert result["gtin"] == test_stock.gtin
|
||||
assert result["total_quantity"] == test_stock.quantity
|
||||
assert result["product_title"] == test_product.title
|
||||
assert result["locations_count"] == 1
|
||||
|
||||
def test_get_total_stock_invalid_gtin(self, db):
|
||||
"""Test getting total stock with invalid GTIN"""
|
||||
with pytest.raises(ValueError, match="Invalid GTIN format"):
|
||||
self.service.get_total_stock(db, "invalid_gtin")
|
||||
|
||||
def test_get_all_stock_no_filters(self, db, test_stock):
|
||||
"""Test getting all stock without filters"""
|
||||
result = self.service.get_all_stock(db)
|
||||
|
||||
assert len(result) >= 1
|
||||
assert any(stock.gtin == test_stock.gtin for stock in result)
|
||||
|
||||
def test_get_all_stock_with_location_filter(self, db, test_stock):
|
||||
"""Test getting all stock with location filter"""
|
||||
result = self.service.get_all_stock(db, location=test_stock.location)
|
||||
|
||||
assert len(result) >= 1
|
||||
assert all(stock.location.upper() == test_stock.location.upper() for stock in result)
|
||||
|
||||
def test_get_all_stock_with_gtin_filter(self, db, test_stock):
|
||||
"""Test getting all stock with GTIN filter"""
|
||||
result = self.service.get_all_stock(db, gtin=test_stock.gtin)
|
||||
|
||||
assert len(result) >= 1
|
||||
assert all(stock.gtin == test_stock.gtin for stock in result)
|
||||
|
||||
def test_get_all_stock_with_pagination(self, db):
|
||||
"""Test getting all stock with pagination"""
|
||||
# Create multiple stock entries
|
||||
for i in range(5):
|
||||
stock = Stock(
|
||||
gtin=f"123456789012{i}",
|
||||
location=f"WAREHOUSE_{i}",
|
||||
quantity=10
|
||||
)
|
||||
db.add(stock)
|
||||
db.commit()
|
||||
|
||||
result = self.service.get_all_stock(db, skip=2, limit=2)
|
||||
|
||||
assert len(result) == 2
|
||||
|
||||
def test_update_stock_success(self, db, test_stock):
|
||||
"""Test updating stock quantity"""
|
||||
stock_update = StockUpdate(quantity=150)
|
||||
|
||||
result = self.service.update_stock(db, test_stock.id, stock_update)
|
||||
|
||||
assert result.id == test_stock.id
|
||||
assert result.quantity == 150
|
||||
|
||||
def test_update_stock_not_found(self, db):
|
||||
"""Test updating non-existent stock entry"""
|
||||
stock_update = StockUpdate(quantity=150)
|
||||
|
||||
with pytest.raises(ValueError, match="Stock entry not found"):
|
||||
self.service.update_stock(db, 99999, stock_update)
|
||||
|
||||
def test_delete_stock_success(self, db, test_stock):
|
||||
"""Test deleting stock entry"""
|
||||
stock_id = test_stock.id
|
||||
|
||||
result = self.service.delete_stock(db, stock_id)
|
||||
|
||||
assert result is True
|
||||
|
||||
# Verify the stock is actually deleted
|
||||
deleted_stock = db.query(Stock).filter(Stock.id == stock_id).first()
|
||||
assert deleted_stock is None
|
||||
|
||||
def test_delete_stock_not_found(self, db):
|
||||
"""Test deleting non-existent stock entry"""
|
||||
with pytest.raises(ValueError, match="Stock entry not found"):
|
||||
self.service.delete_stock(db, 99999)
|
||||
|
||||
def test_get_stock_by_id_success(self, db, test_stock):
|
||||
"""Test getting stock entry by ID"""
|
||||
result = self.service.get_stock_by_id(db, test_stock.id)
|
||||
|
||||
assert result is not None
|
||||
assert result.id == test_stock.id
|
||||
assert result.gtin == test_stock.gtin
|
||||
|
||||
def test_get_stock_by_id_not_found(self, db):
|
||||
"""Test getting non-existent stock entry by ID"""
|
||||
result = self.service.get_stock_by_id(db, 99999)
|
||||
|
||||
assert result is None
|
||||
|
||||
|
||||
# Additional fixtures that might be needed for stock tests
|
||||
@pytest.fixture
|
||||
def test_stock(db):
|
||||
"""Create a test stock entry"""
|
||||
stock = Stock(
|
||||
gtin="1234567890123",
|
||||
location="WAREHOUSE_MAIN",
|
||||
quantity=50
|
||||
)
|
||||
db.add(stock)
|
||||
db.commit()
|
||||
db.refresh(stock)
|
||||
return stock
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def test_product_with_stock(db, test_stock):
|
||||
"""Create a test product that corresponds to the test stock"""
|
||||
product = Product(
|
||||
product_id="STOCK_TEST_001",
|
||||
title="Stock Test Product",
|
||||
gtin=test_stock.gtin,
|
||||
price="29.99",
|
||||
brand="TestBrand",
|
||||
marketplace="Letzshop"
|
||||
)
|
||||
db.add(product)
|
||||
db.commit()
|
||||
db.refresh(product)
|
||||
return product
|
||||
Reference in New Issue
Block a user