Skip to main content
Glama

MCP Atlassian

by ArconixForge
conftest.py23.1 kB
""" Shared fixtures for Confluence unit tests. This module provides specialized fixtures for testing Confluence-related functionality. It builds upon the root conftest.py fixtures and integrates with the new test utilities framework to provide efficient, reusable test fixtures with session-scoped caching. """ import os import sys from pathlib import Path from unittest.mock import MagicMock, patch import pytest # Add the root tests directory to PYTHONPATH sys.path.append(str(Path(__file__).parent.parent.parent)) from fixtures.confluence_mocks import ( MOCK_COMMENTS_RESPONSE, MOCK_CQL_SEARCH_RESPONSE, MOCK_LABELS_RESPONSE, MOCK_PAGE_RESPONSE, MOCK_PAGES_FROM_SPACE_RESPONSE, MOCK_SPACES_RESPONSE, ) from mcp_atlassian.confluence.client import ConfluenceClient from mcp_atlassian.confluence.config import ConfluenceConfig from mcp_atlassian.utils.oauth import OAuthConfig from tests.utils.factories import AuthConfigFactory, ConfluencePageFactory from tests.utils.mocks import MockAtlassianClient, MockPreprocessor # ============================================================================ # Session-Scoped Confluence Data Fixtures # ============================================================================ @pytest.fixture(scope="session") def session_confluence_spaces(): """ Session-scoped fixture providing Confluence space definitions. This expensive-to-create data is cached for the entire test session to improve test performance. Returns: List[Dict[str, Any]]: Complete Confluence space definitions """ return [ { "id": 12345, "key": "TEST", "name": "Test Space", "type": "global", "status": "current", "description": {"plain": {"value": "Test space for unit tests"}}, "_links": { "webui": "/spaces/TEST", "self": "https://test.atlassian.net/wiki/rest/api/space/TEST", }, }, { "id": 12346, "key": "DEMO", "name": "Demo Space", "type": "global", "status": "current", "description": {"plain": {"value": "Demo space for testing"}}, "_links": { "webui": "/spaces/DEMO", "self": "https://test.atlassian.net/wiki/rest/api/space/DEMO", }, }, { "id": 12347, "key": "SAMPLE", "name": "Sample Space", "type": "personal", "status": "current", "description": {"plain": {"value": "Sample personal space"}}, "_links": { "webui": "/spaces/SAMPLE", "self": "https://test.atlassian.net/wiki/rest/api/space/SAMPLE", }, }, ] @pytest.fixture(scope="session") def session_confluence_content_types(): """ Session-scoped fixture providing Confluence content type definitions. Returns: List[Dict[str, Any]]: Mock Confluence content type data """ return [ {"name": "page", "type": "content"}, {"name": "blogpost", "type": "content"}, {"name": "comment", "type": "content"}, {"name": "attachment", "type": "content"}, {"name": "space", "type": "space"}, {"name": "user", "type": "user"}, ] @pytest.fixture(scope="session") def session_confluence_macros(): """ Session-scoped fixture providing Confluence macro definitions. Returns: List[Dict[str, Any]]: Mock Confluence macro data """ return [ {"name": "info", "hasBody": True, "bodyType": "rich-text"}, {"name": "warning", "hasBody": True, "bodyType": "rich-text"}, {"name": "note", "hasBody": True, "bodyType": "rich-text"}, {"name": "tip", "hasBody": True, "bodyType": "rich-text"}, {"name": "code", "hasBody": True, "bodyType": "plain-text"}, {"name": "toc", "hasBody": False}, {"name": "children", "hasBody": False}, {"name": "excerpt", "hasBody": True, "bodyType": "rich-text"}, {"name": "include", "hasBody": False}, {"name": "panel", "hasBody": True, "bodyType": "rich-text"}, ] # ============================================================================ # Configuration Fixtures # ============================================================================ @pytest.fixture def confluence_config_factory(): """ Factory for creating ConfluenceConfig instances with customizable options. Returns: Callable: Function that creates ConfluenceConfig instances Example: def test_config(confluence_config_factory): config = confluence_config_factory(url="https://custom.atlassian.net/wiki") assert "custom" in config.url """ def _create_config(**overrides): defaults = { "url": "https://example.atlassian.net/wiki", "auth_type": "basic", "username": "test_user", "api_token": "test_token", } config_data = {**defaults, **overrides} return ConfluenceConfig(**config_data) return _create_config @pytest.fixture def mock_config(confluence_config_factory): """ Create a standard mock ConfluenceConfig instance. This fixture provides a consistent ConfluenceConfig for tests that don't need custom configuration. Returns: ConfluenceConfig: Standard test configuration """ return confluence_config_factory() # ============================================================================ # Environment Fixtures # ============================================================================ @pytest.fixture def mock_env_vars(): """ Mock environment variables for testing. Note: This fixture is maintained for backward compatibility. Consider using the environment fixtures from root conftest.py. """ with patch.dict( "os.environ", { "CONFLUENCE_URL": "https://example.atlassian.net/wiki", "CONFLUENCE_USERNAME": "test_user", "CONFLUENCE_API_TOKEN": "test_token", }, ): yield @pytest.fixture def confluence_auth_environment(): """ Fixture providing Confluence-specific authentication environment. This sets up environment variables specifically for Confluence authentication and can be customized per test. """ auth_config = AuthConfigFactory.create_basic_auth_config() confluence_env = { "CONFLUENCE_URL": f"{auth_config['url']}/wiki", "CONFLUENCE_USERNAME": auth_config["username"], "CONFLUENCE_API_TOKEN": auth_config["api_token"], } with patch.dict(os.environ, confluence_env, clear=False): yield confluence_env # ============================================================================ # Mock Atlassian Client Fixtures # ============================================================================ @pytest.fixture def mock_atlassian_confluence( session_confluence_spaces, session_confluence_content_types ): """ Enhanced mock of the Atlassian Confluence client. This fixture provides a comprehensive mock that uses session-scoped data for improved performance and consistency. Args: session_confluence_spaces: Session-scoped space definitions session_confluence_content_types: Session-scoped content type data Returns: MagicMock: Fully configured mock Confluence client """ with patch("mcp_atlassian.confluence.client.Confluence") as mock: confluence_instance = mock.return_value # Use original mock data to maintain backward compatibility for existing tests confluence_instance.get_all_spaces.return_value = MOCK_SPACES_RESPONSE # Set up common return values using both legacy mocks and new factories confluence_instance.get_page_by_id.return_value = MOCK_PAGE_RESPONSE confluence_instance.get_page_by_title.return_value = MOCK_PAGE_RESPONSE confluence_instance.get_all_pages_from_space.return_value = ( MOCK_PAGES_FROM_SPACE_RESPONSE ) confluence_instance.get_page_comments.return_value = MOCK_COMMENTS_RESPONSE confluence_instance.get_page_labels.return_value = MOCK_LABELS_RESPONSE confluence_instance.cql.return_value = MOCK_CQL_SEARCH_RESPONSE # Enhanced responses using factories confluence_instance.create_page.return_value = ConfluencePageFactory.create( page_id="123456789", title="New Test Page" ) # Mock update_page to return None (as the actual method does) confluence_instance.update_page.return_value = None # Mock delete_page to return None confluence_instance.delete_page.return_value = None # Mock page history confluence_instance.get_page_history.return_value = { "results": [ { "version": {"number": 1}, "when": "2023-01-01T12:00:00.000Z", "by": {"displayName": "Test User"}, "message": "Initial version", } ] } # Mock page ancestors confluence_instance.get_page_ancestors.return_value = [ ConfluencePageFactory.create(page_id="parent123", title="Parent Page") ] # Mock page children confluence_instance.get_page_child_by_type.return_value = { "results": [ ConfluencePageFactory.create(page_id="child123", title="Child Page") ] } yield confluence_instance @pytest.fixture def enhanced_mock_confluence_client(): """ Enhanced mock Confluence client using the new factory system. This provides a more flexible mock that can be easily customized and integrates with the factory system. Returns: MagicMock: Enhanced mock Confluence client with factory integration """ return MockAtlassianClient.create_confluence_client() @pytest.fixture def mock_atlassian_confluence_with_session_data( session_confluence_spaces, session_confluence_content_types ): """ Alternative mock using session-scoped data for new tests. This fixture is recommended for new tests as it uses the efficient session-scoped data. Existing tests should continue using mock_atlassian_confluence for compatibility. Args: session_confluence_spaces: Session-scoped space definitions session_confluence_content_types: Session-scoped content type data Returns: MagicMock: Mock Confluence client with session-scoped data """ with patch("mcp_atlassian.confluence.client.Confluence") as mock: confluence_instance = mock.return_value # Use session-scoped data for improved performance confluence_instance.get_all_spaces.return_value = { "results": session_confluence_spaces, "size": len(session_confluence_spaces), } # Enhanced responses using factories confluence_instance.get_page_by_id.return_value = ConfluencePageFactory.create() confluence_instance.get_page_by_title.return_value = ( ConfluencePageFactory.create() ) confluence_instance.create_page.return_value = ConfluencePageFactory.create( page_id="123456789", title="New Test Page" ) # Use session data for content types confluence_instance.get_content_types.return_value = ( session_confluence_content_types ) yield confluence_instance # ============================================================================ # Preprocessor Fixtures # ============================================================================ @pytest.fixture def mock_preprocessor(): """ Mock the TextPreprocessor with enhanced functionality. This fixture provides a preprocessor mock that can be customized for testing different content processing scenarios. Returns: MagicMock: Mock preprocessor with common methods """ preprocessor_instance = MagicMock() # Default processing behavior preprocessor_instance.process_html_content.return_value = ( "<p>Processed HTML</p>", "Processed Markdown", ) # Additional processing methods preprocessor_instance.clean_html.return_value = "<p>Clean HTML</p>" preprocessor_instance.html_to_markdown.return_value = "# Markdown Content" preprocessor_instance.markdown_to_html.return_value = "<h1>HTML Content</h1>" yield preprocessor_instance @pytest.fixture def preprocessor_factory(): """ Factory for creating preprocessor mocks with different behaviors. Returns: Dict[str, Callable]: Factory functions for different preprocessor types Example: def test_preprocessing(preprocessor_factory): html_processor = preprocessor_factory["html_to_markdown"]() markdown_processor = preprocessor_factory["markdown_to_html"]() """ return { "html_to_markdown": MockPreprocessor.create_html_to_markdown, "markdown_to_html": MockPreprocessor.create_markdown_to_html, } # ============================================================================ # Client Instance Fixtures # ============================================================================ @pytest.fixture def oauth_confluence_client(mock_preprocessor): """ Create a ConfluenceClient instance configured for OAuth authentication. This fixture provides a Confluence client configured with OAuth settings for testing OAuth-specific functionality. Args: mock_preprocessor: Mock text preprocessor Returns: ConfluenceClient: OAuth-configured client instance """ # Create OAuth configuration oauth_config = OAuthConfig( client_id="test-client-id", client_secret="test-client-secret", redirect_uri="http://localhost:8080/callback", scope="read:confluence-content write:confluence-content", cloud_id="test-cloud-id", ) # Convert to ConfluenceConfig format (use .atlassian.net URL to make is_cloud return True) config = ConfluenceConfig( url="https://test.atlassian.net/wiki", auth_type="oauth", oauth_config=oauth_config, ) # Mock the OAuth session setup and Confluence client with patch( "mcp_atlassian.confluence.client.configure_oauth_session" ) as mock_oauth_session: with patch( "mcp_atlassian.confluence.client.Confluence" ) as mock_confluence_class: with patch( "mcp_atlassian.preprocessing.TextPreprocessor" ) as mock_text_preprocessor: # Mock OAuth session configuration to succeed mock_oauth_session.return_value = True mock_text_preprocessor.return_value = mock_preprocessor # Create the mock Confluence instance mock_confluence_instance = MagicMock() mock_confluence_class.return_value = mock_confluence_instance # Set up OAuth-specific mock responses mock_confluence_instance.get_all_spaces.return_value = ( MOCK_SPACES_RESPONSE ) mock_confluence_instance.get_page_by_id.return_value = ( MOCK_PAGE_RESPONSE ) mock_confluence_instance.create_page.return_value = ( ConfluencePageFactory.create( page_id="v2_123456789", title="OAuth Test Page" ) ) # Mock the session to have OAuth characteristics mock_session = MagicMock() mock_confluence_instance._session = mock_session # Create the client with OAuth config client = ConfluenceClient(config=config) client.confluence = mock_confluence_instance client.preprocessor = mock_preprocessor yield client @pytest.fixture def confluence_client(mock_config, mock_atlassian_confluence, mock_preprocessor): """ Create a ConfluenceClient instance with mocked dependencies. This fixture provides a fully functional ConfluenceClient with mocked Atlassian API calls and content preprocessing for testing. Args: mock_config: Mock configuration mock_atlassian_confluence: Mock Atlassian client mock_preprocessor: Mock text preprocessor Returns: ConfluenceClient: Configured client instance """ # Create a client with a mocked configuration with patch( "mcp_atlassian.preprocessing.TextPreprocessor" ) as mock_text_preprocessor: mock_text_preprocessor.return_value = mock_preprocessor client = ConfluenceClient(config=mock_config) # Replace the actual Confluence instance with our mock client.confluence = mock_atlassian_confluence # Replace the actual preprocessor with our mock client.preprocessor = mock_preprocessor yield client # ============================================================================ # Specialized Test Data Fixtures # ============================================================================ @pytest.fixture def make_confluence_page_with_content(): """ Factory fixture for creating Confluence pages with rich content. Returns: Callable: Function that creates page data with content Example: def test_page_content(make_confluence_page_with_content): page = make_confluence_page_with_content( title="Rich Page", content="<h1>Header</h1><p>Content</p>", labels=["test", "content"] ) """ def _create_page_with_content( title: str = "Test Page", content: str = "<p>Test content</p>", labels: list[str] = None, **overrides, ): labels = labels or ["test"] page = ConfluencePageFactory.create(title=title, **overrides) # Add rich content page["body"]["storage"]["value"] = content # Add labels page["metadata"] = { "labels": {"results": [{"name": label} for label in labels]} } # Add version info page["version"]["message"] = f"Updated {title}" return page return _create_page_with_content @pytest.fixture def make_confluence_search_results(): """ Factory fixture for creating Confluence search results. Returns: Callable: Function that creates CQL search results Example: def test_search(make_confluence_search_results): results = make_confluence_search_results( pages=["Page 1", "Page 2"], total=2 ) """ def _create_search_results(pages: list[str] = None, total: int = None, **overrides): if pages is None: pages = ["Test Page 1", "Test Page 2", "Test Page 3"] if total is None: total = len(pages) page_objects = [ ConfluencePageFactory.create(page_id=str(i), title=title) for i, title in enumerate(pages, 1) ] defaults = { "results": page_objects, "totalSize": total, "start": 0, "limit": 25, } return {**defaults, **overrides} return _create_search_results @pytest.fixture def make_confluence_space(): """ Factory fixture for creating Confluence spaces. Returns: Callable: Function that creates space data Example: def test_space(make_confluence_space): space = make_confluence_space( key="CUSTOM", name="Custom Space", type="personal" ) """ def _create_space( key: str = "TEST", name: str = "Test Space", space_type: str = "global", **overrides, ): defaults = { "id": 12345, "key": key, "name": name, "type": space_type, "status": "current", "description": {"plain": {"value": f"{name} for testing"}}, "_links": { "webui": f"/spaces/{key}", "self": f"https://test.atlassian.net/wiki/rest/api/space/{key}", }, } return {**defaults, **overrides} return _create_space # ============================================================================ # Integration Test Fixtures # ============================================================================ @pytest.fixture def confluence_integration_client(session_auth_configs): """ Create a ConfluenceClient for integration testing. This fixture creates a client that can be used for integration tests when real API credentials are available. Args: session_auth_configs: Session-scoped auth configurations Returns: Optional[ConfluenceClient]: Real client if credentials available, None otherwise """ # Check if integration test environment variables are set required_vars = ["CONFLUENCE_URL", "CONFLUENCE_USERNAME", "CONFLUENCE_API_TOKEN"] if not all(os.environ.get(var) for var in required_vars): pytest.skip("Integration test environment variables not set") config = ConfluenceConfig( url=os.environ["CONFLUENCE_URL"], auth_type="basic", username=os.environ["CONFLUENCE_USERNAME"], api_token=os.environ["CONFLUENCE_API_TOKEN"], ) return ConfluenceClient(config=config) # ============================================================================ # Parameterized Fixtures # ============================================================================ @pytest.fixture def parametrized_confluence_content_type(request): """ Parametrized fixture for testing with different Confluence content types. Use with pytest.mark.parametrize to test functionality across different content types. Example: @pytest.mark.parametrize("parametrized_confluence_content_type", ["page", "blogpost"], indirect=True) def test_content_types(parametrized_confluence_content_type): # Test runs once for each content type pass """ content_type = request.param return ConfluencePageFactory.create(type=content_type) @pytest.fixture def parametrized_confluence_space_type(request): """ Parametrized fixture for testing with different Confluence space types. Use with pytest.mark.parametrize to test functionality across different space types. """ space_type = request.param return { "key": "TEST", "name": "Test Space", "type": space_type, "status": "current", }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ArconixForge/mcp-atlassian'

If you have feedback or need assistance with the MCP directory API, please join our Discord server