Files

190 lines
6.1 KiB
Python

import importlib.util
import logging
import pathlib
import sys
import types
import unittest
from types import SimpleNamespace
REPO_ROOT = pathlib.Path(__file__).resolve().parents[1]
def _load_workflow_node_module():
package_name = "swf_isolated_test"
package = types.ModuleType(package_name)
package.__path__ = [str(REPO_ROOT)]
sys.modules[package_name] = package
latest = types.ModuleType("comfy_api.latest")
latest.io = SimpleNamespace(
ComfyNode=object,
NodeOutput=lambda *args, **kwargs: (args, kwargs),
Hidden=SimpleNamespace(unique_id="UNIQUE_ID", dynprompt="DYNPROMPT"),
)
comfy_api = types.ModuleType("comfy_api")
comfy_api.latest = latest
sys.modules["comfy_api"] = comfy_api
sys.modules["comfy_api.latest"] = latest
debug_utils = types.ModuleType(f"{package_name}.debug_utils")
debug_utils.configure_logger = lambda name: logging.getLogger(name)
sys.modules[debug_utils.__name__] = debug_utils
workflow_utils = types.ModuleType(f"{package_name}.workflow_utils")
for name in (
"list_workflow_files",
"load_workflow_file",
"load_workflow_url",
"build_expansion",
"build_modifier_source_expansion",
"apply_control_after_generate",
"validate_workflow_nodes_installed",
"get_workflow_interface",
):
setattr(workflow_utils, name, lambda *args, **kwargs: None)
sys.modules[workflow_utils.__name__] = workflow_utils
module_name = f"{package_name}.workflow_node"
spec = importlib.util.spec_from_file_location(
module_name,
REPO_ROOT / "workflow_node.py",
)
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)
return module
workflow_node = _load_workflow_node_module()
class FakePrompt:
def __init__(self, nodes=None):
self.nodes = nodes or {}
def has_node(self, node_id):
return node_id in self.nodes
def get_node(self, node_id):
return self.nodes[node_id]
class ModifierSourceTargetTests(unittest.TestCase):
def test_resolves_file_and_url_subworkflow_targets(self):
prompt = FakePrompt({
"12": {
"class_type": "SWF_Subworkflow",
"inputs": {
"workflow": "inner.json",
"reload_each_execution": False,
},
},
"13": {
"class_type": "SWF_SubworkflowFromURL",
"inputs": {
"url": "https://example.test/workflow.json",
"verify_ssl": False,
"reload_each_execution": True,
},
},
})
workflow_node.SubworkflowModifierSource.hidden = SimpleNamespace(
dynprompt=prompt
)
file_target = workflow_node.SubworkflowModifierSource._resolve_target(
"12 | Local | inner.json"
)
url_target = workflow_node.SubworkflowModifierSource._resolve_target(
"13 | Remote | https://example.test/workflow.json"
)
self.assertIs(file_target[1], workflow_node.Subworkflow)
self.assertEqual(file_target[2], {"workflow": "inner.json"})
self.assertIs(file_target[3], False)
self.assertIs(url_target[1], workflow_node.SubworkflowFromURL)
self.assertEqual(url_target[2]["verify_ssl"], False)
self.assertIs(url_target[3], True)
def test_rejects_non_subworkflow_target(self):
prompt = FakePrompt({
"20": {"class_type": "KSampler", "inputs": {}},
})
workflow_node.SubworkflowModifierSource.hidden = SimpleNamespace(
dynprompt=prompt
)
with self.assertRaisesRegex(ValueError, "not a Subworkflow"):
workflow_node.SubworkflowModifierSource._resolve_target("20 | Sampler")
class WorkflowDataCacheTests(unittest.TestCase):
def setUp(self):
workflow_node.BaseSubworkflow._loaded_workflows.clear()
workflow_node.BaseSubworkflow._loaded_workflow_instances.clear()
workflow_node.BaseSubworkflow._prompt_workflows.clear()
self.loads = []
self.original_loader = workflow_node.Subworkflow.__dict__["_load_source"]
loads = self.loads
workflow_node.Subworkflow._load_source = classmethod(
lambda cls, workflow, **kwargs: (
loads.append(workflow) or {"load": len(loads)}
)
)
def tearDown(self):
workflow_node.Subworkflow._load_source = self.original_loader
def test_reload_loads_once_per_prompt(self):
prompt = FakePrompt()
first = workflow_node.Subworkflow._get_workflow_data(
True,
instance_id="12",
dynprompt=prompt,
workflow="inner.json",
)
second = workflow_node.Subworkflow._get_workflow_data(
True,
instance_id="12",
dynprompt=prompt,
workflow="inner.json",
)
self.assertIs(first, second)
self.assertEqual(len(self.loads), 1)
workflow_node.Subworkflow._get_workflow_data(
True,
instance_id="12",
dynprompt=FakePrompt(),
workflow="inner.json",
)
self.assertEqual(len(self.loads), 2)
def test_keep_loaded_is_isolated_by_target_node(self):
kept = workflow_node.Subworkflow._get_workflow_data(
False,
instance_id="12",
dynprompt=FakePrompt(),
workflow="inner.json",
)
kept_again = workflow_node.Subworkflow._get_workflow_data(
False,
instance_id="12",
dynprompt=FakePrompt(),
workflow="inner.json",
)
self.assertIs(kept, kept_again)
self.assertEqual(len(self.loads), 1)
workflow_node.Subworkflow._get_workflow_data(
False,
instance_id="99",
dynprompt=FakePrompt(),
workflow="inner.json",
)
self.assertEqual(len(self.loads), 2)
if __name__ == "__main__":
unittest.main()