diff --git a/tests/test_nodes.py b/tests/test_nodes.py new file mode 100644 index 0000000..aa6e372 --- /dev/null +++ b/tests/test_nodes.py @@ -0,0 +1,105 @@ +import math +import unittest + +from simple_prompt.comfyui.nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS, SimplePrompt + + +class TestNodeRegistration(unittest.TestCase): + """ComfyUI lifecycle: __init__.py must export NODE_CLASS_MAPPINGS.""" + + def test_node_class_mappings_contains_simple_prompt(self): + self.assertIn("SimplePrompt", NODE_CLASS_MAPPINGS) + self.assertIs(NODE_CLASS_MAPPINGS["SimplePrompt"], SimplePrompt) + + def test_node_display_name_mappings_contains_simple_prompt(self): + self.assertIn("SimplePrompt", NODE_DISPLAY_NAME_MAPPINGS) + self.assertEqual(NODE_DISPLAY_NAME_MAPPINGS["SimplePrompt"], "Simple Prompt") + + +class TestNodeAttributes(unittest.TestCase): + """Verify all required ComfyUI node class attributes are present and correct.""" + + def test_category(self): + self.assertEqual(SimplePrompt.CATEGORY, "SimplePrompt") + + def test_function(self): + self.assertEqual(SimplePrompt.FUNCTION, "run") + + def test_return_types(self): + self.assertEqual(SimplePrompt.RETURN_TYPES, ("STRING",)) + + def test_return_names(self): + self.assertEqual(SimplePrompt.RETURN_NAMES, ("prompt",)) + + def test_output_node_is_false(self): + self.assertFalse(SimplePrompt.OUTPUT_NODE) + + def test_has_description(self): + self.assertIsInstance(SimplePrompt.DESCRIPTION, str) + self.assertTrue(len(SimplePrompt.DESCRIPTION) > 0) + + +class TestInputTypes(unittest.TestCase): + """INPUT_TYPES must return the structure expected by ComfyUI.""" + + def test_returns_dict(self): + result = SimplePrompt.INPUT_TYPES() + self.assertIsInstance(result, dict) + + def test_has_required_key(self): + result = SimplePrompt.INPUT_TYPES() + self.assertIn("required", result) + + def test_prompt_text_input_exists(self): + result = SimplePrompt.INPUT_TYPES() + self.assertIn("prompt_text", result["required"]) + + def test_prompt_text_type_is_string(self): + result = SimplePrompt.INPUT_TYPES() + prompt_def = result["required"]["prompt_text"] + self.assertEqual(prompt_def[0], "STRING") + + def test_prompt_text_is_multiline(self): + result = SimplePrompt.INPUT_TYPES() + prompt_def = result["required"]["prompt_text"] + self.assertTrue(prompt_def[1].get("multiline", False)) + + def test_no_optional_inputs(self): + result = SimplePrompt.INPUT_TYPES() + self.assertNotIn("optional", result) + + +class TestNodeExecution(unittest.TestCase): + """The run() function must return a tuple matching RETURN_TYPES.""" + + def setUp(self): + self.node = SimplePrompt() + + def test_run_returns_tuple(self): + result = self.node.run("a beautiful cat") + self.assertIsInstance(result, tuple) + + def test_run_passthrough(self): + result = self.node.run("a beautiful cat") + self.assertEqual(result, ("a beautiful cat",)) + + def test_run_empty_string(self): + result = self.node.run("") + self.assertEqual(result, ("",)) + + def test_run_with_special_characters(self): + prompt = "1girl, blue_hair, (masterpiece:1.4)" + result = self.node.run(prompt) + self.assertEqual(result, (prompt,)) + + +class TestIsChanged(unittest.TestCase): + """IS_CHANGED must return NaN so the node always re-executes.""" + + def test_returns_nan(self): + result = SimplePrompt.IS_CHANGED("any prompt") + self.assertTrue(math.isnan(result)) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_server_helpers.py b/tests/test_server_helpers.py new file mode 100644 index 0000000..cf644ef --- /dev/null +++ b/tests/test_server_helpers.py @@ -0,0 +1,83 @@ +import unittest +from unittest.mock import MagicMock + +from simple_prompt.comfyui.server import _parse_categories, _parse_int_query + + +def _make_request(params: dict[str, str]) -> MagicMock: + """Create a mock aiohttp request with the given query parameters.""" + request = MagicMock() + request.query = params + return request + + +class TestParseIntQuery(unittest.TestCase): + """Tests for the _parse_int_query helper used by route handlers.""" + + def test_returns_default_when_param_missing(self): + req = _make_request({}) + self.assertEqual(_parse_int_query(req, "limit", 50), 50) + + def test_parses_valid_integer(self): + req = _make_request({"limit": "10"}) + self.assertEqual(_parse_int_query(req, "limit", 50), 10) + + def test_raises_on_non_integer(self): + req = _make_request({"limit": "abc"}) + with self.assertRaises(ValueError): + _parse_int_query(req, "limit", 50) + + def test_raises_on_empty_string(self): + req = _make_request({"limit": ""}) + with self.assertRaises(ValueError): + _parse_int_query(req, "limit", 50) + + def test_raises_below_minimum(self): + req = _make_request({"limit": "0"}) + with self.assertRaises(ValueError): + _parse_int_query(req, "limit", 50, minimum=1) + + def test_accepts_value_at_minimum(self): + req = _make_request({"limit": "1"}) + self.assertEqual(_parse_int_query(req, "limit", 50, minimum=1), 1) + + def test_clamps_to_maximum(self): + req = _make_request({"limit": "999"}) + self.assertEqual(_parse_int_query(req, "limit", 50, maximum=200), 200) + + def test_value_within_range(self): + req = _make_request({"limit": "100"}) + self.assertEqual(_parse_int_query(req, "limit", 50, minimum=1, maximum=200), 100) + + def test_default_minimum_is_zero(self): + req = _make_request({"offset": "-1"}) + with self.assertRaises(ValueError): + _parse_int_query(req, "offset", 0) + + +class TestParseCategories(unittest.TestCase): + """Tests for the _parse_categories helper used by route handlers.""" + + def test_empty_string_returns_empty_list(self): + self.assertEqual(_parse_categories(""), []) + + def test_single_category(self): + self.assertEqual(_parse_categories("0"), [0]) + + def test_multiple_categories(self): + self.assertEqual(_parse_categories("0,1,4"), [0, 1, 4]) + + def test_strips_whitespace(self): + self.assertEqual(_parse_categories(" 0 , 1 , 4 "), [0, 1, 4]) + + def test_raises_on_invalid(self): + with self.assertRaises(ValueError): + _parse_categories("abc") + + def test_raises_on_partial_invalid(self): + with self.assertRaises(ValueError): + _parse_categories("0,abc,4") + + +if __name__ == "__main__": + unittest.main()