Files
lda-wf/tests/wf_client/test_codec.py
T

179 lines
5.1 KiB
Python

from __future__ import annotations
from typing import Any
import pytest
from wf_client.codec import (
decode_dependency_diagnostics,
decode_deployment,
decode_run_result,
decode_trace_result,
decode_workflow_artifact,
)
from wf_client.errors import InvalidResponse
def _constant_plan_payload() -> dict[str, Any]:
return {
"name": "report",
"input_schema": {"type": "object", "properties": {}},
"state_schema": {
"type": "object",
"properties": {"result": {"type": "string", "reducer": "wf.std.replace"}},
},
"output_schema": {
"type": "object",
"properties": {"result": {"type": "string"}},
"required": ["result"],
},
"outcomes": ["ok"],
"start": "constant",
"nodes": [
{
"id": "constant",
"type": "node",
"node": "wf.std.constant",
"input": [{"value": "hello", "target": "local.value"}],
"output": [{"source": "local.value", "target": "state.result"}],
}
],
"edges": [{"from": "constant", "outcome": "ok", "to": "__end__"}],
"output": [{"path": "state.result", "target": "result"}],
}
def _workflow_artifact_payload(*, plan: dict[str, Any]) -> dict[str, Any]:
return {
"id": "report",
"version": 1,
"title": "Report",
"kind": "workflow",
"description": None,
"input_schema": {"type": "object", "properties": {}},
"output_schema": {
"type": "object",
"properties": {"result": {"type": "string"}},
},
"outcomes": ["ok"],
"plan": plan,
"required_capabilities": [],
"workflow_dependencies": {},
"created_from_catalog_version": None,
}
def test_decode_workflow_artifact_validates_plan() -> None:
artifact, workflow = decode_workflow_artifact(
_workflow_artifact_payload(plan=_constant_plan_payload())
)
assert artifact.id == "report"
assert workflow.name == "report"
assert workflow.start == "constant"
def test_decode_workflow_artifact_rejects_invalid_nested_plan() -> None:
with pytest.raises(InvalidResponse, match="workflow.artifacts.inspect"):
decode_workflow_artifact(_workflow_artifact_payload(plan={"name": "broken"}))
def test_decode_deployment_returns_domain_model() -> None:
deployment = decode_deployment(
{
"id": "production",
"artifact_id": "report",
"artifact_version": 1,
"bindings": [],
"drift_policy": "block",
}
)
assert deployment.id == "production"
assert deployment.artifact_id == "report"
def test_decode_dependency_diagnostics_returns_domain_models() -> None:
diagnostics = decode_dependency_diagnostics(
[
{
"severity": "error",
"code": "missing_source",
"logical_ref": "demo",
"bound_source": None,
"message": "missing",
"repair_hint": "bind it",
}
]
)
assert diagnostics[0].code == "missing_source"
assert diagnostics[0].severity.value == "error"
def _run_payload(*, trace: list[dict[str, Any]] | None = None) -> dict[str, Any]:
payload: dict[str, Any] = {
"artifact_id": "report",
"artifact_version": 1,
"deployment_id": "production",
"status": "completed",
"run_id": "run-1",
"resume_readiness": None,
"interrupt": None,
"outcome": "ok",
"error": None,
"output": {"result": "hello"},
"trace_count": 0 if trace is None else len(trace),
"max_steps": 10_000,
"steps_executed": 1,
"steps_remaining": 9_999,
"diagnostics": [],
"next_actions": {
"can_continue": False,
"can_save_now": None,
"recommended_next_tool": None,
"reason": "done",
"patch_examples": [],
"warnings": [],
},
}
if trace is not None:
payload.update(
trace=trace,
trace_start=0,
trace_limit=25,
trace_truncated=False,
)
return payload
def test_decode_run_result_returns_typed_domain_boundary() -> None:
result = decode_run_result(_run_payload())
assert result.run_id == "run-1"
assert result.output == {"result": "hello"}
assert result.diagnostics == ()
def test_decode_trace_result_decodes_bounded_trace() -> None:
result = decode_trace_result(
_run_payload(
trace=[
{
"frame_id": "root",
"node_id": "constant",
"step_type": "node",
"resolved_input": {},
"outcome": "ok",
"next_node_id": "__end__",
"output": {},
"state_changes": {},
}
]
)
)
assert result.trace_start == 0
assert result.trace is not None
assert result.trace[0]["node_id"] == "constant"