Agent8Agent / tests /test_graph.py
smlflg's picture
Initial public upload from Projekte/Agent8Agent
f2c56d3 verified
Raw History Blame Contribute Delete
4.64 kB
"""Mockup-Graph + scharfer intake_router.
Flow-Tests forcieren via Monkeypatch den Keyword-Fallback (deterministisch, kein
Register-Zufall). Ein separater Live-Test trifft den echten hermes-MCP.
"""
import uuid
import pytest
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import Command
import agent8agent.nodes as nodes
from agent8agent.graph import build_graph
def _cfg():
return {"configurable": {"thread_id": str(uuid.uuid4())}}
@pytest.fixture
def offline_intake(monkeypatch):
"""Erzwingt den Fallback-Pfad: kein Register-Treffer → Keyword-Klassifikation."""
monkeypatch.setattr(nodes, "call_intake_tool_sync",
lambda name, args=None: {"matches": [], "success": False})
def _run_to_interrupt(raw: str):
g = build_graph(checkpointer=InMemorySaver())
cfg = _cfg()
return g, cfg, g.invoke({"raw_input": raw}, cfg)
# --- Topologie -------------------------------------------------------------
def test_compiles_and_has_all_nodes():
nodes_set = set(build_graph().get_graph().nodes)
for n in [
"intake_router", "priority_gate", "execute_with_codex", "research_with_opus",
"research_subagent", "learning_agent", "meta_agent", "park",
"verifier_mediator", "human_escalation", "hai_owner_packet", "automation_output",
]:
assert n in nodes_set
# --- Routing (pure) --------------------------------------------------------
@pytest.mark.parametrize("classification,expected", [
("aufgabe", "execute_with_codex"),
("idee", "research_with_opus"),
("entscheidung", "research_with_opus"),
("reflexion", "meta_agent"),
("meeting", "park"),
("sonstiges", "park"),
])
def test_route_thought_maps_action_type(classification, expected):
assert nodes.route_thought({"classification": classification}) == expected
def test_priority_gate_parks_meeting():
out = nodes.priority_gate({"classification": "meeting"})
assert out["priority"] == "park" and out["route"] == "E"
def test_priority_gate_maps_urgency():
out = nodes.priority_gate({"classification": "aufgabe", "intake": {"urgency": "hoch"}})
assert out["priority"] == "P0" and out["route"] == "A"
# --- Fanout (pure) ---------------------------------------------------------
def test_fan_out_research_sends_three():
sends = nodes.fan_out_research({"raw_input": "x"})
assert len(sends) == 3
# --- Graph-Flow (offline / Fallback) --------------------------------------
@pytest.mark.parametrize("raw,target_gate", [
("execute the task", "execute_with_codex"),
("research what exists", "research_with_opus"),
])
def test_flow_routes_via_fallback(offline_intake, raw, target_gate):
g, cfg, result = _run_to_interrupt(raw)
assert "__interrupt__" in result # läuft bis zur Taille
history_gates = {s.values.get("current_gate") for s in g.get_state_history(cfg)}
assert target_gate in history_gates
def test_flow_research_fanout_reduces_three(offline_intake):
g, cfg, result = _run_to_interrupt("research what already exists")
prior_art = [e for e in result.get("evidence", []) if e["kind"] == "prior-art"]
assert len(prior_art) == 3
def test_flow_park_terminates(offline_intake):
g, cfg, result = _run_to_interrupt("park this for later")
assert result["priority"] == "park"
assert "__interrupt__" not in result
assert result["current_gate"] == "park"
def test_flow_interrupt_then_resume(offline_intake):
g, cfg, result = _run_to_interrupt("execute the task")
assert "__interrupt__" in result
final = g.invoke(Command(resume="approve"), cfg)
assert final["current_gate"] == "automation_output"
assert len(final["owner_packet"]["decisions"]) <= 3
def test_flow_conflict_routes_through_escalation(offline_intake):
g, cfg, result = _run_to_interrupt("execute but there is a conflict")
assert result["verifier_status"] == "conflict"
assert "__interrupt__" in result
# --- LIVE: echter intake-router-hermes MCP --------------------------------
@pytest.mark.live
def test_intake_router_live_register_match():
"""Trifft den echten MCP. Übersprungen wenn hermes nicht erreichbar."""
out = nodes.intake_router({"raw_input": "IR-2026-0001"})
note = out["intake"]["note"]
if note.startswith("mcp-unavailable"):
pytest.skip(f"hermes nicht erreichbar: {note}")
assert out["intake"]["id"] == "IR-2026-0001"
assert out["classification"] in {"aufgabe", "idee", "entscheidung", "reflexion",
"meeting", "sonstiges"}
assert out["current_gate"] == "intake_router"