style: apply black and isort formatting across entire codebase
- Standardize quote style (single to double quotes) - Reorder and group imports alphabetically - Fix line breaks and indentation for consistency - Apply PEP 8 formatting standards Also updated Makefile to exclude both venv and .venv from code quality checks. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,14 +1,10 @@
|
||||
# tests/unit/services/test_admin_service.py
|
||||
import pytest
|
||||
|
||||
from app.exceptions import (
|
||||
UserNotFoundException,
|
||||
UserStatusChangeException,
|
||||
CannotModifySelfException,
|
||||
VendorNotFoundException,
|
||||
VendorVerificationException,
|
||||
AdminOperationException,
|
||||
)
|
||||
from app.exceptions import (AdminOperationException, CannotModifySelfException,
|
||||
UserNotFoundException, UserStatusChangeException,
|
||||
VendorNotFoundException,
|
||||
VendorVerificationException)
|
||||
from app.services.admin_service import AdminService
|
||||
from app.services.stats_service import stats_service
|
||||
from models.database.marketplace_import_job import MarketplaceImportJob
|
||||
@@ -85,7 +81,9 @@ class TestAdminService:
|
||||
assert exception.error_code == "CANNOT_MODIFY_SELF"
|
||||
assert "deactivate account" in exception.message
|
||||
|
||||
def test_toggle_user_status_cannot_modify_admin(self, db, test_admin, another_admin):
|
||||
def test_toggle_user_status_cannot_modify_admin(
|
||||
self, db, test_admin, another_admin
|
||||
):
|
||||
"""Test that admin cannot modify another admin"""
|
||||
with pytest.raises(UserStatusChangeException) as exc_info:
|
||||
self.service.toggle_user_status(db, another_admin.id, test_admin.id)
|
||||
@@ -148,7 +146,7 @@ class TestAdminService:
|
||||
assert "99999" in exception.message
|
||||
|
||||
def test_toggle_vendor_status_deactivate(self, db, test_vendor):
|
||||
"""Test deactivating a vendor """
|
||||
"""Test deactivating a vendor"""
|
||||
original_status = test_vendor.is_active
|
||||
|
||||
vendor, message = self.service.toggle_vendor_status(db, test_vendor.id)
|
||||
@@ -170,21 +168,26 @@ class TestAdminService:
|
||||
assert exception.error_code == "VENDOR_NOT_FOUND"
|
||||
|
||||
# Marketplace Import Jobs Tests
|
||||
def test_get_marketplace_import_jobs_no_filters(self, db, test_marketplace_import_job):
|
||||
def test_get_marketplace_import_jobs_no_filters(
|
||||
self, db, test_marketplace_import_job
|
||||
):
|
||||
"""Test getting marketplace import jobs without filters"""
|
||||
result = self.service.get_marketplace_import_jobs(db, skip=0, limit=10)
|
||||
|
||||
assert len(result) >= 1
|
||||
# Find our test job in the results
|
||||
test_job = next(
|
||||
(job for job in result if job.job_id == test_marketplace_import_job.id), None
|
||||
(job for job in result if job.job_id == test_marketplace_import_job.id),
|
||||
None,
|
||||
)
|
||||
assert test_job is not None
|
||||
assert test_job.marketplace == test_marketplace_import_job.marketplace
|
||||
assert test_job.vendor_name == test_marketplace_import_job.name
|
||||
assert test_job.status == test_marketplace_import_job.status
|
||||
|
||||
def test_get_marketplace_import_jobs_with_marketplace_filter(self, db, test_marketplace_import_job):
|
||||
def test_get_marketplace_import_jobs_with_marketplace_filter(
|
||||
self, db, test_marketplace_import_job
|
||||
):
|
||||
"""Test filtering marketplace import jobs by marketplace"""
|
||||
result = self.service.get_marketplace_import_jobs(
|
||||
db, marketplace=test_marketplace_import_job.marketplace, skip=0, limit=10
|
||||
@@ -192,9 +195,14 @@ class TestAdminService:
|
||||
|
||||
assert len(result) >= 1
|
||||
for job in result:
|
||||
assert test_marketplace_import_job.marketplace.lower() in job.marketplace.lower()
|
||||
assert (
|
||||
test_marketplace_import_job.marketplace.lower()
|
||||
in job.marketplace.lower()
|
||||
)
|
||||
|
||||
def test_get_marketplace_import_jobs_with_vendor_filter(self, db, test_marketplace_import_job):
|
||||
def test_get_marketplace_import_jobs_with_vendor_filter(
|
||||
self, db, test_marketplace_import_job
|
||||
):
|
||||
"""Test filtering marketplace import jobs by vendor name"""
|
||||
result = self.service.get_marketplace_import_jobs(
|
||||
db, vendor_name=test_marketplace_import_job.name, skip=0, limit=10
|
||||
@@ -204,7 +212,9 @@ class TestAdminService:
|
||||
for job in result:
|
||||
assert test_marketplace_import_job.name.lower() in job.vendor_name.lower()
|
||||
|
||||
def test_get_marketplace_import_jobs_with_status_filter(self, db, test_marketplace_import_job):
|
||||
def test_get_marketplace_import_jobs_with_status_filter(
|
||||
self, db, test_marketplace_import_job
|
||||
):
|
||||
"""Test filtering marketplace import jobs by status"""
|
||||
result = self.service.get_marketplace_import_jobs(
|
||||
db, status=test_marketplace_import_job.status, skip=0, limit=10
|
||||
@@ -214,7 +224,9 @@ class TestAdminService:
|
||||
for job in result:
|
||||
assert job.status == test_marketplace_import_job.status
|
||||
|
||||
def test_get_marketplace_import_jobs_pagination(self, db, test_marketplace_import_job):
|
||||
def test_get_marketplace_import_jobs_pagination(
|
||||
self, db, test_marketplace_import_job
|
||||
):
|
||||
"""Test marketplace import jobs pagination"""
|
||||
result_page1 = self.service.get_marketplace_import_jobs(db, skip=0, limit=1)
|
||||
result_page2 = self.service.get_marketplace_import_jobs(db, skip=1, limit=1)
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
# tests/test_auth_service.py
|
||||
import pytest
|
||||
|
||||
from app.exceptions.auth import (
|
||||
UserAlreadyExistsException,
|
||||
InvalidCredentialsException,
|
||||
UserNotActiveException,
|
||||
)
|
||||
from app.exceptions.auth import (InvalidCredentialsException,
|
||||
UserAlreadyExistsException,
|
||||
UserNotActiveException)
|
||||
from app.exceptions.base import ValidationException
|
||||
from app.services.auth_service import AuthService
|
||||
from models.schema.auth import UserLogin, UserRegister
|
||||
@@ -218,11 +216,14 @@ class TestAuthService:
|
||||
|
||||
def test_create_access_token_failure(self, test_user, monkeypatch):
|
||||
"""Test creating access token handles failures"""
|
||||
|
||||
# Mock the auth_manager to raise an exception
|
||||
def mock_create_token(*args, **kwargs):
|
||||
raise Exception("Token creation failed")
|
||||
|
||||
monkeypatch.setattr(self.service.auth_manager, "create_access_token", mock_create_token)
|
||||
monkeypatch.setattr(
|
||||
self.service.auth_manager, "create_access_token", mock_create_token
|
||||
)
|
||||
|
||||
with pytest.raises(ValidationException) as exc_info:
|
||||
self.service.create_access_token(test_user)
|
||||
@@ -250,11 +251,14 @@ class TestAuthService:
|
||||
|
||||
def test_hash_password_failure(self, monkeypatch):
|
||||
"""Test password hashing handles failures"""
|
||||
|
||||
# Mock the auth_manager to raise an exception
|
||||
def mock_hash_password(*args, **kwargs):
|
||||
raise Exception("Hashing failed")
|
||||
|
||||
monkeypatch.setattr(self.service.auth_manager, "hash_password", mock_hash_password)
|
||||
monkeypatch.setattr(
|
||||
self.service.auth_manager, "hash_password", mock_hash_password
|
||||
)
|
||||
|
||||
with pytest.raises(ValidationException) as exc_info:
|
||||
self.service.hash_password("testpassword")
|
||||
@@ -267,9 +271,7 @@ class TestAuthService:
|
||||
def test_register_user_database_error(self, db_with_error):
|
||||
"""Test user registration handles database errors"""
|
||||
user_data = UserRegister(
|
||||
email="test@example.com",
|
||||
username="testuser",
|
||||
password="password123"
|
||||
email="test@example.com", username="testuser", password="password123"
|
||||
)
|
||||
|
||||
with pytest.raises(ValidationException) as exc_info:
|
||||
|
||||
@@ -3,19 +3,17 @@ import uuid
|
||||
|
||||
import pytest
|
||||
|
||||
from app.exceptions import (InsufficientInventoryException,
|
||||
InvalidInventoryOperationException,
|
||||
InvalidQuantityException,
|
||||
InventoryNotFoundException,
|
||||
InventoryValidationException,
|
||||
NegativeInventoryException, ValidationException)
|
||||
from app.services.inventory_service import InventoryService
|
||||
from app.exceptions import (
|
||||
InventoryNotFoundException,
|
||||
InsufficientInventoryException,
|
||||
InvalidInventoryOperationException,
|
||||
InventoryValidationException,
|
||||
NegativeInventoryException,
|
||||
InvalidQuantityException,
|
||||
ValidationException,
|
||||
)
|
||||
from models.schema.inventory import InventoryAdd, InventoryCreate, InventoryUpdate
|
||||
from models.database.marketplace_product import MarketplaceProduct
|
||||
from models.database.inventory import Inventory
|
||||
from models.database.marketplace_product import MarketplaceProduct
|
||||
from models.schema.inventory import (InventoryAdd, InventoryCreate,
|
||||
InventoryUpdate)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -40,10 +38,14 @@ class TestInventoryService:
|
||||
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("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
|
||||
assert (
|
||||
self.service._normalize_gtin("12345678901234") == "12345678901234"
|
||||
) # GTIN-14
|
||||
|
||||
# Test with decimal points (should be removed)
|
||||
assert self.service._normalize_gtin("1234567890123.0") == "1234567890123"
|
||||
@@ -52,11 +54,17 @@ class TestInventoryService:
|
||||
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
|
||||
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
|
||||
assert (
|
||||
self.service._normalize_gtin("123456789012345") == "3456789012345"
|
||||
) # Truncated to 13
|
||||
|
||||
def test_normalize_gtin_edge_cases(self):
|
||||
"""Test GTIN normalization edge cases."""
|
||||
@@ -65,9 +73,15 @@ class TestInventoryService:
|
||||
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
|
||||
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."""
|
||||
@@ -162,7 +176,9 @@ class TestInventoryService:
|
||||
|
||||
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)
|
||||
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)
|
||||
@@ -180,11 +196,12 @@ class TestInventoryService:
|
||||
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
|
||||
remove_quantity = min(
|
||||
10, original_quantity
|
||||
) # Ensure we don't remove more than available
|
||||
|
||||
inventory_data = InventoryAdd(
|
||||
gtin=test_inventory.gtin,
|
||||
@@ -212,7 +229,9 @@ class TestInventoryService:
|
||||
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["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):
|
||||
@@ -231,7 +250,9 @@ class TestInventoryService:
|
||||
|
||||
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)
|
||||
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)
|
||||
@@ -254,7 +275,9 @@ class TestInventoryService:
|
||||
# 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):
|
||||
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)
|
||||
|
||||
@@ -265,14 +288,20 @@ class TestInventoryService:
|
||||
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):
|
||||
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)
|
||||
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)
|
||||
@@ -301,7 +330,9 @@ class TestInventoryService:
|
||||
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):
|
||||
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)
|
||||
|
||||
@@ -364,7 +395,9 @@ class TestInventoryService:
|
||||
|
||||
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
|
||||
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."""
|
||||
@@ -404,7 +437,9 @@ class TestInventoryService:
|
||||
assert result is True
|
||||
|
||||
# Verify the inventory is actually deleted
|
||||
deleted_inventory = db.query(Inventory).filter(Inventory.id == inventory_id).first()
|
||||
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):
|
||||
@@ -415,7 +450,9 @@ class TestInventoryService:
|
||||
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):
|
||||
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
|
||||
@@ -424,7 +461,9 @@ class TestInventoryService:
|
||||
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)
|
||||
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
|
||||
@@ -440,9 +479,13 @@ class TestInventoryService:
|
||||
|
||||
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)
|
||||
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["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
|
||||
@@ -450,7 +493,9 @@ class TestInventoryService:
|
||||
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}")
|
||||
result = self.service.get_inventory_summary_by_location(
|
||||
db, f"EMPTY_LOCATION_{unique_id}"
|
||||
)
|
||||
|
||||
assert result["total_items"] == 0
|
||||
assert result["total_quantity"] == 0
|
||||
@@ -459,12 +504,16 @@ class TestInventoryService:
|
||||
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)
|
||||
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)
|
||||
inventory_data_add = InventoryAdd(
|
||||
gtin="1234567890123", location="WAREHOUSE_B", quantity=0
|
||||
)
|
||||
with pytest.raises(InvalidQuantityException):
|
||||
self.service.add_inventory(db, inventory_data_add)
|
||||
|
||||
@@ -477,10 +526,10 @@ class TestInventoryService:
|
||||
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 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)
|
||||
|
||||
@@ -4,19 +4,18 @@ from datetime import datetime
|
||||
|
||||
import pytest
|
||||
|
||||
from app.exceptions.marketplace_import_job import (
|
||||
ImportJobNotFoundException,
|
||||
ImportJobNotOwnedException,
|
||||
ImportJobCannotBeCancelledException,
|
||||
ImportJobCannotBeDeletedException,
|
||||
)
|
||||
from app.exceptions.vendor import VendorNotFoundException, UnauthorizedVendorAccessException
|
||||
from app.exceptions.base import ValidationException
|
||||
from app.services.marketplace_import_job_service import MarketplaceImportJobService
|
||||
from models.schema.marketplace_import_job import MarketplaceImportJobRequest
|
||||
from app.exceptions.marketplace_import_job import (
|
||||
ImportJobCannotBeCancelledException, ImportJobCannotBeDeletedException,
|
||||
ImportJobNotFoundException, ImportJobNotOwnedException)
|
||||
from app.exceptions.vendor import (UnauthorizedVendorAccessException,
|
||||
VendorNotFoundException)
|
||||
from app.services.marketplace_import_job_service import \
|
||||
MarketplaceImportJobService
|
||||
from models.database.marketplace_import_job import MarketplaceImportJob
|
||||
from models.database.vendor import Vendor
|
||||
from models.database.user import User
|
||||
from models.database.vendor import Vendor
|
||||
from models.schema.marketplace_import_job import MarketplaceImportJobRequest
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -31,7 +30,9 @@ class TestMarketplaceService:
|
||||
test_vendor.owner_user_id = test_user.id
|
||||
db.commit()
|
||||
|
||||
result = self.service.validate_vendor_access(db, test_vendor.vendor_code, test_user)
|
||||
result = self.service.validate_vendor_access(
|
||||
db, test_vendor.vendor_code, test_user
|
||||
)
|
||||
|
||||
assert result.vendor_code == test_vendor.vendor_code
|
||||
assert result.owner_user_id == test_user.id
|
||||
@@ -39,8 +40,10 @@ class TestMarketplaceService:
|
||||
def test_validate_vendor_access_admin_can_access_any_vendor(
|
||||
self, db, test_vendor, test_admin
|
||||
):
|
||||
"""Test that admin users can access any vendor """
|
||||
result = self.service.validate_vendor_access(db, test_vendor.vendor_code, test_admin)
|
||||
"""Test that admin users can access any vendor"""
|
||||
result = self.service.validate_vendor_access(
|
||||
db, test_vendor.vendor_code, test_admin
|
||||
)
|
||||
|
||||
assert result.vendor_code == test_vendor.vendor_code
|
||||
|
||||
@@ -57,7 +60,7 @@ class TestMarketplaceService:
|
||||
def test_validate_vendor_access_permission_denied(
|
||||
self, db, test_vendor, test_user, other_user
|
||||
):
|
||||
"""Test vendor access validation when user doesn't own the vendor """
|
||||
"""Test vendor access validation when user doesn't own the vendor"""
|
||||
# Set the vendor owner to a different user
|
||||
test_vendor.owner_user_id = other_user.id
|
||||
db.commit()
|
||||
@@ -93,7 +96,7 @@ class TestMarketplaceService:
|
||||
assert result.vendor_name == test_vendor.name
|
||||
|
||||
def test_create_import_job_invalid_vendor(self, db, test_user):
|
||||
"""Test import job creation with invalid vendor """
|
||||
"""Test import job creation with invalid vendor"""
|
||||
request = MarketplaceImportJobRequest(
|
||||
url="https://example.com/products.csv",
|
||||
marketplace="Amazon",
|
||||
@@ -108,7 +111,9 @@ class TestMarketplaceService:
|
||||
assert exception.error_code == "VENDOR_NOT_FOUND"
|
||||
assert "INVALID_VENDOR" in exception.message
|
||||
|
||||
def test_create_import_job_unauthorized_access(self, db, test_vendor, test_user, other_user):
|
||||
def test_create_import_job_unauthorized_access(
|
||||
self, db, test_vendor, test_user, other_user
|
||||
):
|
||||
"""Test import job creation with unauthorized vendor access"""
|
||||
# Set the vendor owner to a different user
|
||||
test_vendor.owner_user_id = other_user.id
|
||||
@@ -127,7 +132,9 @@ class TestMarketplaceService:
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "UNAUTHORIZED_VENDOR_ACCESS"
|
||||
|
||||
def test_get_import_job_by_id_success(self, db, test_marketplace_import_job, test_user):
|
||||
def test_get_import_job_by_id_success(
|
||||
self, db, test_marketplace_import_job, test_user
|
||||
):
|
||||
"""Test getting import job by ID for job owner"""
|
||||
result = self.service.get_import_job_by_id(
|
||||
db, test_marketplace_import_job.id, test_user
|
||||
@@ -161,14 +168,18 @@ class TestMarketplaceService:
|
||||
):
|
||||
"""Test access denied when user doesn't own the job"""
|
||||
with pytest.raises(ImportJobNotOwnedException) as exc_info:
|
||||
self.service.get_import_job_by_id(db, test_marketplace_import_job.id, other_user)
|
||||
self.service.get_import_job_by_id(
|
||||
db, test_marketplace_import_job.id, other_user
|
||||
)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "IMPORT_JOB_NOT_OWNED"
|
||||
assert exception.status_code == 403
|
||||
assert str(test_marketplace_import_job.id) in exception.message
|
||||
|
||||
def test_get_import_jobs_user_filter(self, db, test_marketplace_import_job, test_user):
|
||||
def test_get_import_jobs_user_filter(
|
||||
self, db, test_marketplace_import_job, test_user
|
||||
):
|
||||
"""Test getting import jobs filtered by user"""
|
||||
jobs = self.service.get_import_jobs(db, test_user)
|
||||
|
||||
@@ -176,7 +187,9 @@ class TestMarketplaceService:
|
||||
assert any(job.id == test_marketplace_import_job.id for job in jobs)
|
||||
assert test_marketplace_import_job.user_id == test_user.id
|
||||
|
||||
def test_get_import_jobs_admin_sees_all(self, db, test_marketplace_import_job, test_admin):
|
||||
def test_get_import_jobs_admin_sees_all(
|
||||
self, db, test_marketplace_import_job, test_admin
|
||||
):
|
||||
"""Test that admin sees all import jobs"""
|
||||
jobs = self.service.get_import_jobs(db, test_admin)
|
||||
|
||||
@@ -192,7 +205,9 @@ class TestMarketplaceService:
|
||||
)
|
||||
|
||||
assert len(jobs) >= 1
|
||||
assert any(job.marketplace == test_marketplace_import_job.marketplace for job in jobs)
|
||||
assert any(
|
||||
job.marketplace == test_marketplace_import_job.marketplace for job in jobs
|
||||
)
|
||||
|
||||
def test_get_import_jobs_with_pagination(self, db, test_user, test_vendor):
|
||||
"""Test getting import jobs with pagination"""
|
||||
@@ -330,10 +345,14 @@ class TestMarketplaceService:
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "IMPORT_JOB_NOT_FOUND"
|
||||
|
||||
def test_cancel_import_job_access_denied(self, db, test_marketplace_import_job, other_user):
|
||||
def test_cancel_import_job_access_denied(
|
||||
self, db, test_marketplace_import_job, other_user
|
||||
):
|
||||
"""Test cancelling import job without access"""
|
||||
with pytest.raises(ImportJobNotOwnedException) as exc_info:
|
||||
self.service.cancel_import_job(db, test_marketplace_import_job.id, other_user)
|
||||
self.service.cancel_import_job(
|
||||
db, test_marketplace_import_job.id, other_user
|
||||
)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "IMPORT_JOB_NOT_OWNED"
|
||||
@@ -347,7 +366,9 @@ class TestMarketplaceService:
|
||||
db.commit()
|
||||
|
||||
with pytest.raises(ImportJobCannotBeCancelledException) as exc_info:
|
||||
self.service.cancel_import_job(db, test_marketplace_import_job.id, test_user)
|
||||
self.service.cancel_import_job(
|
||||
db, test_marketplace_import_job.id, test_user
|
||||
)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "IMPORT_JOB_CANNOT_BE_CANCELLED"
|
||||
@@ -396,10 +417,14 @@ class TestMarketplaceService:
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "IMPORT_JOB_NOT_FOUND"
|
||||
|
||||
def test_delete_import_job_access_denied(self, db, test_marketplace_import_job, other_user):
|
||||
def test_delete_import_job_access_denied(
|
||||
self, db, test_marketplace_import_job, other_user
|
||||
):
|
||||
"""Test deleting import job without access"""
|
||||
with pytest.raises(ImportJobNotOwnedException) as exc_info:
|
||||
self.service.delete_import_job(db, test_marketplace_import_job.id, other_user)
|
||||
self.service.delete_import_job(
|
||||
db, test_marketplace_import_job.id, other_user
|
||||
)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "IMPORT_JOB_NOT_OWNED"
|
||||
@@ -440,11 +465,15 @@ class TestMarketplaceService:
|
||||
db.commit()
|
||||
|
||||
# Test with lowercase vendor code
|
||||
result = self.service.validate_vendor_access(db, test_vendor.vendor_code.lower(), test_user)
|
||||
result = self.service.validate_vendor_access(
|
||||
db, test_vendor.vendor_code.lower(), test_user
|
||||
)
|
||||
assert result.vendor_code == test_vendor.vendor_code
|
||||
|
||||
# Test with uppercase vendor code
|
||||
result = self.service.validate_vendor_access(db, test_vendor.vendor_code.upper(), test_user)
|
||||
result = self.service.validate_vendor_access(
|
||||
db, test_vendor.vendor_code.upper(), test_user
|
||||
)
|
||||
assert result.vendor_code == test_vendor.vendor_code
|
||||
|
||||
def test_create_import_job_database_error(self, db_with_error, test_user):
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
# tests/test_product_service.py
|
||||
import pytest
|
||||
|
||||
from app.exceptions import (InvalidMarketplaceProductDataException,
|
||||
MarketplaceProductAlreadyExistsException,
|
||||
MarketplaceProductNotFoundException,
|
||||
MarketplaceProductValidationException,
|
||||
ValidationException)
|
||||
from app.services.marketplace_product_service import MarketplaceProductService
|
||||
from app.exceptions import (
|
||||
MarketplaceProductNotFoundException,
|
||||
MarketplaceProductAlreadyExistsException,
|
||||
InvalidMarketplaceProductDataException,
|
||||
MarketplaceProductValidationException,
|
||||
ValidationException,
|
||||
)
|
||||
from models.schema.marketplace_product import MarketplaceProductCreate, MarketplaceProductUpdate
|
||||
from models.database.marketplace_product import MarketplaceProduct
|
||||
from models.schema.marketplace_product import (MarketplaceProductCreate,
|
||||
MarketplaceProductUpdate)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -98,7 +97,10 @@ class TestProductService:
|
||||
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
|
||||
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"""
|
||||
@@ -117,9 +119,14 @@ class TestProductService:
|
||||
|
||||
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)
|
||||
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.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):
|
||||
@@ -152,21 +159,35 @@ class TestProductService:
|
||||
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)
|
||||
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"
|
||||
title="Updated MarketplaceProduct Title", price="39.99"
|
||||
)
|
||||
|
||||
updated_product = self.service.update_product(db, test_marketplace_product.marketplace_product_id, update_data)
|
||||
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
|
||||
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"""
|
||||
@@ -183,7 +204,9 @@ class TestProductService:
|
||||
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)
|
||||
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)
|
||||
@@ -194,7 +217,9 @@ class TestProductService:
|
||||
update_data = MarketplaceProductUpdate(title="")
|
||||
|
||||
with pytest.raises(MarketplaceProductValidationException) as exc_info:
|
||||
self.service.update_product(db, test_marketplace_product.marketplace_product_id, update_data)
|
||||
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)
|
||||
@@ -205,7 +230,9 @@ class TestProductService:
|
||||
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)
|
||||
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)
|
||||
@@ -213,12 +240,16 @@ class TestProductService:
|
||||
|
||||
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)
|
||||
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)
|
||||
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):
|
||||
@@ -229,10 +260,14 @@ class TestProductService:
|
||||
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):
|
||||
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']
|
||||
marketplace_product = test_marketplace_product_with_inventory[
|
||||
"marketplace_product"
|
||||
]
|
||||
|
||||
inventory_info = self.service.get_inventory_info(db, marketplace_product.gtin)
|
||||
|
||||
@@ -243,13 +278,17 @@ class TestProductService:
|
||||
|
||||
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")
|
||||
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)
|
||||
exists = self.service.product_exists(
|
||||
db, test_marketplace_product.marketplace_product_id
|
||||
)
|
||||
assert exists is True
|
||||
|
||||
def test_product_exists_false(self, db):
|
||||
@@ -265,7 +304,9 @@ class TestProductService:
|
||||
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
|
||||
assert csv_lines[0].startswith(
|
||||
"marketplace_product_id,title,description"
|
||||
) # Check header
|
||||
|
||||
# Check that test product appears in CSV
|
||||
csv_content = "".join(csv_lines)
|
||||
@@ -274,8 +315,7 @@ class TestProductService:
|
||||
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
|
||||
db, marketplace=test_marketplace_product.marketplace
|
||||
)
|
||||
|
||||
csv_lines = list(csv_generator)
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
import pytest
|
||||
|
||||
from app.services.stats_service import StatsService
|
||||
from models.database.marketplace_product import MarketplaceProduct
|
||||
from models.database.inventory import Inventory
|
||||
from models.database.marketplace_product import MarketplaceProduct
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -15,7 +15,9 @@ class TestStatsService:
|
||||
"""Setup method following the same pattern as other service tests"""
|
||||
self.service = StatsService()
|
||||
|
||||
def test_get_comprehensive_stats_basic(self, db, test_marketplace_product, test_inventory):
|
||||
def test_get_comprehensive_stats_basic(
|
||||
self, db, test_marketplace_product, test_inventory
|
||||
):
|
||||
"""Test getting comprehensive stats with basic data"""
|
||||
stats = self.service.get_comprehensive_stats(db)
|
||||
|
||||
@@ -31,7 +33,9 @@ class TestStatsService:
|
||||
assert stats["total_inventory_entries"] >= 1
|
||||
assert stats["total_inventory_quantity"] >= 10 # test_inventory has quantity 10
|
||||
|
||||
def test_get_comprehensive_stats_multiple_products(self, db, test_marketplace_product):
|
||||
def test_get_comprehensive_stats_multiple_products(
|
||||
self, db, test_marketplace_product
|
||||
):
|
||||
"""Test comprehensive stats with multiple products across different dimensions"""
|
||||
# Create products with different brands, categories, marketplaces
|
||||
additional_products = [
|
||||
@@ -87,7 +91,7 @@ class TestStatsService:
|
||||
brand=None, # Null brand
|
||||
google_product_category=None, # Null category
|
||||
marketplace=None, # Null marketplace
|
||||
vendor_name=None, # Null vendor
|
||||
vendor_name=None, # Null vendor
|
||||
price="10.00",
|
||||
currency="EUR",
|
||||
),
|
||||
@@ -97,7 +101,7 @@ class TestStatsService:
|
||||
brand="", # Empty brand
|
||||
google_product_category="", # Empty category
|
||||
marketplace="", # Empty marketplace
|
||||
vendor_name="", # Empty vendor
|
||||
vendor_name="", # Empty vendor
|
||||
price="15.00",
|
||||
currency="EUR",
|
||||
),
|
||||
@@ -124,7 +128,11 @@ class TestStatsService:
|
||||
|
||||
# Find our test marketplace in the results
|
||||
test_marketplace_stat = next(
|
||||
(stat for stat in stats if stat["marketplace"] == test_marketplace_product.marketplace),
|
||||
(
|
||||
stat
|
||||
for stat in stats
|
||||
if stat["marketplace"] == test_marketplace_product.marketplace
|
||||
),
|
||||
None,
|
||||
)
|
||||
assert test_marketplace_stat is not None
|
||||
@@ -309,7 +317,9 @@ class TestStatsService:
|
||||
|
||||
count = self.service._get_unique_marketplaces_count(db)
|
||||
|
||||
assert count >= 2 # At least Amazon and eBay, plus test_marketplace_product marketplace
|
||||
assert (
|
||||
count >= 2
|
||||
) # At least Amazon and eBay, plus test_marketplace_product marketplace
|
||||
assert isinstance(count, int)
|
||||
|
||||
def test_get_unique_vendors_count(self, db, test_marketplace_product):
|
||||
@@ -338,7 +348,9 @@ class TestStatsService:
|
||||
|
||||
count = self.service._get_unique_vendors_count(db)
|
||||
|
||||
assert count >= 2 # At least VendorA and VendorB, plus test_marketplace_product vendor
|
||||
assert (
|
||||
count >= 2
|
||||
) # At least VendorA and VendorB, plus test_marketplace_product vendor
|
||||
assert isinstance(count, int)
|
||||
|
||||
def test_get_inventory_statistics(self, db, test_inventory):
|
||||
@@ -438,7 +450,7 @@ class TestStatsService:
|
||||
db.add_all(marketplace_products)
|
||||
db.commit()
|
||||
|
||||
vendors =self.service._get_vendors_by_marketplace(db, "TestMarketplace")
|
||||
vendors = self.service._get_vendors_by_marketplace(db, "TestMarketplace")
|
||||
|
||||
assert len(vendors) == 2
|
||||
assert "TestVendor1" in vendors
|
||||
@@ -482,7 +494,9 @@ class TestStatsService:
|
||||
|
||||
def test_get_products_by_marketplace_not_found(self, db):
|
||||
"""Test getting product count for non-existent marketplace"""
|
||||
count = self.service._get_products_by_marketplace_count(db, "NonExistentMarketplace")
|
||||
count = self.service._get_products_by_marketplace_count(
|
||||
db, "NonExistentMarketplace"
|
||||
)
|
||||
|
||||
assert count == 0
|
||||
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
# tests/test_vendor_service.py (updated to use custom exceptions)
|
||||
import pytest
|
||||
|
||||
from app.exceptions import (InvalidVendorDataException,
|
||||
MarketplaceProductNotFoundException,
|
||||
MaxVendorsReachedException,
|
||||
ProductAlreadyExistsException,
|
||||
UnauthorizedVendorAccessException,
|
||||
ValidationException, VendorAlreadyExistsException,
|
||||
VendorNotFoundException)
|
||||
from app.services.vendor_service import VendorService
|
||||
from app.exceptions import (
|
||||
VendorNotFoundException,
|
||||
VendorAlreadyExistsException,
|
||||
UnauthorizedVendorAccessException,
|
||||
InvalidVendorDataException,
|
||||
MarketplaceProductNotFoundException,
|
||||
ProductAlreadyExistsException,
|
||||
MaxVendorsReachedException,
|
||||
ValidationException,
|
||||
)
|
||||
from models.schema.vendor import VendorCreate
|
||||
from models.schema.product import ProductCreate
|
||||
from models.schema.vendor import VendorCreate
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -38,15 +35,17 @@ class TestVendorService:
|
||||
assert vendor is not None
|
||||
assert vendor.vendor_code == "NEWVENDOR"
|
||||
assert vendor.owner_user_id == test_user.id
|
||||
assert vendor.is_verified is False # Regular user creates unverified vendor
|
||||
assert vendor.is_verified is False # Regular user creates unverified vendor
|
||||
|
||||
def test_create_vendor_admin_auto_verify(self, db, test_admin, vendor_factory):
|
||||
"""Test admin creates verified vendor automatically"""
|
||||
vendor_data = VendorCreate(vendor_code="ADMINVENDOR", vendor_name="Admin Test Vendor")
|
||||
vendor_data = VendorCreate(
|
||||
vendor_code="ADMINVENDOR", vendor_name="Admin Test Vendor"
|
||||
)
|
||||
|
||||
vendor = self.service.create_vendor(db, vendor_data, test_admin)
|
||||
|
||||
assert vendor.is_verified is True # Admin creates verified vendor
|
||||
assert vendor.is_verified is True # Admin creates verified vendor
|
||||
|
||||
def test_create_vendor_duplicate_code(self, db, test_user, test_vendor):
|
||||
"""Test vendor creation fails with duplicate vendor code"""
|
||||
@@ -88,7 +87,9 @@ class TestVendorService:
|
||||
|
||||
def test_create_vendor_invalid_code_format(self, db, test_user):
|
||||
"""Test vendor creation fails with invalid vendor code format"""
|
||||
vendor_data = VendorCreate(vendor_code="INVALID@CODE!", vendor_name="Test Vendor")
|
||||
vendor_data = VendorCreate(
|
||||
vendor_code="INVALID@CODE!", vendor_name="Test Vendor"
|
||||
)
|
||||
|
||||
with pytest.raises(InvalidVendorDataException) as exc_info:
|
||||
self.service.create_vendor(db, vendor_data, test_user)
|
||||
@@ -105,7 +106,9 @@ class TestVendorService:
|
||||
def mock_check_vendor_limit(self, db, user):
|
||||
raise MaxVendorsReachedException(max_vendors=5, user_id=user.id)
|
||||
|
||||
monkeypatch.setattr(VendorService, "_check_vendor_limit", mock_check_vendor_limit)
|
||||
monkeypatch.setattr(
|
||||
VendorService, "_check_vendor_limit", mock_check_vendor_limit
|
||||
)
|
||||
|
||||
vendor_data = VendorCreate(vendor_code="NEWVENDOR", vendor_name="New Vendor")
|
||||
|
||||
@@ -118,7 +121,9 @@ class TestVendorService:
|
||||
assert exception.details["max_vendors"] == 5
|
||||
assert exception.details["user_id"] == test_user.id
|
||||
|
||||
def test_get_vendors_regular_user(self, db, test_user, test_vendor, inactive_vendor):
|
||||
def test_get_vendors_regular_user(
|
||||
self, db, test_user, test_vendor, inactive_vendor
|
||||
):
|
||||
"""Test regular user can only see active verified vendors and own vendors"""
|
||||
vendors, total = self.service.get_vendors(db, test_user, skip=0, limit=10)
|
||||
|
||||
@@ -127,7 +132,7 @@ class TestVendorService:
|
||||
assert inactive_vendor.vendor_code not in vendor_codes
|
||||
|
||||
def test_get_vendors_admin_user(
|
||||
self, db, test_admin, test_vendor, inactive_vendor, verified_vendor
|
||||
self, db, test_admin, test_vendor, inactive_vendor, verified_vendor
|
||||
):
|
||||
"""Test admin user can see all vendors with filters"""
|
||||
vendors, total = self.service.get_vendors(
|
||||
@@ -140,14 +145,16 @@ class TestVendorService:
|
||||
assert verified_vendor.vendor_code in vendor_codes
|
||||
|
||||
def test_get_vendor_by_code_owner_access(self, db, test_user, test_vendor):
|
||||
"""Test vendor owner can access their own vendor """
|
||||
vendor = self.service.get_vendor_by_code(db, test_vendor.vendor_code.lower(), test_user)
|
||||
"""Test vendor owner can access their own vendor"""
|
||||
vendor = self.service.get_vendor_by_code(
|
||||
db, test_vendor.vendor_code.lower(), test_user
|
||||
)
|
||||
|
||||
assert vendor is not None
|
||||
assert vendor.id == test_vendor.id
|
||||
|
||||
def test_get_vendor_by_code_admin_access(self, db, test_admin, test_vendor):
|
||||
"""Test admin can access any vendor """
|
||||
"""Test admin can access any vendor"""
|
||||
vendor = self.service.get_vendor_by_code(
|
||||
db, test_vendor.vendor_code.lower(), test_admin
|
||||
)
|
||||
@@ -178,16 +185,14 @@ class TestVendorService:
|
||||
assert exception.details["user_id"] == test_user.id
|
||||
|
||||
def test_add_product_to_vendor_success(self, db, test_vendor, unique_product):
|
||||
"""Test successfully adding product to vendor """
|
||||
"""Test successfully adding product to vendor"""
|
||||
product_data = ProductCreate(
|
||||
marketplace_product_id=unique_product.marketplace_product_id,
|
||||
price="15.99",
|
||||
is_featured=True,
|
||||
)
|
||||
|
||||
product = self.service.add_product_to_catalog(
|
||||
db, test_vendor, product_data
|
||||
)
|
||||
product = self.service.add_product_to_catalog(db, test_vendor, product_data)
|
||||
|
||||
assert product is not None
|
||||
assert product.vendor_id == test_vendor.id
|
||||
@@ -195,7 +200,9 @@ class TestVendorService:
|
||||
|
||||
def test_add_product_to_vendor_product_not_found(self, db, test_vendor):
|
||||
"""Test adding non-existent product to vendor fails"""
|
||||
product_data = ProductCreate(marketplace_product_id="NONEXISTENT", price="15.99")
|
||||
product_data = ProductCreate(
|
||||
marketplace_product_id="NONEXISTENT", price="15.99"
|
||||
)
|
||||
|
||||
with pytest.raises(MarketplaceProductNotFoundException) as exc_info:
|
||||
self.service.add_product_to_catalog(db, test_vendor, product_data)
|
||||
@@ -209,7 +216,8 @@ class TestVendorService:
|
||||
def test_add_product_to_vendor_already_exists(self, db, test_vendor, test_product):
|
||||
"""Test adding product that's already in vendor fails"""
|
||||
product_data = ProductCreate(
|
||||
marketplace_product_id=test_product.marketplace_product.marketplace_product_id, price="15.99"
|
||||
marketplace_product_id=test_product.marketplace_product.marketplace_product_id,
|
||||
price="15.99",
|
||||
)
|
||||
|
||||
with pytest.raises(ProductAlreadyExistsException) as exc_info:
|
||||
@@ -219,11 +227,12 @@ class TestVendorService:
|
||||
assert exception.status_code == 409
|
||||
assert exception.error_code == "PRODUCT_ALREADY_EXISTS"
|
||||
assert exception.details["vendor_code"] == test_vendor.vendor_code
|
||||
assert exception.details["marketplace_product_id"] == test_product.marketplace_product.marketplace_product_id
|
||||
assert (
|
||||
exception.details["marketplace_product_id"]
|
||||
== test_product.marketplace_product.marketplace_product_id
|
||||
)
|
||||
|
||||
def test_get_products_owner_access(
|
||||
self, db, test_user, test_vendor, test_product
|
||||
):
|
||||
def test_get_products_owner_access(self, db, test_user, test_vendor, test_product):
|
||||
"""Test vendor owner can get vendor products"""
|
||||
products, total = self.service.get_products(db, test_vendor, test_user)
|
||||
|
||||
@@ -291,7 +300,9 @@ class TestVendorService:
|
||||
assert exception.error_code == "VALIDATION_ERROR"
|
||||
assert "Failed to retrieve vendors" in exception.message
|
||||
|
||||
def test_add_product_database_error(self, db, test_vendor, unique_product, monkeypatch):
|
||||
def test_add_product_database_error(
|
||||
self, db, test_vendor, unique_product, monkeypatch
|
||||
):
|
||||
"""Test add product handles database errors gracefully"""
|
||||
|
||||
def mock_commit():
|
||||
|
||||
Reference in New Issue
Block a user