diff --git a/src/builtin_actions.py b/src/builtin_actions.py index 3d72ad9..e54eb2f 100644 --- a/src/builtin_actions.py +++ b/src/builtin_actions.py @@ -478,7 +478,7 @@ def _result_has_work(result: str | None) -> bool: 'No new emails to summarize', 'Tagged 0 / Moved 0', etc. when nothing was done. Used to decide whether to record the run or noop it. """ - if not result: + if not isinstance(result, str) or not result: return False low = result.lower() if "processed 0" in low or "no new" in low or "nothing to" in low: @@ -554,7 +554,7 @@ _HEURISTIC_CRITICAL = ["surgery", "court", "wedding day", "funeral", "delivery d def _classify_event_heuristic(summary: str) -> tuple: """Quick heuristic classification — returns (event_type, importance) or (None, None) if unclear.""" - s = (summary or "").lower() + s = (summary if isinstance(summary, str) else "").lower() etype = None for t, kws in _HEURISTIC_TYPES.items(): if any(k in s for k in kws): diff --git a/tests/test_builtin_actions_nonstring.py b/tests/test_builtin_actions_nonstring.py new file mode 100644 index 0000000..61bd34f --- /dev/null +++ b/tests/test_builtin_actions_nonstring.py @@ -0,0 +1,21 @@ +"""Regression: builtin_actions heuristics must tolerate non-string input. + +_result_has_work did `result.lower()` after a falsy-only guard, and +_classify_event_heuristic did `(summary or "").lower()`; a truthy non-string +(e.g. a dict) raised AttributeError. They now coerce/guard non-strings. +""" +from src.builtin_actions import _result_has_work, _classify_event_heuristic + + +def test_result_has_work_non_string(): + assert _result_has_work({"x": 1}) is False + assert _result_has_work(123) is False + + +def test_classify_event_heuristic_non_string(): + out = _classify_event_heuristic(123) + assert isinstance(out, tuple) + + +def test_valid_inputs_unchanged(): + assert _result_has_work("Processed 0 emails") is False