477 lines
13 KiB
Python
477 lines
13 KiB
Python
# 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()
|
|
|