We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/sooperset/mcp-atlassian'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
"""Tests for the Jira config module."""
import os
from unittest.mock import patch
import pytest
from mcp_atlassian.jira.config import JiraConfig
from mcp_atlassian.utils.oauth import OAuthConfig
def test_from_env_basic_auth():
"""Test that from_env correctly loads basic auth configuration."""
with patch.dict(
os.environ,
{
"JIRA_URL": "https://test.atlassian.net",
"JIRA_USERNAME": "test_username",
"JIRA_API_TOKEN": "test_token",
},
clear=True,
):
config = JiraConfig.from_env()
assert config.url == "https://test.atlassian.net"
assert config.auth_type == "basic"
assert config.username == "test_username"
assert config.api_token == "test_token"
assert config.personal_token is None
assert config.ssl_verify is True
def test_from_env_token_auth():
"""Test that from_env correctly loads token auth configuration."""
with patch.dict(
os.environ,
{
"JIRA_URL": "https://jira.example.com",
"JIRA_PERSONAL_TOKEN": "test_personal_token",
"JIRA_SSL_VERIFY": "false",
},
clear=True,
):
config = JiraConfig.from_env()
assert config.url == "https://jira.example.com"
assert config.auth_type == "pat"
assert config.username is None
assert config.api_token is None
assert config.personal_token == "test_personal_token"
assert config.ssl_verify is False
def test_from_env_missing_url():
"""Test that from_env raises ValueError when URL is missing."""
original_env = os.environ.copy()
try:
os.environ.clear()
with pytest.raises(
ValueError, match="Missing required JIRA_URL environment variable"
):
JiraConfig.from_env()
finally:
# Restore original environment
os.environ.clear()
os.environ.update(original_env)
def test_from_env_missing_cloud_auth():
"""Test that from_env raises ValueError when cloud auth credentials are missing."""
with patch.dict(
os.environ,
{
"JIRA_URL": "https://test.atlassian.net", # Cloud URL
},
clear=True,
):
with pytest.raises(
ValueError,
match="Cloud authentication requires JIRA_USERNAME and JIRA_API_TOKEN",
):
JiraConfig.from_env()
def test_from_env_missing_server_auth():
"""Test that from_env raises ValueError when server auth credentials are missing."""
with patch.dict(
os.environ,
{
"JIRA_URL": "https://jira.example.com", # Server URL
},
clear=True,
):
with pytest.raises(
ValueError,
match="Server/Data Center authentication requires JIRA_PERSONAL_TOKEN",
):
JiraConfig.from_env()
def test_is_cloud():
"""Test that is_cloud property returns correct value."""
# Arrange & Act - Cloud URL
config = JiraConfig(
url="https://example.atlassian.net",
auth_type="basic",
username="test",
api_token="test",
)
# Assert
assert config.is_cloud is True
# Arrange & Act - Server URL
config = JiraConfig(
url="https://jira.example.com",
auth_type="pat",
personal_token="test",
)
# Assert
assert config.is_cloud is False
# Arrange & Act - Localhost URL (Data Center/Server)
config = JiraConfig(
url="http://localhost:8080",
auth_type="pat",
personal_token="test",
)
# Assert
assert config.is_cloud is False
# Arrange & Act - IP localhost URL (Data Center/Server)
config = JiraConfig(
url="http://127.0.0.1:8080",
auth_type="pat",
personal_token="test",
)
# Assert
assert config.is_cloud is False
def test_from_env_proxy_settings():
"""Test that from_env correctly loads proxy environment variables."""
with patch.dict(
os.environ,
{
"JIRA_URL": "https://test.atlassian.net",
"JIRA_USERNAME": "test_username",
"JIRA_API_TOKEN": "test_token",
"HTTP_PROXY": "http://proxy.example.com:8080",
"HTTPS_PROXY": "https://proxy.example.com:8443",
"SOCKS_PROXY": "socks5://user:pass@proxy.example.com:1080",
"NO_PROXY": "localhost,127.0.0.1",
},
clear=True,
):
config = JiraConfig.from_env()
assert config.http_proxy == "http://proxy.example.com:8080"
assert config.https_proxy == "https://proxy.example.com:8443"
assert config.socks_proxy == "socks5://user:pass@proxy.example.com:1080"
assert config.no_proxy == "localhost,127.0.0.1"
# Service-specific overrides
with patch.dict(
os.environ,
{
"JIRA_URL": "https://test.atlassian.net",
"JIRA_USERNAME": "test_username",
"JIRA_API_TOKEN": "test_token",
"JIRA_HTTP_PROXY": "http://jira-proxy.example.com:8080",
"JIRA_HTTPS_PROXY": "https://jira-proxy.example.com:8443",
"JIRA_SOCKS_PROXY": "socks5://user:pass@jira-proxy.example.com:1080",
"JIRA_NO_PROXY": "localhost,127.0.0.1,.internal.example.com",
},
clear=True,
):
config = JiraConfig.from_env()
assert config.http_proxy == "http://jira-proxy.example.com:8080"
assert config.https_proxy == "https://jira-proxy.example.com:8443"
assert config.socks_proxy == "socks5://user:pass@jira-proxy.example.com:1080"
assert config.no_proxy == "localhost,127.0.0.1,.internal.example.com"
def test_is_cloud_oauth_with_cloud_id():
"""Test that is_cloud returns True for OAuth with cloud_id regardless of URL."""
from mcp_atlassian.utils.oauth import BYOAccessTokenOAuthConfig
# OAuth with cloud_id and no URL - should be Cloud
oauth_config = BYOAccessTokenOAuthConfig(
cloud_id="test-cloud-id", access_token="test-token"
)
config = JiraConfig(
url=None, # URL can be None in Multi-Cloud OAuth mode
auth_type="oauth",
oauth_config=oauth_config,
)
assert config.is_cloud is True
# OAuth with cloud_id and server URL - should still be Cloud
config = JiraConfig(
url="https://jira.example.com", # Server-like URL
auth_type="oauth",
oauth_config=oauth_config,
)
assert config.is_cloud is True
def test_from_env_pat_priority_over_oauth(caplog):
"""Test that PAT takes priority over OAuth for Server/DC (fixes #824)."""
with patch.dict(
os.environ,
{
"JIRA_URL": "https://jira.example.com", # Server/DC URL
"JIRA_PERSONAL_TOKEN": "test_pat",
"ATLASSIAN_OAUTH_ENABLE": "true", # OAuth also enabled
},
clear=True,
):
config = JiraConfig.from_env()
assert config.auth_type == "pat"
assert config.personal_token == "test_pat"
# Verify warning is logged when both are configured
assert "Both PAT and OAuth configured for Server/DC. Using PAT." in caplog.text
def test_from_env_with_client_cert():
"""Test loading config with client certificate settings from environment."""
with patch.dict(
"os.environ",
{
"JIRA_URL": "https://jira.example.com",
"JIRA_PERSONAL_TOKEN": "test_pat",
"JIRA_CLIENT_CERT": "/path/to/cert.pem",
"JIRA_CLIENT_KEY": "/path/to/key.pem",
"JIRA_CLIENT_KEY_PASSWORD": "secret",
},
clear=True,
):
config = JiraConfig.from_env()
assert config.url == "https://jira.example.com"
assert config.client_cert == "/path/to/cert.pem"
assert config.client_key == "/path/to/key.pem"
assert config.client_key_password == "secret"
def test_from_env_without_client_cert():
"""Test loading config without client certificate settings."""
with patch.dict(
"os.environ",
{
"JIRA_URL": "https://jira.example.com",
"JIRA_PERSONAL_TOKEN": "test_pat",
},
clear=True,
):
config = JiraConfig.from_env()
assert config.url == "https://jira.example.com"
assert config.client_cert is None
assert config.client_key is None
assert config.client_key_password is None
def test_jira_config_timeout_from_env():
"""Test that timeout is read from JIRA_TIMEOUT env var."""
with patch.dict(
os.environ,
{
"JIRA_URL": "https://jira.example.com",
"JIRA_PERSONAL_TOKEN": "test_pat",
"JIRA_TIMEOUT": "120",
},
clear=True,
):
config = JiraConfig.from_env()
assert config.timeout == 120
def test_jira_config_timeout_default():
"""Test that timeout defaults to 75 when no env var is set."""
with patch.dict(
os.environ,
{
"JIRA_URL": "https://jira.example.com",
"JIRA_PERSONAL_TOKEN": "test_pat",
},
clear=True,
):
config = JiraConfig.from_env()
assert config.timeout == 75
def test_jira_config_is_cloud_false_for_dc_oauth():
"""Test is_cloud returns False when DC OAuth is configured."""
dc_oauth = OAuthConfig(
client_id="c",
client_secret="s",
redirect_uri="r",
scope="sc",
base_url="https://jira.corp.com",
)
config = JiraConfig(
url="https://jira.corp.com",
auth_type="oauth",
oauth_config=dc_oauth,
)
assert config.is_cloud is False
def test_jira_config_is_cloud_true_for_cloud_oauth():
"""Test is_cloud returns True when Cloud OAuth is configured."""
cloud_oauth = OAuthConfig(
client_id="c",
client_secret="s",
redirect_uri="r",
scope="sc",
cloud_id="cloud-123",
)
config = JiraConfig(
url="https://test.atlassian.net",
auth_type="oauth",
oauth_config=cloud_oauth,
)
assert config.is_cloud is True
def test_jira_config_is_auth_configured_dc_oauth():
"""Test is_auth_configured returns True for DC OAuth with client_id + secret."""
dc_oauth = OAuthConfig(
client_id="c",
client_secret="s",
redirect_uri="r",
scope="sc",
base_url="https://jira.corp.com",
)
config = JiraConfig(
url="https://jira.corp.com",
auth_type="oauth",
oauth_config=dc_oauth,
)
assert config.is_auth_configured() is True
def test_from_env_oauth_enable_no_url():
"""Test BYOT OAuth mode — ATLASSIAN_OAUTH_ENABLE=true without URL."""
with patch.dict(
os.environ,
{"ATLASSIAN_OAUTH_ENABLE": "true"},
clear=True,
):
config = JiraConfig.from_env()
assert config.auth_type == "oauth"
assert config.url == ""
assert config.is_cloud is False
def test_from_env_oauth_enable_no_url_with_cloud_id():
"""Test BYOT OAuth mode — ATLASSIAN_OAUTH_ENABLE=true with cloud_id but no URL."""
with patch.dict(
os.environ,
{
"ATLASSIAN_OAUTH_ENABLE": "true",
"ATLASSIAN_OAUTH_CLOUD_ID": "test-cloud-id",
},
clear=True,
):
config = JiraConfig.from_env()
assert config.auth_type == "oauth"
assert config.is_cloud is True
def test_from_env_oauth_enable_with_cloud_url():
"""Test BYOT OAuth mode — ATLASSIAN_OAUTH_ENABLE=true with Cloud URL."""
with patch.dict(
os.environ,
{
"ATLASSIAN_OAUTH_ENABLE": "true",
"JIRA_URL": "https://test.atlassian.net",
},
clear=True,
):
config = JiraConfig.from_env()
assert config.url == "https://test.atlassian.net"
assert config.auth_type == "oauth"
assert config.is_cloud is True
def test_from_env_oauth_enable_with_server_url():
"""Test BYOT OAuth mode — ATLASSIAN_OAUTH_ENABLE=true with Server URL."""
with patch.dict(
os.environ,
{
"ATLASSIAN_OAUTH_ENABLE": "true",
"JIRA_URL": "https://jira.example.com",
},
clear=True,
):
config = JiraConfig.from_env()
assert config.url == "https://jira.example.com"
assert config.auth_type == "oauth"
assert config.is_cloud is False