Files
darksidewalker-ComfyUI-DaSi…/test/test_normalize_ref_schema.py
T
Darksidewalker 47a60592de MiniMax H3 Director: normalize ref schema to fix KeyError on mixed v1/v2 workflows
Add normalize_ref_schema() helper that ensures both v1 and v2 ref keys
(subject_defs vs subject_definitions, summary_text vs summary, etc.)
always coexist after merge. Fixes KeyError: 'subject_defs' when loading
workflows where mode/ref-data were saved under different schema versions.

Includes cross-mode compatibility tests.
2026-08-06 09:23:34 +02:00

141 lines
5.9 KiB
Python

"""Tests for normalize_ref_schema cross-mode compatibility."""
import copy
from nodes.helper_minimax_h3_prompt_builder import (
default_builder_state,
normalize_ref_schema,
)
def test_ref2va_defaults_contain_v1_keys_after_normalize():
"""REF2VA defaults use v2 keys; after normalize, v1 keys must exist."""
state = default_builder_state("REF2VA")
normalize_ref_schema(state["ref"])
ref = state["ref"]
# v2 originals
assert "subject_definitions" in ref
assert "summary" in ref
assert "retention_analysis" in ref
assert "detailed_description" in ref
# v1 backfills required by prompt_payload
assert "subject_defs" in ref
assert "summary_text" in ref
assert "retention" in ref
assert "style_line" in ref
assert "detail" in ref
def test_non_ref2va_with_old_v1_ref_still_works():
"""Non-REF2VA mode merges old v1-style ref data without losing keys."""
state = default_builder_state("FL2VA")
old_v1_ref = {
"subject_defs": [{"text": "A cyberpunk cityscape"}, {"text": "Neon rain"}],
"summary_types": ["keyframe completion"],
"summary_text": "Smooth transition between two frames.",
"retention": [
{"label": "city", "context": "Picture 1", "marker": "fully_preserved", "note": "keep layout"}
],
"style_line": "Cinematic lighting, volumetric fog.",
"detail": "Rain on glass, reflections on wet pavement.",
}
state["ref"] = {**default_builder_state("FL2VA")["ref"], **old_v1_ref}
normalize_ref_schema(state["ref"])
ref = state["ref"]
# v1 keys preserved
assert ref["subject_defs"] == old_v1_ref["subject_defs"]
assert ref["summary_text"] == old_v1_ref["summary_text"]
assert len(ref["retention"]) >= 1
assert ref["style_line"] == old_v1_ref["style_line"]
assert ref["detail"] == old_v1_ref["detail"]
# v2 keys derived
assert "subject_definitions" in ref and "cyberpunk cityscape" in ref["subject_definitions"].lower()
assert "summary" in ref and "keyframe completion" in ref["summary"]
assert "retention_analysis" in ref and "city" in ref["retention_analysis"]
assert "detailed_description" in ref and "Cinematic lighting" in ref["detailed_description"]
def test_ref2va_mode_with_legacy_v1_data():
"""REF2VA mode loaded from workflow saved in non-REF2VA era (v1 keys only)."""
state = default_builder_state("REF2VA")
legacy_v1 = {
"subject_defs": [{"text": "Portrait of a woman"}],
"summary_types": ["reference generation"],
"summary_text": "Generate consistent portrait.",
"retention": [],
"style_line": "",
"detail": "",
}
state["ref"] = {**default_builder_state("REF2VA")["ref"], **legacy_v1}
normalize_ref_schema(state["ref"])
ref = state["ref"]
assert ref["subject_defs"] == legacy_v1["subject_defs"]
assert "subject_definitions" in ref and "woman" in ref["subject_definitions"].lower()
assert "summary" in ref and "reference generation" in ref["summary"]
assert "summary_text" in ref
def test_ref2va_mode_with_new_v2_data():
"""REF2VA mode with pure v2 keys still produces v1 fallbacks."""
state = default_builder_state("REF2VA")
v2_only = {
"subject_definitions": "Golden retriever puppy\nMountain lake background",
"summary": "[reference generation] Consistent dog portrait.",
"retention_analysis": "dog: fully_preserved - keep breed features\nbackground: weak_reference - approximate mood",
"detailed_description": "Soft natural light, shallow depth of field.\nPuppy looking at camera with curiosity.",
}
state["ref"] = {**default_builder_state("REF2VA")["ref"], **v2_only}
normalize_ref_schema(state["ref"])
ref = state["ref"]
# v2 originals untouched
assert ref["subject_definitions"] == v2_only["subject_definitions"]
assert ref["summary"] == v2_only["summary"]
assert ref["retention_analysis"] == v2_only["retention_analysis"]
assert ref["detailed_description"] == v2_only["detailed_description"]
# v1 derived
assert isinstance(ref["subject_defs"], list) and len(ref["subject_defs"]) == 2
assert ref["summary_text"] == "Consistent dog portrait."
assert isinstance(ref["retention"], list) and len(ref["retention"]) == 2
assert ref["style_line"] == "Soft natural light, shallow depth of field."
assert ref["detail"] == "Puppy looking at camera with curiosity."
def test_empty_ref_does_not_crash():
"""Empty ref dict should normalize without errors."""
ref = {}
normalize_ref_schema(ref)
assert isinstance(ref["subject_defs"], list)
assert isinstance(ref["retention"], list)
assert isinstance(ref["summary_text"], str)
assert isinstance(ref["style_line"], str)
assert isinstance(ref["detail"], str)
def test_roundtrip_v1_to_v2_to_v1():
"""Converting v1→v2→v1 should preserve core content."""
original = {
"subject_defs": [{"text": "Subject A"}, {"text": "Subject B"}],
"summary_types": ["video editing"],
"summary_text": "Edit sequence smoothly.",
"retention": [
{"label": "face", "context": "Shot 1", "marker": "fully_preserved", "note": "critical"}
],
"style_line": "HDR cinematic.",
"detail": "Slow motion, color graded.",
}
state = default_builder_state("FL2VA")
state["ref"] = {**default_builder_state("FL2VA")["ref"], **copy.deepcopy(original)}
normalize_ref_schema(state["ref"])
ref = state["ref"]
# v1 keys still correct
assert ref["subject_defs"] == original["subject_defs"]
assert ref["summary_text"] == original["summary_text"]
assert len(ref["retention"]) >= 1
assert ref["style_line"] == original["style_line"]
assert ref["detail"] == original["detail"]
# v2 keys contain equivalent info
assert "Subject A" in ref["subject_definitions"]
assert "video editing" in ref["summary"]
assert "face" in ref["retention_analysis"]
assert "HDR cinematic" in ref["detailed_description"]