From 815bdf57d5d747d63639f2aaf3d95b6da4d41211 Mon Sep 17 00:00:00 2001 From: red person Date: Wed, 3 Jun 2026 08:06:49 +0300 Subject: [PATCH] Ignore non-string task CLI previews (#1559) --- scripts/odysseus-tasks | 9 +++++++-- tests/test_tasks_cli_preview.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 tests/test_tasks_cli_preview.py diff --git a/scripts/odysseus-tasks b/scripts/odysseus-tasks index 1c45d54..d0484db 100755 --- a/scripts/odysseus-tasks +++ b/scripts/odysseus-tasks @@ -26,13 +26,18 @@ except ModuleNotFoundError as e: sys.exit(2) +def _preview_text(value, limit: int = 200) -> str: + text = value if isinstance(value, str) else "" + return text[:limit] + ("…" if len(text) > limit else "") + + def _serialize_task(t: "ScheduledTask") -> dict: return { "id": t.id, "name": t.name, "task_type": t.task_type, "action": t.action, - "prompt": (t.prompt or "")[:200] + ("…" if t.prompt and len(t.prompt) > 200 else ""), + "prompt": _preview_text(t.prompt), "schedule": t.schedule, "scheduled_time": t.scheduled_time, "next_run": t.next_run.isoformat() if t.next_run else "", @@ -51,7 +56,7 @@ def _serialize_run(r: "TaskRun") -> dict: "started_at": r.started_at.isoformat() if r.started_at else "", "completed_at": r.completed_at.isoformat() if r.completed_at else "", "status": r.status, - "output_preview": (getattr(r, "output", "") or "")[:200], + "output_preview": _preview_text(getattr(r, "output", "")), } diff --git a/tests/test_tasks_cli_preview.py b/tests/test_tasks_cli_preview.py new file mode 100644 index 0000000..731a2b0 --- /dev/null +++ b/tests/test_tasks_cli_preview.py @@ -0,0 +1,31 @@ +import importlib.machinery +import importlib.util +import sys +import types +from pathlib import Path +from unittest.mock import MagicMock + + +ROOT = Path(__file__).resolve().parents[1] + + +def _load_cli(monkeypatch): + db = types.ModuleType("core.database") + db.SessionLocal = MagicMock() + db.ScheduledTask = MagicMock() + db.TaskRun = MagicMock() + monkeypatch.setitem(sys.modules, "core.database", db) + path = ROOT / "scripts" / "odysseus-tasks" + loader = importlib.machinery.SourceFileLoader("odysseus_tasks_cli", str(path)) + spec = importlib.util.spec_from_loader(loader.name, loader) + module = importlib.util.module_from_spec(spec) + loader.exec_module(module) + return module + + +def test_preview_text_ignores_non_string_values(monkeypatch): + cli = _load_cli(monkeypatch) + + assert cli._preview_text(None) == "" + assert cli._preview_text({"bad": "row"}) == "" + assert cli._preview_text("x" * 201) == ("x" * 200) + "…"