Files
JasonHoku-ComfyUI-Ultimate-…/tests/test_examples.py
T
Jason HokuandClaude Fable 5.1 8ce4398c4d feat(builder-polish): run-API full pass, vae drift fix, honest minimize_reloads wording
- Record session_settings + has_optional_inputs in manifest meta (run_meta_extras) so the
  Dashboard "Full pass for survivors" can queue POST /config_tester/run headless; the iframe
  falls through to it when the parent has no Builder / no queuePrompt.
- Exclude the deliberately omitted "vae" key from _EXPECTED_CONFIG_KEYS; test_examples asserts zero drift.
- minimize_reloads: log VAE switches + a "changed nothing" note, accurate tooltip/README wording.
- examples/README: Klein base-variant note; fix stale LTX stub comment.
- Tests: run_meta_extras, job order, state_to_configs_json, e2e full pass (new file).

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

186 lines
7.9 KiB
Python

"""Validation of the shipped examples/*.json workflows (no torch, no ComfyUI).
Checks the graph is well formed, the Builder state restores/emits correctly for
each model family, and the image counts promised in examples/README.md are real.
"""
import ast
import glob
import json
import os
import re
import sys
import pytest
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from config_builder_node import UltimateConfigBuilder
from config_utils import expand_configs
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
EX_DIR = os.path.join(ROOT, "examples")
FILES = sorted(glob.glob(os.path.join(EX_DIR, "*.json")))
# Images each example expands to (must match the "**N images**" line in examples/README.md).
EXPECTED_IMAGES = {
"uscg-sdxl-illustrious.json": 16,
"uscg-anima.json": 4,
"uscg-z-image-turbo.json": 2,
"uscg-krea2-turbo.json": 2,
"uscg-flux2-klein-9b.json": 4, # Advanced Sampling on + off (expand_configs grids both)
"uscg-moss-image.json": 2,
}
# Every top-level key of the Builder's new-state object (web/conf_builder/conf-builder-main.js,
# "Initialize default state structure"). A saved state missing one would silently fall back to defaults.
STATE_KEYS = {
"session_name", "config_name", "auto_save", "include_none", "label_mode", "global_positive_groups",
"global_negative", "distribution_enabled", "worker_urls", "claim_timeout", "use_master_encoding",
"config_arrays", "upscaling", "cooldown", "image_format", "overwrite_existing", "flush_batch_every",
"lora_triggerwords_mode", "save_conditioning_cache_to_file", "enable_model_cache", "vae_batch_size",
"add_random_seeds_to_gens",
}
# Filenames the official template set uses, per example (placeholders are only valid if listed here).
ALLOWED_FILES = {
"uscg-sdxl-illustrious.json": {"sd_xl_base_1.0.safetensors"},
"uscg-anima.json": {"anima-base-v1.0.safetensors", "qwen_3_06b_base.safetensors", "qwen_image_vae.safetensors"},
"uscg-z-image-turbo.json": {"z_image_turbo_bf16.safetensors", "qwen_3_4b.safetensors", "ae.safetensors"},
"uscg-krea2-turbo.json": {"krea2_turbo_fp8_scaled.safetensors", "qwen3vl_4b_fp8_scaled.safetensors", "qwen_image_vae.safetensors"},
"uscg-flux2-klein-9b.json": {"flux-2-klein-9b-fp8.safetensors", "qwen_3_8b_fp8mixed.safetensors", "flux2-vae.safetensors"},
"uscg-moss-image.json": {"moss_image_sft_int8_convrot.safetensors", "qwen3.5_9b_w4a8_gptq.safetensors", "flux2-vae.safetensors"},
}
def _node_class_names():
tree = ast.parse(open(os.path.join(ROOT, "__init__.py"), encoding="utf-8").read())
for node in ast.walk(tree):
if isinstance(node, ast.Assign) and any(getattr(t, "id", "") == "NODE_CLASS_MAPPINGS" for t in node.targets):
return {k.value for k in node.value.keys}
raise AssertionError("NODE_CLASS_MAPPINGS not found")
def _load(path):
return json.load(open(path, encoding="utf-8"))
def _state(wf):
builder = next(n for n in wf["nodes"] if n["type"] == "UltimateConfigBuilder")
return json.loads(builder["widgets_values"][6])
def test_example_files_present():
assert {os.path.basename(f) for f in FILES} == set(EXPECTED_IMAGES)
@pytest.mark.parametrize("path", FILES, ids=os.path.basename)
def test_graph_is_well_formed(path):
wf = _load(path)
assert {"version", "nodes", "links"} <= set(wf)
known = _node_class_names() | {"MarkdownNote", "Note"}
types = [n["type"] for n in wf["nodes"]]
assert set(types) <= known
for t in ("UltimateConfigBuilder", "UltimateSamplerGrid", "UltimateGridDashboard"):
assert types.count(t) == 1
node_ids = [n["id"] for n in wf["nodes"]]
assert len(set(node_ids)) == len(node_ids)
link_ids = [l[0] for l in wf["links"]]
assert len(set(link_ids)) == len(link_ids)
assert wf["last_node_id"] >= max(node_ids)
assert wf["last_link_id"] >= max(link_ids)
by_id = {n["id"]: n for n in wf["nodes"]}
for lid, src, src_slot, dst, dst_slot, typ in wf["links"]:
assert lid in by_id[src]["outputs"][src_slot]["links"]
assert by_id[dst]["inputs"][dst_slot]["link"] == lid
assert by_id[src]["outputs"][src_slot]["type"] == typ == by_id[dst]["inputs"][dst_slot]["type"]
for n in wf["nodes"]:
for i in n["inputs"]:
if i.get("link") is not None:
assert i["link"] in link_ids
for o in n["outputs"]:
for l in o.get("links") or []:
assert l in link_ids
builder = next(n for n in wf["nodes"] if n["type"] == "UltimateConfigBuilder")
gen = next(n for n in wf["nodes"] if n["type"] == "UltimateSamplerGrid")
dash = next(n for n in wf["nodes"] if n["type"] == "UltimateGridDashboard")
assert any(l[1] == builder["id"] and l[2] == 0 and l[3] == gen["id"] and gen["inputs"][l[4]]["name"] == "configs_json" for l in wf["links"])
assert any(l[1] == gen["id"] and l[2] == 0 and l[3] == dash["id"] and dash["inputs"][l[4]]["name"] == "dashboard_html" for l in wf["links"])
@pytest.mark.parametrize("path", FILES, ids=os.path.basename)
def test_builder_state_restorable(path):
wf = _load(path)
builder = next(n for n in wf["nodes"] if n["type"] == "UltimateConfigBuilder")
assert len(builder["widgets_values"]) == 9
state = _state(wf)
assert state["config_arrays"]
assert STATE_KEYS <= set(state)
dash = next(n for n in wf["nodes"] if n["type"] == "UltimateGridDashboard")
assert dash["widgets_values"][0] == state["session_name"]
@pytest.mark.parametrize("path", FILES, ids=os.path.basename)
def test_emits_and_counts(path, capsys):
name = os.path.basename(path)
state = _state(_load(path))
out = json.loads(UltimateConfigBuilder.state_to_configs_json(state))
drift = [l for l in capsys.readouterr().out.splitlines() if "Config schema drift" in l]
assert not drift, drift
assert out["_session_settings"]["add_random_seeds_to_gens"] == 0
count = len(expand_configs(out["configs"], [""], [""], [1.0], 0, [], None))
assert count == EXPECTED_IMAGES[name]
readme = open(os.path.join(EX_DIR, "README.md"), encoding="utf-8").read()
assert f"**{count} images**" in readme
@pytest.mark.parametrize("path", FILES, ids=os.path.basename)
def test_no_local_paths_and_known_filenames(path):
name = os.path.basename(path)
text = open(path, encoding="utf-8").read()
assert "\\\\" not in text.replace("\\n", "") # no escaped backslashes (Windows paths)
assert not re.search(r"[A-Za-z]:[\\/]", text.replace("\\n", " "))
assert "Users" not in text
state = _state(_load(path))
used = set()
for a in state["config_arrays"]:
for m in a["models"]:
used.add(m["path"] if isinstance(m, dict) else m)
used.update(a["text_encoders"])
used.update(v for v in a["vaes"] if v != "None")
assert used <= ALLOWED_FILES[name]
def _cfgs(key):
return json.loads(UltimateConfigBuilder.state_to_configs_json(_state(_load(os.path.join(EX_DIR, key)))))["configs"]
def test_family_settings():
sdxl = _cfgs("uscg-sdxl-illustrious.json")[0]
assert sdxl["model_type"] == "checkpoint"
anima = _cfgs("uscg-anima.json")[0]
assert anima["model_type"] == "diffusion_model"
assert anima["clip_type"] == "stable_diffusion"
assert anima["text_encoders"] == ["qwen_3_06b_base.safetensors"]
assert anima["vae"] == "qwen_image_vae.safetensors"
z = _cfgs("uscg-z-image-turbo.json")[0]
assert z["clip_type"] == "lumina2"
assert z["model_sampling_override"] == "aura_flow"
assert _cfgs("uscg-krea2-turbo.json")[0]["clip_type"] == "krea2"
klein = _cfgs("uscg-flux2-klein-9b.json")[0]
assert klein["clip_type"] == "flux2"
assert klein["advanced_scheduler"] == "flux2"
assert klein["use_advanced_sampling"] is True
moss = _cfgs("uscg-moss-image.json")
assert len(moss) == 2
assert moss[0]["clip_type"] == "moss_image"
assert moss[0]["use_slg"] is True and moss[0]["slg_single_layers"] == "22"
assert moss[1]["use_slg"] is False