123 lines
4.0 KiB
Python
123 lines
4.0 KiB
Python
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
|
# Copyright (C) 2026 Artificial Sweetener and contributors
|
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
"""Tests for v3 wrappers that delegate to existing implementation classes."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, ClassVar
|
|
|
|
from simple_syrup.nodes_v3.legacy_node_adapter import LegacyNodeV3Adapter
|
|
|
|
|
|
class _FakeHidden:
|
|
"""Hidden holder double for adapter execution tests."""
|
|
|
|
prompt = {"node": "metadata"}
|
|
|
|
|
|
class _FakeLegacyNode:
|
|
"""Legacy implementation double with visible and hidden inputs."""
|
|
|
|
RETURN_TYPES = ("STRING",)
|
|
RETURN_NAMES = ("result",)
|
|
OUTPUT_TOOLTIPS = ("Delegated result.",)
|
|
FUNCTION = "run"
|
|
CATEGORY = "SimpleSyrup/Test"
|
|
DESCRIPTION = "Delegates test inputs."
|
|
SEARCH_ALIASES = ["adapter"]
|
|
|
|
calls: ClassVar[list[tuple[str, object]]] = []
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls) -> dict[str, dict[str, tuple[Any, ...] | str]]:
|
|
"""Return a small legacy contract with one hidden input."""
|
|
|
|
return {
|
|
"required": {
|
|
"text": (
|
|
"STRING",
|
|
{"default": "hello", "tooltip": "Text to delegate."},
|
|
),
|
|
},
|
|
"hidden": {
|
|
"prompt": "PROMPT",
|
|
"ignored": "UNSUPPORTED_SECRET_SENTINEL",
|
|
},
|
|
}
|
|
|
|
def run(self, text: str, prompt: object | None = None) -> tuple[str]:
|
|
"""Record delegated inputs and return the visible value."""
|
|
|
|
self.calls.append((text, prompt))
|
|
return (text,)
|
|
|
|
|
|
class _FakeAdapter(LegacyNodeV3Adapter):
|
|
"""Concrete adapter used to verify generic wrapper behavior."""
|
|
|
|
LEGACY_NODE_CLASS = _FakeLegacyNode
|
|
NODE_ID = "SimpleSyrup.FakeAdapter"
|
|
DISPLAY_NAME = "Fake Adapter"
|
|
hidden = _FakeHidden()
|
|
|
|
|
|
class _FakeOptionalLegacyNode(_FakeLegacyNode):
|
|
"""Expose one optional socket that historically preceded a required socket."""
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls) -> dict[str, dict[str, tuple[Any, ...] | str]]:
|
|
"""Return split legacy sections for an interleaved v3 socket order."""
|
|
|
|
return {
|
|
"required": {
|
|
"first": ("STRING", {"default": ""}),
|
|
"last": ("STRING", {"default": ""}),
|
|
},
|
|
"optional": {"middle": ("STRING", {"default": ""})},
|
|
}
|
|
|
|
|
|
class _FakeOrderedAdapter(LegacyNodeV3Adapter):
|
|
"""Preserve historical socket indices while changing requiredness."""
|
|
|
|
LEGACY_NODE_CLASS = _FakeOptionalLegacyNode
|
|
NODE_ID = "SimpleSyrup.FakeOrderedAdapter"
|
|
DISPLAY_NAME = "Fake Ordered Adapter"
|
|
WORKFLOW_INPUT_ORDER = ("first", "middle", "last")
|
|
|
|
|
|
def test_legacy_node_v3_adapter_builds_schema() -> None:
|
|
"""The adapter converts legacy metadata into a v3 schema."""
|
|
|
|
schema = _FakeAdapter.define_schema()
|
|
|
|
assert schema.node_id == "SimpleSyrup.FakeAdapter"
|
|
assert schema.display_name == "Fake Adapter"
|
|
assert schema.category == "SimpleSyrup/Test"
|
|
assert schema.inputs[0].id == "text"
|
|
assert schema.inputs[0].tooltip == "Text to delegate."
|
|
assert len(schema.hidden) == 1
|
|
assert schema.hidden[0].value == "PROMPT"
|
|
assert schema.outputs[0].id == "result"
|
|
assert schema.outputs[0].tooltip == "Delegated result."
|
|
|
|
|
|
def test_legacy_node_v3_adapter_delegates_execution_with_hidden_inputs() -> None:
|
|
"""The adapter passes v3 visible and hidden values to the implementation."""
|
|
|
|
_FakeLegacyNode.calls.clear()
|
|
|
|
assert _FakeAdapter.execute(text="value") == ("value",)
|
|
assert _FakeLegacyNode.calls == [("value", {"node": "metadata"})]
|
|
|
|
|
|
def test_legacy_node_v3_adapter_preserves_explicit_workflow_socket_order() -> None:
|
|
"""Interleave optional inputs without shifting persisted connection slots."""
|
|
|
|
schema = _FakeOrderedAdapter.define_schema()
|
|
|
|
assert [value.id for value in schema.inputs] == ["first", "middle", "last"]
|
|
assert [value.optional for value in schema.inputs] == [False, True, False]
|