import pytest import os import tempfile import shutil from fastapi.testclient import TestClient from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from sqlalchemy.pool import StaticPool from unittest.mock import patch, MagicMock # Import the app and models import sys sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from main import app from models.user import AdminUser from models.coupon import Coupon from utils.auth import Base, get_db, hash_password from utils.template_loader import templates # Test database configuration TEST_DATABASE_URL = "sqlite:///:memory:" @pytest.fixture(scope="session") def test_engine(): """Create test database engine""" engine = create_engine( TEST_DATABASE_URL, connect_args={"check_same_thread": False}, poolclass=StaticPool, ) return engine @pytest.fixture(scope="session") def test_session_factory(test_engine): """Create test session factory""" TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=test_engine) return TestingSessionLocal @pytest.fixture(scope="session") def test_db_setup(test_engine): """Create test database tables once for the session""" Base.metadata.create_all(bind=test_engine) yield Base.metadata.drop_all(bind=test_engine) @pytest.fixture(scope="function") def test_db(test_engine, test_session_factory, test_db_setup): """Create test database session""" # Create session session = test_session_factory() # Clear any existing data for table in reversed(Base.metadata.sorted_tables): session.execute(table.delete()) session.commit() yield session # Cleanup - rollback and close session.rollback() session.close() @pytest.fixture(scope="function") def client(test_db): """Create test client with database dependency override""" def override_get_db(): try: yield test_db finally: pass app.dependency_overrides[get_db] = override_get_db with TestClient(app) as test_client: yield test_client app.dependency_overrides.clear() @pytest.fixture def admin_user(test_db): """Create a test admin user""" # Clear existing users first test_db.query(AdminUser).delete() test_db.commit() user = AdminUser( username="testadmin", password_hash=hash_password("testpassword123") ) test_db.add(user) test_db.commit() test_db.refresh(user) return user @pytest.fixture def sample_coupons(test_db): """Create sample coupon codes for testing""" # Clear existing coupons first test_db.query(Coupon).delete() test_db.commit() coupons = [] codes = ["TEST123", "SAMPLE456", "DEMO789"] for code in codes: coupon = Coupon(code=code, usage_count=0) test_db.add(coupon) coupons.append(coupon) test_db.commit() for coupon in coupons: test_db.refresh(coupon) return coupons @pytest.fixture def used_coupon(test_db): """Create a used coupon for testing""" from datetime import datetime import pytz # Clear existing coupons first test_db.query(Coupon).delete() test_db.commit() coupon = Coupon( code="USED123", usage_count=1, used_at=datetime.now(pytz.timezone('Asia/Kolkata')) ) test_db.add(coupon) test_db.commit() test_db.refresh(coupon) return coupon @pytest.fixture def temp_translation_dir(): """Create temporary directory for translation files""" temp_dir = tempfile.mkdtemp() original_dir = os.path.join(os.path.dirname(__file__), '..', 'translationfile') # Mock the translation directory path with patch('routes.auth.TRANSLATION_DIR', temp_dir): with patch('routes.auth.TRANSLATION_PATH', os.path.join(temp_dir, 'translation.xlsx')): yield temp_dir # Cleanup shutil.rmtree(temp_dir, ignore_errors=True) @pytest.fixture def mock_templates(): """Mock Jinja2 templates""" mock_template = MagicMock() mock_template.TemplateResponse.return_value = MagicMock() with patch('routes.auth.templates', mock_template): yield mock_template @pytest.fixture def auth_headers(): """Return headers for authenticated requests""" return {"Cookie": "admin_logged_in=true"} @pytest.fixture def mock_logger(): """Mock logger to avoid file operations during tests""" with patch('utils.logger.setup_logger') as mock: mock.return_value = MagicMock() yield mock