dep validation of deployment

core wf valication uses a &mut List, this returns a List, hmmmm
This commit is contained in:
lda
2026-05-11 15:24:52 +07:00 Verified
parent 958195ddce
commit 32f80f756d
8 changed files with 388 additions and 22 deletions
+178
View File
@@ -0,0 +1,178 @@
from __future__ import annotations
from wf_artifacts import (
AvailableCapability,
AvailableSource,
DriftPolicy,
RequiredCapability,
WorkflowArtifact,
WorkflowDeployment,
validate_deployment_dependencies,
)
def required_capability(
*,
logical_source: str = "context7",
capability_name: str = "query-docs",
input_hash: str = "sha256:input",
output_hash: str = "sha256:output",
) -> RequiredCapability:
return RequiredCapability(
logical_source=logical_source,
capability_name=capability_name,
kind="tool",
input_schema_hash=input_hash,
input_schema_snapshot={"type": "object", "properties": {}},
output_schema_hash=output_hash,
output_schema_snapshot={"type": "object", "properties": {}},
)
def artifact_with(capability: RequiredCapability) -> WorkflowArtifact:
return WorkflowArtifact(
id="summarize_docs",
version=1,
title="Summarize Docs",
input_schema={"type": "object", "properties": {}},
output_schema={"type": "object", "properties": {}},
outcomes=("done",),
plan={"name": "summarize_docs", "nodes": [], "edges": []},
required_capabilities={
f"{capability.logical_source}.{capability.capability_name}": capability
},
)
def deployment(
*,
bindings: dict[str, str] | None = None,
drift_policy: DriftPolicy = DriftPolicy.BLOCK,
) -> WorkflowDeployment:
return WorkflowDeployment(
id="summarize_docs.personal",
artifact_id="summarize_docs",
artifact_version=1,
bindings={"context7": "context7.personal"} if bindings is None else bindings,
drift_policy=drift_policy,
)
def source(
*,
id: str = "context7.personal",
enabled: bool = True,
capability_name: str = "query-docs",
input_hash: str = "sha256:input",
output_hash: str = "sha256:output",
) -> AvailableSource:
return AvailableSource(
id=id,
enabled=enabled,
capabilities={
capability_name: AvailableCapability(
name=capability_name,
kind="tool",
input_schema_hash=input_hash,
output_schema_hash=output_hash,
)
},
)
def test_validate_deployment_accepts_matching_bound_capability() -> None:
diagnostics = validate_deployment_dependencies(
artifact=artifact_with(required_capability()),
deployment=deployment(),
sources=[source()],
)
assert diagnostics == []
def test_validate_deployment_reports_missing_binding() -> None:
diagnostics = validate_deployment_dependencies(
artifact=artifact_with(required_capability()),
deployment=deployment(bindings={}),
sources=[source()],
)
assert len(diagnostics) == 1
assert diagnostics[0].severity == "error"
assert diagnostics[0].code == "binding_missing"
assert diagnostics[0].logical_ref == "context7.query-docs"
assert diagnostics[0].bound_source is None
def test_validate_deployment_reports_missing_source() -> None:
diagnostics = validate_deployment_dependencies(
artifact=artifact_with(required_capability()),
deployment=deployment(),
sources=[],
)
assert len(diagnostics) == 1
assert diagnostics[0].severity == "error"
assert diagnostics[0].code == "source_missing"
assert diagnostics[0].logical_ref == "context7.query-docs"
assert diagnostics[0].bound_source == "context7.personal"
def test_validate_deployment_reports_disabled_source() -> None:
diagnostics = validate_deployment_dependencies(
artifact=artifact_with(required_capability()),
deployment=deployment(),
sources=[source(enabled=False)],
)
assert len(diagnostics) == 1
assert diagnostics[0].severity == "error"
assert diagnostics[0].code == "source_disabled"
assert diagnostics[0].bound_source == "context7.personal"
def test_validate_deployment_reports_missing_capability() -> None:
diagnostics = validate_deployment_dependencies(
artifact=artifact_with(required_capability()),
deployment=deployment(),
sources=[source(capability_name="other-tool")],
)
assert len(diagnostics) == 1
assert diagnostics[0].severity == "error"
assert diagnostics[0].code == "capability_missing"
assert diagnostics[0].logical_ref == "context7.query-docs"
def test_validate_deployment_blocks_changed_schema_by_default() -> None:
diagnostics = validate_deployment_dependencies(
artifact=artifact_with(required_capability()),
deployment=deployment(),
sources=[source(input_hash="sha256:changed")],
)
assert len(diagnostics) == 1
assert diagnostics[0].severity == "error"
assert diagnostics[0].code == "schema_changed"
def test_validate_deployment_warns_for_changed_schema_when_policy_warns() -> None:
diagnostics = validate_deployment_dependencies(
artifact=artifact_with(required_capability()),
deployment=deployment(drift_policy=DriftPolicy.WARN),
sources=[source(output_hash="sha256:changed")],
)
assert len(diagnostics) == 1
assert diagnostics[0].severity == "warning"
assert diagnostics[0].code == "schema_changed"
def test_validate_deployment_allows_changed_schema_when_policy_allows() -> None:
diagnostics = validate_deployment_dependencies(
artifact=artifact_with(required_capability()),
deployment=deployment(drift_policy=DriftPolicy.ALLOW),
sources=[source(input_hash="sha256:changed")],
)
assert diagnostics == []
+5 -6
View File
@@ -79,9 +79,7 @@ def test_create_broker_server_exposes_tools_resources_and_prompts() -> None:
server.call_tool("get_planner_catalog", {})
)
planner_catalog = cast(dict[str, Any], cast(object, planner_catalog_raw))
planner_names = [
node["qualified_name"] for node in planner_catalog["nodes"]
]
planner_names = [node["qualified_name"] for node in planner_catalog["nodes"]]
assert "demo.personal.echo_tool" in planner_names
assert "wf.mcp.call_tool" in planner_names
assert "wf.std.runtime_error" in planner_names
@@ -106,9 +104,10 @@ def test_broker_admin_tools_are_backed_by_wf_admin_source() -> None:
assert "list_spec_sources" in tool_names
assert "get_planner_catalog" in tool_names
assert "wf.admin.list_sources" in service.capability_sources[
"wf.admin"
].capabilities.tools
assert (
"wf.admin.list_sources"
in service.capability_sources["wf.admin"].capabilities.tools
)
def test_build_service_from_config_registers_connections() -> None:
+7 -13
View File
@@ -462,12 +462,8 @@ def test_service_excludes_disabled_connection_specs_from_planner_catalog() -> No
service.capability_sources["demo.personal"].enabled = False
planner_payload = service.get_planner_catalog().as_payload()
planner_names = [
node["qualified_name"] for node in planner_payload["nodes"]
]
available_names = [
entry.qualified_name for entry in service.list_available_specs()
]
planner_names = [node["qualified_name"] for node in planner_payload["nodes"]]
available_names = [entry.qualified_name for entry in service.list_available_specs()]
assert "demo.personal.echo_tool" not in planner_names
assert "demo.personal.echo_tool" not in available_names
@@ -501,7 +497,9 @@ def test_service_preserves_disabled_connection_source_on_reregistration() -> Non
assert "demo.personal.echo_tool" not in source.capabilities.node_specs
def test_service_excludes_planner_hidden_connection_specs_from_planner_catalog() -> None:
def test_service_excludes_planner_hidden_connection_specs_from_planner_catalog() -> (
None
):
service = WfMcpService(
store=FileStore(local_temp_root() / "hidden_connection_spec_store")
)
@@ -516,12 +514,8 @@ def test_service_excludes_planner_hidden_connection_specs_from_planner_catalog()
)
planner_payload = service.get_planner_catalog().as_payload()
planner_names = [
node["qualified_name"] for node in planner_payload["nodes"]
]
available_names = [
entry.qualified_name for entry in service.list_available_specs()
]
planner_names = [node["qualified_name"] for node in planner_payload["nodes"]]
available_names = [entry.qualified_name for entry in service.list_available_specs()]
assert "demo.personal.echo_tool" not in planner_names
assert "demo.personal.echo_tool" not in available_names