# 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.rollback() db_session.close() # Tables will be dropped by the client fixture @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""" stock = Stock( gtin=test_product.gtin, # Fixed: use gtin instead of product_id location=test_shop.shop_code, # Fixed: use location instead of shop_code 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): # 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 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()