New nodes: - Isulion Censored Part: blur/pixelate/black bar/noise over a mask and/or a percent rectangle; accepts 2D/3D/4D masks and fails closed (blacks the item out) instead of passing an uncensored image through - Prompt Vault Save/Load: named prompt library stored in ComfyUI's user directory (outside the checkout, so updates never conflict), seeded from prompt_vault.example.json; Load re-runs when the vault changes Refactor: - SimpleSelectorNode base: ~20 selector nodes use an instance RNG instead of reseeding the global random module (same seed => same output) - Theme handlers declare DISPLAY_NAME; theme package auto-discovers modules - __init__ imports node classes directly Fixes: - net_utils: re-validate every redirect hop, unwrap IPv4-mapped IPv6, block NAT64 and all non-global addresses (SSRF) - Multiple Prompt Generator: no duplicate prompts for themes listed in two categories; 6 themes missing from every category are now reachable - Shutdown node: parameter renamed to match its "any" input (TypeError) - ConfigManager: deterministic (sorted) merge order, warns on key collisions; drop stale space_colony copy from tropical_paradise_config - Mega Prompt V3: seed honored again with randomize=disable Breaking: Civitai api_key widget removed (use CIVITAI_API_TOKEN), absolute paths refused by Load Images, Negative Prompt pools de-duplicated (shorter "strict" output), Dynamic Random theme order is now alphabetical. Tooling: pytest suite (51 tests), GitHub CI (compileall, pytest, ruff), ruff config, uv.lock. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
42 lines
1.7 KiB
Python
42 lines
1.7 KiB
Python
"""Registration-level checks for the top-level custom-node package."""
|
|
|
|
from inspect import getattr_static
|
|
from isulion_testkit import import_package, load_fixture
|
|
|
|
|
|
def test_node_class_mappings_keys_match_expected_baseline():
|
|
pkg = import_package()
|
|
expected = sorted(load_fixture("expected_node_keys.json"))
|
|
assert sorted(pkg.NODE_CLASS_MAPPINGS.keys()) == expected
|
|
|
|
|
|
def test_every_mapped_class_exposes_core_comfyui_contract():
|
|
pkg = import_package()
|
|
for name in sorted(pkg.NODE_CLASS_MAPPINGS):
|
|
cls = pkg.NODE_CLASS_MAPPINGS[name]
|
|
for attr in ("INPUT_TYPES", "RETURN_TYPES", "FUNCTION", "CATEGORY"):
|
|
assert hasattr(cls, attr), f"{name} is missing required attribute {attr!r}"
|
|
# INPUT_TYPES must be a classmethod (bound to the class itself).
|
|
assert callable(cls.INPUT_TYPES), f"{name}.INPUT_TYPES is not callable"
|
|
assert getattr(cls.INPUT_TYPES, "__self__", None) is cls, (
|
|
f"{name}.INPUT_TYPES must be a @classmethod (bound to the class)"
|
|
)
|
|
static = getattr_static(cls, "INPUT_TYPES", None)
|
|
assert isinstance(static, classmethod), (
|
|
f"{name}.INPUT_TYPES should be declared with @classmethod"
|
|
)
|
|
|
|
|
|
def test_display_name_mappings_cover_every_registered_node():
|
|
pkg = import_package()
|
|
node_keys = set(pkg.NODE_CLASS_MAPPINGS)
|
|
display_keys = set(pkg.NODE_DISPLAY_NAME_MAPPINGS)
|
|
missing = sorted(node_keys - display_keys)
|
|
assert not missing, (
|
|
"NODE_DISPLAY_NAME_MAPPINGS is missing entries for: "
|
|
f"{missing}"
|
|
)
|
|
extras = sorted(display_keys - node_keys)
|
|
if extras:
|
|
print(f"[warn] display-name entries without a node mapping: {extras}")
|