# tests/unit/models/schema/test_store.py """Unit tests for store Pydantic schemas.""" import pytest from pydantic import ValidationError from app.modules.tenancy.schemas.store import ( StoreCreate, StoreDetailResponse, StoreListResponse, StoreResponse, StoreSummary, StoreUpdate, ) @pytest.mark.unit @pytest.mark.schema class TestStoreCreateSchema: """Test StoreCreate schema validation.""" def test_valid_store_create(self): """Test valid store creation data.""" store = StoreCreate( merchant_id=1, store_code="TECHSTORE", subdomain="techstore", name="Tech Store", ) assert store.merchant_id == 1 assert store.store_code == "TECHSTORE" assert store.subdomain == "techstore" assert store.name == "Tech Store" def test_merchant_id_required(self): """Test merchant_id is required.""" with pytest.raises(ValidationError) as exc_info: StoreCreate( store_code="TECHSTORE", subdomain="techstore", name="Tech Store", ) assert "merchant_id" in str(exc_info.value).lower() def test_merchant_id_must_be_positive(self): """Test merchant_id must be > 0.""" with pytest.raises(ValidationError) as exc_info: StoreCreate( merchant_id=0, store_code="TECHSTORE", subdomain="techstore", name="Tech Store", ) assert "merchant_id" in str(exc_info.value).lower() def test_store_code_required(self): """Test store_code is required.""" with pytest.raises(ValidationError) as exc_info: StoreCreate( merchant_id=1, subdomain="techstore", name="Tech Store", ) assert "store_code" in str(exc_info.value).lower() def test_store_code_uppercase_normalized(self): """Test store_code is normalized to uppercase.""" store = StoreCreate( merchant_id=1, store_code="techstore", subdomain="techstore", name="Tech Store", ) assert store.store_code == "TECHSTORE" def test_store_code_min_length(self): """Test store_code minimum length (2).""" with pytest.raises(ValidationError) as exc_info: StoreCreate( merchant_id=1, store_code="T", subdomain="techstore", name="Tech Store", ) assert "store_code" in str(exc_info.value).lower() def test_subdomain_required(self): """Test subdomain is required.""" with pytest.raises(ValidationError) as exc_info: StoreCreate( merchant_id=1, store_code="TECHSTORE", name="Tech Store", ) assert "subdomain" in str(exc_info.value).lower() def test_subdomain_uppercase_invalid(self): """Test subdomain with uppercase is invalid (validated before normalization).""" with pytest.raises(ValidationError) as exc_info: StoreCreate( merchant_id=1, store_code="TECHSTORE", subdomain="TechStore", name="Tech Store", ) assert "subdomain" in str(exc_info.value).lower() def test_subdomain_valid_format(self): """Test subdomain with valid format.""" store = StoreCreate( merchant_id=1, store_code="TECHSTORE", subdomain="tech-store-123", name="Tech Store", ) assert store.subdomain == "tech-store-123" def test_subdomain_invalid_format(self): """Test subdomain with invalid characters raises ValidationError.""" with pytest.raises(ValidationError) as exc_info: StoreCreate( merchant_id=1, store_code="TECHSTORE", subdomain="tech_store!", name="Tech Store", ) assert "subdomain" in str(exc_info.value).lower() def test_name_required(self): """Test name is required.""" with pytest.raises(ValidationError) as exc_info: StoreCreate( merchant_id=1, store_code="TECHSTORE", subdomain="techstore", ) assert "name" in str(exc_info.value).lower() def test_name_min_length(self): """Test name minimum length (2).""" with pytest.raises(ValidationError) as exc_info: StoreCreate( merchant_id=1, store_code="TECHSTORE", subdomain="techstore", name="T", ) assert "name" in str(exc_info.value).lower() def test_optional_fields(self): """Test optional fields.""" store = StoreCreate( merchant_id=1, store_code="TECHSTORE", subdomain="techstore", name="Tech Store", description="Best tech store", letzshop_csv_url_fr="https://example.com/fr.csv", contact_email="contact@techstore.com", website="https://techstore.com", ) assert store.description == "Best tech store" assert store.letzshop_csv_url_fr == "https://example.com/fr.csv" assert store.contact_email == "contact@techstore.com" @pytest.mark.unit @pytest.mark.schema class TestStoreUpdateSchema: """Test StoreUpdate schema validation.""" def test_partial_update(self): """Test partial update with only some fields.""" update = StoreUpdate(name="New Tech Store") assert update.name == "New Tech Store" assert update.subdomain is None assert update.is_active is None def test_empty_update_is_valid(self): """Test empty update is valid.""" update = StoreUpdate() assert update.model_dump(exclude_unset=True) == {} def test_subdomain_normalized_to_lowercase(self): """Test subdomain is normalized to lowercase.""" update = StoreUpdate(subdomain="NewSubdomain") assert update.subdomain == "newsubdomain" def test_subdomain_stripped(self): """Test subdomain is stripped of whitespace.""" update = StoreUpdate(subdomain=" newsubdomain ") assert update.subdomain == "newsubdomain" def test_name_min_length(self): """Test name minimum length (2).""" with pytest.raises(ValidationError): StoreUpdate(name="X") def test_is_active_update(self): """Test is_active can be updated.""" update = StoreUpdate(is_active=False) assert update.is_active is False def test_is_verified_update(self): """Test is_verified can be updated.""" update = StoreUpdate(is_verified=True) assert update.is_verified is True def test_reset_contact_to_merchant_flag(self): """Test reset_contact_to_merchant flag.""" update = StoreUpdate(reset_contact_to_merchant=True) assert update.reset_contact_to_merchant is True @pytest.mark.unit @pytest.mark.schema class TestStoreResponseSchema: """Test StoreResponse schema.""" def test_from_dict(self): """Test creating response from dict.""" from datetime import datetime data = { "id": 1, "store_code": "TECHSTORE", "subdomain": "techstore", "name": "Tech Store", "description": "Best tech store", "merchant_id": 1, "letzshop_csv_url_fr": None, "letzshop_csv_url_en": None, "letzshop_csv_url_de": None, "is_active": True, "is_verified": False, "created_at": datetime.now(), "updated_at": datetime.now(), } response = StoreResponse(**data) assert response.id == 1 assert response.store_code == "TECHSTORE" assert response.is_active is True @pytest.mark.unit @pytest.mark.schema class TestStoreDetailResponseSchema: """Test StoreDetailResponse schema.""" def test_from_dict(self): """Test creating detail response from dict.""" from datetime import datetime data = { "id": 1, "store_code": "TECHSTORE", "subdomain": "techstore", "name": "Tech Store", "description": None, "merchant_id": 1, "letzshop_csv_url_fr": None, "letzshop_csv_url_en": None, "letzshop_csv_url_de": None, "is_active": True, "is_verified": True, "created_at": datetime.now(), "updated_at": datetime.now(), # Additional detail fields "merchant_name": "Tech Corp", "owner_email": "owner@techcorp.com", "owner_username": "owner", "contact_email": "contact@techstore.com", "contact_email_inherited": False, } response = StoreDetailResponse(**data) assert response.merchant_name == "Tech Corp" assert response.owner_email == "owner@techcorp.com" assert response.contact_email_inherited is False @pytest.mark.unit @pytest.mark.schema class TestStoreListResponseSchema: """Test StoreListResponse schema.""" def test_valid_list_response(self): """Test valid list response structure.""" response = StoreListResponse( stores=[], total=0, skip=0, limit=10, ) assert response.stores == [] assert response.total == 0 @pytest.mark.unit @pytest.mark.schema class TestStoreSummarySchema: """Test StoreSummary schema.""" def test_from_dict(self): """Test creating summary from dict.""" data = { "id": 1, "store_code": "TECHSTORE", "subdomain": "techstore", "name": "Tech Store", "merchant_id": 1, "is_active": True, } summary = StoreSummary(**data) assert summary.id == 1 assert summary.store_code == "TECHSTORE" assert summary.is_active is True