diff --git a/app/core/config.py b/app/core/config.py
index ae6b9138..24876c20 100644
--- a/app/core/config.py
+++ b/app/core/config.py
@@ -18,7 +18,43 @@ class Settings(BaseSettings):
# Project information
project_name: str = "Ecommerce Backend API with Marketplace Support"
- description: str = "Advanced product management system with JWT authentication"
+ description: str = """
+ ## ๐ Letzshop Import API
+
+ Complete marketplace product import and management system built with FastAPI.
+
+ ### ๐ Documentation Links
+
+ - **[Complete Documentation Site](#)** - Full project documentation
+ - **[Getting Started Guide](#)** - Installation and setup
+ - **[User Guides](#)** - How-to guides and tutorials
+ - **[API Authentication Guide](#)** - Security and authentication
+ - **[Testing Documentation](#)** - Test suite and conventions
+
+ ### ๐ Quick Links
+
+ - **[Alternative API Docs](/redoc)** - ReDoc interface
+ - **[Health Check](/health)** - System status
+ - **[OpenAPI Spec](/openapi.json)** - Machine-readable API spec
+
+ ### ๐ Key Features
+
+ - **Product Management** - Complete CRUD operations with validation
+ - **Multi-Shop Support** - Independent shop configurations
+ - **CSV Import System** - Bulk import from various marketplace formats
+ - **Stock Management** - Inventory tracking across locations
+ - **User Management** - Role-based access control
+ - **Marketplace Integration** - Import from multiple platforms
+
+ ### ๐๏ธ Architecture
+
+ Built with modern Python stack:
+ - **FastAPI** - High-performance async API framework
+ - **SQLAlchemy** - Powerful ORM with PostgreSQL
+ - **Pydantic** - Data validation and serialization
+ - **JWT Authentication** - Secure token-based auth
+ - **pytest** - Comprehensive test suite
+ """
version: str = "2.2.0"
# Database
diff --git a/tests/test_background_tasks.py b/backup/test_background_tasks.py
similarity index 100%
rename from tests/test_background_tasks.py
rename to backup/test_background_tasks.py
diff --git a/tests/test_export.py b/backup/test_export.py
similarity index 100%
rename from tests/test_export.py
rename to backup/test_export.py
diff --git a/tests/test_filtering.py b/backup/test_filtering.py
similarity index 100%
rename from tests/test_filtering.py
rename to backup/test_filtering.py
diff --git a/tests/test_integration.py b/backup/test_integration.py
similarity index 100%
rename from tests/test_integration.py
rename to backup/test_integration.py
diff --git a/tests/test_middleware.py b/backup/test_middleware.py
similarity index 100%
rename from tests/test_middleware.py
rename to backup/test_middleware.py
diff --git a/comprehensive_readme.md b/comprehensive_readme.md
index 3a4531e7..2a27c680 100644
--- a/comprehensive_readme.md
+++ b/comprehensive_readme.md
@@ -318,9 +318,9 @@ pytest tests/ -m integration -v # Integration tests only
pytest tests/ -m "not slow" -v # Fast tests only
# Run specific test files
-pytest tests/test_auth.py -v # Authentication tests
-pytest tests/test_product.py -v # Product tests
-pytest tests/test_stock.py -v # Stock management tests
+pytest tests/test_authentication_endpoints.py -v # Authentication tests
+pytest tests/test_product_endpoints.py -v # Product tests
+pytest tests/test_stock_endpoints.py -v # Stock management tests
```
### Test Coverage
diff --git a/enhanced_pytest_config.txt b/enhanced_pytest_config.txt
deleted file mode 100644
index 4baa5db2..00000000
--- a/enhanced_pytest_config.txt
+++ /dev/null
@@ -1,80 +0,0 @@
-# pytest.ini - Enhanced configuration for your FastAPI test suite
-[tool:pytest]
-testpaths = tests
-python_files = test_*.py
-python_classes = Test*
-python_functions = test_*
-
-# Enhanced addopts for better development experience
-addopts =
- -v
- --tb=short
- --strict-markers
- --strict-config
- --color=yes
- --durations=10
- --showlocals
- -ra
- --cov=app
- --cov=models
- --cov=utils
- --cov=middleware
- --cov-report=term-missing
- --cov-report=html:htmlcov
- --cov-fail-under=80
-
-# Test discovery and execution settings
-minversion = 6.0
-testmon = true
-python_paths = .
-
-# Markers for your specific test organization
-markers =
- # Test Types (for your new structure)
- unit: Unit tests - fast, isolated components
- integration: Integration tests - multiple components working together
- system: System tests - full application behavior
- e2e: End-to-end tests - complete user workflows
-
- # Performance and Speed
- slow: Slow running tests (deselect with '-m "not slow"')
- performance: Performance and load tests
-
- # Domain-specific markers (matching your application structure)
- auth: Authentication and authorization tests
- products: Product management functionality
- stock: Stock and inventory management
- shops: Shop management functionality
- admin: Admin functionality and permissions
- marketplace: Marketplace import functionality
- stats: Statistics and reporting
-
- # Infrastructure markers
- database: Tests that require database operations
- external: Tests that require external services
- api: API endpoint tests
- security: Security-related tests
-
- # Test environment markers
- ci: Tests that should only run in CI
- dev: Development-specific tests
-
-# Test filtering shortcuts
-filterwarnings =
- ignore::UserWarning
- ignore::DeprecationWarning
- ignore::PendingDeprecationWarning
- ignore::sqlalchemy.exc.SAWarning
-
-# Timeout settings
-timeout = 300
-timeout_method = thread
-
-# Parallel execution settings (uncomment if using pytest-xdist)
-# addopts = -n auto
-
-# Additional logging configuration
-log_cli = true
-log_cli_level = INFO
-log_cli_format = %(asctime)s [%(levelname)8s] %(name)s: %(message)s
-log_cli_date_format = %Y-%m-%d %H:%M:%S
\ No newline at end of file
diff --git a/init_files.py b/init_files.py
deleted file mode 100644
index d5fffe2a..00000000
--- a/init_files.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# tests/fixtures/__init__.py
-"""Test fixtures for the FastAPI application test suite."""
-
-# tests/unit/__init__.py
-"""Unit tests - fast, isolated component tests."""
-
-# tests/unit/models/__init__.py
-"""Database and API model unit tests."""
-
-# tests/unit/utils/__init__.py
-"""Utility function unit tests."""
-
-# tests/unit/services/__init__.py
-"""Service layer unit tests."""
-
-# tests/integration/__init__.py
-"""Integration tests - multiple components working together."""
-
-# tests/integration/api/__init__.py
-"""API integration tests."""
-
-# tests/integration/api/v1/__init__.py
-"""API v1 endpoint integration tests."""
-
-# tests/integration/security/__init__.py
-"""Security integration tests."""
-
-# tests/performance/__init__.py
-"""Performance and load tests."""
-
-# tests/system/__init__.py
-"""System-level tests - full application behavior."""
-
-# tests/integration/conftest.py
-"""Integration test specific fixtures."""
-import pytest
-
-# Add any integration-specific fixtures here if needed
-
-# tests/unit/conftest.py
-"""Unit test specific fixtures."""
-import pytest
-
-# Add any unit-specific fixtures here if needed
-
-# tests/performance/conftest.py
-"""Performance test specific fixtures."""
-import pytest
-
-@pytest.fixture
-def performance_db_session(db):
- """Database session optimized for performance testing"""
- # You can add performance-specific DB configurations here
- return db
-
-# tests/system/conftest.py
-"""System test specific fixtures."""
-import pytest
-
-# Add any system-specific fixtures here if needed
\ No newline at end of file
diff --git a/main_conftest.py b/main_conftest.py
deleted file mode 100644
index 91c1242d..00000000
--- a/main_conftest.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# tests/conftest.py - Updated main conftest with core fixtures only
-import pytest
-from fastapi.testclient import TestClient
-from sqlalchemy import create_engine
-from sqlalchemy.orm import sessionmaker
-from sqlalchemy.pool import StaticPool
-
-from app.core.database import Base, get_db
-from main import app
-# Import all models to ensure they're registered with Base metadata
-from models.database_models import (MarketplaceImportJob, Product, Shop,
- ShopProduct, Stock, User)
-
-# 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):
- """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]
-
-
-# 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()
-
-
-# Import fixtures from fixture modules
-pytest_plugins = [
- "tests.fixtures.auth_fixtures",
- "tests.fixtures.product_fixtures",
- "tests.fixtures.shop_fixtures",
- "tests.fixtures.marketplace_fixtures",
-]
\ No newline at end of file
diff --git a/powershell_migration_script.ps1 b/powershell_migration_script.ps1
deleted file mode 100644
index 8b57a865..00000000
--- a/powershell_migration_script.ps1
+++ /dev/null
@@ -1,113 +0,0 @@
-# migrate_tests.ps1 - PowerShell script to migrate your test structure
-
-Write-Host "๐ Starting test structure migration..." -ForegroundColor Cyan
-
-# Create new directory structure
-Write-Host "๐ Creating directory structure..." -ForegroundColor Yellow
-$directories = @(
- "tests\fixtures",
- "tests\unit", "tests\unit\models", "tests\unit\utils", "tests\unit\services",
- "tests\integration", "tests\integration\api", "tests\integration\api\v1", "tests\integration\security",
- "tests\performance",
- "tests\system",
- "tests\test_data", "tests\test_data\csv"
-)
-
-foreach ($dir in $directories) {
- New-Item -Path $dir -ItemType Directory -Force | Out-Null
- Write-Host " Created: $dir" -ForegroundColor Gray
-}
-
-# Create __init__.py files
-Write-Host "๐ Creating __init__.py files..." -ForegroundColor Yellow
-$initFiles = @(
- "tests\fixtures\__init__.py",
- "tests\unit\__init__.py", "tests\unit\models\__init__.py", "tests\unit\utils\__init__.py", "tests\unit\services\__init__.py",
- "tests\integration\__init__.py", "tests\integration\api\__init__.py", "tests\integration\api\v1\__init__.py", "tests\integration\security\__init__.py",
- "tests\performance\__init__.py",
- "tests\system\__init__.py"
-)
-
-foreach ($file in $initFiles) {
- New-Item -Path $file -ItemType File -Force | Out-Null
- Write-Host " Created: $file" -ForegroundColor Gray
-}
-
-# Create conftest.py files for each test category
-Write-Host "โ๏ธ Creating conftest.py files..." -ForegroundColor Yellow
-$conftestFiles = @(
- "tests\unit\conftest.py",
- "tests\integration\conftest.py",
- "tests\performance\conftest.py",
- "tests\system\conftest.py"
-)
-
-foreach ($file in $conftestFiles) {
- New-Item -Path $file -ItemType File -Force | Out-Null
- Write-Host " Created: $file" -ForegroundColor Gray
-}
-
-# Backup original files
-Write-Host "๐พ Backing up original files..." -ForegroundColor Yellow
-New-Item -Path "tests\backup" -ItemType Directory -Force | Out-Null
-
-if (Test-Path "tests\conftest.py") {
- Copy-Item "tests\conftest.py" "tests\backup\" -Force
- Write-Host " Backed up: conftest.py" -ForegroundColor Gray
-}
-
-Get-ChildItem "tests\test_*.py" -ErrorAction SilentlyContinue | ForEach-Object {
- Copy-Item $_.FullName "tests\backup\" -Force
- Write-Host " Backed up: $($_.Name)" -ForegroundColor Gray
-}
-
-# Create sample test data file
-Write-Host "๐ Creating sample test data..." -ForegroundColor Yellow
-$csvContent = @"
-product_id,title,price,currency,brand,marketplace
-TEST001,Sample Product 1,19.99,EUR,TestBrand,TestMarket
-TEST002,Sample Product 2,29.99,EUR,TestBrand,TestMarket
-TEST003,Sample Product 3,39.99,USD,AnotherBrand,TestMarket
-"@
-
-$csvContent | Out-File -FilePath "tests\test_data\csv\sample_products.csv" -Encoding UTF8
-Write-Host " Created: sample_products.csv" -ForegroundColor Gray
-
-Write-Host "โ
Directory structure created!" -ForegroundColor Green
-Write-Host ""
-Write-Host "๐ Next steps:" -ForegroundColor Cyan
-Write-Host "1. Copy the fixture files I provided to tests\fixtures\" -ForegroundColor White
-Write-Host "2. Update tests\conftest.py with the new version" -ForegroundColor White
-Write-Host "3. Move test files to their new locations:" -ForegroundColor White
-Write-Host " - test_database.py โ tests\unit\models\test_database_models.py" -ForegroundColor Gray
-Write-Host " - test_utils.py โ tests\unit\utils\test_data_processing.py" -ForegroundColor Gray
-Write-Host " - test_admin_service.py โ tests\unit\services\" -ForegroundColor Gray
-Write-Host " - test_admin.py โ tests\integration\api\v1\test_admin_endpoints.py" -ForegroundColor Gray
-Write-Host " - test_pagination.py โ tests\integration\api\v1\" -ForegroundColor Gray
-Write-Host " - test_performance.py โ tests\performance\test_api_performance.py" -ForegroundColor Gray
-Write-Host " - test_error_handling.py โ tests\system\" -ForegroundColor Gray
-Write-Host " - Split test_security.py into security subdirectory" -ForegroundColor Gray
-Write-Host ""
-Write-Host "4. Update imports in moved test files" -ForegroundColor White
-Write-Host "5. Add pytest markers to test classes" -ForegroundColor White
-Write-Host "6. Update pytest.ini with the enhanced configuration" -ForegroundColor White
-Write-Host ""
-Write-Host "๐งช Test the migration with:" -ForegroundColor Cyan
-Write-Host "pytest tests\unit -v" -ForegroundColor Yellow
-Write-Host "pytest tests\integration -v" -ForegroundColor Yellow
-Write-Host "pytest -m unit" -ForegroundColor Yellow
-Write-Host ""
-Write-Host "๐ง Quick test commands after migration:" -ForegroundColor Cyan
-Write-Host "pytest -m unit # Fast unit tests" -ForegroundColor White
-Write-Host "pytest -m integration # Integration tests" -ForegroundColor White
-Write-Host "pytest -m `"not slow`" # Skip slow tests" -ForegroundColor White
-Write-Host "pytest tests\unit\models\ # Model tests only" -ForegroundColor White
-Write-Host "pytest --cov=app --cov-report=html # Coverage report" -ForegroundColor White
-Write-Host ""
-Write-Host "โจ Migration structure ready! Follow the steps above to complete the migration." -ForegroundColor Green
-Write-Host "๐ All your original files are backed up in tests\backup\" -ForegroundColor Green
-
-# Optional: Pause to let user read the output
-Write-Host ""
-Write-Host "Press any key to continue..." -ForegroundColor DarkGray
-$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
\ No newline at end of file
diff --git a/pytest.ini b/pytest.ini
new file mode 100644
index 00000000..a3864354
--- /dev/null
+++ b/pytest.ini
@@ -0,0 +1,65 @@
+[pytest]
+testpaths = tests
+python_files = test_*.py
+python_classes = Test*
+python_functions = test_*
+
+# Enhanced addopts for better development experience
+addopts =
+ -v
+ --tb=short
+ --strict-markers
+ --strict-config
+ --color=yes
+ --durations=10
+ --showlocals
+ -ra
+ --cov=app
+ --cov=models
+ --cov=utils
+ --cov=middleware
+ --cov-report=term-missing
+ --cov-report=html:htmlcov
+ --cov-fail-under=80
+
+# Test discovery and execution settings
+minversion = 6.0
+
+# Markers for your specific test organization
+markers =
+ unit: marks tests as unit tests - fast, isolated components
+ integration: marks tests as integration tests - multiple components working together
+ system: marks tests as system tests - full application behavior
+ e2e: marks tests as end-to-end tests - complete user workflows
+ slow: marks tests as slow running tests (deselect with '-m "not slow"')
+ performance: marks tests as performance and load tests
+ auth: marks tests as authentication and authorization tests
+ products: marks tests as product management functionality
+ stock: marks tests as stock and inventory management
+ shops: marks tests as shop management functionality
+ admin: marks tests as admin functionality and permissions
+ marketplace: marks tests as marketplace import functionality
+ stats: marks tests as statistics and reporting
+ database: marks tests as tests that require database operations
+ external: marks tests as tests that require external services
+ api: marks tests as API endpoint tests
+ security: marks tests as security-related tests
+ ci: marks tests as tests that should only run in CI
+ dev: marks tests as development-specific tests
+
+# Test filtering shortcuts
+filterwarnings =
+ ignore::UserWarning
+ ignore::DeprecationWarning
+ ignore::PendingDeprecationWarning
+ ignore::sqlalchemy.exc.SAWarning
+
+# Timeout settings
+timeout = 300
+timeout_method = thread
+
+# Additional logging configuration
+log_cli = true
+log_cli_level = INFO
+log_cli_format = %(asctime)s [%(levelname)8s] %(name)s: %(message)s
+log_cli_date_format = %Y-%m-%d %H:%M:%S
diff --git a/tests/.coverage b/tests/.coverage
new file mode 100644
index 00000000..7451e622
Binary files /dev/null and b/tests/.coverage differ
diff --git a/tests/conftest.py b/tests/conftest.py
index df3036c1..91c1242d 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,6 +1,4 @@
-# tests/conftest.py
-import uuid
-
+# tests/conftest.py - Updated main conftest with core fixtures only
import pytest
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
@@ -9,7 +7,6 @@ from sqlalchemy.pool import StaticPool
from app.core.database import Base, get_db
from main import app
-from middleware.auth import AuthManager
# Import all models to ensure they're registered with Base metadata
from models.database_models import (MarketplaceImportJob, Product, Shop,
ShopProduct, Stock, User)
@@ -54,7 +51,7 @@ def db(engine, testing_session_local):
@pytest.fixture(scope="function")
-def client(db): # Now client depends on db
+def client(db):
"""Create a test client with database dependency override"""
# Override the dependency to use our test database
@@ -75,172 +72,6 @@ def client(db): # Now client depends on db
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():
@@ -250,227 +81,10 @@ def cleanup():
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()
+# Import fixtures from fixture modules
+pytest_plugins = [
+ "tests.fixtures.auth_fixtures",
+ "tests.fixtures.product_fixtures",
+ "tests.fixtures.shop_fixtures",
+ "tests.fixtures.marketplace_fixtures",
+]
\ No newline at end of file
diff --git a/tests/ecommerce.db b/tests/ecommerce.db
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/fixtures/__init__.py b/tests/fixtures/__init__.py
new file mode 100644
index 00000000..6b4c7600
--- /dev/null
+++ b/tests/fixtures/__init__.py
@@ -0,0 +1,3 @@
+# tests/fixtures/__init__.py
+"""Test fixtures for the FastAPI application test suite."""
+
diff --git a/auth_fixtures.py b/tests/fixtures/auth_fixtures.py
similarity index 100%
rename from auth_fixtures.py
rename to tests/fixtures/auth_fixtures.py
diff --git a/marketplace_fixtures.py b/tests/fixtures/marketplace_fixtures.py
similarity index 100%
rename from marketplace_fixtures.py
rename to tests/fixtures/marketplace_fixtures.py
diff --git a/product_fixtures.py b/tests/fixtures/product_fixtures.py
similarity index 100%
rename from product_fixtures.py
rename to tests/fixtures/product_fixtures.py
diff --git a/shop_fixtures.py b/tests/fixtures/shop_fixtures.py
similarity index 100%
rename from shop_fixtures.py
rename to tests/fixtures/shop_fixtures.py
diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py
new file mode 100644
index 00000000..d939ba99
--- /dev/null
+++ b/tests/integration/__init__.py
@@ -0,0 +1,3 @@
+# tests/integration/__init__.py
+"""Integration tests - multiple components working together."""
+
diff --git a/tests/integration/api/__init__.py b/tests/integration/api/__init__.py
new file mode 100644
index 00000000..5c1601f1
--- /dev/null
+++ b/tests/integration/api/__init__.py
@@ -0,0 +1,3 @@
+# tests/integration/api/__init__.py
+"""API integration tests."""
+
diff --git a/tests/integration/api/v1/__init__.py b/tests/integration/api/v1/__init__.py
new file mode 100644
index 00000000..bcc8c7d5
--- /dev/null
+++ b/tests/integration/api/v1/__init__.py
@@ -0,0 +1,3 @@
+# tests/integration/api/v1/__init__.py
+"""API v1 endpoint integration tests."""
+
diff --git a/integration_admin_endpoints.py b/tests/integration/api/v1/test_admin_endpoints.py
similarity index 92%
rename from integration_admin_endpoints.py
rename to tests/integration/api/v1/test_admin_endpoints.py
index 5e756e26..0db2b198 100644
--- a/integration_admin_endpoints.py
+++ b/tests/integration/api/v1/test_admin_endpoints.py
@@ -24,8 +24,8 @@ class TestAdminAPI:
assert response.status_code == 403
assert (
- "Access denied" in response.json()["detail"]
- or "admin" in response.json()["detail"].lower()
+ "Access denied" in response.json()["detail"]
+ or "admin" in response.json()["detail"].lower()
)
def test_toggle_user_status_admin(self, client, admin_headers, test_user):
@@ -48,7 +48,7 @@ class TestAdminAPI:
assert "User not found" in response.json()["detail"]
def test_toggle_user_status_cannot_deactivate_self(
- self, client, admin_headers, test_admin
+ self, client, admin_headers, test_admin
):
"""Test that admin cannot deactivate their own account"""
response = client.put(
@@ -79,8 +79,8 @@ class TestAdminAPI:
assert response.status_code == 403
assert (
- "Access denied" in response.json()["detail"]
- or "admin" in response.json()["detail"].lower()
+ "Access denied" in response.json()["detail"]
+ or "admin" in response.json()["detail"].lower()
)
def test_verify_shop_admin(self, client, admin_headers, test_shop):
@@ -120,7 +120,7 @@ class TestAdminAPI:
assert "Shop not found" in response.json()["detail"]
def test_get_marketplace_import_jobs_admin(
- self, client, admin_headers, test_marketplace_job
+ self, client, admin_headers, test_marketplace_job
):
"""Test admin getting marketplace import jobs"""
response = client.get(
@@ -136,7 +136,7 @@ class TestAdminAPI:
assert test_marketplace_job.id in job_ids
def test_get_marketplace_import_jobs_with_filters(
- self, client, admin_headers, test_marketplace_job
+ self, client, admin_headers, test_marketplace_job
):
"""Test admin getting marketplace import jobs with filters"""
response = client.get(
@@ -160,8 +160,8 @@ class TestAdminAPI:
assert response.status_code == 403
assert (
- "Access denied" in response.json()["detail"]
- or "admin" in response.json()["detail"].lower()
+ "Access denied" in response.json()["detail"]
+ or "admin" in response.json()["detail"].lower()
)
def test_admin_pagination_users(self, client, admin_headers, test_user, test_admin):
diff --git a/tests/test_auth.py b/tests/integration/api/v1/test_authentication_endpoints.py
similarity index 99%
rename from tests/test_auth.py
rename to tests/integration/api/v1/test_authentication_endpoints.py
index 96aef38e..b5fd8c50 100644
--- a/tests/test_auth.py
+++ b/tests/integration/api/v1/test_authentication_endpoints.py
@@ -1,4 +1,4 @@
-# tests/test_auth.py
+# tests/test_authentication_endpoints.py
import pytest
from fastapi import HTTPException
diff --git a/tests/test_marketplace.py b/tests/integration/api/v1/test_marketplace_endpoints.py
similarity index 98%
rename from tests/test_marketplace.py
rename to tests/integration/api/v1/test_marketplace_endpoints.py
index 446ef3d3..fcac37e3 100644
--- a/tests/test_marketplace.py
+++ b/tests/integration/api/v1/test_marketplace_endpoints.py
@@ -1,4 +1,4 @@
-# tests/test_marketplace.py
+# tests/test_marketplace_endpoints.py
from unittest.mock import AsyncMock, patch
import pytest
diff --git a/integration_pagination.py b/tests/integration/api/v1/test_pagination.py
similarity index 97%
rename from integration_pagination.py
rename to tests/integration/api/v1/test_pagination.py
index 849e3efa..a0cc4207 100644
--- a/integration_pagination.py
+++ b/tests/integration/api/v1/test_pagination.py
@@ -1,13 +1,14 @@
# tests/integration/api/v1/test_pagination.py
import pytest
-from models.database_models import Product
+from models.database_models import Product, Shop
@pytest.mark.integration
@pytest.mark.api
@pytest.mark.database
@pytest.mark.products
+@pytest.mark.shops
class TestPagination:
def test_product_pagination(self, client, auth_headers, db):
"""Test pagination for product listing"""
diff --git a/tests/test_product.py b/tests/integration/api/v1/test_product_endpoints.py
similarity index 99%
rename from tests/test_product.py
rename to tests/integration/api/v1/test_product_endpoints.py
index ddf70aab..065a1bc5 100644
--- a/tests/test_product.py
+++ b/tests/integration/api/v1/test_product_endpoints.py
@@ -1,4 +1,4 @@
-# tests/test_product.py
+# tests/test_product_endpoints.py
import pytest
diff --git a/tests/test_shop.py b/tests/integration/api/v1/test_shop_endpoints.py
similarity index 98%
rename from tests/test_shop.py
rename to tests/integration/api/v1/test_shop_endpoints.py
index cead5ad2..8b094a7b 100644
--- a/tests/test_shop.py
+++ b/tests/integration/api/v1/test_shop_endpoints.py
@@ -1,4 +1,4 @@
-# tests/test_shop.py
+# tests/test_shop_endpoints.py
import pytest
diff --git a/tests/test_stats.py b/tests/integration/api/v1/test_stats_endpoints.py
similarity index 97%
rename from tests/test_stats.py
rename to tests/integration/api/v1/test_stats_endpoints.py
index cd9ba5be..fdf7a078 100644
--- a/tests/test_stats.py
+++ b/tests/integration/api/v1/test_stats_endpoints.py
@@ -1,4 +1,4 @@
-# tests/test_stats.py
+# tests/test_stats_endpoints.py
import pytest
diff --git a/tests/test_stock.py b/tests/integration/api/v1/test_stock_endpoints.py
similarity index 99%
rename from tests/test_stock.py
rename to tests/integration/api/v1/test_stock_endpoints.py
index 3c84dc24..5aa72de3 100644
--- a/tests/test_stock.py
+++ b/tests/integration/api/v1/test_stock_endpoints.py
@@ -1,4 +1,4 @@
-# tests/test_stock.py
+# tests/test_stock_endpoints.py
import pytest
from models.database_models import Stock
diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py
new file mode 100644
index 00000000..703f8685
--- /dev/null
+++ b/tests/integration/conftest.py
@@ -0,0 +1,6 @@
+# tests/integration/conftest.py
+"""Integration test specific fixtures."""
+import pytest
+
+# Add any integration-specific fixtures here if needed
+
diff --git a/tests/integration/security/__init__.py b/tests/integration/security/__init__.py
new file mode 100644
index 00000000..dc1e90f7
--- /dev/null
+++ b/tests/integration/security/__init__.py
@@ -0,0 +1,3 @@
+# tests/integration/security/__init__.py
+"""Security integration tests."""
+
diff --git a/integration_authentication.py b/tests/integration/security/test_authentication.py
similarity index 100%
rename from integration_authentication.py
rename to tests/integration/security/test_authentication.py
diff --git a/integration_authorization.py b/tests/integration/security/test_authorization.py
similarity index 100%
rename from integration_authorization.py
rename to tests/integration/security/test_authorization.py
diff --git a/integration_input_validation.py b/tests/integration/security/test_input_validation.py
similarity index 100%
rename from integration_input_validation.py
rename to tests/integration/security/test_input_validation.py
diff --git a/tests/performance/__init__.py b/tests/performance/__init__.py
new file mode 100644
index 00000000..2f4789ba
--- /dev/null
+++ b/tests/performance/__init__.py
@@ -0,0 +1,3 @@
+# tests/performance/__init__.py
+"""Performance and load tests."""
+
diff --git a/tests/performance/conftest.py b/tests/performance/conftest.py
new file mode 100644
index 00000000..22259230
--- /dev/null
+++ b/tests/performance/conftest.py
@@ -0,0 +1,10 @@
+# tests/performance/conftest.py
+"""Performance test specific fixtures."""
+import pytest
+
+@pytest.fixture
+def performance_db_session(db):
+ """Database session optimized for performance testing"""
+ # You can add performance-specific DB configurations here
+ return db
+
diff --git a/performance_tests.py b/tests/performance/test_api_performance.py
similarity index 100%
rename from performance_tests.py
rename to tests/performance/test_api_performance.py
diff --git a/tests/pytest.ini b/tests/pytest.ini
deleted file mode 100644
index 4bb15305..00000000
--- a/tests/pytest.ini
+++ /dev/null
@@ -1,21 +0,0 @@
-# tests/pytest.ini
-[tool:pytest]
-testpaths = tests
-python_files = test_*.py
-python_classes = Test*
-python_functions = test_*
-addopts =
- -v
- --tb=short
- --strict-markers
- --disable-warnings
- --color=yes
-markers =
- slow: marks tests as slow (deselect with '-m "not slow"')
- integration: marks tests as integration tests
- unit: marks tests as unit tests
- auth: marks tests related to authentication
- products: marks tests related to products
- stock: marks tests related to stock management
- shops: marks tests related to shop management
- admin: marks tests related to admin functionality
diff --git a/tests/requirements-test.txt b/tests/requirements-test.txt
index 3e0fe59d..8bf5f468 100644
--- a/tests/requirements-test.txt
+++ b/tests/requirements-test.txt
@@ -7,3 +7,4 @@ pytest-mock>=3.11.0
httpx>=0.24.0
faker>=19.0.0
pytest-repeat>=0.9.4
+pytest-timeout>=2.1.0
diff --git a/tests/system/__init__.py b/tests/system/__init__.py
new file mode 100644
index 00000000..ac25623d
--- /dev/null
+++ b/tests/system/__init__.py
@@ -0,0 +1,3 @@
+# tests/system/__init__.py
+"""System-level tests - full application behavior."""
+
diff --git a/tests/system/conftest.py b/tests/system/conftest.py
new file mode 100644
index 00000000..8c6bc72a
--- /dev/null
+++ b/tests/system/conftest.py
@@ -0,0 +1,6 @@
+# tests/system/conftest.py
+"""System test specific fixtures."""
+import pytest
+
+# Add any system-specific fixtures here if needed
+
diff --git a/system_error_handling.py b/tests/system/test_error_handling.py
similarity index 100%
rename from system_error_handling.py
rename to tests/system/test_error_handling.py
diff --git a/tests/test_admin.py b/tests/test_admin.py
deleted file mode 100644
index 0bf46621..00000000
--- a/tests/test_admin.py
+++ /dev/null
@@ -1,192 +0,0 @@
-# tests/test_admin.py
-import pytest
-
-
-class TestAdminAPI:
- def test_get_all_users_admin(self, client, admin_headers, test_user):
- """Test admin getting all users"""
- response = client.get("/api/v1/admin/users", headers=admin_headers)
-
- assert response.status_code == 200
- data = response.json()
- assert len(data) >= 2 # test_user + admin user
-
- # Check that test_user is in the response
- user_ids = [user["id"] for user in data if "id" in user]
- assert test_user.id in user_ids
-
- def test_get_all_users_non_admin(self, client, auth_headers):
- """Test non-admin trying to access admin endpoint"""
- response = client.get("/api/v1/admin/users", headers=auth_headers)
-
- assert response.status_code == 403
- assert (
- "Access denied" in response.json()["detail"]
- or "admin" in response.json()["detail"].lower()
- )
-
- def test_toggle_user_status_admin(self, client, admin_headers, test_user):
- """Test admin toggling user status"""
- response = client.put(
- f"/api/v1/admin/users/{test_user.id}/status", headers=admin_headers
- )
-
- assert response.status_code == 200
- message = response.json()["message"]
- assert "deactivated" in message or "activated" in message
- # Verify the username is in the message
- assert test_user.username in message
-
- def test_toggle_user_status_user_not_found(self, client, admin_headers):
- """Test admin toggling status for non-existent user"""
- response = client.put("/api/v1/admin/users/99999/status", headers=admin_headers)
-
- assert response.status_code == 404
- assert "User not found" in response.json()["detail"]
-
- def test_toggle_user_status_cannot_deactivate_self(
- self, client, admin_headers, test_admin
- ):
- """Test that admin cannot deactivate their own account"""
- response = client.put(
- f"/api/v1/admin/users/{test_admin.id}/status", headers=admin_headers
- )
-
- assert response.status_code == 400
- assert "Cannot deactivate your own account" in response.json()["detail"]
-
- def test_get_all_shops_admin(self, client, admin_headers, test_shop):
- """Test admin getting all shops"""
- response = client.get("/api/v1/admin/shops", headers=admin_headers)
-
- assert response.status_code == 200
- data = response.json()
- assert data["total"] >= 1
- assert len(data["shops"]) >= 1
-
- # Check that test_shop is in the response
- shop_codes = [
- shop["shop_code"] for shop in data["shops"] if "shop_code" in shop
- ]
- assert test_shop.shop_code in shop_codes
-
- def test_get_all_shops_non_admin(self, client, auth_headers):
- """Test non-admin trying to access admin shop endpoint"""
- response = client.get("/api/v1/admin/shops", headers=auth_headers)
-
- assert response.status_code == 403
- assert (
- "Access denied" in response.json()["detail"]
- or "admin" in response.json()["detail"].lower()
- )
-
- def test_verify_shop_admin(self, client, admin_headers, test_shop):
- """Test admin verifying/unverifying shop"""
- response = client.put(
- f"/api/v1/admin/shops/{test_shop.id}/verify", headers=admin_headers
- )
-
- assert response.status_code == 200
- message = response.json()["message"]
- assert "verified" in message or "unverified" in message
- assert test_shop.shop_code in message
-
- def test_verify_shop_not_found(self, client, admin_headers):
- """Test admin verifying non-existent shop"""
- response = client.put("/api/v1/admin/shops/99999/verify", headers=admin_headers)
-
- assert response.status_code == 404
- assert "Shop not found" in response.json()["detail"]
-
- def test_toggle_shop_status_admin(self, client, admin_headers, test_shop):
- """Test admin toggling shop status"""
- response = client.put(
- f"/api/v1/admin/shops/{test_shop.id}/status", headers=admin_headers
- )
-
- assert response.status_code == 200
- message = response.json()["message"]
- assert "activated" in message or "deactivated" in message
- assert test_shop.shop_code in message
-
- def test_toggle_shop_status_not_found(self, client, admin_headers):
- """Test admin toggling status for non-existent shop"""
- response = client.put("/api/v1/admin/shops/99999/status", headers=admin_headers)
-
- assert response.status_code == 404
- assert "Shop not found" in response.json()["detail"]
-
- def test_get_marketplace_import_jobs_admin(
- self, client, admin_headers, test_marketplace_job
- ):
- """Test admin getting marketplace import jobs"""
- response = client.get(
- "/api/v1/admin/marketplace-import-jobs", headers=admin_headers
- )
-
- assert response.status_code == 200
- data = response.json()
- assert len(data) >= 1
-
- # Check that test_marketplace_job is in the response
- job_ids = [job["job_id"] for job in data if "job_id" in job]
- assert test_marketplace_job.id in job_ids
-
- def test_get_marketplace_import_jobs_with_filters(
- self, client, admin_headers, test_marketplace_job
- ):
- """Test admin getting marketplace import jobs with filters"""
- response = client.get(
- "/api/v1/admin/marketplace-import-jobs",
- params={"marketplace": test_marketplace_job.marketplace},
- headers=admin_headers,
- )
-
- assert response.status_code == 200
- data = response.json()
- assert len(data) >= 1
- assert all(
- job["marketplace"] == test_marketplace_job.marketplace for job in data
- )
-
- def test_get_marketplace_import_jobs_non_admin(self, client, auth_headers):
- """Test non-admin trying to access marketplace import jobs"""
- response = client.get(
- "/api/v1/admin/marketplace-import-jobs", headers=auth_headers
- )
-
- assert response.status_code == 403
- assert (
- "Access denied" in response.json()["detail"]
- or "admin" in response.json()["detail"].lower()
- )
-
- def test_admin_pagination_users(self, client, admin_headers, test_user, test_admin):
- """Test user pagination works correctly"""
- # Test first page
- response = client.get(
- "/api/v1/admin/users?skip=0&limit=1", headers=admin_headers
- )
- assert response.status_code == 200
- data = response.json()
- assert len(data) == 1
-
- # Test second page
- response = client.get(
- "/api/v1/admin/users?skip=1&limit=1", headers=admin_headers
- )
- assert response.status_code == 200
- data = response.json()
- assert len(data) >= 0 # Could be 1 or 0 depending on total users
-
- def test_admin_pagination_shops(self, client, admin_headers, test_shop):
- """Test shop pagination works correctly"""
- response = client.get(
- "/api/v1/admin/shops?skip=0&limit=1", headers=admin_headers
- )
- assert response.status_code == 200
- data = response.json()
- assert data["total"] >= 1
- assert len(data["shops"]) >= 0
- assert "skip" in data
- assert "limit" in data
diff --git a/tests/test_admin_service.py b/tests/test_admin_service.py
deleted file mode 100644
index f5574f21..00000000
--- a/tests/test_admin_service.py
+++ /dev/null
@@ -1,388 +0,0 @@
-# tests/test_admin_service.py
-from datetime import datetime
-
-import pytest
-from fastapi import HTTPException
-
-from app.services.admin_service import AdminService
-from models.database_models import MarketplaceImportJob, Shop, User
-
-
-class TestAdminService:
- """Test suite for AdminService following the application's testing patterns"""
-
- def setup_method(self):
- """Setup method following the same pattern as product service tests"""
- self.service = AdminService()
-
- def test_get_all_users(self, db, test_user, test_admin):
- """Test getting all users with pagination"""
- users = self.service.get_all_users(db, skip=0, limit=10)
-
- assert len(users) >= 2 # test_user + test_admin
- user_ids = [user.id for user in users]
- assert test_user.id in user_ids
- assert test_admin.id in user_ids
-
- def test_get_all_users_with_pagination(self, db, test_user, test_admin):
- """Test user pagination works correctly"""
- users = self.service.get_all_users(db, skip=0, limit=1)
-
- assert len(users) == 1
-
- users_second_page = self.service.get_all_users(db, skip=1, limit=1)
- assert len(users_second_page) == 1
- assert users[0].id != users_second_page[0].id
-
- def test_toggle_user_status_deactivate(self, db, test_user, test_admin):
- """Test deactivating a user"""
- assert test_user.is_active is True
-
- user, message = self.service.toggle_user_status(db, test_user.id, test_admin.id)
-
- assert user.id == test_user.id
- assert user.is_active is False
- assert f"{user.username} has been deactivated" in message
-
- def test_toggle_user_status_activate(self, db, test_user, test_admin):
- """Test activating a user"""
- # First deactivate the user
- test_user.is_active = False
- db.commit()
-
- user, message = self.service.toggle_user_status(db, test_user.id, test_admin.id)
-
- assert user.id == test_user.id
- assert user.is_active is True
- assert f"{user.username} has been activated" in message
-
- def test_toggle_user_status_user_not_found(self, db, test_admin):
- """Test toggle user status when user not found"""
- with pytest.raises(HTTPException) as exc_info:
- self.service.toggle_user_status(db, 99999, test_admin.id)
-
- assert exc_info.value.status_code == 404
- assert "User not found" in str(exc_info.value.detail)
-
- def test_toggle_user_status_cannot_deactivate_self(self, db, test_admin):
- """Test that admin cannot deactivate their own account"""
- with pytest.raises(HTTPException) as exc_info:
- self.service.toggle_user_status(db, test_admin.id, test_admin.id)
-
- assert exc_info.value.status_code == 400
- assert "Cannot deactivate your own account" in str(exc_info.value.detail)
-
- def test_get_all_shops(self, db, test_shop):
- """Test getting all shops with total count"""
- shops, total = self.service.get_all_shops(db, skip=0, limit=10)
-
- assert total >= 1
- assert len(shops) >= 1
- shop_codes = [shop.shop_code for shop in shops]
- assert test_shop.shop_code in shop_codes
-
- def test_get_all_shops_with_pagination(self, db, test_shop):
- """Test shop pagination works correctly"""
- # Create additional shop for pagination test using the helper function
- # from conftest import create_test_import_job # If you added the helper function
-
- # Or create directly with proper fields
- additional_shop = Shop(
- shop_code=f"{test_shop.shop_code}_2",
- shop_name="Test Shop 2",
- owner_id=test_shop.owner_id,
- is_active=True,
- is_verified=False,
- )
- db.add(additional_shop)
- db.commit()
-
- shops_page_1 = self.service.get_all_shops(db, skip=0, limit=1)
- assert len(shops_page_1[0]) == 1
-
- shops_page_2 = self.service.get_all_shops(db, skip=1, limit=1)
- assert len(shops_page_2[0]) == 1
-
- # Ensure different shops on different pages
- assert shops_page_1[0][0].id != shops_page_2[0][0].id
-
- def test_verify_shop_mark_verified(self, db, test_shop):
- """Test marking shop as verified"""
- # Ensure shop starts unverified
- test_shop.is_verified = False
- db.commit()
-
- shop, message = self.service.verify_shop(db, test_shop.id)
-
- assert shop.id == test_shop.id
- assert shop.is_verified is True
- assert f"{shop.shop_code} has been verified" in message
-
- def test_verify_shop_mark_unverified(self, db, test_shop):
- """Test marking shop as unverified"""
- # Ensure shop starts verified
- test_shop.is_verified = True
- db.commit()
-
- shop, message = self.service.verify_shop(db, test_shop.id)
-
- assert shop.id == test_shop.id
- assert shop.is_verified is False
- assert f"{shop.shop_code} has been unverified" in message
-
- def test_verify_shop_not_found(self, db):
- """Test verify shop when shop not found"""
- with pytest.raises(HTTPException) as exc_info:
- self.service.verify_shop(db, 99999)
-
- assert exc_info.value.status_code == 404
- assert "Shop not found" in str(exc_info.value.detail)
-
- def test_toggle_shop_status_deactivate(self, db, test_shop):
- """Test deactivating a shop"""
- assert test_shop.is_active is True
-
- shop, message = self.service.toggle_shop_status(db, test_shop.id)
-
- assert shop.id == test_shop.id
- assert shop.is_active is False
- assert f"{shop.shop_code} has been deactivated" in message
-
- def test_toggle_shop_status_activate(self, db, test_shop):
- """Test activating a shop"""
- # First deactivate the shop
- test_shop.is_active = False
- db.commit()
-
- shop, message = self.service.toggle_shop_status(db, test_shop.id)
-
- assert shop.id == test_shop.id
- assert shop.is_active is True
- assert f"{shop.shop_code} has been activated" in message
-
- def test_toggle_shop_status_not_found(self, db):
- """Test toggle shop status when shop not found"""
- with pytest.raises(HTTPException) as exc_info:
- self.service.toggle_shop_status(db, 99999)
-
- assert exc_info.value.status_code == 404
- assert "Shop not found" in str(exc_info.value.detail)
-
- def test_get_marketplace_import_jobs_no_filters(self, db, test_marketplace_job):
- """Test getting marketplace import jobs without filters using fixture"""
- 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_job.id), None
- )
- assert test_job is not None
- assert test_job.marketplace == test_marketplace_job.marketplace
- assert test_job.shop_name == test_marketplace_job.shop_name
- assert test_job.status == test_marketplace_job.status
-
- def test_get_marketplace_import_jobs_with_marketplace_filter(
- self, db, test_marketplace_job, test_user, test_shop
- ):
- """Test getting marketplace import jobs filtered by marketplace"""
- # Create additional job with different marketplace
- other_job = MarketplaceImportJob(
- marketplace="ebay",
- shop_name="eBay Shop",
- status="completed",
- source_url="https://ebay.example.com/import",
- shop_id=test_shop.id,
- user_id=test_user.id, # Fixed: Added missing user_id
- )
- db.add(other_job)
- db.commit()
-
- # Filter by the test marketplace job's marketplace
- result = self.service.get_marketplace_import_jobs(
- db, marketplace=test_marketplace_job.marketplace
- )
-
- assert len(result) >= 1
- # All results should match the marketplace filter
- for job in result:
- assert test_marketplace_job.marketplace.lower() in job.marketplace.lower()
-
- def test_get_marketplace_import_jobs_with_shop_filter(
- self, db, test_marketplace_job, test_user, test_shop
- ):
- """Test getting marketplace import jobs filtered by shop name"""
- # Create additional job with different shop name
- other_job = MarketplaceImportJob(
- marketplace="amazon",
- shop_name="Different Shop Name",
- status="completed",
- source_url="https://different.example.com/import",
- shop_id=test_shop.id,
- user_id=test_user.id, # Fixed: Added missing user_id
- )
- db.add(other_job)
- db.commit()
-
- # Filter by the test marketplace job's shop name
- result = self.service.get_marketplace_import_jobs(
- db, shop_name=test_marketplace_job.shop_name
- )
-
- assert len(result) >= 1
- # All results should match the shop name filter
- for job in result:
- assert test_marketplace_job.shop_name.lower() in job.shop_name.lower()
-
- def test_get_marketplace_import_jobs_with_status_filter(
- self, db, test_marketplace_job, test_user, test_shop
- ):
- """Test getting marketplace import jobs filtered by status"""
- # Create additional job with different status
- other_job = MarketplaceImportJob(
- marketplace="amazon",
- shop_name="Test Shop",
- status="pending",
- source_url="https://pending.example.com/import",
- shop_id=test_shop.id,
- user_id=test_user.id, # Fixed: Added missing user_id
- )
- db.add(other_job)
- db.commit()
-
- # Filter by the test marketplace job's status
- result = self.service.get_marketplace_import_jobs(
- db, status=test_marketplace_job.status
- )
-
- assert len(result) >= 1
- # All results should match the status filter
- for job in result:
- assert job.status == test_marketplace_job.status
-
- def test_get_marketplace_import_jobs_with_multiple_filters(
- self, db, test_marketplace_job, test_shop, test_user
- ):
- """Test getting marketplace import jobs with multiple filters"""
- # Create jobs that don't match all filters
- non_matching_job1 = MarketplaceImportJob(
- marketplace="ebay", # Different marketplace
- shop_name=test_marketplace_job.shop_name,
- status=test_marketplace_job.status,
- source_url="https://non-matching1.example.com/import",
- shop_id=test_shop.id,
- user_id=test_user.id, # Fixed: Added missing user_id
- )
- non_matching_job2 = MarketplaceImportJob(
- marketplace=test_marketplace_job.marketplace,
- shop_name="Different Shop", # Different shop name
- status=test_marketplace_job.status,
- source_url="https://non-matching2.example.com/import",
- shop_id=test_shop.id,
- user_id=test_user.id, # Fixed: Added missing user_id
- )
- db.add_all([non_matching_job1, non_matching_job2])
- db.commit()
-
- # Apply all three filters matching the test job
- result = self.service.get_marketplace_import_jobs(
- db,
- marketplace=test_marketplace_job.marketplace,
- shop_name=test_marketplace_job.shop_name,
- status=test_marketplace_job.status,
- )
-
- 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_job.id), None
- )
- assert test_job is not None
- assert test_job.marketplace == test_marketplace_job.marketplace
- assert test_job.shop_name == test_marketplace_job.shop_name
- assert test_job.status == test_marketplace_job.status
-
- def test_get_marketplace_import_jobs_null_values(self, db, test_user, test_shop):
- """Test that marketplace import jobs handle null values correctly"""
- # Create job with null values but required fields
- job = MarketplaceImportJob(
- shop_id=test_shop.id,
- user_id=test_user.id, # Fixed: Added missing user_id
- marketplace="test",
- shop_name="Test Shop",
- status="pending",
- source_url="https://test.example.com/import",
- imported_count=None,
- updated_count=None,
- total_processed=None,
- error_count=None,
- error_message=None,
- )
- db.add(job)
- db.commit()
-
- result = self.service.get_marketplace_import_jobs(db)
-
- assert len(result) >= 1
- # Find the job with null values
- null_job = next((j for j in result if j.job_id == job.id), None)
- assert null_job is not None
- assert null_job.imported == 0 # None converted to 0
- assert null_job.updated == 0
- assert null_job.total_processed == 0
- assert null_job.error_count == 0
- assert null_job.error_message is None
-
- def test_get_user_by_id(self, db, test_user):
- """Test getting user by ID using fixture"""
- user = self.service.get_user_by_id(db, test_user.id)
-
- assert user is not None
- assert user.id == test_user.id
- assert user.email == test_user.email
- assert user.username == test_user.username
-
- def test_get_user_by_id_not_found(self, db):
- """Test getting user by ID when user doesn't exist"""
- user = self.service.get_user_by_id(db, 99999)
-
- assert user is None
-
- def test_get_shop_by_id(self, db, test_shop):
- """Test getting shop by ID using fixture"""
- shop = self.service.get_shop_by_id(db, test_shop.id)
-
- assert shop is not None
- assert shop.id == test_shop.id
- assert shop.shop_code == test_shop.shop_code
- assert shop.shop_name == test_shop.shop_name
-
- def test_get_shop_by_id_not_found(self, db):
- """Test getting shop by ID when shop doesn't exist"""
- shop = self.service.get_shop_by_id(db, 99999)
-
- assert shop is None
-
- def test_user_exists_true(self, db, test_user):
- """Test user_exists returns True when user exists"""
- exists = self.service.user_exists(db, test_user.id)
-
- assert exists is True
-
- def test_user_exists_false(self, db):
- """Test user_exists returns False when user doesn't exist"""
- exists = self.service.user_exists(db, 99999)
-
- assert exists is False
-
- def test_shop_exists_true(self, db, test_shop):
- """Test shop_exists returns True when shop exists"""
- exists = self.service.shop_exists(db, test_shop.id)
-
- assert exists is True
-
- def test_shop_exists_false(self, db):
- """Test shop_exists returns False when shop doesn't exist"""
- exists = self.service.shop_exists(db, 99999)
-
- assert exists is False
diff --git a/tests/test_data/csv/sample_products.csv b/tests/test_data/csv/sample_products.csv
new file mode 100644
index 00000000..bd8d6733
--- /dev/null
+++ b/tests/test_data/csv/sample_products.csv
@@ -0,0 +1,4 @@
+product_id,title,price,currency,brand,marketplace
+TEST001,Sample Product 1,19.99,EUR,TestBrand,TestMarket
+TEST002,Sample Product 2,29.99,EUR,TestBrand,TestMarket
+TEST003,Sample Product 3,39.99,USD,AnotherBrand,TestMarket
diff --git a/tests/test_database.py b/tests/test_database.py
deleted file mode 100644
index f2894a77..00000000
--- a/tests/test_database.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# tests/test_database.py
-import pytest
-from sqlalchemy import text
-
-from models.database_models import Product, Shop, Stock, User
-
-
-class TestDatabaseModels:
- def test_user_model(self, db):
- """Test User model creation and relationships"""
- user = User(
- email="db_test@example.com",
- username="dbtest",
- hashed_password="hashed_password_123",
- role="user",
- is_active=True,
- )
-
- db.add(user)
- db.commit()
- db.refresh(user)
-
- assert user.id is not None
- assert user.email == "db_test@example.com"
- assert user.created_at is not None
- assert user.updated_at is not None
-
- def test_product_model(self, db):
- """Test Product model creation"""
- product = Product(
- product_id="DB_TEST_001",
- title="Database Test Product",
- description="Testing product model",
- price="25.99",
- currency="USD",
- brand="DBTest",
- gtin="1234567890123",
- availability="in stock",
- marketplace="TestDB",
- shop_name="DBTestShop",
- )
-
- db.add(product)
- db.commit()
- db.refresh(product)
-
- assert product.id is not None
- assert product.product_id == "DB_TEST_001"
- assert product.created_at is not None
-
- def test_stock_model(self, db):
- """Test Stock model creation"""
- stock = Stock(gtin="1234567890123", location="DB_WAREHOUSE", quantity=150)
-
- db.add(stock)
- db.commit()
- db.refresh(stock)
-
- assert stock.id is not None
- assert stock.gtin == "1234567890123"
- assert stock.location == "DB_WAREHOUSE"
- assert stock.quantity == 150
-
- def test_shop_model_with_owner(self, db, test_user):
- """Test Shop model with owner relationship"""
- shop = Shop(
- shop_code="DBTEST",
- shop_name="Database Test Shop",
- description="Testing shop model",
- owner_id=test_user.id,
- is_active=True,
- is_verified=False,
- )
-
- db.add(shop)
- db.commit()
- db.refresh(shop)
-
- assert shop.id is not None
- assert shop.shop_code == "DBTEST"
- assert shop.owner_id == test_user.id
- assert shop.owner.username == test_user.username
-
- def test_database_constraints(self, db):
- """Test database constraints and unique indexes"""
- # Test unique product_id constraint
- product1 = Product(product_id="UNIQUE_001", title="Product 1")
- db.add(product1)
- db.commit()
-
- # This should raise an integrity error
- with pytest.raises(Exception): # Could be IntegrityError or similar
- product2 = Product(product_id="UNIQUE_001", title="Product 2")
- db.add(product2)
- db.commit()
diff --git a/tests/test_error_handling.py b/tests/test_error_handling.py
deleted file mode 100644
index 66951aa0..00000000
--- a/tests/test_error_handling.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# tests/test_error_handling.py
-import pytest
-
-
-class TestErrorHandling:
- def test_invalid_json(self, client, auth_headers):
- """Test handling of invalid JSON"""
- response = client.post(
- "/api/v1/product", headers=auth_headers, content="invalid json"
- )
-
- assert response.status_code == 422 # Validation error
-
- def test_missing_required_fields(self, client, auth_headers):
- """Test handling of missing required fields"""
- response = client.post(
- "/api/v1/product", headers=auth_headers, json={"title": "Test"}
- ) # Missing product_id
-
- assert response.status_code == 422
-
- def test_invalid_authentication(self, client):
- """Test handling of invalid authentication"""
- response = client.get(
- "/api/v1/product", headers={"Authorization": "Bearer invalid_token"}
- )
-
- assert response.status_code == 401 # Token is not valid
-
- def test_nonexistent_resource(self, client, auth_headers):
- """Test handling of nonexistent resource access"""
- response = client.get("/api/v1/product/NONEXISTENT", headers=auth_headers)
- assert response.status_code == 404
-
- response = client.get("/api/v1/shop/NONEXISTENT", headers=auth_headers)
- assert response.status_code == 404
-
- def test_duplicate_resource_creation(self, client, auth_headers, test_product):
- """Test handling of duplicate resource creation"""
- product_data = {
- "product_id": test_product.product_id, # Duplicate ID
- "title": "Another Product",
- }
-
- response = client.post(
- "/api/v1/product", headers=auth_headers, json=product_data
- )
- assert response.status_code == 400
diff --git a/tests/test_pagination.py b/tests/test_pagination.py
deleted file mode 100644
index d2d28eda..00000000
--- a/tests/test_pagination.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# tests/test_pagination.py
-import pytest
-
-from models.database_models import Product
-
-
-class TestPagination:
- def test_product_pagination(self, client, auth_headers, db):
- """Test pagination for product listing"""
- # Create multiple products
- products = []
- for i in range(25):
- product = Product(
- product_id=f"PAGE{i:03d}",
- title=f"Pagination Test Product {i}",
- marketplace="PaginationTest",
- )
- products.append(product)
-
- db.add_all(products)
- db.commit()
-
- # Test first page
- response = client.get("/api/v1/product?limit=10&skip=0", headers=auth_headers)
- assert response.status_code == 200
- data = response.json()
- assert len(data["products"]) == 10
- assert data["total"] == 25
- assert data["skip"] == 0
- assert data["limit"] == 10
-
- # Test second page
- response = client.get("/api/v1/product?limit=10&skip=10", headers=auth_headers)
- assert response.status_code == 200
- data = response.json()
- assert len(data["products"]) == 10
- assert data["skip"] == 10
-
- # Test last page
- response = client.get("/api/v1/product?limit=10&skip=20", headers=auth_headers)
- assert response.status_code == 200
- data = response.json()
- assert len(data["products"]) == 5 # Only 5 remaining
-
- def test_pagination_boundaries(self, client, auth_headers):
- """Test pagination boundary conditions"""
- # Test negative skip
- response = client.get("/api/v1/product?skip=-1", headers=auth_headers)
- assert response.status_code == 422 # Validation error
-
- # Test zero limit
- response = client.get("/api/v1/product?limit=0", headers=auth_headers)
- assert response.status_code == 422 # Validation error
-
- # Test excessive limit
- response = client.get("/api/v1/product?limit=10000", headers=auth_headers)
- assert response.status_code == 422 # Should be limited
diff --git a/tests/test_performance.py b/tests/test_performance.py
deleted file mode 100644
index f71eade4..00000000
--- a/tests/test_performance.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# tests/test_performance.py
-import time
-
-import pytest
-
-from models.database_models import Product
-
-
-class TestPerformance:
- def test_product_list_performance(self, client, auth_headers, db):
- """Test performance of product listing with many products"""
- # Create multiple products
- products = []
- for i in range(100):
- product = Product(
- product_id=f"PERF{i:03d}",
- title=f"Performance Test Product {i}",
- price=f"{i}.99",
- marketplace="Performance",
- )
- products.append(product)
-
- db.add_all(products)
- db.commit()
-
- # Time the request
- start_time = time.time()
- response = client.get("/api/v1/product?limit=100", headers=auth_headers)
- end_time = time.time()
-
- assert response.status_code == 200
- assert len(response.json()["products"]) == 100
- assert end_time - start_time < 2.0 # Should complete within 2 seconds
-
- def test_search_performance(self, client, auth_headers, db):
- """Test search performance"""
- # Create products with searchable content
- products = []
- for i in range(50):
- product = Product(
- product_id=f"SEARCH{i:03d}",
- title=f"Searchable Product {i}",
- description=f"This is a searchable product number {i}",
- brand="SearchBrand",
- marketplace="SearchMarket",
- )
- products.append(product)
-
- db.add_all(products)
- db.commit()
-
- # Time search request
- start_time = time.time()
- response = client.get("/api/v1/product?search=Searchable", headers=auth_headers)
- end_time = time.time()
-
- assert response.status_code == 200
- assert response.json()["total"] == 50
- assert end_time - start_time < 1.0 # Search should be fast
diff --git a/tests/test_security.py b/tests/test_security.py
deleted file mode 100644
index dc1ac162..00000000
--- a/tests/test_security.py
+++ /dev/null
@@ -1,119 +0,0 @@
-# tests/test_security.py
-from unittest.mock import patch
-
-import pytest
-from fastapi import HTTPException
-
-
-class TestSecurity:
- def test_debug_direct_bearer(self, client):
- """Test HTTPBearer directly"""
-
- response = client.get("/api/v1/debug-bearer")
- print(f"Direct Bearer - Status: {response.status_code}")
- print(
- f"Direct Bearer - Response: {response.json() if response.content else 'No content'}"
- )
-
- def test_debug_dependencies(self, client):
- """Debug the dependency chain step by step"""
-
- # Test 1: Direct endpoint with no auth
- response = client.get("/api/v1/admin/users")
- print(f"Admin endpoint - Status: {response.status_code}")
- try:
- print(f"Admin endpoint - Response: {response.json()}")
- except:
- print(f"Admin endpoint - Raw: {response.content}")
-
- # Test 2: Try a regular endpoint that uses get_current_user
- response2 = client.get(
- "/api/v1/product"
- ) # or any endpoint with get_current_user
- print(f"Regular endpoint - Status: {response2.status_code}")
- try:
- print(f"Regular endpoint - Response: {response2.json()}")
- except:
- print(f"Regular endpoint - Raw: {response2.content}")
-
- def test_debug_available_routes(self, client):
- """Debug test to see all available routes"""
- print("\n=== All Available Routes ===")
- for route in client.app.routes:
- if hasattr(route, "path") and hasattr(route, "methods"):
- print(f"{list(route.methods)} {route.path}")
-
- print("\n=== Testing Product Endpoint Variations ===")
- variations = [
- "/api/v1/product", # Your current attempt
- "/api/v1/product/", # With trailing slash
- "/api/v1/product/list", # With list endpoint
- "/api/v1/product/all", # With all endpoint
- ]
-
- for path in variations:
- response = client.get(path)
- print(f"{path}: Status {response.status_code}")
-
- def test_protected_endpoint_without_auth(self, client):
- """Test that protected endpoints reject unauthenticated requests"""
- protected_endpoints = [
- "/api/v1/admin/users",
- "/api/v1/admin/shops",
- "/api/v1/marketplace/import-jobs",
- "/api/v1/product",
- "/api/v1/shop",
- "/api/v1/stats",
- "/api/v1/stock",
- ]
-
- for endpoint in protected_endpoints:
- response = client.get(endpoint)
- assert response.status_code == 401 # Authentication missing
-
- def test_protected_endpoint_with_invalid_token(self, client):
- """Test protected endpoints with invalid token"""
- headers = {"Authorization": "Bearer invalid_token_here"}
-
- response = client.get("/api/v1/product", headers=headers)
- assert response.status_code == 401 # Token is not valid
-
- def test_admin_endpoint_requires_admin_role(self, client, auth_headers):
- """Test that admin endpoints require admin role"""
- response = client.get("/api/v1/admin/users", headers=auth_headers)
- assert (
- response.status_code == 403
- ) # Token is valid but user does not have access.
- # Regular user should be denied
-
- def test_sql_injection_prevention(self, client, auth_headers):
- """Test SQL injection prevention in search parameters"""
- # Try SQL injection in search parameter
- malicious_search = "'; DROP TABLE products; --"
-
- response = client.get(
- f"/api/v1/product?search={malicious_search}", headers=auth_headers
- )
-
- # Should not crash and should return normal response
- assert response.status_code == 200
- # Database should still be intact (no products dropped)
-
- # def test_input_validation(self, client, auth_headers):
- # # TODO: implement sanitization
- # """Test input validation and sanitization"""
- # # Test XSS attempt in product creation
- # xss_payload = ""
- #
- # product_data = {
- # "product_id": "XSS_TEST",
- # "title": xss_payload,
- # "description": xss_payload,
- # }
- #
- # response = client.post("/api/v1/product", headers=auth_headers, json=product_data)
- #
- # assert response.status_code == 200
- # data = response.json()
- # assert "