feat: initial release

This commit is contained in:
Artificial Sweetener
2026-05-22 15:41:49 -04:00
commit 4b6525ce6f
276 changed files with 62039 additions and 0 deletions
+197
View File
@@ -0,0 +1,197 @@
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
# Copyright (C) 2026 Artificial Sweetener and contributors
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Coverage tests for ComfyUI node tooltip metadata."""
from __future__ import annotations
import sys
from collections.abc import Mapping
from types import ModuleType
from typing import Any, Protocol, cast
import pytest
from simple_syrup.nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
from simple_syrup.nodes_v3.encode_prompt_batch_with_prompt_control import (
EncodePromptBatchWithPromptControl,
)
from simple_syrup.nodes_v3.scale_factor import ScaleFactorV3
from simple_syrup.nodes_v3.simple_load_checkpoint import SimpleLoadCheckpointV3
from simple_syrup.nodes_v3.tile_and_tag_segs import TileAndTagSEGSV3
from simple_syrup.nodes_v3.wd14_tagger_loader import WD14TaggerLoaderV3
class _LegacyNode(Protocol):
"""Protocol for legacy ComfyUI node declarations."""
DESCRIPTION: str
@classmethod
def INPUT_TYPES(cls) -> dict[str, dict[str, tuple[Any, ...]]]:
"""Return ComfyUI legacy input metadata."""
class _V3Node(Protocol):
"""Protocol for Comfy v3 node schema declarations."""
@classmethod
def define_schema(cls) -> Any:
"""Return a Comfy v3 schema object."""
class _FakeFolderPaths(ModuleType):
"""Small folder_paths fake for deterministic v3 loader schemas."""
def __init__(self) -> None:
"""Create deterministic ComfyUI filename lists."""
super().__init__("folder_paths")
self.models_dir = "<COMFY_ROOT>\\models"
self.user_directory = "<COMFY_ROOT>\\user"
self._files = {
"checkpoints": ["model.safetensors"],
"vae": ["manual_vae.safetensors"],
"vae_approx": [],
}
def get_filename_list(self, folder_name: str) -> list[str]:
"""Return deterministic filenames for a model folder."""
return self._files[folder_name]
def test_legacy_nodes_provide_tooltip_metadata() -> None:
"""All exported legacy nodes expose descriptions and field-level help."""
for node_id, raw_node_class in NODE_CLASS_MAPPINGS.items():
node_class = cast(_LegacyNode, raw_node_class)
assert node_id in NODE_DISPLAY_NAME_MAPPINGS
description = getattr(node_class, "DESCRIPTION", None)
assert isinstance(description, str) and description.strip(), (
f"{node_id} is missing DESCRIPTION."
)
input_types = node_class.INPUT_TYPES()
assert isinstance(input_types, Mapping), f"{node_id} INPUT_TYPES is invalid."
for section_name in ("required", "optional", "hidden"):
section = input_types.get(section_name, {})
assert isinstance(section, Mapping), (
f"{node_id} {section_name} inputs must be a mapping."
)
for field_name, declaration in section.items():
if section_name == "hidden" and _legacy_hidden_sentinel(declaration):
continue
assert _tooltip_from_legacy_declaration(declaration), (
f"{node_id} {section_name}.{field_name} is missing tooltip "
"metadata."
)
def test_legacy_named_outputs_provide_tooltips() -> None:
"""All named legacy outputs provide matching output tooltip metadata."""
for node_id, node_class in NODE_CLASS_MAPPINGS.items():
return_names = getattr(node_class, "RETURN_NAMES", None)
if return_names is None:
continue
output_tooltips = getattr(node_class, "OUTPUT_TOOLTIPS", None)
assert isinstance(output_tooltips, tuple), (
f"{node_id} is missing OUTPUT_TOOLTIPS."
)
assert len(output_tooltips) == len(return_names), (
f"{node_id} OUTPUT_TOOLTIPS must match RETURN_NAMES length."
)
for output_name, tooltip in zip(return_names, output_tooltips, strict=True):
assert isinstance(tooltip, str) and tooltip.strip(), (
f"{node_id} output.{output_name} is missing tooltip metadata."
)
@pytest.mark.parametrize(
"schema_class",
[
SimpleLoadCheckpointV3,
ScaleFactorV3,
TileAndTagSEGSV3,
WD14TaggerLoaderV3,
EncodePromptBatchWithPromptControl,
],
)
def test_v3_nodes_provide_tooltip_metadata(
monkeypatch: pytest.MonkeyPatch,
schema_class: type[_V3Node],
) -> None:
"""All supported v3 schemas expose descriptions and field-level help."""
monkeypatch.setitem(sys.modules, "folder_paths", _FakeFolderPaths())
schema = schema_class.define_schema()
assert isinstance(schema.description, str) and schema.description.strip(), (
f"{schema.node_id} v3 schema is missing description."
)
for input_item in schema.inputs:
tooltip = getattr(input_item, "tooltip", None)
assert isinstance(tooltip, str) and tooltip.strip(), (
f"{schema.node_id} input.{input_item.id} is missing tooltip metadata."
)
for output in schema.outputs:
tooltip = getattr(output, "tooltip", None)
assert isinstance(tooltip, str) and tooltip.strip(), (
f"{schema.node_id} output.{output.id} is missing tooltip metadata."
)
def test_high_impact_tooltips_explain_direction_and_units() -> None:
"""Important numeric controls explain units or practical direction."""
detail_node = cast(
_LegacyNode,
NODE_CLASS_MAPPINGS["SimpleSyrup.DetailSEGSByScaleFactor"],
)
detail_inputs = detail_node.INPUT_TYPES()["required"]
denoise = _tooltip_from_legacy_declaration(detail_inputs["denoise"])
feather = _tooltip_from_legacy_declaration(detail_inputs["feather"])
assert "lower" in denoise.lower() and "higher" in denoise.lower()
assert "pixels" in feather.lower()
tiled_node = cast(
_LegacyNode,
NODE_CLASS_MAPPINGS["SimpleSyrup.KSamplerTiledDiffusion"],
)
tiled_inputs = tiled_node.INPUT_TYPES()["required"]
overlap = _tooltip_from_legacy_declaration(tiled_inputs["latent_tile_overlap"])
batch_size = _tooltip_from_legacy_declaration(
tiled_inputs["latent_tile_batch_size"]
)
assert "overlap" in overlap.lower() and "seams" in overlap.lower()
assert "memory" in batch_size.lower()
def _tooltip_from_legacy_declaration(declaration: object) -> str:
"""Return a legacy ComfyUI field tooltip or an empty string."""
if not isinstance(declaration, tuple) or len(declaration) < 2:
return ""
options = declaration[1]
if not isinstance(options, dict):
return ""
tooltip: Any = options.get("tooltip")
if not isinstance(tooltip, str):
return ""
return tooltip.strip()
def _legacy_hidden_sentinel(declaration: object) -> bool:
"""Return whether a declaration is a Comfy legacy hidden input sentinel."""
return isinstance(declaration, str) and declaration in {
"PROMPT",
"DYNPROMPT",
"EXTRA_PNGINFO",
"UNIQUE_ID",
"AUTH_TOKEN_COMFY_ORG",
"API_KEY_COMFY_ORG",
}