Files
Isulion-ComfyUI_Isulion/tests/test_selectors.py
T
IsulionandClaude Opus 5.5 96553dee1c feat: v6.1.0 — Censored Part & Prompt Vault nodes, selector RNG, hardening
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>
2026-10-20 18:35:56 +02:00

148 lines
4.9 KiB
Python

"""Seeded-selection determinism checks for representative selector nodes.
Covered: Isulion_AnimalRandom, IsulionTechGenerator, Isulion_EmotionGenerator,
IsulionAlienWorldGenerator. All nodes return their (possibly new) seed as the
LAST element of the output tuple.
"""
import random
from isulion_testkit import import_package
def _callers():
"""Per-node callables taking (randomize, seed) -> output tuple."""
pkg = import_package()
mappings = pkg.NODE_CLASS_MAPPINGS
animal = mappings["IsulionAnimalRandom"]()
tech = mappings["IsulionTechGenerator"]()
emotion = mappings["IsulionEmotionGenerator"]()
alien = mappings["IsulionAlienWorldGenerator"]()
def call_animal(randomize, seed):
return animal.random_animal(randomize=randomize, seed=seed, animal="Cat")
def call_tech(randomize, seed):
return tech.generate(randomize=randomize, tech="quantum blade",
seed=seed, tech_type="any")
def call_emotion(randomize, seed):
return emotion.generate_emotion(randomize=randomize, seed=seed,
emotion="Happy")
def call_alien(randomize, seed):
return alien.generate(randomize=randomize, atmosphere="toxic",
terrain="floating islands", color="purple",
feature="binary suns", seed=seed)
return {
"Isulion_AnimalRandom": call_animal,
"IsulionTechGenerator": call_tech,
"Isulion_EmotionGenerator": call_emotion,
"IsulionAlienWorldGenerator": call_alien,
}
def _assert_seeded_path_deterministic_and_pollution_free(call):
# Deterministic across calls with the same seed...
random.seed(1111)
first = call("enable", 42)
random.seed(2222)
second = call("enable", 42)
assert first == second
assert first[-1] == 42
# ...and unaffected by external global-random pollution.
random.seed(987654321)
third = call("enable", 42)
assert third == first
def _assert_disable_path_passthrough(call, extra_check):
result = call("disable", 77)
assert result[-1] == 77, "disable path must return the original seed"
extra_check(result)
def _assert_fresh_seed_zero_path(call):
out = call("enable", 0)
fresh_seed = out[-1]
assert isinstance(fresh_seed, int) and fresh_seed > 0, (
f"enable + seed=0 must report a fresh positive seed, got {fresh_seed!r}"
)
replay = call("enable", fresh_seed)
assert replay[:-1] == out[:-1], (
"replaying the freshly reported seed must reproduce the same choices"
)
def test_animal_random_seeded_deterministic_and_pollution_free():
_assert_seeded_path_deterministic_and_pollution_free(
_callers()["Isulion_AnimalRandom"])
def test_animal_random_disable_returns_dropdown_value_and_original_seed():
def check(result):
assert result[0] == "Cat", result
_assert_disable_path_passthrough(_callers()["Isulion_AnimalRandom"], check)
def test_animal_random_fresh_seed_zero_is_positive_and_self_consistent():
_assert_fresh_seed_zero_path(_callers()["Isulion_AnimalRandom"])
def test_tech_generator_seeded_deterministic_and_pollution_free():
_assert_seeded_path_deterministic_and_pollution_free(
_callers()["IsulionTechGenerator"])
def test_tech_generator_disable_returns_dropdown_value_and_original_seed():
def check(result):
assert result[0] == "quantum blade", result
_assert_disable_path_passthrough(_callers()["IsulionTechGenerator"], check)
def test_tech_generator_fresh_seed_zero_is_positive_and_self_consistent():
_assert_fresh_seed_zero_path(_callers()["IsulionTechGenerator"])
def test_emotion_generator_seeded_deterministic_and_pollution_free():
_assert_seeded_path_deterministic_and_pollution_free(
_callers()["Isulion_EmotionGenerator"])
def test_emotion_generator_disable_keeps_emotion_and_original_seed():
def check(result):
prompt, emotion = result[0], result[1]
assert emotion == "Happy", result
assert "happy" in prompt, result
_assert_disable_path_passthrough(_callers()["Isulion_EmotionGenerator"], check)
def test_emotion_generator_fresh_seed_zero_is_positive_and_self_consistent():
_assert_fresh_seed_zero_path(_callers()["Isulion_EmotionGenerator"])
def test_alien_world_seeded_deterministic_and_pollution_free():
_assert_seeded_path_deterministic_and_pollution_free(
_callers()["IsulionAlienWorldGenerator"])
def test_alien_world_disable_composes_dropdown_values_with_original_seed():
def check(result):
desc = result[0]
for fragment in ("toxic", "floating islands", "purple", "binary suns"):
assert fragment in desc, (fragment, desc)
_assert_disable_path_passthrough(_callers()["IsulionAlienWorldGenerator"], check)
def test_alien_world_fresh_seed_zero_is_positive_and_self_consistent():
_assert_fresh_seed_zero_path(_callers()["IsulionAlienWorldGenerator"])