# tests/conftest.py import pytest from fastapi.testclient import TestClient from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from sqlalchemy.pool import StaticPool from main import app from app.core.database import get_db, Base # Import all models to ensure they're registered with Base metadata from models.database_models import User, Product, Stock, Shop, MarketplaceImportJob, ShopProduct from middleware.auth import AuthManager import uuid # Use in-memory SQLite database for tests SQLALCHEMY_TEST_DATABASE_URL = "sqlite:///:memory:" @pytest.fixture(scope="session") def engine(): """Create test database engine""" return create_engine( SQLALCHEMY_TEST_DATABASE_URL, connect_args={"check_same_thread": False}, poolclass=StaticPool, echo=False # Set to True for SQL debugging ) @pytest.fixture(scope="session") def testing_session_local(engine): """Create session factory for tests""" return sessionmaker(autocommit=False, autoflush=False, bind=engine) @pytest.fixture(scope="function") def db(engine, testing_session_local): """Create a database session for direct database operations""" # Create all tables Base.metadata.create_all(bind=engine) # Create session db_session = testing_session_local() try: yield db_session finally: db_session.close() # Clean up all data after each test Base.metadata.drop_all(bind=engine) Base.metadata.create_all(bind=engine) @pytest.fixture(scope="function") def client(db): # Now client depends on db """Create a test client with database dependency override""" # Override the dependency to use our test database def override_get_db(): try: yield db finally: pass # Don't close here, the db fixture handles it app.dependency_overrides[get_db] = override_get_db try: client = TestClient(app) yield client finally: # Clean up the dependency override if get_db in app.dependency_overrides: del app.dependency_overrides[get_db] @pytest.fixture(scope="session") def auth_manager(): """Create auth manager instance (session scope since it's stateless)""" return AuthManager() @pytest.fixture def test_user(db, auth_manager): """Create a test user with unique username""" unique_id = str(uuid.uuid4())[:8] # Short unique identifier hashed_password = auth_manager.hash_password("testpass123") user = User( email=f"test_{unique_id}@example.com", username=f"testuser_{unique_id}", hashed_password=hashed_password, role="user", is_active=True ) db.add(user) db.commit() db.refresh(user) return user @pytest.fixture def test_admin(db, auth_manager): """Create a test admin user with unique username""" unique_id = str(uuid.uuid4())[:8] # Short unique identifier hashed_password = auth_manager.hash_password("adminpass123") admin = User( email=f"admin_{unique_id}@example.com", username=f"admin_{unique_id}", hashed_password=hashed_password, role="admin", is_active=True ) db.add(admin) db.commit() db.refresh(admin) return admin @pytest.fixture def auth_headers(client, test_user): """Get authentication headers for test user""" response = client.post("/api/v1/auth/login", json={ "username": test_user.username, "password": "testpass123" }) assert response.status_code == 200, f"Login failed: {response.text}" token = response.json()["access_token"] return {"Authorization": f"Bearer {token}"} @pytest.fixture def admin_headers(client, test_admin): """Get authentication headers for admin user""" response = client.post("/api/v1/auth/login", json={ "username": test_admin.username, "password": "adminpass123" }) assert response.status_code == 200, f"Admin login failed: {response.text}" token = response.json()["access_token"] return {"Authorization": f"Bearer {token}"} @pytest.fixture def test_product(db): """Create a test product""" product = Product( product_id="TEST001", title="Test Product", description="A test product", price="10.99", currency="EUR", brand="TestBrand", gtin="1234567890123", availability="in stock", marketplace="Letzshop", shop_name="TestShop" ) db.add(product) db.commit() db.refresh(product) return product @pytest.fixture def test_shop(db, test_user): """Create a test shop with unique shop code""" unique_id = str(uuid.uuid4())[:8] # Short unique identifier shop = Shop( shop_code=f"TESTSHOP_{unique_id}", shop_name=f"Test Shop {unique_id}", owner_id=test_user.id, is_active=True, is_verified=True ) db.add(shop) db.commit() db.refresh(shop) return shop @pytest.fixture def test_stock(db, test_product, test_shop): """Create test stock entry""" unique_id = str(uuid.uuid4())[:8].upper() # Short unique identifier stock = Stock( gtin=test_product.gtin, # Fixed: use gtin instead of product_id location=f"WAREHOUSE_A_{unique_id}", quantity=10, reserved_quantity=0, shop_id=test_shop.id # Add shop_id reference ) db.add(stock) db.commit() db.refresh(stock) return stock @pytest.fixture def test_marketplace_job(db, test_shop, test_user): # Add test_shop dependency """Create a test marketplace import job""" job = MarketplaceImportJob( marketplace="amazon", shop_name="Test Import Shop", status="completed", source_url="https://test-marketplace.example.com/import", shop_id=test_shop.id, # Add required shop_id user_id=test_user.id, imported_count=5, updated_count=3, total_processed=8, error_count=0, error_message=None ) db.add(job) db.commit() db.refresh(job) return job def create_test_import_job(db, shop_id, **kwargs): # Add shop_id parameter """Helper function to create MarketplaceImportJob with defaults""" defaults = { 'marketplace': 'test', 'shop_name': 'Test Shop', 'status': 'pending', 'source_url': 'https://test.example.com/import', 'shop_id': shop_id, # Add required shop_id 'imported_count': 0, 'updated_count': 0, 'total_processed': 0, 'error_count': 0, 'error_message': None } defaults.update(kwargs) job = MarketplaceImportJob(**defaults) db.add(job) db.commit() db.refresh(job) return job # Cleanup fixture to ensure clean state @pytest.fixture(autouse=True) def cleanup(): """Automatically clean up after each test""" yield # Clear any remaining dependency overrides app.dependency_overrides.clear() # Add these fixtures to your existing conftest.py @pytest.fixture def unique_product(db): """Create a unique product for tests that need isolated product data""" unique_id = str(uuid.uuid4())[:8] product = Product( product_id=f"UNIQUE_{unique_id}", title=f"Unique Product {unique_id}", description=f"A unique test product {unique_id}", price="19.99", currency="EUR", brand=f"UniqueBrand_{unique_id}", gtin=f"123456789{unique_id[:4]}", availability="in stock", marketplace="Letzshop", shop_name=f"UniqueShop_{unique_id}", google_product_category=f"UniqueCategory_{unique_id}" ) db.add(product) db.commit() db.refresh(product) return product @pytest.fixture def unique_shop(db, test_user): """Create a unique shop for tests that need isolated shop data""" unique_id = str(uuid.uuid4())[:8] shop = Shop( shop_code=f"UNIQUESHOP_{unique_id}", shop_name=f"Unique Test Shop {unique_id}", description=f"A unique test shop {unique_id}", owner_id=test_user.id, is_active=True, is_verified=True ) db.add(shop) db.commit() db.refresh(shop) return shop @pytest.fixture def other_user(db, auth_manager): """Create a different user for testing access controls""" unique_id = str(uuid.uuid4())[:8] hashed_password = auth_manager.hash_password("otherpass123") user = User( email=f"other_{unique_id}@example.com", username=f"otheruser_{unique_id}", hashed_password=hashed_password, role="user", is_active=True ) db.add(user) db.commit() db.refresh(user) return user @pytest.fixture def inactive_shop(db, other_user): """Create an inactive shop owned by other_user""" unique_id = str(uuid.uuid4())[:8] shop = Shop( shop_code=f"INACTIVE_{unique_id}", shop_name=f"Inactive Shop {unique_id}", owner_id=other_user.id, is_active=False, is_verified=False ) db.add(shop) db.commit() db.refresh(shop) return shop @pytest.fixture def verified_shop(db, other_user): """Create a verified shop owned by other_user""" unique_id = str(uuid.uuid4())[:8] shop = Shop( shop_code=f"VERIFIED_{unique_id}", shop_name=f"Verified Shop {unique_id}", owner_id=other_user.id, is_active=True, is_verified=True ) db.add(shop) db.commit() db.refresh(shop) return shop @pytest.fixture def shop_product(db, test_shop, unique_product): """Create a shop product relationship""" shop_product = ShopProduct( shop_id=test_shop.id, product_id=unique_product.id, is_active=True ) # Add optional fields if they exist in your model if hasattr(ShopProduct, 'price'): shop_product.price = "24.99" if hasattr(ShopProduct, 'is_featured'): shop_product.is_featured = False if hasattr(ShopProduct, 'stock_quantity'): shop_product.stock_quantity = 10 db.add(shop_product) db.commit() db.refresh(shop_product) return shop_product @pytest.fixture def multiple_products(db): """Create multiple products for testing statistics and pagination""" unique_id = str(uuid.uuid4())[:8] products = [] for i in range(5): product = Product( product_id=f"MULTI_{unique_id}_{i}", title=f"Multi Product {i} {unique_id}", description=f"Multi test product {i}", price=f"{10 + i}.99", currency="EUR", brand=f"MultiBrand_{i % 3}", # Create 3 different brands marketplace=f"MultiMarket_{i % 2}", # Create 2 different marketplaces shop_name=f"MultiShop_{i}", google_product_category=f"MultiCategory_{i % 2}", # Create 2 different categories gtin=f"1234567890{i}{unique_id[:2]}" ) products.append(product) db.add_all(products) db.commit() for product in products: db.refresh(product) return products @pytest.fixture def multiple_stocks(db, multiple_products, test_shop): """Create multiple stock entries for testing""" stocks = [] for i, product in enumerate(multiple_products): stock = Stock( gtin=product.gtin, location=f"LOC_{i}", quantity=10 + (i * 5), # Different quantities reserved_quantity=i, shop_id=test_shop.id ) stocks.append(stock) db.add_all(stocks) db.commit() for stock in stocks: db.refresh(stock) return stocks # Helper fixture factory functions def create_unique_product_factory(): """Factory function to create unique products in tests""" def _create_product(db, **kwargs): unique_id = str(uuid.uuid4())[:8] defaults = { 'product_id': f"FACTORY_{unique_id}", 'title': f"Factory Product {unique_id}", 'price': "15.99", 'currency': "EUR", 'marketplace': "TestMarket", 'shop_name': "TestShop" } defaults.update(kwargs) product = Product(**defaults) db.add(product) db.commit() db.refresh(product) return product return _create_product @pytest.fixture def product_factory(): """Fixture that provides a product factory function""" return create_unique_product_factory() def create_unique_shop_factory(): """Factory function to create unique shops in tests""" def _create_shop(db, owner_id, **kwargs): unique_id = str(uuid.uuid4())[:8] defaults = { 'shop_code': f"FACTORY_{unique_id}", 'shop_name': f"Factory Shop {unique_id}", 'owner_id': owner_id, 'is_active': True, 'is_verified': False } defaults.update(kwargs) shop = Shop(**defaults) db.add(shop) db.commit() db.refresh(shop) return shop return _create_shop @pytest.fixture def shop_factory(): """Fixture that provides a shop factory function""" return create_unique_shop_factory()