Files
StyleRef-styleref-comfyui/tests/test_style.py
T
BehzadandClaude Sonnet 5 f8e5548b46 Drop internal plan references from code comments
The pack cited internal planning-doc items (plan P3-7, decision D1, plan
risk 9.2) that aren't public, leaving outside contributors with pointers
they can't follow. Each comment already states its reason; only the
dangling citation is removed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 23:18:25 +02:00

328 lines
10 KiB
Python

"""
Facet-reading tests, against a fixture captured from the live API
(GET /api/v1/styles/{ref}?format=json) so the section and field names under test
are the real ones, not invented.
"""
from __future__ import annotations
import pytest
from styleref_style import (
COMPOSITION_SECTION,
LIGHTING_SECTION,
attribution,
is_style,
make_style,
mood_terms,
negative_terms,
palette_hexes,
section_summary,
summary_line,
)
SECTIONS = {
"colors": {
"weight": "Absolute",
"values": {
"color_palette": {
"mode": "solid",
"gradients": [],
"solidColors": [
{"hex": "#F6F6F4", "amount": 35},
{"hex": "#D7DDE0", "amount": 15},
{"hex": "#1B2A44", "amount": 10},
],
},
"palette_size": "8 colors",
},
},
"mood_personality": {
"weight": "Primary",
"values": {"mood_personality": ["Serious", "Calm", "Minimalist"], "energy_level": "Low"},
},
"light_shadow": {
"weight": "secondary",
"values": {"light_source_type": "Natural", "shadow_type": "Soft"},
},
"spatial_hierarchy": {
"weight": "secondary",
"values": {"perspective_type": "Eye level", "symmetry": "Asymmetric"},
},
"guardrails": {
"weight": "absolute",
"values": {"avoided_visuals": "Lens flares; bokeh; film grain"},
},
}
@pytest.fixture
def style():
return make_style(
ref="9a2adtz6-cd8d77ee2f51",
name="Real Moments",
sections=SECTIONS,
author="@styleref",
url="https://styleref.io/share/9a2adtz6-cd8d77ee2f51",
)
def test_palette_preserves_declared_order(style):
"""Order matters — it runs most-used first, so [0] is the dominant color."""
assert palette_hexes(style) == ["#F6F6F4", "#D7DDE0", "#1B2A44"]
def test_palette_includes_gradient_stops():
"""A gradient-mode palette would otherwise read as having no colors at all."""
style = make_style(
ref="x",
name="Gradient",
sections={
"colors": {
"values": {
"color_palette": {
"mode": "gradient",
"solidColors": [],
"gradients": [{"stops": [{"hex": "#FF0000"}, {"hex": "#0000FF"}]}],
}
}
}
},
)
assert palette_hexes(style) == ["#FF0000", "#0000FF"]
def test_palette_dedupes_case_insensitively():
style = make_style(
ref="x",
name="Dupes",
sections={
"colors": {
"values": {
"color_palette": {
"solidColors": [{"hex": "#AABBCC"}, {"hex": "#aabbcc"}, {"hex": "#123456"}]
}
}
}
},
)
assert palette_hexes(style) == ["#AABBCC", "#123456"]
def test_palette_is_empty_for_a_style_without_colors():
assert palette_hexes(make_style(ref="x", name="Bare")) == []
def test_mood_list_flattens_to_a_prompt_fragment(style):
assert mood_terms(style) == "Serious, Calm, Minimalist"
def test_negatives_are_normalized_comma_terms(style):
"""
The negative output is negative-channel idiom: terse comma-separated terms
(an SD negative box negates literal tokens, so no directive words, and the
convention there is commas, not semicolons).
"""
assert negative_terms(style) == "Lens flares, bokeh, film grain"
def test_negatives_cover_artifacts_and_avoided_colors():
"""
Guardrails carry more than avoided_visuals — artifacts to avoid
and avoided colors are user-authored constraints and belong in the negative.
"""
style = make_style(
ref="x",
name="Guarded",
sections={
"guardrails": {
"values": {
"avoided_visuals": "Lens flares",
"common_artifacts_to_avoid": ["AI Gloss / Plastic Smoothing", "Uncanny skin"],
"avoided_colors": "neon green",
}
}
},
)
negative = negative_terms(style)
assert "Lens flares" in negative
assert "AI Gloss" in negative
assert "neon green" in negative
def test_negatives_strip_directive_words_and_split_sentences():
"""
"Avoid harsh shadows" in a negative channel would negate the word "avoid".
Users author sentences; the channel needs terms.
"""
from styleref_style import split_guardrail_terms
style = make_style(
ref="x",
name="Messy",
sections={
"guardrails": {
"values": {
"avoided_visuals": "Avoid harsh drop shadows; no neon glow",
"avoided_colors": "never use hot pink",
"avoided_layouts": "cluttered grids",
}
}
},
)
assert negative_terms(style) == "harsh drop shadows, neon glow, hot pink, cluttered grids"
# "black and white" is one concept — never split on "and".
assert split_guardrail_terms("black and white photography") == ["black and white photography"]
def test_negatives_exclude_text_domain_words():
"""avoided_words constrains text output (buzzwords) — not image negatives."""
style = make_style(
ref="x",
name="Wordy",
sections={"guardrails": {"values": {"avoided_words": "synergy; disrupt"}}},
)
assert negative_terms(style) == ""
def test_negatives_dedupe_repeated_fields():
style = make_style(
ref="x",
name="Dupes",
sections={
"guardrails": {
"values": {
"avoided_visuals": "film grain",
"common_artifacts_to_avoid": "Film Grain",
}
}
},
)
assert negative_terms(style).lower().count("film grain") == 1
def test_section_summary_is_readable_lines(style):
summary = section_summary(style, LIGHTING_SECTION)
assert "light source type: Natural" in summary
assert "shadow type: Soft" in summary
def test_section_summary_of_missing_section_is_empty(style):
assert section_summary(style, "does_not_exist") == ""
def test_composition_section_resolves(style):
assert "perspective type: Eye level" in section_summary(style, COMPOSITION_SECTION)
def test_unknown_sections_do_not_break_reading():
"""A section added server-side must flow through, not raise."""
style = make_style(ref="x", name="Future", sections={"brand_new": {"values": {"a": "b"}}})
assert section_summary(style, "brand_new") == "a: b"
assert palette_hexes(style) == []
def test_attribution_includes_author_and_url(style):
assert attribution(style) == "Real Moments by @styleref — https://styleref.io/share/9a2adtz6-cd8d77ee2f51"
def test_attribution_degrades_without_author_or_url():
assert attribution(make_style(ref="x", name="Solo")) == "Solo"
def test_summary_line_counts_fields(style):
assert "5 sections" in summary_line(style)
def test_is_style_rejects_arbitrary_dicts():
"""Guards the node inputs against a mis-wired connection."""
assert is_style({"foo": "bar"}) is False
assert is_style("a string") is False
assert is_style(make_style(ref="x", name="ok")) is True
# ── dimensions (width/height from the style) ─────────────────────────────────
def _fmt_style(aspect: str | None = None, resolution: str | None = None):
values = {}
if aspect:
values["aspect_ratio"] = aspect
if resolution:
values["resolution_target"] = resolution
return make_style(ref="x", name="Fmt", sections={"output_format": {"values": values}})
def test_dimensions_16_9_at_1k():
from styleref_style import dimensions
assert dimensions(_fmt_style("16:9 (Landscape)", "1K")) == (1024, 576)
def test_dimensions_portrait_puts_the_long_edge_on_height():
from styleref_style import dimensions
width, height = dimensions(_fmt_style("9:16 (Portrait)", "2K"))
assert height == 2048
assert width == 1152
def test_dimensions_default_to_square_1024():
from styleref_style import dimensions
assert dimensions(make_style(ref="x", name="Bare")) == (1024, 1024)
# dpi / vector targets aren't pixel sizes — fall back to the default edge.
assert dimensions(_fmt_style("1:1 (Square)", "300dpi")) == (1024, 1024)
def test_dimensions_are_sampler_friendly_multiples_of_8():
from styleref_style import dimensions
width, height = dimensions(_fmt_style("2.39:1 (Cinematic Landscape)", "1K"))
assert width % 8 == 0 and height % 8 == 0
# ── custom items & inspiration urls ──────────────────────────────────────────
def test_custom_item_reads_by_index_and_defaults_empty():
from styleref_style import custom_item
style = make_style(
ref="x",
name="Custom",
sections={"custom_style_items": {"values": {"custom_style_item_2": "brand watermark"}}},
)
assert custom_item(style, 2) == "brand watermark"
assert custom_item(style, 1) == ""
def test_inspiration_image_urls_in_stored_order():
from styleref_style import inspiration_image_urls
style = make_style(
ref="x",
name="Insp",
sections={
"inspiration_images": {
"values": {
"inspiration_images": [
{"url": "https://cdn.styleref.io/a.webp"},
{"url": "https://cdn.styleref.io/b.webp"},
{"notaurl": True},
]
}
}
},
)
assert inspiration_image_urls(style) == [
"https://cdn.styleref.io/a.webp",
"https://cdn.styleref.io/b.webp",
]
def test_inspiration_image_urls_empty_without_section():
from styleref_style import inspiration_image_urls
assert inspiration_image_urls(make_style(ref="x", name="Bare")) == []