""" Additional coverage tests for parser_impl.py helper functions. Targets uncovered lines: 61, 64, 71, 77, 79-80, 103, 105, 119-121, 125-127, 131-133, 145-152, 156-164, 168-175, 179-189, 200-218, 228-250, 256, 262, 270-307, 315-428, 432-494, 574-620, 636-640, ... """ from __future__ import annotations from mjr_am_backend.features.geninfo import parser_impl as p # ─── _clean_model_id edge cases ──────────────────────────────────────────── def test_clean_model_id_none(): assert p._clean_model_id(None) is None def test_clean_model_id_empty_string(): assert p._clean_model_id("") is None assert p._clean_model_id(" ") is None def test_clean_model_id_no_extension(): assert p._clean_model_id("mymodel") == "mymodel" def test_clean_model_id_backslash(): assert p._clean_model_id("C:\\models\\foo.safetensors") == "foo" # ─── _to_int edge cases ───────────────────────────────────────────────────── def test_to_int_none(): assert p._to_int(None) is None def test_to_int_invalid(): assert p._to_int("not_a_number") is None def test_to_int_valid(): assert p._to_int("42") == 42 # ─── _looks_like_prompt_string ───────────────────────────────────────────── def test_looks_like_prompt_string_control_chars(): # Has control char below \x09 → should return False assert p._looks_like_prompt_string("abcdefg\x01") is False def test_looks_like_prompt_string_short(): assert p._looks_like_prompt_string("ab") is False def test_looks_like_prompt_string_non_string(): assert p._looks_like_prompt_string(123) is False # ─── _field family empty string paths ────────────────────────────────────── def test_field_empty_string(): assert p._field("", "high", "src") is None def test_field_name_empty_string(): assert p._field_name("", "high", "src") is None def test_field_size_none_height(): assert p._field_size(512, None, "high", "src") is None # ─── _is_sampler ─────────────────────────────────────────────────────────── def test_is_sampler_no_class_type(): assert p._is_sampler({}) is False def test_is_sampler_ksampler(): assert p._is_sampler({"class_type": "KSampler", "inputs": {"steps": 20, "cfg": 7, "seed": 1}}) is True def test_is_sampler_core_signature(): node = {"class_type": "CustomStep", "inputs": {"steps": 20, "cfg": 7.0, "seed": 42}} assert p._is_sampler(node) is True def test_parse_geninfo_prefers_sink_with_real_sampler_branch(): prompt_graph = { "10": {"class_type": "VHS_VideoCombine", "inputs": {"images": ["11", 0]}}, "11": {"class_type": "CreateVideo", "inputs": {"images": ["12", 0]}}, "12": {"class_type": "LoadImage", "inputs": {"image": "preview.png"}}, "20": {"class_type": "SaveVideo", "inputs": {"video": ["21", 0]}}, "21": {"class_type": "CreateVideo", "inputs": {"images": ["22", 0], "fps": ["30", 0]}}, "22": {"class_type": "VAEDecode", "inputs": {"samples": ["23", 0], "vae": ["27", 0]}}, "23": { "class_type": "SamplerCustomAdvanced", "inputs": { "noise": ["24", 0], "guider": ["25", 0], "sampler": ["26", 0], "sigmas": ["28", 0], "latent_image": ["29", 0], }, }, "24": {"class_type": "RandomNoise", "inputs": {"noise_seed": 42}}, "25": {"class_type": "BasicGuider", "inputs": {"conditioning": ["31", 0], "model": ["32", 0]}}, "26": {"class_type": "KSamplerSelect", "inputs": {"sampler_name": "euler"}}, "27": {"class_type": "VAELoader", "inputs": {"vae_name": "ae.safetensors"}}, "28": {"class_type": "BasicScheduler", "inputs": {"steps": 25, "scheduler": "simple", "model": ["32", 0]}}, "29": {"class_type": "EmptyLatentImage", "inputs": {"width": 1024, "height": 576}}, "30": {"class_type": "Float", "inputs": {"value": 24.0}}, "31": {"class_type": "CLIPTextEncode", "inputs": {"text": "Blooming flower", "clip": ["33", 0]}}, "32": {"class_type": "CheckpointLoaderSimple", "inputs": {"ckpt_name": "ltx-2.3-22b-dev.safetensors"}}, "33": {"class_type": "CLIPLoader", "inputs": {"clip_name": "clip.safetensors"}}, } result = p.parse_geninfo_from_prompt(prompt_graph) assert result.ok, result.error assert result.data is not None assert result.data["positive"]["value"] == "Blooming flower" assert result.data["seed"]["value"] == 42 assert result.data["steps"]["value"] == 25 assert result.data["sampler"]["name"] == "euler" def test_parse_geninfo_traces_final_generated_prompt_through_preview_switch(): prompt_graph = { "1": {"class_type": "SaveImage", "inputs": {"images": ["2", 0]}}, "2": {"class_type": "VAEDecode", "inputs": {"samples": ["3", 0], "vae": ["9", 0]}}, "3": { "class_type": "KSampler", "inputs": { "seed": 11, "steps": 8, "cfg": 1.0, "sampler_name": "euler", "scheduler": "simple", "denoise": 1.0, "model": ["8", 0], "positive": ["4", 0], "negative": ["10", 0], "latent_image": ["11", 0], }, }, "4": {"class_type": "CLIPTextEncode", "inputs": {"text": ["5", 0], "clip": ["7", 0]}}, "5": { "class_type": "ComfySwitchNode", "inputs": {"switch": ["12", 0], "on_false": ["6", 0], "on_true": ["13", 0]}, }, "6": { "class_type": "PreviewAny", "inputs": { "source": ["14", 0], "text_0": "final enhanced prompt with surreal glass and ink doodles", }, }, "7": {"class_type": "CLIPLoader", "inputs": {"clip_name": "clip.safetensors"}}, "8": {"class_type": "UNETLoader", "inputs": {"unet_name": "model.safetensors"}}, "9": {"class_type": "VAELoader", "inputs": {"vae_name": "vae.safetensors"}}, "10": {"class_type": "ConditioningZeroOut", "inputs": {"conditioning": ["4", 0]}}, "11": {"class_type": "EmptyLatentImage", "inputs": {"width": 1024, "height": 1024}}, "12": {"class_type": "PrimitiveBoolean", "inputs": {"value": False}}, "13": { "class_type": "StringConcatenate", "inputs": {"string_a": ["6", 0], "string_b": "trigger", "delimiter": ", "}, }, "14": { "class_type": "TextGenerate", "inputs": {"prompt": ["15", 0], "max_length": 512}, }, "15": { "class_type": "PrimitiveStringMultiline", "inputs": {"value": "original short user prompt"}, }, } result = p.parse_geninfo_from_prompt(prompt_graph) assert result.ok, result.error assert result.data is not None assert result.data["positive"]["value"] == "final enhanced prompt with surreal glass and ink doodles" assert result.data["positive"]["source"] == "CLIPTextEncode:4" def test_parse_geninfo_generator_fallback_uses_user_input_not_system_template(): prompt_graph = { "1": {"class_type": "SaveImage", "inputs": {"images": ["2", 0]}}, "2": {"class_type": "VAEDecode", "inputs": {"samples": ["3", 0], "vae": ["9", 0]}}, "3": { "class_type": "KSampler", "inputs": { "seed": 11, "steps": 8, "cfg": 1.0, "sampler_name": "euler", "scheduler": "simple", "model": ["8", 0], "positive": ["4", 0], "negative": ["10", 0], "latent_image": ["11", 0], }, }, "4": {"class_type": "CLIPTextEncode", "inputs": {"text": ["5", 0], "clip": ["7", 0]}}, "5": {"class_type": "PreviewAny", "inputs": {"source": ["6", 0]}}, "6": {"class_type": "TextGenerate", "inputs": {"prompt": ["12", 0], "max_length": 512}}, "7": {"class_type": "CLIPLoader", "inputs": {"clip_name": "clip.safetensors"}}, "8": {"class_type": "UNETLoader", "inputs": {"unet_name": "model.safetensors"}}, "9": {"class_type": "VAELoader", "inputs": {"vae_name": "vae.safetensors"}}, "10": {"class_type": "ConditioningZeroOut", "inputs": {"conditioning": ["4", 0]}}, "11": {"class_type": "EmptyLatentImage", "inputs": {"width": 1024, "height": 1024}}, "12": { "class_type": "StringConcatenate", "inputs": { "string_a": ["13", 0], "string_b": ["14", 0], "delimiter": "", }, }, "13": { "class_type": "PrimitiveStringMultiline", "inputs": {"value": "You are an expert prompt engineer.\n\nUser's Input:\n\n"}, }, "14": { "class_type": "PrimitiveStringMultiline", "inputs": {"value": "original detailed martini glass illustration prompt"}, }, } result = p.parse_geninfo_from_prompt(prompt_graph) assert result.ok, result.error assert result.data is not None assert result.data["positive"]["value"] == "original detailed martini glass illustration prompt" # ─── _is_named_sampler_type ──────────────────────────────────────────────── def test_is_named_sampler_ksampler_select(): # "ksampler" + "select" → False assert p._is_named_sampler_type("ksamplerselect") is False def test_is_named_sampler_ksampler_only(): assert p._is_named_sampler_type("ksampler") is True def test_is_named_sampler_iterative(): assert p._is_named_sampler_type("iterativelatentupscale") is True def test_is_named_sampler_marigold(): assert p._is_named_sampler_type("marigold_depth") is True def test_is_named_sampler_flux_params(): assert p._is_named_sampler_type("fluxparams") is True def test_is_named_sampler_flux_sampler(): assert p._is_named_sampler_type("fluxsampler") is True def test_is_named_sampler_flux2(): assert p._is_named_sampler_type("flux2") is True def test_is_named_sampler_flux_2(): assert p._is_named_sampler_type("my_flux_2_node") is True def test_is_named_sampler_unrelated(): assert p._is_named_sampler_type("saveimage") is False # ─── _has_core_sampler_signature ─────────────────────────────────────────── def test_has_core_sampler_signature_exception(): # Pass something where _inputs() would fail assert p._has_core_sampler_signature({"inputs": None}) is False def test_has_core_sampler_signature_missing_cfg(): node = {"inputs": {"steps": 10, "seed": 1}} assert p._has_core_sampler_signature(node) is False def test_has_core_sampler_signature_complete(): node = {"inputs": {"steps": 10, "cfg": 7.0, "seed": 1}} assert p._has_core_sampler_signature(node) is True def test_has_core_sampler_signature_guidance(): node = {"inputs": {"steps": 10, "guidance": 3.5, "noise_seed": 42}} assert p._has_core_sampler_signature(node) is True # ─── _is_custom_sampler ──────────────────────────────────────────────────── def test_is_custom_sampler_no_sampler_in_ct(): assert p._is_custom_sampler({"inputs": {}}, "saveimage") is False def test_is_custom_sampler_select_in_ct(): assert p._is_custom_sampler({"inputs": {}}, "mysamplerselect") is False def test_is_custom_sampler_with_model_link(): node = {"inputs": {"model": ["1", 0]}} assert p._is_custom_sampler(node, "mysampler") is True def test_is_custom_sampler_with_text_embeds_link(): node = {"inputs": {"text_embeds": ["2", 0]}} assert p._is_custom_sampler(node, "videosampler") is True def test_is_custom_sampler_with_hyvid_embeds(): node = {"inputs": {"hyvid_embeds": ["3", 0]}} assert p._is_custom_sampler(node, "hyvidsampler") is True def test_is_custom_sampler_inputs_exception(): # inputs=None triggers except path assert p._is_custom_sampler({"inputs": None}, "sampler") is False # ─── _is_advanced_sampler ────────────────────────────────────────────────── def test_is_advanced_sampler_no_ct(): assert p._is_advanced_sampler({}) is False def test_is_advanced_sampler_samplercustom(): assert p._is_advanced_sampler({"class_type": "SamplerCustomAdvanced", "inputs": {}}) is True def test_is_advanced_sampler_guider_sigmas(): node = {"class_type": "FlowSampler", "inputs": {"guider": ["1", 0], "sigmas": ["2", 0]}} assert p._is_advanced_sampler(node) is True def test_is_advanced_sampler_guider_sampler(): node = {"class_type": "FlowSampler", "inputs": {"guider": ["1", 0], "sampler": ["2", 0]}} assert p._is_advanced_sampler(node) is True def test_is_advanced_sampler_all_four(): node = {"class_type": "X", "inputs": { "noise": ["1", 0], "guider": ["2", 0], "sampler": ["3", 0], "sigmas": ["4", 0] }} assert p._is_advanced_sampler(node) is True def test_is_advanced_sampler_exception_path(): # inputs=None → exception → False assert p._is_advanced_sampler({"class_type": "X", "inputs": None}) is False # ─── _extract_posneg_from_text_embeds ────────────────────────────────────── def test_extract_posneg_walk_returns_none(): pos, neg = p._extract_posneg_from_text_embeds({}, None) assert pos is None and neg is None def test_extract_posneg_node_not_found(): nodes = {"1": {"class_type": "X", "inputs": {}}} pos, neg = p._extract_posneg_from_text_embeds(nodes, ["99", 0]) assert pos is None and neg is None def test_extract_posneg_with_prompts(): nodes = { "1": {"class_type": "WanVideoTextEncode", "inputs": { "positive_prompt": "beautiful landscape", "negative_prompt": "ugly", }} } pos, neg = p._extract_posneg_from_text_embeds(nodes, ["1", 0]) assert pos is not None and pos[0] == "beautiful landscape" assert neg is not None and neg[0] == "ugly" def test_extract_posneg_no_prompts(): nodes = {"2": {"class_type": "EmptyNode", "inputs": {"other": 1}}} pos, neg = p._extract_posneg_from_text_embeds(nodes, ["2", 0]) assert pos is None and neg is None # ─── _select_primary_sampler / _select_advanced_sampler ──────────────────── def test_select_primary_sampler_no_sink(): nodes = {"1": {"class_type": "NoSink", "inputs": {}}} sid, conf = p._select_primary_sampler(nodes, "1") assert sid is None def test_select_advanced_sampler_no_sink(): nodes = {} sid, conf = p._select_advanced_sampler(nodes, "99") assert sid is None # ─── _select_sampler_from_sink / _sink_start_source ──────────────────────── def test_sink_start_source_not_dict(): assert p._sink_start_source({}, "99") is None def test_sink_start_source_no_input_link(): nodes = {"1": {"class_type": "SaveImage", "inputs": {}}} assert p._sink_start_source(nodes, "1") is None def test_sink_start_source_with_link(): nodes = { "1": {"class_type": "SaveImage", "inputs": {"images": ["2", 0]}}, "2": {"class_type": "KSampler", "inputs": {"steps": 20, "cfg": 7, "seed": 1}}, } src = p._sink_start_source(nodes, "1") assert src == "2" # ─── _best_candidate ─────────────────────────────────────────────────────── def test_best_candidate_empty(): assert p._best_candidate([]) == (None, "none") def test_best_candidate_single(): assert p._best_candidate([("n1", 0)]) == ("n1", "high") def test_best_candidate_multiple_same_depth(): result = p._best_candidate([("n1", 2), ("n2", 2)]) assert result[1] == "medium" def test_best_candidate_multiple_different_depth(): result = p._best_candidate([("n1", 3), ("n2", 1)]) assert result[0] == "n2" and result[1] == "high" # ─── _select_any_sampler ─────────────────────────────────────────────────── def test_select_any_sampler_no_candidates(): nodes = {"1": {"class_type": "SaveImage", "inputs": {}}} nid, conf = p._select_any_sampler(nodes) assert nid is None and conf == "none" def test_select_any_sampler_with_ksampler(): nodes = { "5": {"class_type": "KSampler", "inputs": {"steps": 20, "cfg": 7, "seed": 1, "model": ["0", 0], "positive": ["1", 0]}}, } nid, conf = p._select_any_sampler(nodes) assert nid == "5" and conf == "low" # ─── _sampler_candidate_score ────────────────────────────────────────────── def test_sampler_candidate_score_empty(): assert p._sampler_candidate_score({}) == 0 def test_sampler_candidate_score_model_positive(): ins = {"model": ["1", 0], "positive": ["2", 0], "steps": 20} score = p._sampler_candidate_score(ins) assert score >= 7 # ─── _stable_numeric_node_id ─────────────────────────────────────────────── def test_stable_numeric_node_id_valid(): assert p._stable_numeric_node_id("42") == 42 def test_stable_numeric_node_id_invalid(): assert p._stable_numeric_node_id("abc") == 10 ** 9 # ─── _trace_sampler_name ─────────────────────────────────────────────────── def test_trace_sampler_name_no_walk(): assert p._trace_sampler_name({}, None) is None def test_trace_sampler_name_node_not_dict(): nodes = {} assert p._trace_sampler_name(nodes, ["99", 0]) is None def test_trace_sampler_name_found(): nodes = {"1": {"class_type": "KSamplerSelect", "inputs": {"sampler_name": "euler"}}} result = p._trace_sampler_name(nodes, ["1", 0]) assert result is not None and result[0] == "euler" # ─── _trace_noise_seed ───────────────────────────────────────────────────── def test_trace_noise_seed_no_walk(): assert p._trace_noise_seed({}, None) is None def test_trace_noise_seed_node_not_dict(): assert p._trace_noise_seed({}, ["99", 0]) is None def test_trace_noise_seed_found(): nodes = {"3": {"class_type": "RandomNoise", "inputs": {"noise_seed": 12345}}} result = p._trace_noise_seed(nodes, ["3", 0]) assert result is not None and result[0] == 12345 # ─── _trace_scheduler_sigmas ─────────────────────────────────────────────── def test_trace_scheduler_sigmas_no_walk(): result = p._trace_scheduler_sigmas({}, None) assert result[0] is None and result[1] is None def test_trace_scheduler_sigmas_found(): nodes = { "5": {"class_type": "BasicScheduler", "inputs": {"steps": 25, "scheduler": "karras", "denoise": 1.0}} } steps, scheduler, denoise, model_link, src_tuple, steps_conf = p._trace_scheduler_sigmas(nodes, ["5", 0]) assert steps == 25 and scheduler == "karras" and denoise == 1.0 # ─── _steps_from_manual_sigmas ───────────────────────────────────────────── def test_steps_from_manual_sigmas_too_few(): ins = {"sigmas": "0.9"} steps, conf = p._steps_from_manual_sigmas(ins) assert steps is None def test_steps_from_manual_sigmas_enough(): ins = {"sigmas": "1.0, 0.7, 0.4, 0.1"} steps, conf = p._steps_from_manual_sigmas(ins) assert steps == 3 # 4 values → max(1, 4-1) # ─── _count_numeric_sigma_values ─────────────────────────────────────────── def test_count_numeric_sigma_values_non_string(): assert p._count_numeric_sigma_values(None) == 0 assert p._count_numeric_sigma_values(42) == 0 def test_count_numeric_sigma_values_csv(): assert p._count_numeric_sigma_values("1.0, 0.5, 0.2") == 3 def test_count_numeric_sigma_values_mixed(): assert p._count_numeric_sigma_values("1.0, bad, 0.2") == 2 # ─── _trace_guidance_from_conditioning ───────────────────────────────────── def test_trace_guidance_from_conditioning_no_walk(): assert p._trace_guidance_from_conditioning({}, None) is None def test_trace_guidance_from_conditioning_found(): nodes = { "1": {"class_type": "CLIPTextEncode", "inputs": {"clip": ["9", 0], "text": "hi", "guidance": 3.5}}, } result = p._trace_guidance_from_conditioning(nodes, ["1", 0]) assert result is not None and result[0] == 3.5 # ─── _scalar ─────────────────────────────────────────────────────────────── def test_scalar_none(): assert p._scalar(None) is None def test_scalar_int(): assert p._scalar(42) == 42 def test_scalar_float(): assert p._scalar(3.14) == 3.14 def test_scalar_list(): assert p._scalar([1, 2]) is None # ─── _extract_ksampler_widget_params ─────────────────────────────────────── def test_extract_ksampler_widget_params_not_dict(): assert p._extract_ksampler_widget_params("not_a_dict") == {} def test_extract_ksampler_widget_params_not_ksampler(): node = {"class_type": "SaveImage", "inputs": {}} assert p._extract_ksampler_widget_params(node) == {} def test_extract_ksampler_widget_params_no_widgets(): node = {"class_type": "KSampler", "inputs": {}} assert p._extract_ksampler_widget_params(node) == {} def test_extract_ksampler_widget_params_full(): node = {"class_type": "KSampler", "widgets_values": [111, "ctrl", 20, 7.5, "euler", "normal", 0.9]} out = p._extract_ksampler_widget_params(node) assert out["seed"] == 111 and out["steps"] == 20 and out["cfg"] == 7.5 assert out["sampler_name"] == "euler" # ─── _ksampler_values_from_widgets ───────────────────────────────────────── def test_ksampler_values_from_widgets_short(): out = p._ksampler_values_from_widgets([42]) assert out["seed"] == 42 assert "steps" not in out # index 2 missing # ─── _extract_lyrics_from_prompt_nodes ───────────────────────────────────── def test_extract_lyrics_from_prompt_nodes_none(): nodes = {"1": {"class_type": "SaveImage", "inputs": {}}} lyr, strength, src = p._extract_lyrics_from_prompt_nodes(nodes) assert lyr is None def test_extract_lyrics_from_prompt_nodes_with_acestep(): nodes = { "1": { "class_type": "AceStep15TaskTextEncode", "inputs": {"task_text": "hello lyrics", "lyrics_strength": 0.8}, } } lyr, strength, src = p._extract_lyrics_from_prompt_nodes(nodes) assert lyr == "hello lyrics" assert strength == 0.8 # ─── _extract_lyrics_from_inputs ─────────────────────────────────────────── def test_extract_lyrics_from_inputs_no_key(): assert p._extract_lyrics_from_inputs({}, "sometextencode") is None def test_extract_lyrics_from_inputs_lyrics_key(): assert p._extract_lyrics_from_inputs({"lyrics": "song text"}, "textnode") == "song text" # ─── _extract_lyrics_strength ────────────────────────────────────────────── def test_extract_lyrics_strength_missing(): assert p._extract_lyrics_strength({}) is None def test_extract_lyrics_strength_present(): assert p._extract_lyrics_strength({"lyrics_strength": 0.75}) == 0.75 # ─── _first_non_empty_string / _first_non_none_scalar ────────────────────── def test_first_non_empty_string_not_found(): assert p._first_non_empty_string({}, ("a", "b")) is None def test_first_non_empty_string_found(): assert p._first_non_empty_string({"b": "hi"}, ("a", "b")) == "hi" def test_first_non_none_scalar_not_found(): assert p._first_non_none_scalar({}, ("a", "b")) is None def test_first_non_none_scalar_found(): assert p._first_non_none_scalar({"a": 5}, ("a", "b")) == 5 # ─── _resolve_scalar_from_link ───────────────────────────────────────────── def test_resolve_scalar_from_link_no_walk(): assert p._resolve_scalar_from_link({}, None) is None def test_resolve_scalar_from_link_node_not_dict(): assert p._resolve_scalar_from_link({}, ["99", 0]) is None def test_resolve_scalar_from_link_found(): nodes = {"4": {"class_type": "Primitive", "inputs": {"value": 77}}} result = p._resolve_scalar_from_link(nodes, ["4", 0]) assert result == 77 # ─── _extract_text ───────────────────────────────────────────────────────── def test_extract_text_no_walk(): assert p._extract_text({}, None) is None def test_extract_text_node_not_dict(): assert p._extract_text({}, ["99", 0]) is None def test_extract_text_no_candidates(): nodes = {"5": {"class_type": "Other", "inputs": {"x": 1}}} assert p._extract_text(nodes, ["5", 0]) is None def test_extract_text_found(): nodes = {"6": {"class_type": "CLIPTextEncode", "inputs": {"text": "my prompt", "clip": ["9", 0]}}} result = p._extract_text(nodes, ["6", 0]) assert result is not None and "my prompt" in result[0] # ─── _looks_like_conditioning_text ───────────────────────────────────────── def test_looks_like_conditioning_text_wrong_ct(): assert p._looks_like_conditioning_text({"class_type": "LoadImage", "inputs": {"text": "x" * 10}}) is False def test_looks_like_conditioning_text_ok(): node = {"class_type": "PromptNode", "inputs": {"text": "a nice prompt"}} assert p._looks_like_conditioning_text(node) is True # ─── _conditioning_key_allowed ───────────────────────────────────────────── def test_conditioning_key_allowed_positive_branch_rejects_negative(): assert p._conditioning_key_allowed("negative_prompt", "positive") is False def test_conditioning_key_allowed_negative_branch_rejects_positive(): assert p._conditioning_key_allowed("positive_prompt", "negative") is False def test_conditioning_key_allowed_no_branch(): assert p._conditioning_key_allowed("anything", None) is True # ─── _conditioning_should_expand ─────────────────────────────────────────── def test_conditioning_should_expand_zeroout_blocked(): node = {"class_type": "ConditioningZeroOut", "inputs": {}} assert p._conditioning_should_expand(node, "negative") is False def test_conditioning_should_expand_reroute(): node = {"class_type": "Reroute", "inputs": {"x": ["1", 0]}} assert p._conditioning_should_expand(node, None) is True def test_conditioning_should_expand_has_conditioning_input(): node = {"class_type": "SomeNode", "inputs": {"conditioning": ["1", 0]}} assert p._conditioning_should_expand(node, None) is True # ─── _select_sampler_context ─────────────────────────────────────────────── def test_select_sampler_context_no_sinks(): nodes = {"1": {"class_type": "NoSink", "inputs": {}}} sid, conf, mode = p._select_sampler_context(nodes, "1") assert mode in {"primary", "advanced", "global", "fallback"}