92 lines
3.4 KiB
Python
92 lines
3.4 KiB
Python
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from control_plane.agents.models import ProgenySignal
|
|
from graph.models import ExecutionGraphDefinition, ExecutionGraphVersion, ExecutionGraphVersionStatus, GraphRun, GraphRunStatus
|
|
from graph.registry import NodeHandlerRegistry, NodeResult
|
|
from graph.spec import ExecutionGraphSpec, outgoing_edges
|
|
from graph.task_execution import task_execution_graph_v1
|
|
|
|
|
|
class FixtureNode:
|
|
node_type = "fixture"
|
|
idempotent = True
|
|
replay_safe = True
|
|
destructive = False
|
|
|
|
def run(self, context: object) -> NodeResult:
|
|
return NodeResult("COMPLETE", "success", {"ok": True})
|
|
|
|
|
|
def test_task_execution_graph_v1_is_serializable_and_valid() -> None:
|
|
spec = task_execution_graph_v1()
|
|
|
|
payload = spec.to_dict()
|
|
restored = ExecutionGraphSpec.from_dict(payload)
|
|
restored.validate()
|
|
|
|
assert restored.name == "task_execution"
|
|
assert restored.version == 1
|
|
assert restored.entry == "claim_task"
|
|
assert restored.nodes["coder"].metadata["contains_internal_tool_loop"] is True
|
|
assert {edge.condition for edge in outgoing_edges(restored, "review")} == {"PASS", "REWORK_REQUIRED", "REJECTED"}
|
|
assert {edge.condition for edge in outgoing_edges(restored, "retry_or_fail")} == {"retry_available", "retry_exhausted"}
|
|
|
|
|
|
def test_node_registry_resolves_handlers_without_arbitrary_execution() -> None:
|
|
registry = NodeHandlerRegistry()
|
|
registry.register(FixtureNode())
|
|
|
|
assert registry.get("fixture").run(object()).output_metadata == {"ok": True}
|
|
with pytest.raises(KeyError):
|
|
registry.get("missing")
|
|
|
|
|
|
def test_graph_models_persist_versioned_run_state() -> None:
|
|
spec = task_execution_graph_v1()
|
|
definition = ExecutionGraphDefinition.objects.create(
|
|
name=spec.name,
|
|
graph_type=spec.graph_type,
|
|
description="Task execution graph",
|
|
)
|
|
version = ExecutionGraphVersion.objects.create(
|
|
graph=definition,
|
|
version=spec.version,
|
|
status=ExecutionGraphVersionStatus.CHAMPION,
|
|
graph_spec=spec.to_dict(),
|
|
metadata={"immutable_after_use": True},
|
|
)
|
|
|
|
run = GraphRun.objects.create(
|
|
execution_graph_version=version,
|
|
status=GraphRunStatus.PENDING,
|
|
current_node=spec.entry,
|
|
metadata={"node_count": len(spec.nodes)},
|
|
)
|
|
|
|
assert run.execution_graph_version.graph.name == "task_execution"
|
|
assert run.execution_graph_version.status == ExecutionGraphVersionStatus.CHAMPION
|
|
assert run.metadata["node_count"] == 12
|
|
|
|
|
|
def test_progeny_signal_can_reference_graph_lineage() -> None:
|
|
spec = task_execution_graph_v1()
|
|
definition = ExecutionGraphDefinition.objects.create(name="lineage_graph", graph_type=spec.graph_type)
|
|
version = ExecutionGraphVersion.objects.create(graph=definition, version=1, status=ExecutionGraphVersionStatus.CHAMPION, graph_spec=spec.to_dict())
|
|
run = GraphRun.objects.create(execution_graph_version=version, current_node=spec.entry)
|
|
node_run = run.node_runs.create(node_id="coder", node_type="coder", visit_index=1)
|
|
|
|
signal = ProgenySignal.objects.create(
|
|
source="runtime",
|
|
severity="high",
|
|
failure_category="MODEL_OUTPUT_INVALID",
|
|
summary="Malformed model JSON",
|
|
graph_run=run,
|
|
graph_node_run=node_run,
|
|
execution_graph_version=version,
|
|
)
|
|
|
|
assert signal.graph_run == run
|
|
assert signal.graph_node_run == node_run
|
|
assert signal.execution_graph_version == version
|