460959f0d4
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
112 lines
4.4 KiB
Python
112 lines
4.4 KiB
Python
"""Tests for the tool-use fixes from the 2026-05-08 journal session."""
|
|
|
|
from types import SimpleNamespace
|
|
from unittest.mock import AsyncMock, patch
|
|
|
|
import pytest
|
|
|
|
|
|
def _project(pid: int, title: str, description: str = "", auto_summary: str = ""):
|
|
return SimpleNamespace(
|
|
id=pid, title=title, description=description, auto_summary=auto_summary,
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_search_projects_tool_returns_success_true():
|
|
"""The dispatcher sets status from result['success']; without this key
|
|
the call gets labeled 'error' even when data is returned."""
|
|
from fabledassistant.services.tools import projects as projects_tool
|
|
|
|
fake_projects = [_project(5, "Famous-Supply Work topics", "AT&T fiber circuit")]
|
|
|
|
with patch.object(projects_tool, "list_projects", new=AsyncMock(return_value=fake_projects)):
|
|
result = await projects_tool.search_projects_tool(
|
|
user_id=1, arguments={"query": "famous supply"},
|
|
)
|
|
|
|
assert result["success"] is True
|
|
assert result["type"] == "projects_list"
|
|
assert len(result["data"]["projects"]) == 1
|
|
|
|
|
|
def test_strip_type_nouns_removes_task_word():
|
|
from fabledassistant.services.notes import _strip_type_nouns
|
|
assert _strip_type_nouns("sebring secondary task") == ["sebring", "secondary"]
|
|
|
|
|
|
def test_strip_type_nouns_removes_all_variants():
|
|
from fabledassistant.services.notes import _strip_type_nouns
|
|
assert _strip_type_nouns("project notes task") == []
|
|
|
|
|
|
def test_strip_type_nouns_case_insensitive():
|
|
from fabledassistant.services.notes import _strip_type_nouns
|
|
assert _strip_type_nouns("Sebring Task NOTES") == ["Sebring"]
|
|
|
|
|
|
def test_strip_type_nouns_preserves_real_content_words():
|
|
from fabledassistant.services.notes import _strip_type_nouns
|
|
assert _strip_type_nouns("circuit configuration") == ["circuit", "configuration"]
|
|
|
|
|
|
def test_strip_type_nouns_handles_empty_string():
|
|
from fabledassistant.services.notes import _strip_type_nouns
|
|
assert _strip_type_nouns("") == []
|
|
assert _strip_type_nouns(" ") == []
|
|
|
|
|
|
def test_score_project_match_exact_title_returns_1():
|
|
from fabledassistant.services.tools._helpers import score_project_match
|
|
p = _project(5, "Famous-Supply Work topics")
|
|
assert score_project_match("Famous-Supply Work topics", p) == 1.0
|
|
|
|
|
|
def test_score_project_match_colloquial_substring_at_least_85():
|
|
"""'famous supply' is a substring of normalized 'famous supply work topics'
|
|
after stripping the hyphen. Substring match returns 0.85."""
|
|
from fabledassistant.services.tools._helpers import score_project_match
|
|
p = _project(5, "Famous-Supply Work topics", auto_summary="AT&T fiber circuit")
|
|
score = score_project_match("famous supply project", p)
|
|
assert score >= 0.85, f"expected substring tier (>=0.85), got {score}"
|
|
|
|
|
|
def test_score_project_match_query_in_summary_returns_70():
|
|
from fabledassistant.services.tools._helpers import score_project_match
|
|
p = _project(12, "Minstrel", auto_summary="self-hosted music server")
|
|
assert score_project_match("music server", p) == 0.70
|
|
|
|
|
|
def test_score_project_match_unrelated_returns_low():
|
|
from fabledassistant.services.tools._helpers import score_project_match
|
|
p = _project(12, "Minstrel", auto_summary="self-hosted music server")
|
|
assert score_project_match("garden renovation", p) < 0.5
|
|
|
|
|
|
def test_score_project_match_empty_query_returns_zero():
|
|
from fabledassistant.services.tools._helpers import score_project_match
|
|
p = _project(5, "Famous-Supply Work topics")
|
|
assert score_project_match("", p) == 0.0
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_search_projects_tool_ranks_substring_match_above_others():
|
|
"""Substring hits (score 0.85) must rank above SequenceMatcher misses
|
|
for unrelated projects."""
|
|
from fabledassistant.services.tools import projects as projects_tool
|
|
|
|
fake_projects = [
|
|
_project(12, "Minstrel", auto_summary="self-hosted music server"),
|
|
_project(5, "Famous-Supply Work topics", auto_summary="AT&T fiber circuit"),
|
|
_project(13, "ImageRepo", auto_summary="self-hosted Flask app"),
|
|
]
|
|
|
|
with patch.object(projects_tool, "list_projects", new=AsyncMock(return_value=fake_projects)):
|
|
result = await projects_tool.search_projects_tool(
|
|
user_id=1, arguments={"query": "famous supply project"},
|
|
)
|
|
|
|
top = result["data"]["projects"][0]
|
|
assert top["id"] == 5, f"expected Famous-Supply to rank first, got {top}"
|
|
assert top["score"] >= 0.85
|