Skip to main content
Glama

MCP Atlassian

by ArconixForge
conftest.py17.4 kB
""" Test fixtures for Jira unit tests. This module provides specialized fixtures for testing Jira-related functionality. It builds upon the root conftest.py fixtures and provides Jira-specific mocks, configurations, and utilities with efficient session-scoped caching. """ import os from unittest.mock import MagicMock, patch import pytest from mcp_atlassian.jira.client import JiraClient from mcp_atlassian.jira.config import JiraConfig from tests.utils.factories import AuthConfigFactory, JiraIssueFactory from tests.utils.mocks import MockAtlassianClient # ============================================================================ # Session-Scoped Jira Data Fixtures # ============================================================================ @pytest.fixture(scope="session") def session_jira_field_definitions(): """ Session-scoped fixture providing Jira field definitions. This expensive-to-create data is cached for the entire test session to improve test performance. Returns: List[Dict[str, Any]]: Complete Jira field definitions """ return [ {"id": "summary", "name": "Summary", "schema": {"type": "string"}}, {"id": "description", "name": "Description", "schema": {"type": "string"}}, {"id": "issuetype", "name": "Issue Type", "schema": {"type": "issuetype"}}, {"id": "status", "name": "Status", "schema": {"type": "status"}}, {"id": "priority", "name": "Priority", "schema": {"type": "priority"}}, { "id": "labels", "name": "Labels", "schema": {"type": "array", "items": "string"}, }, {"id": "assignee", "name": "Assignee", "schema": {"type": "user"}}, {"id": "reporter", "name": "Reporter", "schema": {"type": "user"}}, {"id": "created", "name": "Created", "schema": {"type": "datetime"}}, {"id": "updated", "name": "Updated", "schema": {"type": "datetime"}}, { "id": "fixVersions", "name": "Fix Version/s", "schema": {"type": "array", "items": "version"}, }, { "id": "customfield_10010", "name": "Epic Link", "schema": { "type": "string", "custom": "com.pyxis.greenhopper.jira:gh-epic-link", }, }, { "id": "customfield_10011", "name": "Epic Name", "schema": { "type": "string", "custom": "com.pyxis.greenhopper.jira:gh-epic-label", }, }, { "id": "customfield_10012", "name": "Story Points", "schema": {"type": "number"}, }, { "id": "customfield_10013", "name": "Sprint", "schema": {"type": "array", "items": "string"}, }, { "id": "components", "name": "Component/s", "schema": {"type": "array", "items": "component"}, }, { "id": "resolution", "name": "Resolution", "schema": {"type": "resolution"}, }, { "id": "resolutiondate", "name": "Resolved", "schema": {"type": "datetime"}, }, { "id": "workratio", "name": "Work Ratio", "schema": {"type": "number"}, }, { "id": "timeestimate", "name": "Remaining Estimate", "schema": {"type": "timetracking"}, }, { "id": "timespent", "name": "Time Spent", "schema": {"type": "timetracking"}, }, { "id": "timeoriginalestimate", "name": "Original Estimate", "schema": {"type": "timetracking"}, }, ] @pytest.fixture(scope="session") def session_jira_projects(): """ Session-scoped fixture providing Jira project definitions. Returns: List[Dict[str, Any]]: Mock Jira project data """ return [ { "id": "10000", "key": "TEST", "name": "Test Project", "projectTypeKey": "software", "lead": {"displayName": "Test Lead"}, "description": "Test project for unit tests", }, { "id": "10001", "key": "DEMO", "name": "Demo Project", "projectTypeKey": "business", "lead": {"displayName": "Demo Lead"}, "description": "Demo project for testing", }, { "id": "10002", "key": "SAMPLE", "name": "Sample Project", "projectTypeKey": "service_desk", "lead": {"displayName": "Sample Lead"}, "description": "Sample project with service desk", }, ] @pytest.fixture(scope="session") def session_jira_issue_types(): """ Session-scoped fixture providing Jira issue type definitions. Returns: List[Dict[str, Any]]: Mock Jira issue type data """ return [ {"id": "1", "name": "Bug", "iconUrl": "bug.png", "subtask": False}, {"id": "2", "name": "Task", "iconUrl": "task.png", "subtask": False}, {"id": "3", "name": "Story", "iconUrl": "story.png", "subtask": False}, {"id": "4", "name": "Epic", "iconUrl": "epic.png", "subtask": False}, {"id": "5", "name": "Sub-task", "iconUrl": "subtask.png", "subtask": True}, { "id": "6", "name": "Improvement", "iconUrl": "improvement.png", "subtask": False, }, { "id": "7", "name": "New Feature", "iconUrl": "newfeature.png", "subtask": False, }, ] # ============================================================================ # Configuration Fixtures # ============================================================================ @pytest.fixture def jira_config_factory(): """ Factory for creating JiraConfig instances with customizable options. Returns: Callable: Function that creates JiraConfig instances Example: def test_config(jira_config_factory): config = jira_config_factory(url="https://custom.atlassian.net") assert config.url == "https://custom.atlassian.net" """ def _create_config(**overrides): defaults = { "url": "https://test.atlassian.net", "auth_type": "basic", "username": "test_username", "api_token": "test_token", } config_data = {**defaults, **overrides} return JiraConfig(**config_data) return _create_config @pytest.fixture def mock_config(jira_config_factory): """ Create a standard mock JiraConfig instance. This fixture provides a consistent JiraConfig for tests that don't need custom configuration. Returns: JiraConfig: Standard test configuration """ return jira_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, { "JIRA_URL": "https://test.atlassian.net", "JIRA_USERNAME": "test_username", "JIRA_API_TOKEN": "test_token", }, clear=True, # Clear existing environment variables ): yield @pytest.fixture def jira_auth_environment(): """ Fixture providing Jira-specific authentication environment. This sets up environment variables specifically for Jira authentication and can be customized per test. """ auth_config = AuthConfigFactory.create_basic_auth_config() jira_env = { "JIRA_URL": auth_config["url"], "JIRA_USERNAME": auth_config["username"], "JIRA_API_TOKEN": auth_config["api_token"], } with patch.dict(os.environ, jira_env, clear=False): yield jira_env # ============================================================================ # Mock Atlassian Client Fixtures # ============================================================================ @pytest.fixture def mock_atlassian_jira( session_jira_field_definitions, session_jira_projects, session_jira_issue_types ): """ Enhanced mock of the Atlassian Jira client. This fixture provides a comprehensive mock that uses session-scoped data for improved performance and consistency. Args: session_jira_field_definitions: Session-scoped field definitions session_jira_projects: Session-scoped project data session_jira_issue_types: Session-scoped issue type data Returns: MagicMock: Fully configured mock Jira client """ mock_jira = MagicMock() # Use session-scoped data for consistent responses mock_jira.get_all_fields.return_value = session_jira_field_definitions mock_jira.projects.return_value = session_jira_projects mock_jira.issue_types.return_value = session_jira_issue_types # Set up common method returns using factory mock_jira.myself.return_value = { "accountId": "test-account-id", "displayName": "Test User", } mock_jira.get_issue.return_value = JiraIssueFactory.create() # Search results mock_jira.jql.return_value = { "issues": [ JiraIssueFactory.create("TEST-1"), JiraIssueFactory.create("TEST-2"), JiraIssueFactory.create("TEST-3"), ], "total": 3, "startAt": 0, "maxResults": 50, } # Issue creation mock_jira.create_issue.return_value = JiraIssueFactory.create() # Issue update (returns None like real API) mock_jira.update_issue.return_value = None # Worklog operations mock_jira.get_issue_worklog.return_value = { "worklogs": [ { "id": "10000", "timeSpent": "3h", "timeSpentSeconds": 10800, "comment": "Test work", "started": "2023-01-01T09:00:00.000+0000", "author": {"displayName": "Test User"}, } ] } # Comments mock_jira.get_issue_comments.return_value = { "comments": [ { "id": "10000", "body": "Test comment", "author": {"displayName": "Test User"}, "created": "2023-01-01T12:00:00.000+0000", } ] } yield mock_jira @pytest.fixture def enhanced_mock_jira_client(): """ Enhanced mock Jira 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 Jira client with factory integration """ return MockAtlassianClient.create_jira_client() # ============================================================================ # Client Instance Fixtures # ============================================================================ @pytest.fixture def jira_client(mock_config, mock_atlassian_jira): """ Create a JiraClient instance with mocked dependencies. This fixture provides a fully functional JiraClient with mocked Atlassian API calls for testing. Args: mock_config: Mock configuration mock_atlassian_jira: Mock Atlassian client Returns: JiraClient: Configured client instance """ with patch("atlassian.Jira") as mock_jira_class: mock_jira_class.return_value = mock_atlassian_jira client = JiraClient(config=mock_config) # Replace the actual Jira instance with our mock client.jira = mock_atlassian_jira yield client @pytest.fixture def jira_fetcher(mock_config, mock_atlassian_jira): """ Create a JiraFetcher instance with mocked dependencies. Note: This fixture is maintained for backward compatibility. Args: mock_config: Mock configuration mock_atlassian_jira: Mock Atlassian client Returns: JiraFetcher: Configured fetcher instance """ from mcp_atlassian.jira import JiraFetcher with patch("atlassian.Jira") as mock_jira_class: mock_jira_class.return_value = mock_atlassian_jira fetcher = JiraFetcher(config=mock_config) # Replace the actual Jira instance with our mock fetcher.jira = mock_atlassian_jira yield fetcher # ============================================================================ # Specialized Test Data Fixtures # ============================================================================ @pytest.fixture def make_jira_issue_with_worklog(): """ Factory fixture for creating Jira issues with worklog data. Returns: Callable: Function that creates issue data with worklog Example: def test_worklog(make_jira_issue_with_worklog): issue = make_jira_issue_with_worklog( key="TEST-123", worklog_hours=5, worklog_comment="Development work" ) """ def _create_issue_with_worklog( key: str = "TEST-123", worklog_hours: int = 3, worklog_comment: str = "Test work", **overrides, ): issue = JiraIssueFactory.create(key, **overrides) issue["fields"]["worklog"] = { "worklogs": [ { "id": "10000", "timeSpent": f"{worklog_hours}h", "timeSpentSeconds": worklog_hours * 3600, "comment": worklog_comment, "started": "2023-01-01T09:00:00.000+0000", "author": {"displayName": "Test User"}, } ] } return issue return _create_issue_with_worklog @pytest.fixture def make_jira_search_results(): """ Factory fixture for creating Jira search results. Returns: Callable: Function that creates JQL search results Example: def test_search(make_jira_search_results): results = make_jira_search_results( issues=["TEST-1", "TEST-2"], total=2 ) """ def _create_search_results( issues: list[str] = None, total: int = None, **overrides ): if issues is None: issues = ["TEST-1", "TEST-2", "TEST-3"] if total is None: total = len(issues) issue_objects = [JiraIssueFactory.create(key) for key in issues] defaults = { "issues": issue_objects, "total": total, "startAt": 0, "maxResults": 50, } return {**defaults, **overrides} return _create_search_results # ============================================================================ # Integration Test Fixtures # ============================================================================ @pytest.fixture def jira_integration_client(session_auth_configs): """ Create a JiraClient 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[JiraClient]: Real client if credentials available, None otherwise """ # Check if integration test environment variables are set required_vars = ["JIRA_URL", "JIRA_USERNAME", "JIRA_API_TOKEN"] if not all(os.environ.get(var) for var in required_vars): pytest.skip("Integration test environment variables not set") config = JiraConfig( url=os.environ["JIRA_URL"], auth_type="basic", username=os.environ["JIRA_USERNAME"], api_token=os.environ["JIRA_API_TOKEN"], ) return JiraClient(config=config) # ============================================================================ # Parameterized Fixtures # ============================================================================ @pytest.fixture def parametrized_jira_issue_type(request): """ Parametrized fixture for testing with different Jira issue types. Use with pytest.mark.parametrize to test functionality across different issue types. Example: @pytest.mark.parametrize("parametrized_jira_issue_type", ["Bug", "Task", "Story"], indirect=True) def test_issue_types(parametrized_jira_issue_type): # Test runs once for each issue type pass """ issue_type = request.param return JiraIssueFactory.create(fields={"issuetype": {"name": issue_type}}) @pytest.fixture def parametrized_jira_status(request): """ Parametrized fixture for testing with different Jira statuses. Use with pytest.mark.parametrize to test functionality across different issue statuses. """ status = request.param return JiraIssueFactory.create(fields={"status": {"name": status}})

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