diff --git a/tests/test_utils_shortcut.py b/tests/test_utils_shortcut.py new file mode 100644 index 000000000..660270ea2 --- /dev/null +++ b/tests/test_utils_shortcut.py @@ -0,0 +1,204 @@ +import pytest +import inspect +from unittest.mock import MagicMock, patch +from textwrap import dedent + +from your_module import Shortcut, as_shortcut, shortcuts, Registry + +# Tests for Shortcut class + +class TestShortcutInitialization: + def test_init_with_custom_name(self): + # Verify initialization with custom name + func = lambda x: x * 2 + sc = Shortcut(func, name="Doubler") + assert sc.func(5) == 10 + assert sc.name == "Doubler" + + def test_init_without_name(self): + # Test auto-naming from __name__ + def addition_fct(a, b): + return a + b + sc = Shortcut(addition_fct) + assert sc.name == "addition fct" + assert sc.func(2, 3) == 5 + +def test_run_method(self, capsys): + # Test direct execution while verifying print output + execution_results = [] + + def sample(): + execution_results.append("executed") + return "ok" + + sc = Shortcut(sample, name="TestRun") + task = sc.run(hold=False) # Testing immediate execution + + # Verify the print output + captured = capsys.readouterr() + assert captured.out == 'Running "TestRun"\n' + + # Verify the function executed + assert execution_results == ["executed"] + + # Verify Task properties + assert task.func == sample + assert task.func() == "ok" + assert task.hold is False + + task = sc.run(hold=True) + assert task.hold is True + +def test_repr_output(self): + # Test exact __repr__ format + def my_func(): + pass + + sc = Shortcut(my_func, name="Test Repr") + assert repr(sc) == f"Shortcut \"Test Repr\"" + +def test_source_with_regular_function(self): + # Test source with normal function + def example(): + a = 1 + b = 2 + return a + b + + sc = Shortcut(example) + src = sc.source + assert "def example():" in src + assert "a = 1" in src + assert "b = 2" in src + assert "return a + b" in src + +@patch('inspect.getsource') +def test_source_error_handling(self, mock_getsource): + # Test exception handling when inspect fails + mock_getsource.side_effect = OSError("No source") + sc = Shortcut(lambda x: x) + with pytest.raises(OSError): + _ = sc.source + +# Tests for as_shortcut decorator + +def test_as_shortcut_basic(): + # Test basic decorator without parameters + @as_shortcut + def basic_func(): + return 42 + + assert isinstance(basic_func, Shortcut) + assert basic_func.name == "basic func" + assert basic_func.func() == 42 + +def test_as_shortcut_with_name(): + # Test decorator with name parameter + @as_shortcut(name="CustomName") + def decorated(): + pass + + assert decorated.name == "CustomName" + +def test_as_shortcut_factory_pattern(): + # Test factory pattern usage + decorator = as_shortcut(name="FromFactory") + + @decorator + def sample(): + pass + + assert sample.name == "FromFactory" + +# Tests for shortcuts singleton + +class TestShortcutsSingleton: + def setup_method(self): + # Reset singleton before each test + shortcuts._instance = None + + def test_singleton_behavior(self): + # Verify singleton pattern + inst1 = shortcuts + inst2 = shortcuts + assert inst1 is inst2 + + def test_registration(self): + # Test automatic registration + @as_shortcut(name="FuncA") + def func_a(): + pass + + @as_shortcut(name="FuncB") + def func_b(): + pass + + assert len(shortcuts._get()) == 2 + assert "FuncA" in shortcuts._get() + assert shortcuts["FuncA"].func == func_a.func + + def test_getitem_access(self): + # Test dictionary-style access + @as_shortcut(name="TestAccess") + def access_test(): + return 123 + + assert shortcuts["TestAccess"].func() == 123 + + def test_missing_key(self): + # Test access with invalid key + with pytest.raises(KeyError): + _ = shortcuts["InvalidKey"] + + def test_repr_output(self): + # Test registry display + @as_shortcut(name="DisplayTest") + def to_display(): + pass + + repr_str = repr(shortcuts) + assert "DisplayTest" in repr_str + assert "Shortcut" in repr_str + +# Full integration tests + +class TestFullIntegration: + def setup_method(self): + shortcuts._instance = None + + def test_workflow(self, capsys): + # Test complete workflow from creation to execution + @as_shortcut(name="FullTest") + def integration_test(a, b): + print(f"Calculating {a} + {b}") + return a + b + + sc = shortcuts["FullTest"] + assert sc.name == "FullTest" + + task = sc.run(hold=True) + out, _ = capsys.readouterr() + assert "Running \"FullTest\"" in out + + assert task.func(3, 4) == 7 + + source = sc.source + assert "def integration_test(a, b):" in source + assert "print(f\"Calculating {a} + {b}\")" in source + + def test_multiple_shortcuts(self): + # Test multiple shortcuts coexistence + @as_shortcut(name="First") + def first(): + return 1 + + @as_shortcut(name="Second") + def second(): + return 2 + + assert shortcuts["First"].func() == 1 + assert shortcuts["Second"].func() == 2 + assert len(shortcuts._get()) == 2 + +def test_registry_inheritance(): + # Verify Shortcut inherits from Registry + assert issubclass(Shortcut, Registry) \ No newline at end of file