d3d846f748
Tres funciones para gestionar y ampliar el repositorio de estilos del selector
WAS de ComfyUI (Prompt Styles Selector / Prompt Multiple Styles Selector):
- comfyui_curated_styles_catalog (pure): catálogo curado de 190 estilos en 13
categorías (photography, render3d, painting, anime, pixel, illustration,
comic, lighting, camera, material, scifi, fantasy, mood), formato WAS exacto.
- comfyui_append_styles (impure): merge+dedup no destructivo sobre el styles.json
real, con backup atómico, validación de entradas y preservación de existentes.
- comfyui_generate_styles_llm (impure): genera estilos de una categoría vía
ask_llm (grupo claude-direct); robusta (devuelve {} ante 429/JSON corrupto).
Aplicado en vivo: styles.json 269 -> 503 estilos (+190 curados +44 LLM),
backup hecho, selector verifica 503 en /object_info. Tests offline verdes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
135 lines
4.7 KiB
Python
135 lines
4.7 KiB
Python
"""Tests offline de comfyui_append_styles — no toca la instalación real ni la red.
|
|
|
|
Usa un styles.json temporal en /tmp para validar merge, dedup, backup, validación y dry-run.
|
|
"""
|
|
import json
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
|
|
sys.path.insert(0, os.path.dirname(__file__))
|
|
|
|
from comfyui_append_styles import comfyui_append_styles, DEFAULT_NEGATIVE
|
|
|
|
|
|
def _write_styles(tmpdir: str, data: dict) -> str:
|
|
path = os.path.join(tmpdir, "styles.json")
|
|
with open(path, "w", encoding="utf-8") as fh:
|
|
json.dump(data, fh, ensure_ascii=False, indent=4)
|
|
return path
|
|
|
|
|
|
def test_merge_preserva_existentes_y_anade_nuevos():
|
|
with tempfile.TemporaryDirectory() as d:
|
|
path = _write_styles(d, {
|
|
"A": {"prompt": "a-style", "negative_prompt": "neg-a"},
|
|
"B": {"prompt": "b-style", "negative_prompt": "neg-b"},
|
|
})
|
|
res = comfyui_append_styles(
|
|
{"C": {"prompt": "c-style", "negative_prompt": "neg-c"}},
|
|
styles_path=path,
|
|
)
|
|
assert res["total_before"] == 2
|
|
assert res["total_after"] == 3
|
|
assert res["added"] == ["C"]
|
|
loaded = json.load(open(path, encoding="utf-8"))
|
|
# Los existentes intactos.
|
|
assert loaded["A"] == {"prompt": "a-style", "negative_prompt": "neg-a"}
|
|
assert loaded["B"] == {"prompt": "b-style", "negative_prompt": "neg-b"}
|
|
assert loaded["C"] == {"prompt": "c-style", "negative_prompt": "neg-c"}
|
|
|
|
|
|
def test_dedup_no_pisa_por_defecto():
|
|
with tempfile.TemporaryDirectory() as d:
|
|
path = _write_styles(d, {"A": {"prompt": "orig", "negative_prompt": "n"}})
|
|
res = comfyui_append_styles(
|
|
{"A": {"prompt": "NUEVO", "negative_prompt": "n2"}},
|
|
styles_path=path,
|
|
)
|
|
assert res["skipped_existing"] == ["A"]
|
|
assert res["added"] == []
|
|
assert res["total_after"] == 1
|
|
loaded = json.load(open(path, encoding="utf-8"))
|
|
assert loaded["A"]["prompt"] == "orig" # preservado
|
|
|
|
|
|
def test_overwrite_si_se_pide():
|
|
with tempfile.TemporaryDirectory() as d:
|
|
path = _write_styles(d, {"A": {"prompt": "orig", "negative_prompt": "n"}})
|
|
res = comfyui_append_styles(
|
|
{"A": {"prompt": "NUEVO", "negative_prompt": "n2"}},
|
|
styles_path=path,
|
|
overwrite=True,
|
|
)
|
|
assert res["overwritten"] == ["A"]
|
|
loaded = json.load(open(path, encoding="utf-8"))
|
|
assert loaded["A"]["prompt"] == "NUEVO"
|
|
|
|
|
|
def test_negative_por_defecto_cuando_falta():
|
|
with tempfile.TemporaryDirectory() as d:
|
|
path = _write_styles(d, {})
|
|
res = comfyui_append_styles(
|
|
{"X": {"prompt": "solo-prompt"}}, # sin negative_prompt
|
|
styles_path=path,
|
|
)
|
|
assert res["added"] == ["X"]
|
|
loaded = json.load(open(path, encoding="utf-8"))
|
|
assert loaded["X"]["negative_prompt"] == DEFAULT_NEGATIVE
|
|
|
|
|
|
def test_entradas_invalidas_se_descartan():
|
|
with tempfile.TemporaryDirectory() as d:
|
|
path = _write_styles(d, {})
|
|
res = comfyui_append_styles(
|
|
{
|
|
"ok": {"prompt": "valido"},
|
|
"vacio": {"prompt": " "}, # prompt vacío
|
|
"no_dict": "string", # no es dict
|
|
"sin_prompt": {"negative_prompt": "n"},
|
|
},
|
|
styles_path=path,
|
|
)
|
|
assert res["added"] == ["ok"]
|
|
assert set(res["invalid"]) == {"vacio", "no_dict", "sin_prompt"}
|
|
assert res["total_after"] == 1
|
|
|
|
|
|
def test_backup_creado():
|
|
with tempfile.TemporaryDirectory() as d:
|
|
path = _write_styles(d, {"A": {"prompt": "a", "negative_prompt": "n"}})
|
|
res = comfyui_append_styles(
|
|
{"B": {"prompt": "b"}},
|
|
styles_path=path,
|
|
)
|
|
assert res["backup_path"]
|
|
assert os.path.isfile(res["backup_path"])
|
|
# El backup contiene el estado ANTERIOR (sólo A).
|
|
bk = json.load(open(res["backup_path"], encoding="utf-8"))
|
|
assert list(bk) == ["A"]
|
|
|
|
|
|
def test_dry_run_no_escribe():
|
|
with tempfile.TemporaryDirectory() as d:
|
|
path = _write_styles(d, {"A": {"prompt": "a", "negative_prompt": "n"}})
|
|
before = open(path, encoding="utf-8").read()
|
|
res = comfyui_append_styles(
|
|
{"B": {"prompt": "b"}},
|
|
styles_path=path,
|
|
dry_run=True,
|
|
)
|
|
assert res["dry_run"] is True
|
|
assert res["added"] == ["B"]
|
|
assert res["total_after"] == 2 # calculado
|
|
assert res["backup_path"] == ""
|
|
after = open(path, encoding="utf-8").read()
|
|
assert before == after # archivo intacto
|
|
|
|
|
|
if __name__ == "__main__":
|
|
for name, fn in sorted(globals().items()):
|
|
if name.startswith("test_") and callable(fn):
|
|
fn()
|
|
print("PASS", name)
|
|
print("OK")
|