mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-17 02:05:22 -04:00
fix: skills CLI summary crashes on a non-string description (#1595)
This commit is contained in:
+12
-1
@@ -41,11 +41,22 @@ def _manager() -> SkillsManager:
|
|||||||
return _mgr
|
return _mgr
|
||||||
|
|
||||||
|
|
||||||
|
def _preview_text(value, limit: int = 200) -> str:
|
||||||
|
"""Truncated preview of a text field, tolerant of non-string values.
|
||||||
|
|
||||||
|
A skill whose ``description`` is a non-string (e.g. a number from a
|
||||||
|
hand-edited/legacy store) would crash ``(value or "")[:200]`` with a
|
||||||
|
TypeError; coerce non-strings to "" instead.
|
||||||
|
"""
|
||||||
|
text = value if isinstance(value, str) else ""
|
||||||
|
return text[:limit]
|
||||||
|
|
||||||
|
|
||||||
def _summary(skill: dict) -> dict:
|
def _summary(skill: dict) -> dict:
|
||||||
return {
|
return {
|
||||||
"name": skill.get("name", ""),
|
"name": skill.get("name", ""),
|
||||||
"category": skill.get("category", "general"),
|
"category": skill.get("category", "general"),
|
||||||
"description": (skill.get("description") or "")[:200],
|
"description": _preview_text(skill.get("description")),
|
||||||
"status": skill.get("status", ""),
|
"status": skill.get("status", ""),
|
||||||
"uses": skill.get("uses", 0),
|
"uses": skill.get("uses", 0),
|
||||||
"last_used": skill.get("last_used") or "",
|
"last_used": skill.get("last_used") or "",
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
"""Regression: the skills CLI summary must tolerate a non-string description.
|
||||||
|
|
||||||
|
`_summary` did `(skill.get("description") or "")[:200]`. A non-string
|
||||||
|
description (e.g. a number from a hand-edited/legacy skill store) is truthy, so
|
||||||
|
`123[:200]` raised TypeError. `_preview_text` coerces non-strings to "".
|
||||||
|
"""
|
||||||
|
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):
|
||||||
|
mod = types.ModuleType("services.memory.skills")
|
||||||
|
mod.SkillsManager = MagicMock()
|
||||||
|
monkeypatch.setitem(sys.modules, "services.memory.skills", mod)
|
||||||
|
path = ROOT / "scripts" / "odysseus-skills"
|
||||||
|
loader = importlib.machinery.SourceFileLoader("odysseus_skills_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(monkeypatch):
|
||||||
|
cli = _load_cli(monkeypatch)
|
||||||
|
assert cli._preview_text(None) == ""
|
||||||
|
assert cli._preview_text(123) == ""
|
||||||
|
assert cli._preview_text({"x": 1}) == ""
|
||||||
|
assert cli._preview_text("y" * 250) == "y" * 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_summary_does_not_crash_on_non_string_description(monkeypatch):
|
||||||
|
cli = _load_cli(monkeypatch)
|
||||||
|
out = cli._summary({"name": "n", "description": 123})
|
||||||
|
assert out["description"] == ""
|
||||||
Reference in New Issue
Block a user