242 lines
8.0 KiB
Python
242 lines
8.0 KiB
Python
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from app.domains.rendering.workflow_schema import WorkflowConfig
|
|
|
|
|
|
def test_workflow_schema_rejects_duplicate_edges():
|
|
with pytest.raises(ValidationError, match="duplicate edge is not allowed"):
|
|
WorkflowConfig.model_validate(
|
|
{
|
|
"version": 1,
|
|
"nodes": [
|
|
{"id": "setup", "step": "order_line_setup", "params": {}},
|
|
{"id": "render", "step": "blender_still", "params": {}},
|
|
],
|
|
"edges": [
|
|
{"from": "setup", "to": "render"},
|
|
{"from": "setup", "to": "render"},
|
|
],
|
|
}
|
|
)
|
|
|
|
|
|
def test_workflow_schema_rejects_self_referential_edges():
|
|
with pytest.raises(ValidationError, match="self-referential edge is not allowed"):
|
|
WorkflowConfig.model_validate(
|
|
{
|
|
"version": 1,
|
|
"nodes": [
|
|
{"id": "render", "step": "blender_still", "params": {}},
|
|
],
|
|
"edges": [
|
|
{"from": "render", "to": "render"},
|
|
],
|
|
}
|
|
)
|
|
|
|
|
|
def test_workflow_schema_rejects_cycles():
|
|
with pytest.raises(ValidationError, match="workflow graph must be acyclic"):
|
|
WorkflowConfig.model_validate(
|
|
{
|
|
"version": 1,
|
|
"nodes": [
|
|
{"id": "setup", "step": "order_line_setup", "params": {}},
|
|
{"id": "template", "step": "resolve_template", "params": {}},
|
|
{"id": "render", "step": "blender_still", "params": {}},
|
|
],
|
|
"edges": [
|
|
{"from": "setup", "to": "template"},
|
|
{"from": "template", "to": "render"},
|
|
{"from": "render", "to": "setup"},
|
|
],
|
|
}
|
|
)
|
|
|
|
|
|
def test_workflow_schema_rejects_unknown_node_params():
|
|
with pytest.raises(ValidationError, match="uses unknown param key"):
|
|
WorkflowConfig.model_validate(
|
|
{
|
|
"version": 1,
|
|
"nodes": [
|
|
{
|
|
"id": "bbox",
|
|
"step": "glb_bbox",
|
|
"params": {"glb_path": "/tmp/model.glb", "bogus": "value"},
|
|
},
|
|
],
|
|
"edges": [],
|
|
}
|
|
)
|
|
|
|
|
|
def test_workflow_schema_accepts_known_node_params():
|
|
config = WorkflowConfig.model_validate(
|
|
{
|
|
"version": 1,
|
|
"nodes": [
|
|
{
|
|
"id": "bbox",
|
|
"step": "glb_bbox",
|
|
"params": {"glb_path": "/tmp/model.glb"},
|
|
},
|
|
],
|
|
"edges": [],
|
|
"ui": {"family": "order_line"},
|
|
}
|
|
)
|
|
|
|
assert config.ui is not None
|
|
assert config.ui.family == "order_line"
|
|
|
|
|
|
def test_workflow_schema_rejects_ui_family_mismatch():
|
|
with pytest.raises(ValidationError, match="ui.family"):
|
|
WorkflowConfig.model_validate(
|
|
{
|
|
"version": 1,
|
|
"nodes": [
|
|
{"id": "setup", "step": "order_line_setup", "params": {}},
|
|
{"id": "render", "step": "blender_still", "params": {}},
|
|
],
|
|
"edges": [
|
|
{"from": "setup", "to": "render"},
|
|
],
|
|
"ui": {"family": "cad_file"},
|
|
}
|
|
)
|
|
|
|
|
|
def test_workflow_schema_accepts_explicit_mixed_family_when_declared():
|
|
config = WorkflowConfig.model_validate(
|
|
{
|
|
"version": 1,
|
|
"nodes": [
|
|
{"id": "cad", "step": "resolve_step_path", "params": {}},
|
|
{"id": "render", "step": "blender_still", "params": {}},
|
|
],
|
|
"edges": [],
|
|
"ui": {"family": "mixed"},
|
|
}
|
|
)
|
|
|
|
assert config.ui is not None
|
|
assert config.ui.family == "mixed"
|
|
|
|
|
|
def test_workflow_schema_rejects_invalid_select_value():
|
|
with pytest.raises(ValidationError, match="must be one of"):
|
|
WorkflowConfig.model_validate(
|
|
{
|
|
"version": 1,
|
|
"nodes": [
|
|
{
|
|
"id": "notify",
|
|
"step": "notify",
|
|
"params": {"channel": "email"},
|
|
},
|
|
],
|
|
"edges": [],
|
|
}
|
|
)
|
|
|
|
|
|
def test_workflow_schema_rejects_invalid_number_value():
|
|
with pytest.raises(ValidationError, match="must be a number"):
|
|
WorkflowConfig.model_validate(
|
|
{
|
|
"version": 1,
|
|
"nodes": [
|
|
{
|
|
"id": "render",
|
|
"step": "blender_still",
|
|
"params": {"samples": "high"},
|
|
},
|
|
],
|
|
"edges": [],
|
|
}
|
|
)
|
|
|
|
|
|
def test_workflow_schema_rejects_invalid_boolean_value():
|
|
with pytest.raises(ValidationError, match="must be a boolean"):
|
|
WorkflowConfig.model_validate(
|
|
{
|
|
"version": 1,
|
|
"nodes": [
|
|
{
|
|
"id": "render",
|
|
"step": "blender_still",
|
|
"params": {"use_custom_render_settings": "yes"},
|
|
},
|
|
],
|
|
"edges": [],
|
|
}
|
|
)
|
|
|
|
|
|
def test_workflow_schema_rejects_missing_required_upstream_artifact():
|
|
with pytest.raises(ValidationError, match="missing required input artifact"):
|
|
WorkflowConfig.model_validate(
|
|
{
|
|
"version": 1,
|
|
"nodes": [
|
|
{"id": "setup", "step": "order_line_setup", "params": {}},
|
|
{"id": "render", "step": "blender_still", "params": {}},
|
|
],
|
|
"edges": [
|
|
{"from": "setup", "to": "render"},
|
|
],
|
|
"ui": {"execution_mode": "graph", "family": "order_line"},
|
|
}
|
|
)
|
|
|
|
|
|
def test_workflow_schema_accepts_transitive_contract_wiring():
|
|
config = WorkflowConfig.model_validate(
|
|
{
|
|
"version": 1,
|
|
"nodes": [
|
|
{"id": "setup", "step": "order_line_setup", "params": {}},
|
|
{"id": "template", "step": "resolve_template", "params": {}},
|
|
{"id": "populate_materials", "step": "auto_populate_materials", "params": {}},
|
|
{"id": "bbox", "step": "glb_bbox", "params": {}},
|
|
{"id": "resolve_materials", "step": "material_map_resolve", "params": {}},
|
|
{"id": "render", "step": "blender_still", "params": {}},
|
|
{"id": "output", "step": "output_save", "params": {}},
|
|
],
|
|
"edges": [
|
|
{"from": "setup", "to": "template"},
|
|
{"from": "setup", "to": "populate_materials"},
|
|
{"from": "setup", "to": "bbox"},
|
|
{"from": "template", "to": "resolve_materials"},
|
|
{"from": "populate_materials", "to": "resolve_materials"},
|
|
{"from": "template", "to": "render"},
|
|
{"from": "bbox", "to": "render"},
|
|
{"from": "resolve_materials", "to": "render"},
|
|
{"from": "render", "to": "output"},
|
|
],
|
|
"ui": {"family": "order_line", "execution_mode": "graph"},
|
|
}
|
|
)
|
|
|
|
assert config.ui is not None
|
|
assert config.ui.execution_mode == "graph"
|
|
|
|
|
|
def test_workflow_schema_rejects_mixed_family_graph_execution():
|
|
with pytest.raises(ValidationError, match="single-family"):
|
|
WorkflowConfig.model_validate(
|
|
{
|
|
"version": 1,
|
|
"nodes": [
|
|
{"id": "cad", "step": "resolve_step_path", "params": {}},
|
|
{"id": "render", "step": "blender_still", "params": {}},
|
|
],
|
|
"edges": [],
|
|
"ui": {"execution_mode": "graph", "family": "mixed"},
|
|
}
|
|
)
|