Files
Artificial-Sweetener-Simple…/tests/node_api/test_legacy_node_v3_adapter.py
T

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]