""" NordaBiz Test Configuration and Fixtures ========================================= Shared fixtures for all test types: - app: Flask application instance - client: Test client for HTTP requests - auth_client: Client with logged-in regular user session - admin_client: Client with logged-in admin session - db_session: Database session for integration tests """ import os import sys from pathlib import Path import pytest # Add project root to path for imports PROJECT_ROOT = Path(__file__).parent.parent sys.path.insert(0, str(PROJECT_ROOT)) # Set testing environment before importing app os.environ['FLASK_ENV'] = 'testing' os.environ['TESTING'] = 'true' # If no DATABASE_URL, use SQLite for tests if not os.environ.get('DATABASE_URL'): os.environ['DATABASE_URL'] = 'sqlite:///:memory:' # ============================================================================= # Database Availability Check # ============================================================================= def is_database_available(): """Check if database is available for testing.""" try: from database import engine with engine.connect() as conn: pass return True except Exception: return False # ============================================================================= # Flask Application Fixtures # ============================================================================= @pytest.fixture(scope='session') def app(): """Create Flask application for testing.""" from app import app as flask_app flask_app.config.update({ 'TESTING': True, 'WTF_CSRF_ENABLED': False, # Disable CSRF for tests 'LOGIN_DISABLED': False, 'SERVER_NAME': 'localhost', 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_HTTPONLY': True, }) with flask_app.app_context(): yield flask_app @pytest.fixture def client(app): """Create test client for HTTP requests.""" return app.test_client() @pytest.fixture def runner(app): """Create CLI runner for testing CLI commands.""" return app.test_cli_runner() # ============================================================================= # Authentication Fixtures # ============================================================================= # Test credentials (from CLAUDE.md) TEST_USER_EMAIL = 'test@nordabiznes.pl' TEST_USER_PASSWORD = '&Rc2LdbSw&jiGR0ek@Bz' TEST_ADMIN_EMAIL = 'testadmin@nordabiznes.pl' TEST_ADMIN_PASSWORD = 'cSfQbbwegwv1v3Q2Dm0Q' def _scrypt_available(): """Check if hashlib.scrypt is available (required for password verification).""" try: import hashlib hashlib.scrypt(b'test', salt=b'salt', n=2, r=1, p=1) return True except (AttributeError, ValueError): return False SCRYPT_AVAILABLE = _scrypt_available() @pytest.fixture def auth_client(client, app): """ Create test client with logged-in regular user session. Uses test account: test@nordabiznes.pl Role: MEMBER (regular user) """ if not SCRYPT_AVAILABLE: pytest.skip("hashlib.scrypt not available - cannot test login") with app.app_context(): response = client.post('/login', data={ 'email': TEST_USER_EMAIL, 'password': TEST_USER_PASSWORD, }, follow_redirects=True) # Check if login succeeded by verifying we're not redirected back to login if '/login' in response.request.path: pytest.skip("Login failed - check test user credentials or database") return client @pytest.fixture def admin_client(client, app): """ Create test client with logged-in admin session. Uses test account: testadmin@nordabiznes.pl Role: ADMIN """ if not SCRYPT_AVAILABLE: pytest.skip("hashlib.scrypt not available - cannot test login") with app.app_context(): response = client.post('/login', data={ 'email': TEST_ADMIN_EMAIL, 'password': TEST_ADMIN_PASSWORD, }, follow_redirects=True) # Check if login succeeded if '/login' in response.request.path: pytest.skip("Admin login failed - check test admin credentials or database") return client # ============================================================================= # Database Fixtures # ============================================================================= class DatabaseWrapper: """Wrapper for database access in tests.""" def __init__(self): from database import engine, SessionLocal self.engine = engine self.SessionLocal = SessionLocal self._session = None @property def session(self): if self._session is None: self._session = self.SessionLocal() return self._session def close(self): if self._session: self._session.close() self._session = None @pytest.fixture(scope='session') def db(app): """Get database wrapper instance.""" wrapper = DatabaseWrapper() yield wrapper wrapper.close() @pytest.fixture def db_session(db, app): """ Create database session for integration tests. Rolls back changes after each test. """ with app.app_context(): session = db.SessionLocal() yield session session.rollback() session.close() # ============================================================================= # Mock Fixtures # ============================================================================= @pytest.fixture def mock_gemini(mocker): """Mock Gemini AI service for unit tests.""" mock = mocker.patch('gemini_service.generate_content') mock.return_value = { 'text': 'Mocked AI response for testing', 'companies': [] } return mock @pytest.fixture def mock_email(mocker): """Mock email sending for tests.""" return mocker.patch('flask_mail.Mail.send') # ============================================================================= # Test Data Fixtures # ============================================================================= @pytest.fixture def sample_company(): """Sample company data for tests.""" return { 'name': 'Test Company Sp. z o.o.', 'slug': 'test-company-sp-z-o-o', 'nip': '1234567890', 'category': 'IT', 'short_description': 'Test company for automated tests', 'website': 'https://test-company.pl', 'email': 'contact@test-company.pl', 'phone': '+48 123 456 789', } @pytest.fixture def sample_user(): """Sample user data for tests.""" return { 'email': 'newuser@test.pl', 'password': 'SecurePassword123!', 'first_name': 'Test', 'last_name': 'User', } # ============================================================================= # URL Fixtures # ============================================================================= @pytest.fixture def production_url(): """Production URL for smoke tests.""" return 'https://nordabiznes.pl' @pytest.fixture def staging_url(): """Staging URL for E2E tests.""" return 'https://staging.nordabiznes.pl' # ============================================================================= # Cleanup Fixtures # ============================================================================= @pytest.fixture(autouse=True) def cleanup_after_test(): """Cleanup after each test.""" yield # Add any cleanup logic here if needed # ============================================================================= # Markers Configuration # ============================================================================= def pytest_configure(config): """Configure pytest markers.""" config.addinivalue_line("markers", "unit: Unit tests") config.addinivalue_line("markers", "integration: Integration tests") config.addinivalue_line("markers", "e2e: End-to-end tests") config.addinivalue_line("markers", "smoke: Smoke tests") config.addinivalue_line("markers", "security: Security tests") config.addinivalue_line("markers", "migration: Migration tests") config.addinivalue_line("markers", "dr: Disaster recovery tests") config.addinivalue_line("markers", "slow: Slow tests")