utils.hashing.generate_prompt_hash is now the only prompt hash: PromptManagerBase._generate_hash delegates to it and the inline hashlib.sha256 copies in the prompt tracker and image monitor are gone. Remove PromptManagerBase._open_web_interface (it targeted a web_interface folder that never existed) and the unused restart_gallery.py script. IS_CHANGED on PromptManager and PromptManagerText now includes category and tags, so editing either re-runs the node and updates the saved metadata.
143 lines
5.7 KiB
Python
143 lines
5.7 KiB
Python
"""
|
|
Tests for ComfyUI node execution contract.
|
|
|
|
Ensures all nodes are properly configured for ComfyUI's execution engine:
|
|
- OUTPUT_NODE = True so nodes are always included in the execution graph
|
|
- IS_CHANGED returns correct values for cache invalidation
|
|
|
|
Regression tests for: https://github.com/ComfyAssets/ComfyUI_PromptManager/issues/120
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import time
|
|
import unittest
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
from prompt_manager import PromptManager
|
|
from prompt_manager_text import PromptManagerText
|
|
from prompt_search_list import PromptSearchList
|
|
|
|
ALL_NODE_CLASSES = [PromptManager, PromptManagerText, PromptSearchList]
|
|
|
|
|
|
class TestOutputNode(unittest.TestCase):
|
|
"""OUTPUT_NODE = True is required so ComfyUI always includes these nodes
|
|
in the execution graph. Without it, nodes with side effects (database
|
|
writes, execution tracking) can be silently skipped when downstream
|
|
nodes are cached."""
|
|
|
|
def test_prompt_manager_is_output_node(self):
|
|
self.assertIs(PromptManager.OUTPUT_NODE, True)
|
|
|
|
def test_prompt_manager_text_is_output_node(self):
|
|
self.assertIs(PromptManagerText.OUTPUT_NODE, True)
|
|
|
|
def test_prompt_search_list_is_output_node(self):
|
|
self.assertIs(PromptSearchList.OUTPUT_NODE, True)
|
|
|
|
|
|
class TestIsChangedPromptManager(unittest.TestCase):
|
|
"""IS_CHANGED for PromptManager should return a deterministic hash
|
|
based on text inputs, so the node re-runs only when inputs change."""
|
|
|
|
def test_same_inputs_return_same_hash(self):
|
|
result1 = PromptManager.IS_CHANGED(
|
|
clip=None, text="hello", prepend_text="pre", append_text="post"
|
|
)
|
|
result2 = PromptManager.IS_CHANGED(
|
|
clip=None, text="hello", prepend_text="pre", append_text="post"
|
|
)
|
|
self.assertEqual(result1, result2)
|
|
|
|
def test_different_text_returns_different_hash(self):
|
|
result1 = PromptManager.IS_CHANGED(clip=None, text="hello")
|
|
result2 = PromptManager.IS_CHANGED(clip=None, text="world")
|
|
self.assertNotEqual(result1, result2)
|
|
|
|
def test_different_prepend_returns_different_hash(self):
|
|
result1 = PromptManager.IS_CHANGED(clip=None, text="hello", prepend_text="a")
|
|
result2 = PromptManager.IS_CHANGED(clip=None, text="hello", prepend_text="b")
|
|
self.assertNotEqual(result1, result2)
|
|
|
|
def test_different_append_returns_different_hash(self):
|
|
result1 = PromptManager.IS_CHANGED(clip=None, text="hello", append_text="a")
|
|
result2 = PromptManager.IS_CHANGED(clip=None, text="hello", append_text="b")
|
|
self.assertNotEqual(result1, result2)
|
|
|
|
def test_returns_string(self):
|
|
result = PromptManager.IS_CHANGED(clip=None, text="hello")
|
|
self.assertIsInstance(result, str)
|
|
|
|
def test_different_tags_return_different_hash(self):
|
|
result1 = PromptManager.IS_CHANGED(clip=None, text="hello", tags="a, b")
|
|
result2 = PromptManager.IS_CHANGED(clip=None, text="hello", tags="a, c")
|
|
self.assertNotEqual(result1, result2)
|
|
|
|
def test_different_category_returns_different_hash(self):
|
|
result1 = PromptManager.IS_CHANGED(clip=None, text="hello", category="art")
|
|
result2 = PromptManager.IS_CHANGED(clip=None, text="hello", category="photo")
|
|
self.assertNotEqual(result1, result2)
|
|
|
|
|
|
class TestIsChangedPromptManagerText(unittest.TestCase):
|
|
"""IS_CHANGED for PromptManagerText should behave identically to
|
|
PromptManager — deterministic hash based on text inputs."""
|
|
|
|
def test_same_inputs_return_same_hash(self):
|
|
result1 = PromptManagerText.IS_CHANGED(
|
|
text="hello", prepend_text="pre", append_text="post"
|
|
)
|
|
result2 = PromptManagerText.IS_CHANGED(
|
|
text="hello", prepend_text="pre", append_text="post"
|
|
)
|
|
self.assertEqual(result1, result2)
|
|
|
|
def test_different_text_returns_different_hash(self):
|
|
result1 = PromptManagerText.IS_CHANGED(text="hello")
|
|
result2 = PromptManagerText.IS_CHANGED(text="world")
|
|
self.assertNotEqual(result1, result2)
|
|
|
|
def test_different_prepend_returns_different_hash(self):
|
|
result1 = PromptManagerText.IS_CHANGED(text="hello", prepend_text="a")
|
|
result2 = PromptManagerText.IS_CHANGED(text="hello", prepend_text="b")
|
|
self.assertNotEqual(result1, result2)
|
|
|
|
def test_different_append_returns_different_hash(self):
|
|
result1 = PromptManagerText.IS_CHANGED(text="hello", append_text="a")
|
|
result2 = PromptManagerText.IS_CHANGED(text="hello", append_text="b")
|
|
self.assertNotEqual(result1, result2)
|
|
|
|
def test_different_tags_or_category_return_different_hash(self):
|
|
base = PromptManagerText.IS_CHANGED(text="hello", category="x", tags="a")
|
|
self.assertNotEqual(
|
|
base, PromptManagerText.IS_CHANGED(text="hello", category="x", tags="b")
|
|
)
|
|
self.assertNotEqual(
|
|
base, PromptManagerText.IS_CHANGED(text="hello", category="y", tags="a")
|
|
)
|
|
|
|
def test_returns_string(self):
|
|
result = PromptManagerText.IS_CHANGED(text="hello")
|
|
self.assertIsInstance(result, str)
|
|
|
|
|
|
class TestIsChangedPromptSearchList(unittest.TestCase):
|
|
"""IS_CHANGED for PromptSearchList should always return a unique value
|
|
so the node re-runs every time (database contents may have changed)."""
|
|
|
|
def test_returns_different_value_on_successive_calls(self):
|
|
result1 = PromptSearchList.IS_CHANGED()
|
|
time.sleep(0.01)
|
|
result2 = PromptSearchList.IS_CHANGED()
|
|
self.assertNotEqual(result1, result2)
|
|
|
|
def test_returns_numeric(self):
|
|
result = PromptSearchList.IS_CHANGED()
|
|
self.assertIsInstance(result, float)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|