mirror of
https://github.com/hansjone/netx.git
synced 2026-10-10 22:03:22 +08:00
Trim public tools to 14, expose only kept layout actions/recipes, merge Fabric queries, drop createTopologyView/listTopologyViews, and align skill/docs with the live catalog. Co-authored-by: Cursor <cursoragent@cursor.com>
84 lines
3.4 KiB
Python
84 lines
3.4 KiB
Python
"""Tests for stage-2 pin_beam + soft_petals actions."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from netx_topology_mcp.layout_ops import LayoutParams, build_state_from_nodes_edges
|
|
from netx_topology_mcp.layout_ops.pin_beam import pin_beam_rigid
|
|
from netx_topology_mcp.layout_ops.soft_petals import soft_petals_greedy
|
|
from netx_topology_mcp.layout_tool import ACTIONS, run_layout_on_graph
|
|
|
|
|
|
def _starish():
|
|
"""Two cores + two AN hubs each with a few stubs (positions intentionally messy)."""
|
|
nodes = [
|
|
{"fabric_node_id": "c0", "name": "X-CN0-Y", "x": 100, "y": 500},
|
|
{"fabric_node_id": "c1", "name": "X-CN1-Y", "x": 900, "y": 100},
|
|
{"fabric_node_id": "a0", "name": "X-AN0-Y", "x": 200, "y": 800},
|
|
{"fabric_node_id": "a1", "name": "X-AN1-Y", "x": 800, "y": 50},
|
|
{"fabric_node_id": "e0", "name": "X-EN0-Y", "x": 50, "y": 900},
|
|
{"fabric_node_id": "e1", "name": "X-EN1-Y", "x": 350, "y": 950},
|
|
{"fabric_node_id": "e2", "name": "X-EN2-Y", "x": 750, "y": 20},
|
|
{"fabric_node_id": "e3", "name": "X-EN3-Y", "x": 950, "y": 40},
|
|
{"fabric_node_id": "e4", "name": "X-EN4-Y", "x": 250, "y": 700},
|
|
{"fabric_node_id": "e5", "name": "X-EN5-Y", "x": 850, "y": 200},
|
|
]
|
|
edges = [
|
|
{"a_node_id": "c0", "b_node_id": "c1"},
|
|
{"a_node_id": "c0", "b_node_id": "a0"},
|
|
{"a_node_id": "c1", "b_node_id": "a1"},
|
|
{"a_node_id": "a0", "b_node_id": "e0"},
|
|
{"a_node_id": "a0", "b_node_id": "e1"},
|
|
{"a_node_id": "a0", "b_node_id": "e4"},
|
|
{"a_node_id": "a1", "b_node_id": "e2"},
|
|
{"a_node_id": "a1", "b_node_id": "e3"},
|
|
{"a_node_id": "a1", "b_node_id": "e5"},
|
|
{"a_node_id": "e0", "b_node_id": "e4"},
|
|
{"a_node_id": "e2", "b_node_id": "e5"},
|
|
]
|
|
return nodes, edges
|
|
|
|
|
|
def test_actions_include_pin_and_petals() -> None:
|
|
assert "pin_beam" not in ACTIONS
|
|
assert "soft_petals" not in ACTIONS
|
|
|
|
|
|
def test_pin_beam_aligns_cores() -> None:
|
|
nodes, edges = _starish()
|
|
st = build_state_from_nodes_edges(nodes, edges)
|
|
st.positions = {n["fabric_node_id"]: (float(n["x"]), float(n["y"])) for n in nodes}
|
|
op = pin_beam_rigid(st, LayoutParams())
|
|
assert op.op == "pin_beam"
|
|
ys = [op.state.positions["c0"][1], op.state.positions["c1"][1]]
|
|
# Either accepted with near-collinear cores, or refused (no_improvement) keeping input
|
|
if op.params.get("accepted"):
|
|
assert abs(ys[0] - ys[1]) < 5.0
|
|
else:
|
|
assert op.note in {"no_improvement", "need_ge_2_cores"}
|
|
|
|
|
|
def test_soft_petals_runs() -> None:
|
|
nodes, edges = _starish()
|
|
st = build_state_from_nodes_edges(nodes, edges)
|
|
st.positions = {n["fabric_node_id"]: (float(n["x"]), float(n["y"])) for n in nodes}
|
|
op = soft_petals_greedy(st, LayoutParams())
|
|
assert op.op == "soft_petals"
|
|
assert "accepted_n" in op.params
|
|
|
|
|
|
def test_run_layout_on_graph_pin_beam_unpublished() -> None:
|
|
nodes, edges = _starish()
|
|
try:
|
|
run_layout_on_graph(nodes, edges, action="pin_beam")
|
|
raise AssertionError("pin_beam should be unpublished")
|
|
except ValueError as e:
|
|
assert "unknown_action" in str(e)
|
|
|
|
|
|
def test_run_layout_on_graph_soft_petals_unpublished() -> None:
|
|
nodes, edges = _starish()
|
|
try:
|
|
run_layout_on_graph(nodes, edges, action="soft_petals")
|
|
raise AssertionError("soft_petals should be unpublished")
|
|
except ValueError as e:
|
|
assert "unknown_action" in str(e)
|