Release standalone FromListIdx node pack
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SPEC = importlib.util.spec_from_file_location(
|
||||
"etk_from_list_index_test_package",
|
||||
ROOT / "__init__.py",
|
||||
submodule_search_locations=[str(ROOT)],
|
||||
)
|
||||
PACKAGE = importlib.util.module_from_spec(SPEC)
|
||||
assert SPEC.loader is not None
|
||||
sys.modules[SPEC.name] = PACKAGE
|
||||
SPEC.loader.exec_module(PACKAGE)
|
||||
|
||||
|
||||
EXPECTED_IDS = {
|
||||
"ImageFromListIdx",
|
||||
"LatentFromListIdx",
|
||||
"ConditioningFromListIdx",
|
||||
"AudioFromListIdx",
|
||||
"DictFromListIdx",
|
||||
"StrFromListIdx",
|
||||
"BytesFromListIdx",
|
||||
"IntFromListIdx",
|
||||
"FloatFromListIdx",
|
||||
}
|
||||
|
||||
|
||||
def test_exact_workflow_ids_are_registered():
|
||||
assert set(PACKAGE.NODE_CLASS_MAPPINGS) == EXPECTED_IDS
|
||||
assert PACKAGE.NODE_DISPLAY_NAME_MAPPINGS == {
|
||||
"AudioFromListIdx": "Audio From List Index"
|
||||
}
|
||||
|
||||
|
||||
def test_etk_schema_and_selection_contracts():
|
||||
image = PACKAGE.ImageFromListIdx
|
||||
latent = PACKAGE.LatentFromListIdx
|
||||
conditioning = PACKAGE.ConditioningFromListIdx
|
||||
|
||||
assert image.CATEGORY == "ETK/Image"
|
||||
assert image.INPUT_TYPES()["required"]["images"] == ("LIST", {"default": []})
|
||||
assert image().get_image(["zero", "one"], 1) == ("one",)
|
||||
assert latent().get_latent(["zero", "one"], -1) == ("one",)
|
||||
assert conditioning().get_conditioning(["zero"], 0) == ("zero",)
|
||||
with pytest.raises(IndexError, match="index 2 out of range for list length 1"):
|
||||
conditioning().get_conditioning(["zero"], 2)
|
||||
|
||||
|
||||
def test_swain_schema_and_selection_contracts():
|
||||
assert PACKAGE.DictFromListIdx.INPUT_TYPES() == {
|
||||
"required": {"list": ("LIST", {"default": []})},
|
||||
"optional": {"int": ("INT", {"default": 0, "min": -1000, "max": 1000})},
|
||||
}
|
||||
assert PACKAGE.DictFromListIdx().handler(list=[{"a": 1}], int=0) == ({"a": 1},)
|
||||
assert PACKAGE.StrFromListIdx().handler(list=["a", "b"], int=1) == (
|
||||
"b",
|
||||
["a", "b"],
|
||||
)
|
||||
assert PACKAGE.BytesFromListIdx().handler(list=[[b"value"]], int=0) == (b"value",)
|
||||
assert PACKAGE.IntFromListIdx().handler(list=["7"], int=0) == (7, ["7"])
|
||||
assert PACKAGE.FloatFromListIdx().handler(list=["1.25"], int=0) == (
|
||||
1.25,
|
||||
["1.25"],
|
||||
)
|
||||
|
||||
|
||||
def test_audio_normalization_matches_comfy_audio_shape():
|
||||
torch = pytest.importorskip("torch")
|
||||
source = {"waveform": torch.tensor([0.0, 0.5]), "sample_rate": 24000, "tag": "x"}
|
||||
selected = PACKAGE.AudioFromListIdx().get_audio([source], 0)[0]
|
||||
assert selected["waveform"].shape == (1, 1, 2)
|
||||
assert selected["sample_rate"] == 24000
|
||||
assert selected["tag"] == "x"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"node",
|
||||
[
|
||||
PACKAGE.DictFromListIdx,
|
||||
PACKAGE.StrFromListIdx,
|
||||
PACKAGE.BytesFromListIdx,
|
||||
PACKAGE.IntFromListIdx,
|
||||
PACKAGE.FloatFromListIdx,
|
||||
],
|
||||
)
|
||||
def test_swain_nodes_reject_missing_and_out_of_range_values(node):
|
||||
with pytest.raises(ValueError, match="Must provide an input"):
|
||||
node().handler(list=None, int=0)
|
||||
with pytest.raises(ValueError, match="index must be in the range"):
|
||||
node().handler(list=["only"], int=1)
|
||||
@@ -0,0 +1,58 @@
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from tools import publish_registry_release
|
||||
|
||||
|
||||
def test_publish_stops_before_registry_when_manager_gate_fails(monkeypatch, tmp_path):
|
||||
calls = []
|
||||
|
||||
def fail_gate(source):
|
||||
raise RuntimeError("public clone failed")
|
||||
|
||||
monkeypatch.setattr(publish_registry_release, "verify_manager_install", fail_gate)
|
||||
monkeypatch.setattr(
|
||||
publish_registry_release.subprocess,
|
||||
"run",
|
||||
lambda *args, **kwargs: calls.append((args, kwargs)),
|
||||
)
|
||||
|
||||
with pytest.raises(RuntimeError, match="public clone failed"):
|
||||
publish_registry_release.publish_release(
|
||||
tmp_path,
|
||||
comfy="comfy",
|
||||
token="private-token",
|
||||
changelog="release",
|
||||
)
|
||||
assert calls == []
|
||||
|
||||
|
||||
def test_publish_keeps_token_out_of_arguments_and_environment(monkeypatch, tmp_path):
|
||||
calls = []
|
||||
monkeypatch.setattr(
|
||||
publish_registry_release,
|
||||
"verify_manager_install",
|
||||
lambda source: ("1.0.3", "https://example.test/owner/repo", "abc123"),
|
||||
)
|
||||
|
||||
def record_run(command, **kwargs):
|
||||
calls.append((command, kwargs))
|
||||
|
||||
monkeypatch.setattr(publish_registry_release.subprocess, "run", record_run)
|
||||
publish_registry_release.publish_release(
|
||||
Path(tmp_path),
|
||||
comfy="comfy",
|
||||
token="private-token",
|
||||
changelog="release notes",
|
||||
)
|
||||
|
||||
assert [command for command, _ in calls] == [
|
||||
["comfy", "node", "validate"],
|
||||
["comfy", "node", "publish"],
|
||||
]
|
||||
publish_kwargs = calls[1][1]
|
||||
assert publish_kwargs["input"] == "private-token\n"
|
||||
assert "private-token" not in repr(calls[1][0])
|
||||
assert "private-token" not in repr(publish_kwargs["env"])
|
||||
assert publish_kwargs["env"]["COMFY_NODE_CHANGELOG"] == "release notes"
|
||||
@@ -0,0 +1,55 @@
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
SCRIPT = Path(__file__).parents[1] / "tools" / "verify_manager_install.py"
|
||||
SPEC = importlib.util.spec_from_file_location("verify_manager_install", SCRIPT)
|
||||
release_gate = importlib.util.module_from_spec(SPEC)
|
||||
assert SPEC.loader is not None
|
||||
SPEC.loader.exec_module(release_gate)
|
||||
|
||||
|
||||
def test_manager_clone_command_matches_manager_install_shape(tmp_path):
|
||||
destination = tmp_path / "custom_nodes" / "etk-from-list-index"
|
||||
assert release_gate.manager_clone_command("https://example.test/owner/repo", destination) == [
|
||||
"git",
|
||||
"clone",
|
||||
"-v",
|
||||
"--recursive",
|
||||
"--progress",
|
||||
"--",
|
||||
"https://example.test/owner/repo",
|
||||
str(destination),
|
||||
]
|
||||
|
||||
|
||||
def test_anonymous_environment_removes_ambient_git_credentials(tmp_path):
|
||||
env = release_gate.anonymous_git_environment(
|
||||
{
|
||||
"PATH": "/usr/bin",
|
||||
"GIT_ASKPASS": "credential-helper",
|
||||
"GIT_CONFIG_GLOBAL": "/secret/config",
|
||||
"GIT_SSH_COMMAND": "ssh -i /secret/key",
|
||||
},
|
||||
tmp_path,
|
||||
)
|
||||
assert env["GIT_ASKPASS"] == "/bin/false"
|
||||
assert env["GIT_CONFIG_GLOBAL"] == "/dev/null"
|
||||
assert env["GIT_TERMINAL_PROMPT"] == "0"
|
||||
assert env["HOME"] == str(tmp_path)
|
||||
assert "GIT_SSH_COMMAND" not in env
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"url",
|
||||
[
|
||||
"",
|
||||
"ssh://git@example.test/owner/repo",
|
||||
"https://user:password@example.test/owner/repo",
|
||||
"https://example.test/owner/repo?token=secret",
|
||||
],
|
||||
)
|
||||
def test_public_repository_url_rejects_nonanonymous_forms(url):
|
||||
with pytest.raises(release_gate.VerificationError):
|
||||
release_gate.validate_public_repository_url(url)
|
||||
Reference in New Issue
Block a user