70 lines
2.4 KiB
Python
70 lines
2.4 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
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
|