test(backend): add Jira MCP E2E fakes

This commit is contained in:
Anish Sarkar 2026-05-08 00:15:08 +05:30
parent e6297e2e40
commit 369f0aaff3
5 changed files with 206 additions and 26 deletions

View file

@ -0,0 +1,15 @@
{
"site": {
"cloud_id": "fake-jira-cloud-001",
"name": "SurfSense E2E Atlassian",
"url": "https://surfsense-e2e.atlassian.net"
},
"issues": [
{
"id": "fake-jira-issue-canary-001",
"key": "E2E-101",
"summary": "E2E Canary Jira Issue",
"description": "This Jira issue proves live MCP tool calls work end-to-end. SURFSENSE_E2E_CANARY_TOKEN_JIRA_001"
}
]
}

View file

@ -0,0 +1,136 @@
"""Strict Jira MCP OAuth/tool fakes for Playwright E2E."""
from __future__ import annotations
import json
from pathlib import Path
from types import SimpleNamespace
from typing import Any
from tests.e2e.fakes import mcp_oauth_runtime, mcp_runtime
_FIXTURE_PATH = Path(__file__).parent / "fixtures" / "jira_issues.json"
_AUTHORIZATION_URL = "https://mcp.atlassian.com/v1/authorize"
_REGISTRATION_URL = "https://cf.mcp.atlassian.com/v1/register"
_TOKEN_URL = "https://cf.mcp.atlassian.com/v1/token"
_MCP_URL = "https://mcp.atlassian.com/v1/mcp"
_CLIENT_ID = "fake-jira-mcp-client-id"
_CLIENT_SECRET = "fake-jira-mcp-client-secret"
_ACCESS_TOKEN = "fake-jira-mcp-access-token"
_REFRESH_TOKEN = "fake-jira-mcp-refresh-token"
_OAUTH_CODE = "fake-jira-oauth-code"
def _load_fixture() -> dict[str, Any]:
with _FIXTURE_PATH.open() as f:
return json.load(f)
_FIXTURE = _load_fixture()
def _issue_text(issue: dict[str, Any]) -> str:
return (
f"{issue['key']} {issue['summary']}\n"
f"id: {issue['id']}\n"
f"description: {issue['description']}"
)
async def _list_tools() -> SimpleNamespace:
return SimpleNamespace(
tools=[
SimpleNamespace(
name="getAccessibleAtlassianResources",
description="Get Jira sites accessible to the authenticated Atlassian user.",
inputSchema={
"type": "object",
"properties": {},
"required": [],
},
),
SimpleNamespace(
name="searchJiraIssuesUsingJql",
description="Search Jira issues using a Jira Query Language expression.",
inputSchema={
"type": "object",
"properties": {
"jql": {
"type": "string",
"description": "JQL query used to search Jira issues.",
},
"maxResults": {
"type": "integer",
"description": "Maximum number of matching issues to return.",
},
},
"required": ["jql"],
},
),
]
)
async def _call_tool(
tool_name: str, arguments: dict[str, Any] | None = None
) -> SimpleNamespace:
arguments = arguments or {}
site = _FIXTURE["site"]
issue = _FIXTURE["issues"][0]
if tool_name == "getAccessibleAtlassianResources":
if arguments:
raise ValueError(
f"Unexpected Jira getAccessibleAtlassianResources args: {arguments!r}"
)
text = (
f"{site['name']}\n"
f"cloud_id: {site['cloud_id']}\n"
f"url: {site['url']}"
)
return SimpleNamespace(content=[SimpleNamespace(text=text)])
if tool_name == "searchJiraIssuesUsingJql":
jql = str(arguments.get("jql", ""))
if issue["summary"].lower() not in jql.lower() and issue[
"key"
].lower() not in jql.lower():
raise ValueError(f"Unexpected Jira JQL query: {jql!r}")
text = _issue_text(issue)
return SimpleNamespace(content=[SimpleNamespace(text=text)])
raise NotImplementedError(f"Unexpected Jira MCP tool call: {tool_name!r}")
def install(active_patches: list[Any]) -> None:
"""Register Jira MCP OAuth/tool handlers with the shared dispatchers."""
del active_patches
mcp_oauth_runtime.register_service(
mcp_url=_MCP_URL,
discovery_metadata={
"issuer": "https://mcp.atlassian.com",
"authorization_endpoint": _AUTHORIZATION_URL,
"token_endpoint": _TOKEN_URL,
"registration_endpoint": _REGISTRATION_URL,
"code_challenge_methods_supported": ["S256"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"response_types_supported": ["code"],
},
client_id=_CLIENT_ID,
client_secret=_CLIENT_SECRET,
token_endpoint=_TOKEN_URL,
registration_endpoint=_REGISTRATION_URL,
oauth_code=_OAUTH_CODE,
access_token=_ACCESS_TOKEN,
refresh_token=_REFRESH_TOKEN,
scope="read:jira-work read:me write:jira-work",
redirect_uri_substring="/api/v1/auth/mcp/jira/connector/callback",
)
mcp_runtime.register(
url=_MCP_URL,
expected_bearer=_ACCESS_TOKEN,
list_tools=_list_tools,
call_tool=_call_tool,
)

View file

@ -6,7 +6,8 @@ import json
from pathlib import Path from pathlib import Path
from types import SimpleNamespace from types import SimpleNamespace
from typing import Any from typing import Any
from unittest.mock import patch
from tests.e2e.fakes import mcp_oauth_runtime, mcp_runtime
_FIXTURE_PATH = Path(__file__).parent / "fixtures" / "linear_issues.json" _FIXTURE_PATH = Path(__file__).parent / "fixtures" / "linear_issues.json"
@ -262,29 +263,43 @@ def _fake_streamablehttp_client(
return _FakeStreamableHttpClient(url, headers=headers, **kwargs) return _FakeStreamableHttpClient(url, headers=headers, **kwargs)
async def _list_tools() -> SimpleNamespace:
return await _FakeClientSession(object(), object()).list_tools()
async def _call_tool(tool_name: str, arguments: dict[str, Any]) -> SimpleNamespace:
return await _FakeClientSession(object(), object()).call_tool(
tool_name, arguments=arguments
)
def install(active_patches: list[Any]) -> None: def install(active_patches: list[Any]) -> None:
"""Patch production Linear MCP OAuth/tool boundaries.""" """Register Linear MCP OAuth/tool handlers with the shared dispatchers."""
targets = [ del active_patches
( mcp_oauth_runtime.register_service(
"app.services.mcp_oauth.discovery.discover_oauth_metadata", mcp_url=_MCP_URL,
_fake_discover_oauth_metadata, discovery_metadata={
), "issuer": "https://mcp.linear.app",
("app.services.mcp_oauth.discovery.register_client", _fake_register_client), "authorization_endpoint": _AUTHORIZATION_URL,
( "token_endpoint": _TOKEN_URL,
"app.services.mcp_oauth.discovery.exchange_code_for_tokens", "registration_endpoint": _REGISTRATION_URL,
_fake_exchange_code_for_tokens, "code_challenge_methods_supported": ["S256"],
), "grant_types_supported": ["authorization_code", "refresh_token"],
( "response_types_supported": ["code"],
"app.services.mcp_oauth.discovery.refresh_access_token", },
_fake_refresh_access_token, client_id=_CLIENT_ID,
), client_secret=_CLIENT_SECRET,
( token_endpoint=_TOKEN_URL,
"app.agents.new_chat.tools.mcp_tool.streamablehttp_client", registration_endpoint=_REGISTRATION_URL,
_fake_streamablehttp_client, oauth_code=_OAUTH_CODE,
), access_token=_ACCESS_TOKEN,
("app.agents.new_chat.tools.mcp_tool.ClientSession", _FakeClientSession), refresh_token=_REFRESH_TOKEN,
] scope="read write",
for target, replacement in targets: redirect_uri_substring="/api/v1/auth/mcp/linear/connector/callback",
p = patch(target, replacement) )
p.start() mcp_runtime.register(
active_patches.append(p) url=_MCP_URL,
expected_bearer=_ACCESS_TOKEN,
list_tools=_list_tools,
call_tool=_call_tool,
)

View file

@ -90,7 +90,10 @@ from unittest.mock import patch # noqa: E402
from app.app import app # noqa: E402 from app.app import app # noqa: E402
from tests.e2e.fakes import ( # noqa: E402 from tests.e2e.fakes import ( # noqa: E402
embeddings as _fake_embeddings, embeddings as _fake_embeddings,
jira_module as _fake_jira_module,
linear_module as _fake_linear_module, linear_module as _fake_linear_module,
mcp_oauth_runtime as _fake_mcp_oauth_runtime,
mcp_runtime as _fake_mcp_runtime,
native_google as _fake_native_google, native_google as _fake_native_google,
notion_module as _fake_notion_module, notion_module as _fake_notion_module,
) )
@ -164,6 +167,9 @@ _fake_embeddings.install(_active_patches)
_fake_native_google.install(_active_patches) _fake_native_google.install(_active_patches)
_fake_notion_module.install(_active_patches) _fake_notion_module.install(_active_patches)
_fake_linear_module.install(_active_patches) _fake_linear_module.install(_active_patches)
_fake_jira_module.install(_active_patches)
_fake_mcp_runtime.install(_active_patches)
_fake_mcp_oauth_runtime.install(_active_patches)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------

View file

@ -75,6 +75,10 @@ from unittest.mock import patch # noqa: E402
from app.celery_app import celery_app # noqa: E402 from app.celery_app import celery_app # noqa: E402
from tests.e2e.fakes import ( # noqa: E402 from tests.e2e.fakes import ( # noqa: E402
embeddings as _fake_embeddings, embeddings as _fake_embeddings,
jira_module as _fake_jira_module,
linear_module as _fake_linear_module,
mcp_oauth_runtime as _fake_mcp_oauth_runtime,
mcp_runtime as _fake_mcp_runtime,
native_google as _fake_native_google, native_google as _fake_native_google,
notion_module as _fake_notion_module, notion_module as _fake_notion_module,
) )
@ -146,6 +150,10 @@ _patch_llm_bindings()
_fake_embeddings.install(_active_patches) _fake_embeddings.install(_active_patches)
_fake_native_google.install(_active_patches) _fake_native_google.install(_active_patches)
_fake_notion_module.install(_active_patches) _fake_notion_module.install(_active_patches)
_fake_linear_module.install(_active_patches)
_fake_jira_module.install(_active_patches)
_fake_mcp_runtime.install(_active_patches)
_fake_mcp_oauth_runtime.install(_active_patches)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------