Artifex/tests/test_execution_graph_phase_a.py
2026-08-15 17:11:41 +07:00

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