"""E2E tests for image retrieval tools against Cloud instances.
Tests jira_get_issue_images and confluence_get_page_images
via MCP tool calls with real Jira Cloud/Confluence Cloud instances.
"""
from __future__ import annotations
import json
import os
from typing import Any
from unittest.mock import patch
import pytest
from fastmcp import Client
from fastmcp.client import FastMCPTransport
from mcp.types import CallToolResult, ImageContent, TextContent
from mcp_atlassian.servers import main_mcp
from .conftest import CloudInstanceInfo
pytestmark = [pytest.mark.cloud_e2e, pytest.mark.anyio]
async def call_tool(
client: Client, tool_name: str, arguments: dict[str, Any]
) -> CallToolResult:
"""Helper to call tools via the MCP client."""
return await client.call_tool(tool_name, arguments)
@pytest.fixture
def cloud_env(cloud_instance: CloudInstanceInfo) -> dict[str, str]:
"""Environment variables for configuring MCP server against Cloud."""
return {
"JIRA_URL": cloud_instance.jira_url,
"JIRA_USERNAME": cloud_instance.username,
"JIRA_API_TOKEN": cloud_instance.api_token,
"CONFLUENCE_URL": cloud_instance.confluence_url,
"CONFLUENCE_USERNAME": cloud_instance.username,
"CONFLUENCE_API_TOKEN": cloud_instance.api_token,
"READ_ONLY_MODE": "false",
"TOOLSETS": "all",
}
@pytest.fixture
async def mcp_client(cloud_env: dict[str, str]) -> Any:
"""MCP client connected to the server configured for Cloud."""
with patch.dict(os.environ, cloud_env, clear=False):
transport = FastMCPTransport(main_mcp)
client = Client(transport=transport)
async with client as connected_client:
yield connected_client
class TestJiraGetIssueImages:
"""Tests for jira_get_issue_images tool against Cloud."""
async def test_jira_get_issue_images(
self,
mcp_client: Client,
cloud_image_issue: str,
) -> None:
"""Image issue returns ImageContent with valid PNG."""
result = await call_tool(
mcp_client,
"jira_get_issue_images",
{"issue_key": cloud_image_issue},
)
assert not result.is_error
assert len(result.content) >= 2
# First: JSON summary
assert isinstance(result.content[0], TextContent)
summary = json.loads(result.content[0].text)
assert summary["success"] is True
assert summary["downloaded"] >= 1
# Second: image data
assert isinstance(result.content[1], ImageContent)
assert result.content[1].mimeType.startswith("image/")
assert len(result.content[1].data) > 0
async def test_jira_get_issue_images_no_images(
self,
mcp_client: Client,
cloud_instance: CloudInstanceInfo,
) -> None:
"""Issue without images returns informational message."""
result = await call_tool(
mcp_client,
"jira_get_issue_images",
{"issue_key": cloud_instance.test_issue_key},
)
assert not result.is_error
assert len(result.content) >= 1
assert isinstance(result.content[0], TextContent)
text = result.content[0].text
if text.startswith("{"):
data = json.loads(text)
assert data["downloaded"] == 0
else:
assert "no image" in text.lower() or "No image" in text
class TestConfluenceGetPageImages:
"""Tests for confluence_get_page_images tool against Cloud."""
async def test_confluence_get_page_images(
self,
mcp_client: Client,
cloud_image_page: str,
) -> None:
"""Image page returns ImageContent with valid PNG."""
result = await call_tool(
mcp_client,
"confluence_get_page_images",
{"content_id": cloud_image_page},
)
assert not result.is_error
assert len(result.content) >= 2
# First: JSON summary
assert isinstance(result.content[0], TextContent)
summary = json.loads(result.content[0].text)
assert summary["success"] is True
assert summary["downloaded"] >= 1
# Second: image data
assert isinstance(result.content[1], ImageContent)
assert result.content[1].mimeType.startswith("image/")
assert len(result.content[1].data) > 0
async def test_confluence_get_page_images_no_images(
self,
mcp_client: Client,
cloud_instance: CloudInstanceInfo,
) -> None:
"""Page without images returns informational message."""
result = await call_tool(
mcp_client,
"confluence_get_page_images",
{"content_id": cloud_instance.test_page_id},
)
assert not result.is_error
assert len(result.content) >= 1
assert isinstance(result.content[0], TextContent)
text = result.content[0].text
if text.startswith("{"):
data = json.loads(text)
assert data["downloaded"] == 0
else:
assert "no image" in text.lower() or "No image" in text
class TestAcImageConversion:
"""Verify ac:image macro is converted to markdown in page body."""
async def test_ac_image_converted_to_markdown(
self,
mcp_client: Client,
cloud_image_page: str,
) -> None:
"""Page with ac:image macro shows markdown image reference."""
result = await call_tool(
mcp_client,
"confluence_get_page",
{"page_id": cloud_image_page},
)
assert not result.is_error
assert isinstance(result.content[0], TextContent)
page_text = result.content[0].text
assert "![test.png]" in page_text or "