Path: blob/main/singlestoredb/tests/conftest.py
801 views
#!/usr/bin/env python1"""Pytest configuration for singlestoredb tests23This module sets up automatic Docker container management for tests.4It works with both pytest-style and unittest-style tests.56The conftest automatically:71. Checks if SINGLESTOREDB_URL is set in the environment82. If not set, starts a SingleStore Docker container93. Sets SINGLESTOREDB_URL for all tests to use104. Cleans up the container when tests complete1112Environment Variables:13- SINGLESTOREDB_URL: If set, tests will use this existing server instead14of starting a Docker container. This allows testing15against a specific server instance.16- USE_DATA_API: If set to 1/true/on, tests will use HTTP Data API17instead of MySQL protocol. When set, SINGLESTOREDB_URL18will be set to the HTTP URL, and SINGLESTOREDB_INIT_DB_URL19will be set to the MySQL URL for setup operations.20- SINGLESTORE_LICENSE: Optional. License key for Docker container. If not21set, an empty string is used as fallback.2223Available Fixtures:24- singlestoredb_test_container: Manages Docker container lifecycle25- singlestoredb_connection: Provides a connection to the test server26- singlestoredb_tempdb: Creates a temporary test database with cursor27"""28import logging29import os30from collections.abc import Iterator31from typing import Optional3233import pytest3435from singlestoredb.pytest import _TestContainerManager36from singlestoredb.pytest import execution_mode # noqa: F40137from singlestoredb.pytest import ExecutionMode # noqa: F40138from singlestoredb.pytest import name_allocator # noqa: F40139from singlestoredb.pytest import node_name # noqa: F40140from singlestoredb.pytest import singlestoredb_connection # noqa: F40141from singlestoredb.pytest import singlestoredb_tempdb # noqa: F401424344logger = logging.getLogger(__name__)4546# Global container manager instance47_container_manager: Optional[_TestContainerManager] = None484950def pytest_configure(config: pytest.Config) -> None:51"""52Pytest hook that runs before test collection.5354This ensures the Docker container is started (if needed) before any55test modules are imported. Some test modules try to get connection56parameters at import time, so we need the environment set up early.57"""58global _container_manager5960# Prevent double initialization - pytest_configure can be called multiple times61if _container_manager is not None:62logger.debug('pytest_configure already called, skipping')63return6465if 'SINGLESTOREDB_URL' not in os.environ:66print('\n' + '=' * 70)67print('Starting SingleStoreDB Docker container...')68print('This may take a moment...')69print('=' * 70)70logger.info('SINGLESTOREDB_URL not set, starting Docker container')7172# Create and start the container73_container_manager = _TestContainerManager()7475if not _container_manager.use_existing:76_container_manager.start()77print(f'Container {_container_manager.container_name} started')78print('Waiting for SingleStoreDB to be ready...')7980# Wait for container to be ready81try:82conn = _container_manager.connect()83conn.close()84print('✓ SingleStoreDB is ready!')85logger.info('Docker container is ready')86except Exception as e:87print(f'✗ Failed to connect to Docker container: {e}')88logger.error(f'Failed to connect to Docker container: {e}')89raise9091# Set the environment variable for all tests92# Check if USE_DATA_API is set to use HTTP connection93if os.environ.get('USE_DATA_API', '0').lower() in ('1', 'true', 'on'):94# Use HTTP URL for tests95url = _container_manager.http_connection_url96if url is None:97raise RuntimeError(98'Failed to get HTTP URL from container manager',99)100os.environ['SINGLESTOREDB_URL'] = url101print('=' * 70)102print('USE_DATA_API is enabled - using HTTP Data API for tests')103print(f'Tests will connect via: {url}')104print('=' * 70)105logger.info('USE_DATA_API is enabled - using HTTP Data API for tests')106logger.info(f'Tests will connect via: {url}')107108# Also set INIT_DB_URL to MySQL URL for setup operations109# (like SET GLOBAL) that don't work over HTTP110mysql_url = _container_manager.url111if mysql_url is None:112raise RuntimeError(113'Failed to get MySQL URL from container manager',114)115os.environ['SINGLESTOREDB_INIT_DB_URL'] = mysql_url116print(f'Setup operations will use MySQL protocol: {mysql_url}')117logger.info(118f'Setup operations will use MySQL protocol: {mysql_url}',119)120else:121url = _container_manager.url122if url is None:123raise RuntimeError(124'Failed to get database URL from container manager',125)126os.environ['SINGLESTOREDB_URL'] = url127print('=' * 70)128print(f'Tests will connect via MySQL protocol: {url}')129print('=' * 70)130logger.info(f'Tests will connect via MySQL protocol: {url}')131else:132url = os.environ['SINGLESTOREDB_URL']133logger.debug(f'Using existing SINGLESTOREDB_URL={url}')134135136def pytest_unconfigure(config: pytest.Config) -> None:137"""138Pytest hook that runs after all tests complete.139140Cleans up the Docker container if one was started.141"""142global _container_manager143144if _container_manager is not None and not _container_manager.use_existing:145print('\n' + '=' * 70)146print('Cleaning up Docker container...')147logger.info('Cleaning up Docker container')148try:149_container_manager.stop()150print(f'✓ Container {_container_manager.container_name} stopped')151print('=' * 70)152logger.info('Docker container stopped')153except Exception as e:154print(f'✗ Failed to stop Docker container: {e}')155print('=' * 70)156logger.error(f'Failed to stop Docker container: {e}')157158159@pytest.fixture(scope='session', autouse=True)160def setup_test_environment() -> Iterator[None]:161"""162Automatically set up test environment for all tests.163164This fixture ensures the test environment is ready. The actual container165setup happens in pytest_configure hook to ensure it runs before test166collection. Cleanup happens in pytest_unconfigure hook.167168This fixture exists to ensure proper ordering but doesn't manage the169container lifecycle itself.170"""171# The environment should already be set up by pytest_configure172# This fixture just ensures proper test initialization order173yield174175# Clean up is handled by pytest_unconfigure176177178@pytest.fixture(autouse=True)179def protect_singlestoredb_url() -> Iterator[None]:180"""181Protect SINGLESTOREDB_URL and SINGLESTOREDB_INIT_DB_URL from corruption.182183Some tests (like test_config.py) call reset_option() which resets all184config options to their defaults. Since the 'host' option is registered185with environ=['SINGLESTOREDB_HOST', 'SINGLESTOREDB_URL'], resetting it186overwrites SINGLESTOREDB_URL with just '127.0.0.1' instead of the full187connection string, breaking subsequent tests.188189This fixture saves both URLs before each test and restores them190after, ensuring they're not corrupted.191"""192# Save the current URLs193saved_url = os.environ.get('SINGLESTOREDB_URL')194saved_init_url = os.environ.get('SINGLESTOREDB_INIT_DB_URL')195196yield197198# Restore SINGLESTOREDB_URL if it was set and has been corrupted199if saved_url is not None:200current_url = os.environ.get('SINGLESTOREDB_URL')201if current_url != saved_url:202logger.debug(203f'Restoring SINGLESTOREDB_URL from {current_url!r} to {saved_url!r}',204)205os.environ['SINGLESTOREDB_URL'] = saved_url206207# Restore SINGLESTOREDB_INIT_DB_URL if it was set and has been corrupted208if saved_init_url is not None:209current_init_url = os.environ.get('SINGLESTOREDB_INIT_DB_URL')210if current_init_url != saved_init_url:211logger.debug(212f'Restoring SINGLESTOREDB_INIT_DB_URL from '213f'{current_init_url!r} to {saved_init_url!r}',214)215os.environ['SINGLESTOREDB_INIT_DB_URL'] = saved_init_url216217218