Files
Artificial-Sweetener-Simple…/tests/segmentation/detection/test_grounded_sam_model_info_node.py
T

117 lines
3.9 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 the Grounded SAM Model Info node declaration."""
from __future__ import annotations
from typing import Any, cast
import pytest
from simple_syrup.nodes.grounded_sam_model_info import GroundedSAMModelInfo
def test_model_info_node_contract_constants() -> None:
"""Model info node constants match the public ComfyUI contract."""
assert GroundedSAMModelInfo.RETURN_TYPES == ("STRING",)
assert GroundedSAMModelInfo.RETURN_NAMES == ("model_info",)
assert GroundedSAMModelInfo.FUNCTION == "describe"
assert GroundedSAMModelInfo.CATEGORY == "SimpleSyrup/Masking"
def test_model_info_node_declares_expected_inputs(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Model info node exposes model selectors."""
class FakeChoices:
"""Return the known downloadable selections for declaration tests."""
def sam_choices(self) -> list[str]:
"""Return the expected SAM choice."""
return ["sam_hq_vit_b (379MB)"]
def grounding_dino_choices(self) -> list[str]:
"""Return the expected GroundingDINO choice."""
return ["GroundingDINO_SwinT_OGC (694MB)"]
monkeypatch.setattr(GroundedSAMModelInfo, "_choices", cast(Any, FakeChoices()))
input_types: dict[str, dict[str, tuple[Any, ...]]] = (
GroundedSAMModelInfo.INPUT_TYPES()
)
required = input_types["required"]
assert set(required) == {"sam_model", "grounding_dino_model"}
assert "sam_hq_vit_b (379MB)" in required["sam_model"][0]
assert "GroundingDINO_SwinT_OGC (694MB)" in required["grounding_dino_model"][0]
def test_model_info_node_uses_settings_aware_choices() -> None:
"""Model metadata selectors follow the downloadable-models preference."""
class FakeChoices:
"""Return the local-only choices supplied by settings policy."""
def sam_choices(self) -> list[str]:
"""Return the available SAM choices."""
return ["local-sam"]
def grounding_dino_choices(self) -> list[str]:
"""Return the available GroundingDINO choices."""
return ["local-dino"]
def reject_sentinel(self, selection: str) -> None:
"""Accept the deterministic test selections."""
del selection
original = GroundedSAMModelInfo._choices
GroundedSAMModelInfo._choices = cast(Any, FakeChoices())
try:
required = GroundedSAMModelInfo.INPUT_TYPES()["required"]
finally:
GroundedSAMModelInfo._choices = original
assert required["sam_model"][0] == ["local-sam"]
assert required["grounding_dino_model"][0] == ["local-dino"]
def test_model_info_node_delegates_to_metadata_provider() -> None:
"""Node execution delegates metadata creation to its metadata provider."""
class FakeMetadata:
"""Metadata double for model info."""
def describe_selection(self, sam_model: str, grounding_dino_model: str) -> str:
"""Return deterministic metadata."""
return f"{sam_model}|{grounding_dino_model}"
class FakeChoices:
"""Accept all model selections while exercising metadata delegation."""
def reject_sentinel(self, selection: str) -> None:
"""Accept the deterministic test selections."""
del selection
node = GroundedSAMModelInfo()
original = GroundedSAMModelInfo._metadata
original_choices = GroundedSAMModelInfo._choices
GroundedSAMModelInfo._metadata = FakeMetadata() # type: ignore[assignment]
GroundedSAMModelInfo._choices = cast(Any, FakeChoices())
try:
result = node.describe("sam", "dino")
finally:
GroundedSAMModelInfo._metadata = original
GroundedSAMModelInfo._choices = original_choices
assert result == ("sam|dino",)