Browse Source

tests: add conftest.py for common fixtures and configurations

Shiv Tyagi 5 months ago
parent
commit
5dca47dd7c
1 changed files with 283 additions and 0 deletions
  1. 283 0
      tests/web/conftest.py

+ 283 - 0
tests/web/conftest.py

@@ -0,0 +1,283 @@
+"""
+Pytest configuration and shared fixtures for end-to-end tests.
+
+This module provides fixtures for setting up test environment including:
+- Test client with FastAPI app
+- Mock git repository
+- Predefined remotes.json to avoid version fetching
+- Remote reload authentication tokens
+"""
+import os
+import json
+import tempfile
+import shutil
+from typing import Generator
+from unittest.mock import Mock, MagicMock
+
+import pytest
+from fastapi.testclient import TestClient
+
+
+# Sample remotes.json for testing - no version fetching needed
+TEST_REMOTES_JSON = [
+    {
+        "name": "test-remote-1",
+        "url": "https://github.com/test/ardupilot.git",
+        "vehicles": [
+            {
+                "name": "Copter",
+                "releases": [
+                    {
+                        "release_type": "latest",
+                        "version_number": "4.6.0",
+                        "commit_reference": "refs/heads/master"
+                    },
+                    {
+                        "release_type": "stable",
+                        "version_number": "4.3.0",
+                        "commit_reference": "refs/tags/Copter-4.3.0"
+                    }
+                ]
+            },
+            {
+                "name": "Plane",
+                "releases": [
+                    {
+                        "release_type": "latest",
+                        "version_number": "4.5.0",
+                        "commit_reference": "refs/heads/master"
+                    }
+                ]
+            }
+        ]
+    },
+    {
+        "name": "test-remote-2",
+        "url": "https://github.com/another/ardupilot.git",
+        "vehicles": [
+            {
+                "name": "Rover",
+                "releases": [
+                    {
+                        "release_type": "Custom",
+                        "version_number": "Custom",
+                        "commit_reference": "refs/tags/Rover-4.2.0"
+                    }
+                ]
+            }
+        ]
+    }
+]
+
+
+@pytest.fixture(scope="session")
+def test_base_dir() -> Generator[str, None, None]:
+    """
+    Create a temporary base directory structure for testing.
+
+    Yields:
+        str: Path to the temporary base directory
+    """
+    temp_dir = tempfile.mkdtemp(prefix="custombuild_test_")
+
+    # Create required subdirectories
+    subdirs = ["artifacts", "configs", "workdir", "secrets", "ardupilot"]
+    for subdir in subdirs:
+        os.makedirs(os.path.join(temp_dir, subdir), exist_ok=True)
+
+    # Create remotes.json with test data
+    remotes_json_path = os.path.join(temp_dir, "configs", "remotes.json")
+    with open(remotes_json_path, "w") as f:
+        json.dump(TEST_REMOTES_JSON, f, indent=2)
+
+    # Create remote reload token file
+    token_file_path = os.path.join(temp_dir, "secrets", "reload_token")
+    with open(token_file_path, "w") as f:
+        f.write("test-remote-reload-token-12345")
+
+    yield temp_dir
+
+    # Cleanup
+    shutil.rmtree(temp_dir, ignore_errors=True)
+
+
+@pytest.fixture
+def mock_git_repo():
+    """
+    Create a mock GitRepo object.
+
+    Returns:
+        Mock: Mock GitRepo instance
+    """
+    mock_repo = Mock()
+    mock_repo.path = "/tmp/test/ardupilot"
+    mock_repo.get_current_commit_hash.return_value = "abc123def456"
+    mock_repo.checkout_commit.return_value = True
+    mock_repo.get_tags.return_value = ["Copter-4.3.0", "Copter-4.4.0"]
+    mock_repo.get_checkout_lock.return_value = MagicMock()
+    return mock_repo
+
+
+@pytest.fixture
+def mock_ap_src_metadata_fetcher():
+    """
+    Create a mock APSourceMetadataFetcher for testing.
+
+    Returns:
+        Mock: Mock APSourceMetadataFetcher instance
+    """
+    return Mock()
+
+
+@pytest.fixture
+def mock_versions_fetcher(test_base_dir):
+    """
+    Create a mock VersionsFetcher that doesn't actually fetch versions.
+
+    This allows tests to run without starting background threads or
+    making actual git operations.
+
+    Args:
+        test_base_dir: Test base directory fixture
+
+    Returns:
+        Mock: Mock VersionsFetcher instance
+    """
+    from metadata_manager.versions_fetcher import RemoteInfo
+
+    mock_fetcher = Mock()
+
+    # Mock the reload_remotes_json method
+    mock_fetcher.reload_remotes_json = Mock(return_value=None)
+
+    # Mock get_all_remotes_info to return test remotes
+    test_remotes = [
+        RemoteInfo(name="test-remote-1", url="https://github.com/test/ardupilot.git"),
+        RemoteInfo(name="test-remote-2", url="https://github.com/another/ardupilot.git")
+    ]
+    mock_fetcher.get_all_remotes_info = Mock(return_value=test_remotes)
+
+    # Mock start/stop methods (no-op for tests)
+    mock_fetcher.start = Mock()
+    mock_fetcher.stop = Mock()
+
+    return mock_fetcher
+
+
+@pytest.fixture
+def mock_build_manager():
+    """
+    Create a mock BuildManager for testing.
+
+    Returns:
+        Mock: Mock BuildManager instance
+    """
+    mock_manager = Mock()
+    mock_manager.submit_build = Mock(return_value="test-build-id-123")
+    mock_manager.get_build_progress = Mock(return_value={
+        "build_id": "test-build-id-123",
+        "status": "queued",
+        "progress": 0
+    })
+    return mock_manager
+
+
+@pytest.fixture
+def mock_vehicles_manager():
+    """
+    Create a mock VehiclesManager for testing.
+
+    Returns:
+        Mock: Mock VehiclesManager instance
+    """
+    mock_manager = Mock()
+    mock_manager.get_vehicle_names = Mock(return_value=["Copter", "Plane", "Rover"])
+    return mock_manager
+
+
+@pytest.fixture
+def app_with_mocked_dependencies(
+    test_base_dir,
+    mock_git_repo,
+    mock_versions_fetcher,
+    mock_build_manager,
+    mock_vehicles_manager,
+):
+    """
+    Create a FastAPI app instance with mocked dependencies.
+
+    This fixture sets up the application without requiring actual:
+    - Git repository cloning
+    - Version fetching background tasks
+    - Redis connection
+    - Build artifacts
+
+    Args:
+        test_base_dir: Test base directory
+        mock_git_repo: Mock git repository
+        mock_versions_fetcher: Mock versions fetcher
+        mock_build_manager: Mock build manager
+        mock_vehicles_manager: Mock vehicles manager
+
+    Yields:
+        FastAPI: Configured FastAPI application instance
+    """
+    from contextlib import asynccontextmanager
+    from fastapi import FastAPI
+    from slowapi.middleware import SlowAPIMiddleware
+    from slowapi.errors import RateLimitExceeded
+    from web.api.v1 import router as v1_router
+    from web.core.limiter import limiter, rate_limit_exceeded_handler
+
+    # Set environment variables for test configuration
+    os.environ["CBS_BASEDIR"] = test_base_dir
+    os.environ["CBS_REDIS_HOST"] = "localhost"
+    os.environ["CBS_REDIS_PORT"] = "6379"
+    os.environ["CBS_ENABLE_INBUILT_BUILDER"] = "0"  # Disable builder for tests
+
+    @asynccontextmanager
+    async def test_lifespan(app: FastAPI):
+        """Test lifespan that doesn't start background tasks."""
+        # Setup: Attach mocked dependencies to app state
+        app.state.repo = mock_git_repo
+        app.state.versions_fetcher = mock_versions_fetcher
+        app.state.vehicles_manager = mock_vehicles_manager
+        app.state.build_manager = mock_build_manager
+        app.state.limiter = limiter
+
+        # Create mock AP source metadata fetcher
+        mock_ap_src_fetcher = Mock()
+        app.state.ap_src_metadata_fetcher = mock_ap_src_fetcher
+
+        # Don't start background tasks in test mode
+        # versions_fetcher.start()
+        # cleaner.start()
+        # progress_updater.start()
+
+        yield
+
+        # Shutdown logic also skipped
+
+    app = FastAPI(title="CustomBuild Test API", lifespan=test_lifespan)
+
+    app.add_middleware(SlowAPIMiddleware)
+    app.add_exception_handler(RateLimitExceeded, rate_limit_exceeded_handler)
+
+    app.include_router(v1_router, prefix="/api")
+
+    return app
+
+
+@pytest.fixture
+def client(app_with_mocked_dependencies) -> Generator[TestClient, None, None]:
+    """
+    Create a TestClient for making requests to the app.
+
+    Args:
+        app_with_mocked_dependencies: FastAPI app with mocked dependencies
+
+    Yields:
+        TestClient: Test client for making API requests
+    """
+    with TestClient(app_with_mocked_dependencies) as test_client:
+        yield test_client