Files
JasonHoku-ComfyUI-Ultimate-…/tests/test_workflow_sweep_collect.py
T
Jason HokuandClaude Fable 5.1 e5312812d9 feat(workflow-sweep): template parsing, binding expansion and prompt substitution
Pure-Python core of Workflow Sweep: parse an API-format workflow, suggest bindings,
expand them into jobs (cache-friendly axis order, resume fingerprints), substitute
into the template and append the collector node. Builder state emits kind=workflow
arrays, config_utils.expand_configs dispatches model_type=workflow, and
manifest_utils.append_items adds disk-fresh manifest appends.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-04 00:08:16 -08:00

141 lines
6.3 KiB
Python

"""Workflow Sweep: manifest items (build_collect_item) and the disk-fresh manifest_utils.append_items."""
import importlib.util
import json
import os
import sys
import pytest
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PKG = os.path.basename(ROOT)
sys.path.insert(0, ROOT)
import workflow_sweep as ws # noqa: E402
def _load_real(name, private):
"""tests/conftest.py registers STUBS for several modules; load the real file under a private name."""
spec = importlib.util.spec_from_file_location(f"{PKG}.{private}", os.path.join(ROOT, f"{name}.py"))
mod = importlib.util.module_from_spec(spec)
mod.__package__ = PKG
spec.loader.exec_module(mod)
return mod
@pytest.fixture(scope="module")
def manifest_utils():
return _load_real("manifest_utils", "manifest_utils_real")
@pytest.fixture(scope="module")
def create_image_metadata():
torch = sys.modules.get("torch")
patched = False
if torch is not None and not hasattr(torch, "inference_mode"):
torch.inference_mode = lambda *a, **k: (lambda f: f) # stub torch lacks it; real torch needs no patch
patched = True
try:
return _load_real("image_generation", "image_generation_real").create_image_metadata
finally:
if patched:
del torch.inference_mode
CONF = {"model_type": "workflow", "media_type": "image", "wf_template_hash": "h", "wf_source": "t.json",
"wf_output": "38:0", "wf_subs": [["32", "cfg", 4.0]], "wf_params": {"KSampler.cfg": 4.0},
"wf_fingerprint": "f" * 20, "model": "m.safetensors", "sampler": "er_sde", "scheduler": "simple",
"steps": 25, "cfg": 4.0, "seed": 7, "denoise": 1.0, "positive": "a teapot", "negative": "ugly",
"lora": "None", "lora_expanded": "None", "vae": "v.safetensors"}
def test_build_collect_item_fields(create_image_metadata):
item = ws.build_collect_item(CONF, 832, 1216, 12.5, 0, 3, create_image_metadata=create_image_metadata)
for key, val in {"model": "m.safetensors", "sampler": "er_sde", "scheduler": "simple", "steps": 25, "cfg": 4.0,
"seed": 7, "positive": "a teapot", "negative": "ugly", "config_positive": "a teapot",
"config_negative": "ugly", "width": 832, "height": 1216, "duration": 12.5, "batch_idx": 0,
"gen_index": 3, "model_type": "workflow", "wf_fingerprint": "f" * 20,
"favorited": False, "rejected": False, "note": ""}.items():
assert item[key] == val, key
assert item["denoise"] == 1 and isinstance(item["denoise"], int) # 1.0 -> 1, as flush_batch_with_vae
assert item["wf_params"] == {"KSampler.cfg": 4.0} and item["wf_subs"] == [["32", "cfg", 4.0]]
def test_build_collect_item_manual_use_without_conf(create_image_metadata):
item = ws.build_collect_item({}, 64, 64, 0, 0, 0, create_image_metadata=create_image_metadata)
assert item["model_type"] == "workflow" and item["seed"] == 0 and item["positive"] == ""
assert item["width"] == 64 and item["favorited"] is False
def test_build_collect_item_does_not_mutate_conf(create_image_metadata):
conf = dict(CONF)
ws.build_collect_item(conf, 8, 8, 1, 0, 0, create_image_metadata=create_image_metadata)
assert conf == CONF
def _write(path, data):
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w") as f:
json.dump(data, f)
def test_append_items_inserts_newest_first_and_creates_file(manifest_utils, tmp_path):
mp = str(tmp_path / "s" / "manifest.json")
manifest_utils.append_items(mp, [{"id": 1}], session_name="s", default_meta={"model": "x"})
data = manifest_utils.append_items(mp, [{"id": 2}, {"id": 3}], session_name="s", default_meta={"model": "y"})
assert [i["id"] for i in data["items"]] == [3, 2, 1] # each insert(0), like every other writer
assert data["meta"] == {"model": "x"} and data["session_name"] == "s"
with open(mp) as f:
assert json.load(f) == data
def test_append_items_preserves_disk_favorites_and_other_writers(manifest_utils, tmp_path):
mp = str(tmp_path / "s" / "manifest.json")
_write(mp, {"session_name": "s", "meta": {"m": 1}, "items": [{"id": 10, "favorited": True, "note": "keep"}]})
# a standard run (or the user) edits the file between two sweep jobs
d = manifest_utils.append_items(mp, [{"id": 11}])
assert d["items"][1] == {"id": 10, "favorited": True, "note": "keep"}
with open(mp) as f:
on_disk = json.load(f)
on_disk["items"][0]["favorited"] = True # user favourites the new item
on_disk["items"].append({"id": 5}) # another writer appended something
_write(mp, on_disk)
d = manifest_utils.append_items(mp, [{"id": 12}])
ids = [i["id"] for i in d["items"]]
assert ids == [12, 11, 10, 5]
assert next(i for i in d["items"] if i["id"] == 11)["favorited"] is True
def test_append_items_remove_ids_deletes_item_and_file(manifest_utils, tmp_path):
base = tmp_path / "s"
mp = str(base / "manifest.json")
img = base / "images" / "img_old.webp"
img.parent.mkdir(parents=True)
img.write_bytes(b"x")
outside = tmp_path / "keep.webp"
outside.write_bytes(b"x")
_write(mp, {"items": [{"id": 1, "file": "/view?filename=img_old.webp&type=output&subfolder=benchmarks/s/images"},
{"id": 2, "file": "/view?filename=../../keep.webp&type=output&subfolder=x"},
{"id": 3}]})
d = manifest_utils.append_items(mp, [{"id": 9}], remove_ids=[1, 2])
assert [i["id"] for i in d["items"]] == [9, 3]
assert not img.exists()
assert outside.exists() # never deletes outside <session>/images
def test_append_items_tolerates_corrupt_manifest(manifest_utils, tmp_path):
mp = tmp_path / "s" / "manifest.json"
mp.parent.mkdir()
mp.write_text("{not json")
d = manifest_utils.append_items(str(mp), [{"id": 1}], session_name="s")
assert d["items"] == [{"id": 1}]
def test_session_paths_sanitises_name(monkeypatch, tmp_path):
import folder_paths
monkeypatch.setattr(folder_paths, "get_output_directory", lambda: str(tmp_path), raising=False)
p = ws.session_paths("../../evil name!")
assert p["session"] == "evilname"
assert p["base"] == os.path.join(str(tmp_path), "benchmarks", "evilname")
assert os.path.commonpath([p["manifest"], str(tmp_path / "benchmarks")]) == str(tmp_path / "benchmarks")
assert ws.session_paths("")["session"] == "default_session"