netx/tests/mcp_remote/topology/test_move_nodes.py
oliver 52679a7cfc MCP: serve tools from the API process over Streamable HTTP at /mcp
- netx_api/mcp_remote: ops / topology / bizMonitor tools, Bearer auth,
  per-tool RBAC filtering, ?groups= filter, readOnlyHint and group meta.
- Mount the official MCP Streamable HTTP app at /mcp (stateless);
  AuthAuditMiddleware is pure ASGI and skips /mcp.
- Let uvicorn chunk MCP responses; some Windows LSPs rewrite
  Content-Length replies on */mcp* paths into broken chunked framing.
- NETX_MCP_ALLOWED_HOSTS opts into DNS-rebinding checks (off by default
  so LAN and reverse-proxy hosts work; Bearer is the gate).
- Remove the three stdio Python MCP packages; tests moved to /mcp,
  including real-TCP framing regressions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 21:52:08 +08:00

36 lines
1.2 KiB
Python

"""layoutTopologyView action=move_nodes registration + validation."""
from __future__ import annotations
from netx_api.mcp_remote.topology.http_tools import HTTP_MCP_TOOLS, _move_topology_view_nodes
from netx_api.mcp_remote.topology.layout_tool import list_layout_catalog
def test_move_nodes_in_layout_schema_and_catalog() -> None:
tool = next(t for t in HTTP_MCP_TOOLS if t["name"] == "layoutTopologyView")
enum = tool["inputSchema"]["properties"]["action"]["enum"]
assert "move_nodes" in enum
assert "sink_nodes" not in enum
cat = list_layout_catalog()
assert "move_nodes" in cat["actions"]
assert "sink_nodes" not in cat["actions"]
def test_move_nodes_requires_ids_and_views() -> None:
assert _move_topology_view_nodes({}).get("error") == "view_id_and_source_view_id_required"
assert (
_move_topology_view_nodes(
{
"view_id": "a",
"source_view_id": "a",
"params": {"fabric_node_ids": ["x"]},
}
).get("error")
== "source_and_dest_must_differ"
)
assert (
_move_topology_view_nodes(
{"view_id": "a", "source_view_id": "b", "params": {}}
).get("error")
== "fabric_node_ids_required"
)