Files
falyx/tests/test_action_basic.py
Roland Thomas 7f63e16097 feat: Add module docs, Enum coercion, tracebacks, and toggle improvements
- Add comprehensive module docstrings across the codebase for better clarity and documentation.
- Refactor Enum classes (e.g., FileType, ConfirmType) to use `_missing_` for built-in coercion from strings.
- Add `encoding` attribute to `LoadFileAction`, `SaveFileAction`, and `SelectFileAction` for more flexible file handling.
- Enable lazy file loading by default in `SelectFileAction` to improve performance.
- Simplify bottom bar toggle behavior: all toggles now use `ctrl+<key>`, eliminating the need for key conflict checks with Falyx commands.
- Add `ignore_in_history` attribute to `Command` to refine how `ExecutionRegistry` identifies the last valid result.
- Improve History command output: now includes tracebacks when displaying exceptions.
2025-07-19 14:44:43 -04:00

231 lines
6.1 KiB
Python

import pytest
from falyx.action import (
Action,
ActionGroup,
ChainedAction,
FallbackAction,
LiteralInputAction,
)
from falyx.context import ExecutionContext
from falyx.execution_registry import ExecutionRegistry as er
asyncio_default_fixture_loop_scope = "function"
# --- Helpers ---
async def capturing_hook(context: ExecutionContext):
context.extra["hook_triggered"] = True
# --- Fixtures ---
@pytest.fixture(autouse=True)
def clean_registry():
er.clear()
yield
er.clear()
@pytest.mark.asyncio
async def test_action_callable():
"""Test if Action can be created with a callable."""
action = Action("test_action", lambda: "Hello, World!")
result = await action()
assert result == "Hello, World!"
@pytest.mark.asyncio
async def test_action_async_callable():
"""Test if Action can be created with an async callable."""
async def async_callable():
return "Hello, World!"
action = Action("test_action", async_callable)
result = await action()
assert result == "Hello, World!"
assert (
str(action)
== "Action(name='test_action', action=async_callable, args=(), kwargs={}, retry=False, rollback=False)"
)
assert (
repr(action)
== "Action(name='test_action', action=async_callable, args=(), kwargs={}, retry=False, rollback=False)"
)
@pytest.mark.asyncio
async def test_chained_action():
"""Test if ChainedAction can be created and used."""
action1 = Action("one", lambda: 1)
action2 = Action("two", lambda: 2)
chain = ChainedAction(
name="Simple Chain",
actions=[action1, action2],
return_list=True,
)
print(chain)
result = await chain()
assert result == [1, 2]
assert (
str(chain)
== "ChainedAction(name=Simple Chain, actions=['one', 'two'], args=(), kwargs={}, auto_inject=False, return_list=True)"
)
@pytest.mark.asyncio
async def test_action_group():
"""Test if ActionGroup can be created and used."""
action1 = Action("one", lambda: 1)
action2 = Action("two", lambda: 2)
group = ActionGroup(
name="Simple Group",
actions=[action1, action2],
)
print(group)
result = await group()
assert result == [("one", 1), ("two", 2)]
assert (
str(group)
== "ActionGroup(name=Simple Group, actions=['one', 'two'], args=(), kwargs={}, inject_last_result=False, inject_into=last_result)"
)
@pytest.mark.asyncio
async def test_action_non_callable():
"""Test if Action raises an error when created with a non-callable."""
with pytest.raises(TypeError):
Action("test_action", 42)
@pytest.mark.asyncio
@pytest.mark.parametrize(
"return_list, expected",
[
(True, [1, 2, 3]),
(False, 3),
],
)
async def test_chained_action_return_modes(return_list, expected):
chain = ChainedAction(
name="Simple Chain",
actions=[
Action(name="one", action=lambda: 1),
Action(name="two", action=lambda: 2),
Action(name="three", action=lambda: 3),
],
return_list=return_list,
)
result = await chain()
assert result == expected
@pytest.mark.asyncio
@pytest.mark.parametrize(
"return_list, auto_inject, expected",
[
(True, True, [1, 2, 3]),
(True, False, [1, 2, 3]),
(False, True, 3),
(False, False, 3),
],
)
async def test_chained_action_literals(return_list, auto_inject, expected):
chain = ChainedAction(
name="Literal Chain",
actions=[1, 2, 3],
return_list=return_list,
auto_inject=auto_inject,
)
result = await chain()
assert result == expected
@pytest.mark.asyncio
async def test_literal_input_action():
"""Test if LiteralInputAction can be created and used."""
action = LiteralInputAction("Hello, World!")
result = await action()
assert result == "Hello, World!"
assert action.value == "Hello, World!"
assert str(action) == "LiteralInputAction(value='Hello, World!')"
@pytest.mark.asyncio
async def test_fallback_action():
"""Test if FallbackAction can be created and used."""
action = FallbackAction("Fallback value")
chain = ChainedAction(
name="Fallback Chain",
actions=[
Action(name="one", action=lambda: None),
action,
],
)
result = await chain()
assert result == "Fallback value"
assert str(action) == "FallbackAction(fallback='Fallback value')"
@pytest.mark.asyncio
async def test_remove_action_from_chain():
"""Test if an action can be removed from a chain."""
action1 = Action(name="one", action=lambda: 1)
action2 = Action(name="two", action=lambda: 2)
chain = ChainedAction(
name="Simple Chain",
actions=[action1, action2],
)
assert len(chain.actions) == 2
# Remove the first action
chain.remove_action(action1.name)
assert len(chain.actions) == 1
assert chain.actions[0] == action2
@pytest.mark.asyncio
async def test_has_action_in_chain():
"""Test if an action can be checked for presence in a chain."""
action1 = Action(name="one", action=lambda: 1)
action2 = Action(name="two", action=lambda: 2)
chain = ChainedAction(
name="Simple Chain",
actions=[action1, action2],
)
assert chain.has_action(action1.name) is True
assert chain.has_action(action2.name) is True
# Remove the first action
chain.remove_action(action1.name)
assert chain.has_action(action1.name) is False
assert chain.has_action(action2.name) is True
@pytest.mark.asyncio
async def test_get_action_from_chain():
"""Test if an action can be retrieved from a chain."""
action1 = Action(name="one", action=lambda: 1)
action2 = Action(name="two", action=lambda: 2)
chain = ChainedAction(
name="Simple Chain",
actions=[action1, action2],
)
assert chain.get_action(action1.name) == action1
assert chain.get_action(action2.name) == action2
# Remove the first action
chain.remove_action(action1.name)
assert chain.get_action(action1.name) is None
assert chain.get_action(action2.name) == action2