This commit is contained in:
1 parent
4db3f45164
commit
d25c04cdd2
175 files changed
+56243
-15
No files matched your search
@@ -0,0 +1,616 @@
|
||||
import asyncio
|
||||
import hashlib
|
||||
import json
|
||||
from datetime import UTC, datetime
|
||||
from pathlib import Path
|
||||
|
||||
import httpx2
|
||||
import yaml
|
||||
from mcp import Client
|
||||
from mcp.client.streamable_http import streamable_http_client
|
||||
from mcp.types import TextContent
|
||||
|
||||
from gewu.adapters.git_repository import GitCanonicalRepository
|
||||
from gewu.adapters.sqlite_identity import SQLiteIdentityStore
|
||||
from gewu.adapters.sqlite_search import SQLiteSearchIndex
|
||||
from gewu.application.reading import CanonicalNodeReader
|
||||
from gewu.application.search import CanonicalSearchService
|
||||
from gewu.bootstrap.app import create_app
|
||||
from gewu.domain.contracts import EdgeEndpoint, NodeType, TypedEdge
|
||||
from tests.domain.proposal_fixtures import NODE, PROJECTS, SOURCE_REVISION
|
||||
|
||||
|
||||
def test_authenticated_search_scope_through_http_and_mcp(tmp_path: Path) -> None:
|
||||
async def exercise() -> None:
|
||||
repository = GitCanonicalRepository(tmp_path / "knowledge.git")
|
||||
project_key = hashlib.sha256(NODE.project_id.encode()).hexdigest()
|
||||
node_key = hashlib.sha256(NODE.node_id.encode()).hexdigest()
|
||||
source_key = hashlib.sha256(SOURCE_REVISION.revision_id.encode()).hexdigest()
|
||||
stale_node = NODE.model_copy(
|
||||
update={
|
||||
"as_of": datetime(2024, 1, 1, tzinfo=UTC),
|
||||
"stale_after": datetime(2025, 1, 1, tzinfo=UTC),
|
||||
}
|
||||
)
|
||||
problem_node = NODE.model_copy(
|
||||
update={
|
||||
"node_id": "dome:problem-browse-fixture",
|
||||
"node_type": NodeType.PROBLEM,
|
||||
"title": "Problem browse fixture",
|
||||
"value": None,
|
||||
"unit": None,
|
||||
}
|
||||
)
|
||||
problem_key = hashlib.sha256(problem_node.node_id.encode()).hexdigest()
|
||||
older_source = SOURCE_REVISION.model_copy(
|
||||
update={
|
||||
"source_id": "dome:older-pilot-report",
|
||||
"revision_id": "dome:older-pilot-report:r1",
|
||||
"title": "Older pilot installation report",
|
||||
"captured_at": datetime(2025, 1, 1, tzinfo=UTC),
|
||||
}
|
||||
)
|
||||
related = NODE.model_copy(update={"node_id": "z:dome-related"})
|
||||
related_key = hashlib.sha256(related.node_id.encode()).hexdigest()
|
||||
related_two = NODE.model_copy(update={"node_id": "zz:dome-related-two"})
|
||||
related_two_key = hashlib.sha256(related_two.node_id.encode()).hexdigest()
|
||||
edge = TypedEdge(
|
||||
edge_id="dome:related-edge",
|
||||
edge_type="compared_with",
|
||||
source=EdgeEndpoint(kind="node", id=NODE.node_id),
|
||||
target=EdgeEndpoint(kind="node", id=related.node_id),
|
||||
)
|
||||
edge_two = TypedEdge(
|
||||
edge_id="dome:related-edge-two",
|
||||
edge_type="compared_with",
|
||||
source=EdgeEndpoint(kind="node", id=NODE.node_id),
|
||||
target=EdgeEndpoint(kind="node", id=related_two.node_id),
|
||||
)
|
||||
repository.publish_files(
|
||||
expected_head=repository.head(),
|
||||
files={
|
||||
f"projects/{project_key}/nodes/{node_key}.yaml": yaml.safe_dump(
|
||||
stale_node.model_dump(mode="json")
|
||||
).encode(),
|
||||
f"projects/{project_key}/nodes/{problem_key}.yaml": yaml.safe_dump(
|
||||
problem_node.model_dump(mode="json")
|
||||
).encode(),
|
||||
f"projects/{project_key}/nodes/{related_key}.yaml": yaml.safe_dump(
|
||||
related.model_dump(mode="json")
|
||||
).encode(),
|
||||
f"projects/{project_key}/nodes/{related_two_key}.yaml": yaml.safe_dump(
|
||||
related_two.model_dump(mode="json")
|
||||
).encode(),
|
||||
(
|
||||
f"sources/{hashlib.sha256(SOURCE_REVISION.source_id.encode()).hexdigest()}/"
|
||||
f"{source_key}.yaml"
|
||||
): yaml.safe_dump(SOURCE_REVISION.model_dump(mode="json")).encode(),
|
||||
(
|
||||
f"sources/{hashlib.sha256(older_source.source_id.encode()).hexdigest()}/"
|
||||
f"{hashlib.sha256(older_source.revision_id.encode()).hexdigest()}.yaml"
|
||||
): yaml.safe_dump(older_source.model_dump(mode="json")).encode(),
|
||||
f"edges/{hashlib.sha256(edge.edge_id.encode()).hexdigest()}.yaml": yaml.safe_dump(
|
||||
edge.model_dump(mode="json")
|
||||
).encode(),
|
||||
(
|
||||
f"edges/{hashlib.sha256(edge_two.edge_id.encode()).hexdigest()}.yaml"
|
||||
): yaml.safe_dump(edge_two.model_dump(mode="json")).encode(),
|
||||
},
|
||||
message="Add searchable node",
|
||||
index_directory=tmp_path / "publishing",
|
||||
)
|
||||
identities = SQLiteIdentityStore(tmp_path / "identity.sqlite3")
|
||||
token = identities.provision(
|
||||
credential_id="search-key",
|
||||
principal_id="search-reader",
|
||||
roles=("viewer",),
|
||||
project_ids=("dome-performance",),
|
||||
)
|
||||
search = CanonicalSearchService(
|
||||
index=SQLiteSearchIndex(tmp_path / "search.sqlite3", repository),
|
||||
project_ids_provider=lambda: PROJECTS.keys(),
|
||||
canonical_head=repository.head,
|
||||
)
|
||||
app = create_app(
|
||||
identity_provider=identities,
|
||||
node_reader=CanonicalNodeReader(repository),
|
||||
search_service=search,
|
||||
)
|
||||
async with app.router.lifespan_context(app):
|
||||
async with httpx2.AsyncClient(
|
||||
transport=httpx2.ASGITransport(app=app),
|
||||
base_url="http://localhost",
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
) as http:
|
||||
diagnostic_unbuilt = await http.get("/v1/diagnostics?projects=dome-performance")
|
||||
assert diagnostic_unbuilt.status_code == 200
|
||||
unbuilt_state = diagnostic_unbuilt.json()
|
||||
assert unbuilt_state["index_state"] == "not_built"
|
||||
assert unbuilt_state["index_revision"] is None
|
||||
assert unbuilt_state["index_schema_version"] == 4
|
||||
result = await http.post(
|
||||
"/v1/search",
|
||||
json={
|
||||
"query": "400 visitors",
|
||||
"scope": {
|
||||
"projects": ["dome-performance"],
|
||||
"include_shared": False,
|
||||
},
|
||||
"limit": 10,
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert result.status_code == 200, result.text
|
||||
envelope = result.json()
|
||||
assert envelope["retrieval_mode"] == "keyword"
|
||||
assert envelope["index_revision"] == repository.head()
|
||||
assert envelope["data"][0]["node"]["node_id"] == NODE.node_id
|
||||
assert envelope["citations"][0]["quote"] == NODE.claims[0].evidence[0].quote
|
||||
listed_sources = await http.post(
|
||||
"/v1/sources",
|
||||
json={
|
||||
"scope": {"projects": ["dome-performance"], "include_shared": False},
|
||||
"limit": 10,
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert listed_sources.status_code == 200, listed_sources.text
|
||||
assert listed_sources.json()["data"][0]["source"]["revision_id"] == (
|
||||
SOURCE_REVISION.revision_id
|
||||
)
|
||||
source_page_one = await http.post(
|
||||
"/v1/sources",
|
||||
json={
|
||||
"scope": {"projects": ["dome-performance"], "include_shared": False},
|
||||
"limit": 1,
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
source_cursor = source_page_one.json()["next_cursor"]
|
||||
assert source_cursor
|
||||
source_page_two = await http.post(
|
||||
"/v1/sources",
|
||||
json={
|
||||
"scope": {"projects": ["dome-performance"], "include_shared": False},
|
||||
"limit": 1,
|
||||
"cursor": source_cursor,
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert source_page_two.status_code == 200
|
||||
assert source_page_two.json()["data"][0]["source"]["revision_id"] == (
|
||||
older_source.revision_id
|
||||
)
|
||||
denied_sources = await http.post(
|
||||
"/v1/sources",
|
||||
json={
|
||||
"scope": {"projects": ["interactive-video"], "include_shared": False},
|
||||
"limit": 10,
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert denied_sources.status_code == 403
|
||||
problem_filter = await http.post(
|
||||
"/v1/search",
|
||||
json={
|
||||
"query": "unmatched phrase",
|
||||
"scope": {"projects": ["dome-performance"], "include_shared": False},
|
||||
"node_types": ["problem"],
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert problem_filter.status_code == 200, problem_filter.text
|
||||
assert problem_filter.json()["data"] == []
|
||||
problem_browse = await http.post(
|
||||
"/v1/search",
|
||||
json={
|
||||
"query": "",
|
||||
"scope": {"projects": ["dome-performance"], "include_shared": False},
|
||||
"node_types": ["problem"],
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert problem_browse.status_code == 200, problem_browse.text
|
||||
assert [row["node"]["node_id"] for row in problem_browse.json()["data"]] == [
|
||||
problem_node.node_id
|
||||
]
|
||||
unbounded_browse = await http.post(
|
||||
"/v1/search",
|
||||
json={
|
||||
"query": "",
|
||||
"scope": {"projects": ["dome-performance"], "include_shared": False},
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert unbounded_browse.status_code == 422
|
||||
stale_filter = await http.post(
|
||||
"/v1/search",
|
||||
json={
|
||||
"query": "400 visitors",
|
||||
"scope": {"projects": ["dome-performance"], "include_shared": False},
|
||||
"stale_only": True,
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert stale_filter.status_code == 200, stale_filter.text
|
||||
assert stale_filter.json()["data"][0]["node"]["node_id"] == NODE.node_id
|
||||
facet_filter = await http.post(
|
||||
"/v1/search",
|
||||
json={
|
||||
"query": "400 visitors",
|
||||
"scope": {"projects": ["dome-performance"], "include_shared": False},
|
||||
"tags": ["attendance"],
|
||||
"audience": ["product"],
|
||||
"use_for": ["comparison"],
|
||||
"domain": ["production"],
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert facet_filter.status_code == 200, facet_filter.text
|
||||
assert facet_filter.json()["data"][0]["node"]["node_id"] == NODE.node_id
|
||||
unmatched_facet = await http.post(
|
||||
"/v1/search",
|
||||
json={
|
||||
"query": "400 visitors",
|
||||
"scope": {"projects": ["dome-performance"], "include_shared": False},
|
||||
"domain": ["unrelated-domain"],
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert unmatched_facet.status_code == 200, unmatched_facet.text
|
||||
assert unmatched_facet.json()["data"] == []
|
||||
diagnostic_current = await http.get("/v1/diagnostics?projects=dome-performance")
|
||||
assert diagnostic_current.status_code == 200
|
||||
current_state = diagnostic_current.json()
|
||||
assert current_state["index_state"] == "current"
|
||||
assert current_state["index_revision"] == repository.head()
|
||||
assert "proposal_submit" not in current_state["authorized_operations"]
|
||||
projects = await http.get("/v1/projects")
|
||||
assert projects.status_code == 200
|
||||
assert projects.json() == ["dome-performance"]
|
||||
page_one = await http.post(
|
||||
"/v1/search",
|
||||
json={
|
||||
"query": "visitors",
|
||||
"scope": {"projects": ["dome-performance"], "include_shared": False},
|
||||
"limit": 1,
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert page_one.status_code == 200
|
||||
cursor = page_one.json()["next_cursor"]
|
||||
assert cursor
|
||||
page_two = await http.post(
|
||||
"/v1/search",
|
||||
json={
|
||||
"query": "visitors",
|
||||
"scope": {"projects": ["dome-performance"], "include_shared": False},
|
||||
"limit": 1,
|
||||
"cursor": cursor,
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert page_two.status_code == 200
|
||||
assert (
|
||||
page_two.json()["data"][0]["node"]["node_id"]
|
||||
!= page_one.json()["data"][0]["node"]["node_id"]
|
||||
)
|
||||
reused_cursor = await http.post(
|
||||
"/v1/search",
|
||||
json={
|
||||
"query": "400 visitors",
|
||||
"scope": {"projects": ["dome-performance"], "include_shared": False},
|
||||
"limit": 1,
|
||||
"cursor": cursor,
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert reused_cursor.status_code == 422
|
||||
changed_facet_cursor = await http.post(
|
||||
"/v1/search",
|
||||
json={
|
||||
"query": "visitors",
|
||||
"scope": {"projects": ["dome-performance"], "include_shared": False},
|
||||
"node_types": ["problem"],
|
||||
"limit": 1,
|
||||
"cursor": cursor,
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert changed_facet_cursor.status_code == 422
|
||||
changed_tag_cursor = await http.post(
|
||||
"/v1/search",
|
||||
json={
|
||||
"query": "visitors",
|
||||
"scope": {"projects": ["dome-performance"], "include_shared": False},
|
||||
"tags": ["attendance"],
|
||||
"limit": 1,
|
||||
"cursor": cursor,
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert changed_tag_cursor.status_code == 422
|
||||
adjacent = await http.post(
|
||||
"/v1/neighbors",
|
||||
json={
|
||||
"node_id": NODE.node_id,
|
||||
"scope": {"projects": ["dome-performance"], "include_shared": False},
|
||||
"limit": 1,
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert adjacent.status_code == 200, adjacent.text
|
||||
assert adjacent.json()["data"][0]["neighbor"]["id"] == related.node_id
|
||||
neighbor_cursor = adjacent.json()["next_cursor"]
|
||||
assert neighbor_cursor
|
||||
adjacent_two = await http.post(
|
||||
"/v1/neighbors",
|
||||
json={
|
||||
"node_id": NODE.node_id,
|
||||
"scope": {"projects": ["dome-performance"], "include_shared": False},
|
||||
"limit": 1,
|
||||
"cursor": neighbor_cursor,
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert adjacent_two.status_code == 200
|
||||
assert adjacent_two.json()["data"][0]["neighbor"]["id"] == related_two.node_id
|
||||
assert adjacent_two.json()["next_cursor"] is None
|
||||
assert {citation["quote"] for citation in adjacent.json()["citations"]} == {
|
||||
NODE.claims[0].evidence[0].quote
|
||||
}
|
||||
newer = NODE.model_copy(update={"node_id": "new:unrelated-revision-node"})
|
||||
newer_key = hashlib.sha256(newer.node_id.encode()).hexdigest()
|
||||
repository.publish_files(
|
||||
expected_head=repository.head(),
|
||||
files={
|
||||
f"projects/{project_key}/nodes/{newer_key}.yaml": yaml.safe_dump(
|
||||
newer.model_dump(mode="json")
|
||||
).encode()
|
||||
},
|
||||
message="Advance canonical index for cursor-expiry check",
|
||||
index_directory=tmp_path / "publishing",
|
||||
)
|
||||
diagnostic_lag = await http.get("/v1/diagnostics?projects=dome-performance")
|
||||
assert diagnostic_lag.status_code == 200
|
||||
lag_state = diagnostic_lag.json()
|
||||
assert lag_state["index_state"] == "lagging"
|
||||
assert lag_state["index_revision"] != repository.head()
|
||||
assert lag_state["canonical_revision"] == repository.head()
|
||||
expired_search = await http.post(
|
||||
"/v1/search",
|
||||
json={
|
||||
"query": "visitors",
|
||||
"scope": {"projects": ["dome-performance"], "include_shared": False},
|
||||
"limit": 1,
|
||||
"cursor": cursor,
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert expired_search.status_code == 409
|
||||
expired_neighbors = await http.post(
|
||||
"/v1/neighbors",
|
||||
json={
|
||||
"node_id": NODE.node_id,
|
||||
"scope": {"projects": ["dome-performance"], "include_shared": False},
|
||||
"limit": 1,
|
||||
"cursor": neighbor_cursor,
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert expired_neighbors.status_code == 409
|
||||
too_small = await http.post(
|
||||
"/v1/search",
|
||||
json={
|
||||
"query": "visitors",
|
||||
"scope": {
|
||||
"projects": ["dome-performance"],
|
||||
"include_shared": False,
|
||||
},
|
||||
"budget_tokens": 256,
|
||||
},
|
||||
)
|
||||
assert too_small.status_code == 413
|
||||
dated_confidence_search = await http.post(
|
||||
"/v1/search",
|
||||
json={
|
||||
"query": "visitors",
|
||||
"scope": {
|
||||
"projects": ["dome-performance"],
|
||||
"include_shared": False,
|
||||
},
|
||||
"confidences": ["medium"],
|
||||
"as_of_from": "2024-01-01",
|
||||
"as_of_to": "2024-12-31",
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert dated_confidence_search.status_code == 200, dated_confidence_search.text
|
||||
assert [
|
||||
row["node"]["node_id"] for row in dated_confidence_search.json()["data"]
|
||||
] == [NODE.node_id]
|
||||
filtered_page = await http.post(
|
||||
"/v1/search",
|
||||
json={
|
||||
"query": "visitors",
|
||||
"scope": {
|
||||
"projects": ["dome-performance"],
|
||||
"include_shared": False,
|
||||
},
|
||||
"confidences": ["medium"],
|
||||
"limit": 1,
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert filtered_page.status_code == 200
|
||||
filter_cursor = filtered_page.json()["next_cursor"]
|
||||
assert filter_cursor
|
||||
changed_filter_page = await http.post(
|
||||
"/v1/search",
|
||||
json={
|
||||
"query": "visitors",
|
||||
"scope": {
|
||||
"projects": ["dome-performance"],
|
||||
"include_shared": False,
|
||||
},
|
||||
"confidences": ["low"],
|
||||
"limit": 1,
|
||||
"cursor": filter_cursor,
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert changed_filter_page.status_code == 422
|
||||
forbidden = await http.post(
|
||||
"/v1/search",
|
||||
json={
|
||||
"query": "visitors",
|
||||
"scope": {
|
||||
"projects": ["interactive-video"],
|
||||
"include_shared": False,
|
||||
},
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert forbidden.status_code == 403
|
||||
async with Client(
|
||||
streamable_http_client("http://localhost/mcp", http_client=http)
|
||||
) as mcp:
|
||||
tools = await mcp.list_tools()
|
||||
assert {tool.name for tool in tools.tools} == {
|
||||
"diagnostics",
|
||||
"get_node",
|
||||
"node_history",
|
||||
"get_source",
|
||||
"list_projects",
|
||||
"search",
|
||||
"neighbors",
|
||||
"list_sources",
|
||||
}
|
||||
listed = await mcp.call_tool("list_projects", {})
|
||||
assert not listed.is_error
|
||||
assert isinstance(listed.content[0], TextContent)
|
||||
assert json.loads(listed.content[0].text) == ["dome-performance"]
|
||||
searched = await mcp.call_tool(
|
||||
"search",
|
||||
{
|
||||
"query": "400 visitors",
|
||||
"projects": ["dome-performance"],
|
||||
"include_shared": False,
|
||||
"all_projects": False,
|
||||
"limit": 10,
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert not searched.is_error
|
||||
assert isinstance(searched.content[0], TextContent)
|
||||
assert (
|
||||
json.loads(searched.content[0].text)["data"][0]["node"]["node_id"]
|
||||
== NODE.node_id
|
||||
)
|
||||
dated_confidence_tool = await mcp.call_tool(
|
||||
"search",
|
||||
{
|
||||
"query": "visitors",
|
||||
"projects": ["dome-performance"],
|
||||
"include_shared": False,
|
||||
"all_projects": False,
|
||||
"limit": 10,
|
||||
"budget_tokens": 5000,
|
||||
"confidences": ["medium"],
|
||||
"as_of_from": "2024-01-01",
|
||||
"as_of_to": "2024-12-31",
|
||||
},
|
||||
)
|
||||
assert not dated_confidence_tool.is_error
|
||||
assert [
|
||||
row["node"]["node_id"]
|
||||
for row in json.loads(dated_confidence_tool.content[0].text)["data"]
|
||||
] == [NODE.node_id]
|
||||
filtered_search = await mcp.call_tool(
|
||||
"search",
|
||||
{
|
||||
"query": "unmatched phrase",
|
||||
"projects": ["dome-performance"],
|
||||
"include_shared": False,
|
||||
"all_projects": False,
|
||||
"limit": 10,
|
||||
"budget_tokens": 5000,
|
||||
"node_types": ["problem"],
|
||||
},
|
||||
)
|
||||
assert not filtered_search.is_error
|
||||
assert json.loads(filtered_search.content[0].text)["data"] == []
|
||||
browsed_problems = await mcp.call_tool(
|
||||
"search",
|
||||
{
|
||||
"query": "",
|
||||
"projects": ["dome-performance"],
|
||||
"include_shared": False,
|
||||
"all_projects": False,
|
||||
"limit": 10,
|
||||
"budget_tokens": 5000,
|
||||
"node_types": ["problem"],
|
||||
},
|
||||
)
|
||||
assert not browsed_problems.is_error
|
||||
assert [
|
||||
row["node"]["node_id"]
|
||||
for row in json.loads(browsed_problems.content[0].text)["data"]
|
||||
] == [problem_node.node_id]
|
||||
facet_search = await mcp.call_tool(
|
||||
"search",
|
||||
{
|
||||
"query": "400 visitors",
|
||||
"projects": ["dome-performance"],
|
||||
"include_shared": False,
|
||||
"all_projects": False,
|
||||
"limit": 10,
|
||||
"budget_tokens": 5000,
|
||||
"tags": ["attendance"],
|
||||
"audience": ["product"],
|
||||
"use_for": ["comparison"],
|
||||
"domain": ["production"],
|
||||
},
|
||||
)
|
||||
assert not facet_search.is_error
|
||||
assert (
|
||||
json.loads(facet_search.content[0].text)["data"][0]["node"]["node_id"]
|
||||
== NODE.node_id
|
||||
)
|
||||
listed_sources_tool = await mcp.call_tool(
|
||||
"list_sources",
|
||||
{
|
||||
"projects": ["dome-performance"],
|
||||
"include_shared": False,
|
||||
"all_projects": False,
|
||||
"limit": 10,
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert not listed_sources_tool.is_error
|
||||
assert (
|
||||
json.loads(listed_sources_tool.content[0].text)["data"][0]["source"][
|
||||
"revision_id"
|
||||
]
|
||||
== SOURCE_REVISION.revision_id
|
||||
)
|
||||
adjacent_tool = await mcp.call_tool(
|
||||
"neighbors",
|
||||
{
|
||||
"node_id": NODE.node_id,
|
||||
"projects": ["dome-performance"],
|
||||
"include_shared": False,
|
||||
"all_projects": False,
|
||||
"edge_types": [],
|
||||
"limit": 10,
|
||||
"budget_tokens": 5000,
|
||||
},
|
||||
)
|
||||
assert not adjacent_tool.is_error
|
||||
assert isinstance(adjacent_tool.content[0], TextContent)
|
||||
assert (
|
||||
json.loads(adjacent_tool.content[0].text)["data"][0]["neighbor"]["id"]
|
||||
== related.node_id
|
||||
)
|
||||
|
||||
asyncio.run(exercise())
|
||||
Reference in new issue
Block a user