from __future__ import annotations import importlib.util from pathlib import Path import sys import types def _load_render_pipeline_script(): candidates = [ Path(__file__).resolve().parents[3] / "scripts" / "test_render_pipeline.py", Path("/compose/scripts/test_render_pipeline.py"), ] script_path = next((candidate for candidate in candidates if candidate.exists()), None) assert script_path is not None if "requests" not in sys.modules: requests_stub = types.ModuleType("requests") requests_stub.Response = object requests_stub.Session = object requests_stub.exceptions = types.SimpleNamespace( ConnectionError=RuntimeError, ChunkedEncodingError=RuntimeError, ReadTimeout=RuntimeError, ) sys.modules["requests"] = requests_stub spec = importlib.util.spec_from_file_location("test_render_pipeline_script", script_path) assert spec is not None assert spec.loader is not None module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module def test_build_output_type_workflow_link_payload_sets_graph_rollout_mode_explicitly(): module = _load_render_pipeline_script() payload = module.build_output_type_workflow_link_payload( workflow_definition_id="workflow-graph-123", execution_mode="graph", ) assert payload == { "workflow_definition_id": "workflow-graph-123", "workflow_rollout_mode": "graph", "is_active": True, } def test_build_output_type_workflow_link_payload_sets_shadow_rollout_mode_explicitly(): module = _load_render_pipeline_script() payload = module.build_output_type_workflow_link_payload( workflow_definition_id="workflow-shadow-123", execution_mode="shadow", ) assert payload == { "workflow_definition_id": "workflow-shadow-123", "workflow_rollout_mode": "shadow", "is_active": True, } def test_build_output_type_workflow_link_payload_keeps_legacy_rollout_implicit(): module = _load_render_pipeline_script() payload = module.build_output_type_workflow_link_payload( workflow_definition_id="workflow-legacy-123", execution_mode="legacy", ) assert payload == { "workflow_definition_id": "workflow-legacy-123", "is_active": True, } def test_build_graph_still_config_matches_canonical_still_graph_contract(): module = _load_render_pipeline_script() config = module.build_graph_still_config( execution_mode="shadow", render_params={ "resolution": [1920, 1080], "engine": "cycles", "samples": 128, }, ) assert config["ui"] == { "preset": "still_graph", "execution_mode": "shadow", "family": "order_line", } assert [node["id"] for node in config["nodes"]] == [ "setup", "template", "populate_materials", "bbox", "resolve_materials", "render", "output", "notify", ] assert config["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": "resolve_materials", "to": "render"}, {"from": "bbox", "to": "render"}, {"from": "template", "to": "render"}, {"from": "render", "to": "output"}, {"from": "render", "to": "notify"}, ] render_node = next(node for node in config["nodes"] if node["id"] == "render") assert render_node["params"] == { "width": 1920, "height": 1080, "render_engine": "cycles", "samples": 128, "use_custom_render_settings": False, } def test_render_template_candidates_for_output_type_matches_m2m_and_legacy_fields(): module = _load_render_pipeline_script() templates = [ { "id": "template-active-m2m", "is_active": True, "output_type_ids": ["ot-1", "ot-2"], "output_type_id": None, }, { "id": "template-active-legacy", "is_active": True, "output_type_ids": [], "output_type_id": "ot-1", }, { "id": "template-inactive", "is_active": False, "output_type_ids": ["ot-1"], "output_type_id": None, }, ] matches = module.render_template_candidates_for_output_type(templates, "ot-1") assert [template["id"] for template in matches] == [ "template-active-m2m", "template-active-legacy", ] def test_build_graph_still_config_can_inherit_output_type_render_settings(): module = _load_render_pipeline_script() config = module.build_graph_still_config( execution_mode="shadow", use_custom_render_settings=False, ) render_node = next(node for node in config["nodes"] if node["id"] == "render") assert render_node["params"] == { "use_custom_render_settings": False, } def test_choose_template_backed_output_type_prefers_requested_name(): module = _load_render_pipeline_script() output_types = [ { "id": "ot-1", "name": "HQ-Blender-Alpha-HDR", "renderer": "blender", "artifact_kind": "still_image", "is_animation": False, }, { "id": "ot-2", "name": "Turntable", "renderer": "blender", "artifact_kind": "turntable_video", "is_animation": True, }, ] templates = [ { "id": "template-1", "is_active": True, "output_type_ids": ["ot-1"], "output_type_id": None, } ] output_type, matches = module.choose_template_backed_output_type( output_types, templates, preferred_name="HQ-Blender-Alpha-HDR", ) assert output_type["id"] == "ot-1" assert [template["id"] for template in matches] == ["template-1"] def test_build_output_type_workflow_snapshot_keeps_restore_contract(): module = _load_render_pipeline_script() snapshot = module.build_output_type_workflow_snapshot( { "workflow_definition_id": "workflow-123", "workflow_rollout_mode": "shadow", "is_active": False, } ) assert snapshot == { "workflow_definition_id": "workflow-123", "workflow_rollout_mode": "shadow", "is_active": False, }