mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-04 02:04:06 +00:00
fix(mcp): call the Mintlify search endpoint the documentation moved to (#12578)
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
"""Tests for the Prowler documentation search tool.
|
||||
|
||||
Mintlify moved the docs search to a new endpoint that answers with page
|
||||
sections, so a result is a part of a page and has to read as one.
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from fastmcp import Client
|
||||
|
||||
SEARCH = "/api/search/prowler"
|
||||
|
||||
|
||||
def search_match(
|
||||
path: str = "getting-started/installation",
|
||||
*,
|
||||
header: str = "Requirements",
|
||||
breadcrumbs: tuple[str, ...] = ("Get Started", "Installation"),
|
||||
anchor: str | None = "requirements",
|
||||
score: float = 4.9,
|
||||
):
|
||||
"""One match as Mintlify answers with it: a section of a page, not the page."""
|
||||
return {
|
||||
"page": path,
|
||||
"header": header,
|
||||
"content": "Prowler runs on Python 3.9 or later.",
|
||||
"metadata": {
|
||||
"title": header,
|
||||
"breadcrumbs": list(breadcrumbs),
|
||||
"icon": "",
|
||||
"hash": anchor,
|
||||
"openapi": "",
|
||||
},
|
||||
"score": score,
|
||||
}
|
||||
|
||||
|
||||
def stub_search_hit(docs_router, *matches):
|
||||
"""Serve the search endpoint, with one default match when none are given."""
|
||||
if not matches:
|
||||
matches = (search_match(),)
|
||||
return docs_router.add("POST", SEARCH, json={"results": list(matches)})
|
||||
|
||||
|
||||
async def test_search_returns_the_matching_sections(mcp_root_server, docs_router):
|
||||
"""Every field of a result, since the shape of one changed with the endpoint."""
|
||||
stub_search_hit(docs_router)
|
||||
|
||||
async with Client(mcp_root_server) as client:
|
||||
result = await client.call_tool("prowler_docs_search", {"term": "install"})
|
||||
|
||||
match = result.data[0]
|
||||
assert match["path"] == "getting-started/installation"
|
||||
# The page's title, so a result reads as more than the heading it matched.
|
||||
assert match["title"] == "Installation"
|
||||
assert match["section"] == "Requirements"
|
||||
assert match["breadcrumbs"] == ["Get Started", "Installation"]
|
||||
assert match["excerpt"] == "Prowler runs on Python 3.9 or later."
|
||||
assert match["score"] == 4.9
|
||||
# Anchored: a match is a section, and the page it is on can be a long one.
|
||||
assert match["url"] == (
|
||||
"https://docs.prowler.com/getting-started/installation#requirements"
|
||||
)
|
||||
|
||||
|
||||
async def test_the_search_query_is_sent_as_the_api_expects_it(
|
||||
mcp_root_server, docs_router
|
||||
):
|
||||
"""The endpoint takes a POST body, not the payload the old one took."""
|
||||
stub_search_hit(docs_router)
|
||||
|
||||
async with Client(mcp_root_server) as client:
|
||||
await client.call_tool("prowler_docs_search", {"term": "install"})
|
||||
|
||||
request = docs_router.request_for("POST", SEARCH)
|
||||
assert json.loads(request.content) == {"query": "install", "filters": {}}
|
||||
|
||||
|
||||
async def test_a_section_with_no_anchor_links_to_the_page(mcp_root_server, docs_router):
|
||||
"""The API sends "" for a page's first section and null for pages without anchors."""
|
||||
stub_search_hit(
|
||||
docs_router,
|
||||
search_match(anchor=""),
|
||||
search_match(path="getting-started/requirements", anchor=None),
|
||||
)
|
||||
|
||||
async with Client(mcp_root_server) as client:
|
||||
result = await client.call_tool("prowler_docs_search", {"term": "install"})
|
||||
|
||||
assert result.data[0]["url"] == (
|
||||
"https://docs.prowler.com/getting-started/installation"
|
||||
)
|
||||
assert result.data[1]["url"] == (
|
||||
"https://docs.prowler.com/getting-started/requirements"
|
||||
)
|
||||
|
||||
|
||||
async def test_page_size_caps_a_response_the_api_did_not_size(
|
||||
mcp_root_server, docs_router
|
||||
):
|
||||
"""The endpoint takes no size argument, so the cap has to be applied here."""
|
||||
stub_search_hit(docs_router, *(search_match() for _ in range(6)))
|
||||
|
||||
async with Client(mcp_root_server) as client:
|
||||
result = await client.call_tool(
|
||||
"prowler_docs_search", {"term": "install", "page_size": 2}
|
||||
)
|
||||
|
||||
assert len(result.data) == 2
|
||||
Reference in New Issue
Block a user