import pytest from unittest.mock import patch, MagicMock mock_nodes = MagicMock() mock_nodes.MAX_RESOLUTION = 16384 mock_server = MagicMock() with patch.dict("sys.modules", {"nodes": mock_nodes, "server": mock_server}): from comfy_extras.nodes_number_convert import NumberConvertNode class TestNumberConvertExecute: @staticmethod def _exec(value) -> object: return NumberConvertNode.execute(value) # --- INT input --- def test_int_input(self): result = self._exec(42) assert result[0] == 42.0 assert result[1] == 42 def test_int_zero(self): result = self._exec(0) assert result[0] == 0.0 assert result[1] == 0 def test_int_negative(self): result = self._exec(-7) assert result[0] == -7.0 assert result[1] == -7 # --- FLOAT input --- def test_float_input(self): result = self._exec(3.14) assert result[0] == 3.14 assert result[1] == 3 def test_float_truncation_toward_zero(self): result = self._exec(-2.9) assert result[0] == -2.9 assert result[1] == -2 # int() truncates toward zero, not floor def test_float_output_type(self): result = self._exec(5) assert isinstance(result[0], float) def test_int_output_type(self): result = self._exec(5.7) assert isinstance(result[1], int) # --- BOOL input --- def test_bool_true(self): result = self._exec(True) assert result[0] == 1.0 assert result[1] == 1 def test_bool_false(self): result = self._exec(False) assert result[0] == 0.0 assert result[1] == 0 # --- STRING input --- def test_string_integer(self): result = self._exec("42") assert result[0] == 42.0 assert result[1] == 42 def test_string_float(self): result = self._exec("3.14") assert result[0] == 3.14 assert result[1] == 3 def test_string_negative(self): result = self._exec("-5.5") assert result[0] == -5.5 assert result[1] == -5 def test_string_with_whitespace(self): result = self._exec(" 7.0 ") assert result[0] == 7.0 assert result[1] == 7 def test_string_scientific_notation(self): result = self._exec("1e3") assert result[0] == 1000.0 assert result[1] == 1000 # --- Large number precision (string input) --- def test_string_large_int_above_2_53(self): """Text-to-int must not lose precision for integers beyond 2^53.""" big = 2**53 + 1 # 9007199254740993 result = self._exec(str(big)) assert result[1] == big def test_string_large_negative_int_above_2_53(self): big = -(2**53 + 1) result = self._exec(str(big)) assert result[1] == big def test_string_very_large_int(self): big = 2**63 + 42 result = self._exec(str(big)) assert result[1] == big def test_string_large_int_float_output_is_float(self): """FLOAT output is still a float (may lose precision, but must be float type).""" result = self._exec(str(2**53 + 1)) assert isinstance(result[0], float) # --- Large number precision (int input) --- def test_int_large_above_2_53(self): """Native int input must preserve its value in the INT output.""" big = 2**53 + 1 result = self._exec(big) assert result[1] == big def test_int_large_negative_above_2_53(self): big = -(2**53 + 1) result = self._exec(big) assert result[1] == big def test_int_very_large(self): big = 2**100 result = self._exec(big) assert result[1] == big # --- String decimal / scientific notation fallback --- def test_string_decimal_still_truncates(self): """Strings with decimal points fall back to int(float(...)) truncation.""" result = self._exec("3.7") assert result[1] == 3 def test_string_negative_decimal_truncates(self): result = self._exec("-2.9") assert result[1] == -2 def test_string_scientific_large(self): result = self._exec("1e18") assert result[0] == 1e18 assert result[1] == 10**18 # --- STRING error paths --- def test_empty_string_raises(self): with pytest.raises(ValueError, match="Cannot convert empty string"): self._exec("") def test_whitespace_only_string_raises(self): with pytest.raises(ValueError, match="Cannot convert empty string"): self._exec(" ") def test_non_numeric_string_raises(self): with pytest.raises(ValueError, match="Cannot convert string to number"): self._exec("abc") def test_string_inf_raises(self): with pytest.raises(ValueError, match="non-finite"): self._exec("inf") def test_string_nan_raises(self): with pytest.raises(ValueError, match="non-finite"): self._exec("nan") def test_string_negative_inf_raises(self): with pytest.raises(ValueError, match="non-finite"): self._exec("-inf") # --- Unsupported type --- def test_unsupported_type_raises(self): with pytest.raises(TypeError, match="Unsupported input type"): self._exec([1, 2, 3])