|
|
|
@@ -14,6 +14,56 @@ from app.agents.note_agent import NoteAgent
|
|
|
|
from app.agents.project_agent import ProjectAgent
|
|
|
|
from app.agents.project_agent import ProjectAgent
|
|
|
|
from app.agents.task_agent import TaskAgent
|
|
|
|
from app.agents.task_agent import TaskAgent
|
|
|
|
from app.core.agent_registry import registry
|
|
|
|
from app.core.agent_registry import registry
|
|
|
|
|
|
|
|
from app.core.ws_context import clear_client_executor, set_client_executor
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ── WS executor mock ──────────────────────────────────────────────────
|
|
|
|
|
|
|
|
#
|
|
|
|
|
|
|
|
# Tools call execute_on_client() which reads a ContextVar set by the WS
|
|
|
|
|
|
|
|
# handler. In unit tests there is no WS session, so we install a fake
|
|
|
|
|
|
|
|
# executor that returns plausible data for each action type.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_FAKE_ROW: dict[str, Any] = {
|
|
|
|
|
|
|
|
"id": "fake-id",
|
|
|
|
|
|
|
|
"title": "Fake Title",
|
|
|
|
|
|
|
|
"name": "Fake Name",
|
|
|
|
|
|
|
|
"status": "todo",
|
|
|
|
|
|
|
|
"priority": "medium",
|
|
|
|
|
|
|
|
"content": "Fake content",
|
|
|
|
|
|
|
|
"date": 1700000000000,
|
|
|
|
|
|
|
|
"taskId": "fake-task-id",
|
|
|
|
|
|
|
|
"author": "Alice",
|
|
|
|
|
|
|
|
"projectId": None,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def _fake_executor(payload: dict) -> dict:
|
|
|
|
|
|
|
|
action = payload.get("action", "")
|
|
|
|
|
|
|
|
if action == "select":
|
|
|
|
|
|
|
|
return {"rows": []}
|
|
|
|
|
|
|
|
if action == "insert":
|
|
|
|
|
|
|
|
data = payload.get("data", {})
|
|
|
|
|
|
|
|
return {"row": {**_FAKE_ROW, **data}}
|
|
|
|
|
|
|
|
if action == "update":
|
|
|
|
|
|
|
|
data = payload.get("data", {})
|
|
|
|
|
|
|
|
row = {**_FAKE_ROW, "id": data.get("id", "fake-id"), **data.get("updates", {})}
|
|
|
|
|
|
|
|
return {"row": row}
|
|
|
|
|
|
|
|
if action == "delete":
|
|
|
|
|
|
|
|
return {"deleted": True}
|
|
|
|
|
|
|
|
if action == "get":
|
|
|
|
|
|
|
|
data = payload.get("data", {})
|
|
|
|
|
|
|
|
return {"row": {**_FAKE_ROW, "id": data.get("id", "fake-id")}}
|
|
|
|
|
|
|
|
if action == "vector_upsert":
|
|
|
|
|
|
|
|
return {"ok": True}
|
|
|
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
|
|
|
|
|
|
def ws_executor():
|
|
|
|
|
|
|
|
"""Install a fake WS executor for every test so tools can run without a real WS."""
|
|
|
|
|
|
|
|
set_client_executor(_fake_executor)
|
|
|
|
|
|
|
|
yield
|
|
|
|
|
|
|
|
clear_client_executor()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ── Helpers ──────────────────────────────────────────────────────────
|
|
|
|
# ── Helpers ──────────────────────────────────────────────────────────
|
|
|
|
@@ -148,110 +198,142 @@ class TestTaskAgentTools:
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_list_tasks_defaults(self) -> None:
|
|
|
|
async def test_list_tasks_defaults(self) -> None:
|
|
|
|
from app.agents.task_agent import list_tasks
|
|
|
|
from app.agents.task_agent import list_tasks
|
|
|
|
|
|
|
|
with patch("app.agents.task_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
|
|
|
|
m.return_value = {"rows": []}
|
|
|
|
result = await list_tasks.ainvoke({})
|
|
|
|
result = await list_tasks.ainvoke({})
|
|
|
|
data = json.loads(result)
|
|
|
|
m.assert_called_once_with(
|
|
|
|
assert data["action"] == "list"
|
|
|
|
action="select", table="tasks",
|
|
|
|
assert data["table"] == "tasks"
|
|
|
|
filters={"projectId": None, "status": None, "search": None, "orderBy": None},
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
assert result == "No tasks found matching the given filters."
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_list_tasks_with_status_filter(self) -> None:
|
|
|
|
async def test_list_tasks_with_status_filter(self) -> None:
|
|
|
|
from app.agents.task_agent import list_tasks
|
|
|
|
from app.agents.task_agent import list_tasks
|
|
|
|
result = await list_tasks.ainvoke({"status": "done"})
|
|
|
|
with patch("app.agents.task_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
data = json.loads(result)
|
|
|
|
m.return_value = {"rows": []}
|
|
|
|
assert data["filters"]["status"] == "done"
|
|
|
|
await list_tasks.ainvoke({"status": "done"})
|
|
|
|
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
|
|
|
|
assert call_kwargs["filters"]["status"] == "done"
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_create_task_defaults(self) -> None:
|
|
|
|
async def test_create_task_defaults(self) -> None:
|
|
|
|
from app.agents.task_agent import create_task
|
|
|
|
from app.agents.task_agent import create_task
|
|
|
|
|
|
|
|
fake_row = {"id": "t1", "title": "Test task", "status": "todo", "priority": "medium"}
|
|
|
|
|
|
|
|
with patch("app.agents.task_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
|
|
|
|
m.return_value = {"row": fake_row}
|
|
|
|
result = await create_task.ainvoke({"title": "Test task"})
|
|
|
|
result = await create_task.ainvoke({"title": "Test task"})
|
|
|
|
data = json.loads(result)
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
assert data["action"] == "create_record"
|
|
|
|
assert call_kwargs["action"] == "insert"
|
|
|
|
assert data["table"] == "tasks"
|
|
|
|
assert call_kwargs["table"] == "tasks"
|
|
|
|
assert data["data"]["title"] == "Test task"
|
|
|
|
assert call_kwargs["data"]["title"] == "Test task"
|
|
|
|
assert data["data"]["status"] == "todo"
|
|
|
|
assert call_kwargs["data"]["status"] == "todo"
|
|
|
|
assert data["data"]["priority"] == "medium"
|
|
|
|
assert call_kwargs["data"]["priority"] == "medium"
|
|
|
|
|
|
|
|
assert "Test task" in result
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_create_task_with_all_fields(self) -> None:
|
|
|
|
async def test_create_task_with_all_fields(self) -> None:
|
|
|
|
from app.agents.task_agent import create_task
|
|
|
|
from app.agents.task_agent import create_task
|
|
|
|
result = await create_task.ainvoke({
|
|
|
|
fake_row = {"id": "t1", "title": "Deploy", "status": "in_progress", "priority": "high"}
|
|
|
|
"title": "Deploy",
|
|
|
|
with patch("app.agents.task_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
"priority": "high",
|
|
|
|
m.return_value = {"row": fake_row}
|
|
|
|
"status": "in_progress",
|
|
|
|
await create_task.ainvoke({
|
|
|
|
"project_id": "p1",
|
|
|
|
"title": "Deploy", "priority": "high", "status": "in_progress",
|
|
|
|
"is_ai_suggested": 1,
|
|
|
|
"project_id": "p1", "is_ai_suggested": 1,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
data = json.loads(result)
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
assert data["data"]["priority"] == "high"
|
|
|
|
assert call_kwargs["data"]["priority"] == "high"
|
|
|
|
assert data["data"]["status"] == "in_progress"
|
|
|
|
assert call_kwargs["data"]["status"] == "in_progress"
|
|
|
|
assert data["data"]["projectId"] == "p1"
|
|
|
|
assert call_kwargs["data"]["projectId"] == "p1"
|
|
|
|
assert data["data"]["isAiSuggested"] == 1
|
|
|
|
assert call_kwargs["data"]["isAiSuggested"] == 1
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_update_task_with_status(self) -> None:
|
|
|
|
async def test_update_task_with_status(self) -> None:
|
|
|
|
from app.agents.task_agent import update_task
|
|
|
|
from app.agents.task_agent import update_task
|
|
|
|
|
|
|
|
fake_row = {"id": "t1", "title": "Buy groceries", "status": "done"}
|
|
|
|
|
|
|
|
with patch("app.agents.task_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
|
|
|
|
m.return_value = {"row": fake_row}
|
|
|
|
result = await update_task.ainvoke({"task_id": "t1", "status": "done"})
|
|
|
|
result = await update_task.ainvoke({"task_id": "t1", "status": "done"})
|
|
|
|
data = json.loads(result)
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
assert data["action"] == "update_record"
|
|
|
|
assert call_kwargs["action"] == "update"
|
|
|
|
assert data["data"]["id"] == "t1"
|
|
|
|
assert call_kwargs["data"]["id"] == "t1"
|
|
|
|
assert data["data"]["updates"]["status"] == "done"
|
|
|
|
assert call_kwargs["data"]["updates"]["status"] == "done"
|
|
|
|
|
|
|
|
assert "t1" in result
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_update_task_empty_updates(self) -> None:
|
|
|
|
async def test_update_task_empty_updates(self) -> None:
|
|
|
|
from app.agents.task_agent import update_task
|
|
|
|
from app.agents.task_agent import update_task
|
|
|
|
result = await update_task.ainvoke({"task_id": "t1"})
|
|
|
|
fake_row = {"id": "t1", "title": "Task", "status": "todo"}
|
|
|
|
data = json.loads(result)
|
|
|
|
with patch("app.agents.task_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
assert data["data"]["updates"] == {}
|
|
|
|
m.return_value = {"row": fake_row}
|
|
|
|
|
|
|
|
await update_task.ainvoke({"task_id": "t1"})
|
|
|
|
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
|
|
|
|
assert call_kwargs["data"]["updates"] == {}
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_delete_task(self) -> None:
|
|
|
|
async def test_delete_task(self) -> None:
|
|
|
|
from app.agents.task_agent import delete_task
|
|
|
|
from app.agents.task_agent import delete_task
|
|
|
|
|
|
|
|
with patch("app.agents.task_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
|
|
|
|
m.return_value = {"deleted": True}
|
|
|
|
result = await delete_task.ainvoke({"task_id": "t1"})
|
|
|
|
result = await delete_task.ainvoke({"task_id": "t1"})
|
|
|
|
data = json.loads(result)
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
assert data["action"] == "delete_record"
|
|
|
|
assert call_kwargs["action"] == "delete"
|
|
|
|
assert data["table"] == "tasks"
|
|
|
|
assert call_kwargs["table"] == "tasks"
|
|
|
|
assert data["data"]["id"] == "t1"
|
|
|
|
assert call_kwargs["data"]["id"] == "t1"
|
|
|
|
|
|
|
|
assert "t1" in result
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_list_tasks_due_today(self) -> None:
|
|
|
|
async def test_list_tasks_due_today(self) -> None:
|
|
|
|
from app.agents.task_agent import list_tasks_due_today
|
|
|
|
from app.agents.task_agent import list_tasks_due_today
|
|
|
|
|
|
|
|
with patch("app.agents.task_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
|
|
|
|
m.return_value = {"rows": []}
|
|
|
|
result = await list_tasks_due_today.ainvoke({})
|
|
|
|
result = await list_tasks_due_today.ainvoke({})
|
|
|
|
data = json.loads(result)
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
assert data["action"] == "list_due_today"
|
|
|
|
assert call_kwargs["action"] == "select"
|
|
|
|
assert data["table"] == "tasks"
|
|
|
|
assert call_kwargs["table"] == "tasks"
|
|
|
|
|
|
|
|
assert "dueDateFrom" in call_kwargs["filters"]
|
|
|
|
|
|
|
|
assert result == "No tasks are due today."
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_list_task_comments(self) -> None:
|
|
|
|
async def test_list_task_comments(self) -> None:
|
|
|
|
from app.agents.task_agent import list_task_comments
|
|
|
|
from app.agents.task_agent import list_task_comments
|
|
|
|
|
|
|
|
with patch("app.agents.task_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
|
|
|
|
m.return_value = {"rows": []}
|
|
|
|
result = await list_task_comments.ainvoke({"task_id": "t1"})
|
|
|
|
result = await list_task_comments.ainvoke({"task_id": "t1"})
|
|
|
|
data = json.loads(result)
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
assert data["action"] == "list"
|
|
|
|
assert call_kwargs["action"] == "select"
|
|
|
|
assert data["table"] == "taskComments"
|
|
|
|
assert call_kwargs["table"] == "taskComments"
|
|
|
|
assert data["filters"]["taskId"] == "t1"
|
|
|
|
assert call_kwargs["filters"]["taskId"] == "t1"
|
|
|
|
|
|
|
|
assert "t1" in result
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_add_task_comment(self) -> None:
|
|
|
|
async def test_add_task_comment(self) -> None:
|
|
|
|
from app.agents.task_agent import add_task_comment
|
|
|
|
from app.agents.task_agent import add_task_comment
|
|
|
|
|
|
|
|
fake_row = {"id": "c1", "taskId": "t1", "author": "Alice", "content": "Looks good!"}
|
|
|
|
|
|
|
|
with patch("app.agents.task_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
|
|
|
|
m.return_value = {"row": fake_row}
|
|
|
|
result = await add_task_comment.ainvoke({
|
|
|
|
result = await add_task_comment.ainvoke({
|
|
|
|
"task_id": "t1",
|
|
|
|
"task_id": "t1", "author": "Alice", "content": "Looks good!",
|
|
|
|
"author": "Alice",
|
|
|
|
|
|
|
|
"content": "Looks good!",
|
|
|
|
|
|
|
|
})
|
|
|
|
})
|
|
|
|
data = json.loads(result)
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
assert data["action"] == "create_record"
|
|
|
|
assert call_kwargs["action"] == "insert"
|
|
|
|
assert data["table"] == "taskComments"
|
|
|
|
assert call_kwargs["table"] == "taskComments"
|
|
|
|
assert data["data"]["taskId"] == "t1"
|
|
|
|
assert call_kwargs["data"]["taskId"] == "t1"
|
|
|
|
assert data["data"]["author"] == "Alice"
|
|
|
|
assert call_kwargs["data"]["author"] == "Alice"
|
|
|
|
assert data["data"]["content"] == "Looks good!"
|
|
|
|
assert call_kwargs["data"]["content"] == "Looks good!"
|
|
|
|
|
|
|
|
assert "Alice" in result
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_delete_task_comment(self) -> None:
|
|
|
|
async def test_delete_task_comment(self) -> None:
|
|
|
|
from app.agents.task_agent import delete_task_comment
|
|
|
|
from app.agents.task_agent import delete_task_comment
|
|
|
|
|
|
|
|
with patch("app.agents.task_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
|
|
|
|
m.return_value = {"deleted": True}
|
|
|
|
result = await delete_task_comment.ainvoke({"comment_id": "c1"})
|
|
|
|
result = await delete_task_comment.ainvoke({"comment_id": "c1"})
|
|
|
|
data = json.loads(result)
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
assert data["action"] == "delete_record"
|
|
|
|
assert call_kwargs["action"] == "delete"
|
|
|
|
assert data["table"] == "taskComments"
|
|
|
|
assert call_kwargs["table"] == "taskComments"
|
|
|
|
assert data["data"]["id"] == "c1"
|
|
|
|
assert call_kwargs["data"]["id"] == "c1"
|
|
|
|
|
|
|
|
assert "c1" in result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ── CheckpointAgent ───────────────────────────────────────────────────
|
|
|
|
# ── CheckpointAgent ───────────────────────────────────────────────────
|
|
|
|
@@ -301,74 +383,86 @@ class TestCheckpointAgentTools:
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_list_checkpoints_no_project(self) -> None:
|
|
|
|
async def test_list_checkpoints_no_project(self) -> None:
|
|
|
|
from app.agents.checkpoint_agent import list_checkpoints
|
|
|
|
from app.agents.checkpoint_agent import list_checkpoints
|
|
|
|
|
|
|
|
with patch("app.agents.checkpoint_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
|
|
|
|
m.return_value = {"rows": []}
|
|
|
|
result = await list_checkpoints.ainvoke({})
|
|
|
|
result = await list_checkpoints.ainvoke({})
|
|
|
|
data = json.loads(result)
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
assert data["action"] == "list"
|
|
|
|
assert call_kwargs["action"] == "select"
|
|
|
|
assert data["table"] == "checkpoints"
|
|
|
|
assert call_kwargs["table"] == "checkpoints"
|
|
|
|
assert data["filters"]["projectId"] is None
|
|
|
|
assert call_kwargs["filters"]["projectId"] is None
|
|
|
|
|
|
|
|
assert result == "No checkpoints found."
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_list_checkpoints_with_project(self) -> None:
|
|
|
|
async def test_list_checkpoints_with_project(self) -> None:
|
|
|
|
from app.agents.checkpoint_agent import list_checkpoints
|
|
|
|
from app.agents.checkpoint_agent import list_checkpoints
|
|
|
|
result = await list_checkpoints.ainvoke({"project_id": "p1"})
|
|
|
|
with patch("app.agents.checkpoint_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
data = json.loads(result)
|
|
|
|
m.return_value = {"rows": []}
|
|
|
|
assert data["filters"]["projectId"] == "p1"
|
|
|
|
await list_checkpoints.ainvoke({"project_id": "p1"})
|
|
|
|
|
|
|
|
assert m.call_args.kwargs["filters"]["projectId"] == "p1"
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_create_checkpoint(self) -> None:
|
|
|
|
async def test_create_checkpoint(self) -> None:
|
|
|
|
from app.agents.checkpoint_agent import create_checkpoint
|
|
|
|
from app.agents.checkpoint_agent import create_checkpoint
|
|
|
|
|
|
|
|
fake_row = {"id": "cp1", "title": "Beta release", "date": 1700000000000}
|
|
|
|
|
|
|
|
with patch("app.agents.checkpoint_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
|
|
|
|
m.return_value = {"row": fake_row}
|
|
|
|
result = await create_checkpoint.ainvoke({
|
|
|
|
result = await create_checkpoint.ainvoke({
|
|
|
|
"project_id": "p1",
|
|
|
|
"project_id": "p1", "title": "Beta release", "date": 1700000000000,
|
|
|
|
"title": "Beta release",
|
|
|
|
|
|
|
|
"date": 1700000000000,
|
|
|
|
|
|
|
|
})
|
|
|
|
})
|
|
|
|
data = json.loads(result)
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
assert data["action"] == "create_record"
|
|
|
|
assert call_kwargs["action"] == "insert"
|
|
|
|
assert data["table"] == "checkpoints"
|
|
|
|
assert call_kwargs["table"] == "checkpoints"
|
|
|
|
assert data["data"]["projectId"] == "p1"
|
|
|
|
assert call_kwargs["data"]["projectId"] == "p1"
|
|
|
|
assert data["data"]["title"] == "Beta release"
|
|
|
|
assert call_kwargs["data"]["title"] == "Beta release"
|
|
|
|
assert data["data"]["date"] == 1700000000000
|
|
|
|
assert call_kwargs["data"]["date"] == 1700000000000
|
|
|
|
|
|
|
|
assert "Beta release" in result
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_create_checkpoint_ai_suggested(self) -> None:
|
|
|
|
async def test_create_checkpoint_ai_suggested(self) -> None:
|
|
|
|
from app.agents.checkpoint_agent import create_checkpoint
|
|
|
|
from app.agents.checkpoint_agent import create_checkpoint
|
|
|
|
result = await create_checkpoint.ainvoke({
|
|
|
|
fake_row = {"id": "cp1", "title": "Review", "date": 1700000000000}
|
|
|
|
"project_id": "p1",
|
|
|
|
with patch("app.agents.checkpoint_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
"title": "Review",
|
|
|
|
m.return_value = {"row": fake_row}
|
|
|
|
"date": 1700000000000,
|
|
|
|
await create_checkpoint.ainvoke({
|
|
|
|
"is_ai_suggested": 1,
|
|
|
|
"project_id": "p1", "title": "Review", "date": 1700000000000, "is_ai_suggested": 1,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
data = json.loads(result)
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
assert data["data"]["isAiSuggested"] == 1
|
|
|
|
assert call_kwargs["data"]["isAiSuggested"] == 1
|
|
|
|
assert data["data"]["isApproved"] == 0
|
|
|
|
assert call_kwargs["data"]["isApproved"] == 0
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_update_checkpoint_approve(self) -> None:
|
|
|
|
async def test_update_checkpoint_approve(self) -> None:
|
|
|
|
from app.agents.checkpoint_agent import update_checkpoint
|
|
|
|
from app.agents.checkpoint_agent import update_checkpoint
|
|
|
|
result = await update_checkpoint.ainvoke({
|
|
|
|
fake_row = {"id": "c1", "title": "MVP", "isApproved": 1}
|
|
|
|
"checkpoint_id": "c1",
|
|
|
|
with patch("app.agents.checkpoint_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
"is_approved": 1,
|
|
|
|
m.return_value = {"row": fake_row}
|
|
|
|
})
|
|
|
|
result = await update_checkpoint.ainvoke({"checkpoint_id": "c1", "is_approved": 1})
|
|
|
|
data = json.loads(result)
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
assert data["action"] == "update_record"
|
|
|
|
assert call_kwargs["action"] == "update"
|
|
|
|
assert data["data"]["id"] == "c1"
|
|
|
|
assert call_kwargs["data"]["id"] == "c1"
|
|
|
|
assert data["data"]["updates"]["isApproved"] == 1
|
|
|
|
assert call_kwargs["data"]["updates"]["isApproved"] == 1
|
|
|
|
|
|
|
|
assert "c1" in result
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_update_checkpoint_empty_updates(self) -> None:
|
|
|
|
async def test_update_checkpoint_empty_updates(self) -> None:
|
|
|
|
from app.agents.checkpoint_agent import update_checkpoint
|
|
|
|
from app.agents.checkpoint_agent import update_checkpoint
|
|
|
|
result = await update_checkpoint.ainvoke({"checkpoint_id": "c1"})
|
|
|
|
fake_row = {"id": "c1", "title": "MVP"}
|
|
|
|
data = json.loads(result)
|
|
|
|
with patch("app.agents.checkpoint_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
assert data["data"]["updates"] == {}
|
|
|
|
m.return_value = {"row": fake_row}
|
|
|
|
|
|
|
|
await update_checkpoint.ainvoke({"checkpoint_id": "c1"})
|
|
|
|
|
|
|
|
assert m.call_args.kwargs["data"]["updates"] == {}
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_delete_checkpoint(self) -> None:
|
|
|
|
async def test_delete_checkpoint(self) -> None:
|
|
|
|
from app.agents.checkpoint_agent import delete_checkpoint
|
|
|
|
from app.agents.checkpoint_agent import delete_checkpoint
|
|
|
|
|
|
|
|
with patch("app.agents.checkpoint_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
|
|
|
|
m.return_value = {"deleted": True}
|
|
|
|
result = await delete_checkpoint.ainvoke({"checkpoint_id": "c1"})
|
|
|
|
result = await delete_checkpoint.ainvoke({"checkpoint_id": "c1"})
|
|
|
|
data = json.loads(result)
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
assert data["action"] == "delete_record"
|
|
|
|
assert call_kwargs["action"] == "delete"
|
|
|
|
assert data["table"] == "checkpoints"
|
|
|
|
assert call_kwargs["table"] == "checkpoints"
|
|
|
|
assert data["data"]["id"] == "c1"
|
|
|
|
assert call_kwargs["data"]["id"] == "c1"
|
|
|
|
|
|
|
|
assert "c1" in result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ── ProjectAgent ──────────────────────────────────────────────────────
|
|
|
|
# ── ProjectAgent ──────────────────────────────────────────────────────
|
|
|
|
@@ -425,75 +519,101 @@ class TestProjectAgentTools:
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_list_projects_defaults(self) -> None:
|
|
|
|
async def test_list_projects_defaults(self) -> None:
|
|
|
|
from app.agents.project_agent import list_projects
|
|
|
|
from app.agents.project_agent import list_projects
|
|
|
|
|
|
|
|
with patch("app.agents.project_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
|
|
|
|
m.return_value = {"rows": []}
|
|
|
|
result = await list_projects.ainvoke({})
|
|
|
|
result = await list_projects.ainvoke({})
|
|
|
|
data = json.loads(result)
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
assert data["action"] == "list"
|
|
|
|
assert call_kwargs["action"] == "select"
|
|
|
|
assert data["table"] == "projects"
|
|
|
|
assert call_kwargs["table"] == "projects"
|
|
|
|
assert data["filters"]["includeArchived"] is False
|
|
|
|
assert call_kwargs["filters"]["includeArchived"] is False
|
|
|
|
|
|
|
|
assert result == "No projects found."
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_list_projects_include_archived(self) -> None:
|
|
|
|
async def test_list_projects_include_archived(self) -> None:
|
|
|
|
from app.agents.project_agent import list_projects
|
|
|
|
from app.agents.project_agent import list_projects
|
|
|
|
result = await list_projects.ainvoke({"include_archived": 1})
|
|
|
|
with patch("app.agents.project_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
data = json.loads(result)
|
|
|
|
m.return_value = {"rows": []}
|
|
|
|
assert data["filters"]["includeArchived"] is True
|
|
|
|
await list_projects.ainvoke({"include_archived": 1})
|
|
|
|
|
|
|
|
assert m.call_args.kwargs["filters"]["includeArchived"] is True
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_list_all_projects(self) -> None:
|
|
|
|
async def test_list_all_projects(self) -> None:
|
|
|
|
from app.agents.project_agent import list_all_projects
|
|
|
|
from app.agents.project_agent import list_all_projects
|
|
|
|
|
|
|
|
with patch("app.agents.project_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
|
|
|
|
m.return_value = {"rows": []}
|
|
|
|
result = await list_all_projects.ainvoke({})
|
|
|
|
result = await list_all_projects.ainvoke({})
|
|
|
|
data = json.loads(result)
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
assert data["action"] == "list_all"
|
|
|
|
assert call_kwargs["action"] == "select"
|
|
|
|
assert data["table"] == "projects"
|
|
|
|
assert call_kwargs["table"] == "projects"
|
|
|
|
|
|
|
|
assert result == "No projects found."
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_get_project(self) -> None:
|
|
|
|
async def test_get_project(self) -> None:
|
|
|
|
from app.agents.project_agent import get_project
|
|
|
|
from app.agents.project_agent import get_project
|
|
|
|
|
|
|
|
fake_row = {"id": "p1", "name": "Alpha", "status": "active", "clientId": None}
|
|
|
|
|
|
|
|
with patch("app.agents.project_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
|
|
|
|
m.return_value = {"row": fake_row}
|
|
|
|
result = await get_project.ainvoke({"project_id": "p1"})
|
|
|
|
result = await get_project.ainvoke({"project_id": "p1"})
|
|
|
|
data = json.loads(result)
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
assert data["action"] == "get"
|
|
|
|
assert call_kwargs["action"] == "get"
|
|
|
|
assert data["table"] == "projects"
|
|
|
|
assert call_kwargs["table"] == "projects"
|
|
|
|
assert data["data"]["id"] == "p1"
|
|
|
|
assert call_kwargs["data"]["id"] == "p1"
|
|
|
|
|
|
|
|
assert "Alpha" in result
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_create_project_name_only(self) -> None:
|
|
|
|
async def test_create_project_name_only(self) -> None:
|
|
|
|
from app.agents.project_agent import create_project
|
|
|
|
from app.agents.project_agent import create_project
|
|
|
|
|
|
|
|
fake_row = {"id": "p1", "name": "Alpha"}
|
|
|
|
|
|
|
|
with patch("app.agents.project_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
|
|
|
|
m.return_value = {"row": fake_row}
|
|
|
|
result = await create_project.ainvoke({"name": "Alpha"})
|
|
|
|
result = await create_project.ainvoke({"name": "Alpha"})
|
|
|
|
data = json.loads(result)
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
assert data["action"] == "create_record"
|
|
|
|
assert call_kwargs["action"] == "insert"
|
|
|
|
assert data["data"]["name"] == "Alpha"
|
|
|
|
assert call_kwargs["data"]["name"] == "Alpha"
|
|
|
|
assert data["data"]["clientId"] is None
|
|
|
|
assert call_kwargs["data"]["clientId"] is None
|
|
|
|
|
|
|
|
assert "Alpha" in result
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_create_project_with_client(self) -> None:
|
|
|
|
async def test_create_project_with_client(self) -> None:
|
|
|
|
from app.agents.project_agent import create_project
|
|
|
|
from app.agents.project_agent import create_project
|
|
|
|
result = await create_project.ainvoke({"name": "Beta", "client_id": "cl1"})
|
|
|
|
fake_row = {"id": "p1", "name": "Beta"}
|
|
|
|
data = json.loads(result)
|
|
|
|
with patch("app.agents.project_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
assert data["data"]["clientId"] == "cl1"
|
|
|
|
m.return_value = {"row": fake_row}
|
|
|
|
|
|
|
|
await create_project.ainvoke({"name": "Beta", "client_id": "cl1"})
|
|
|
|
|
|
|
|
assert m.call_args.kwargs["data"]["clientId"] == "cl1"
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_update_project_archive(self) -> None:
|
|
|
|
async def test_update_project_archive(self) -> None:
|
|
|
|
from app.agents.project_agent import update_project
|
|
|
|
from app.agents.project_agent import update_project
|
|
|
|
|
|
|
|
fake_row = {"id": "p1", "name": "Alpha", "status": "archived"}
|
|
|
|
|
|
|
|
with patch("app.agents.project_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
|
|
|
|
m.return_value = {"row": fake_row}
|
|
|
|
result = await update_project.ainvoke({"project_id": "p1", "status": "archived"})
|
|
|
|
result = await update_project.ainvoke({"project_id": "p1", "status": "archived"})
|
|
|
|
data = json.loads(result)
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
assert data["action"] == "update_record"
|
|
|
|
assert call_kwargs["action"] == "update"
|
|
|
|
assert data["data"]["id"] == "p1"
|
|
|
|
assert call_kwargs["data"]["id"] == "p1"
|
|
|
|
assert data["data"]["updates"]["status"] == "archived"
|
|
|
|
assert call_kwargs["data"]["updates"]["status"] == "archived"
|
|
|
|
|
|
|
|
assert "p1" in result
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_update_project_empty_updates(self) -> None:
|
|
|
|
async def test_update_project_empty_updates(self) -> None:
|
|
|
|
from app.agents.project_agent import update_project
|
|
|
|
from app.agents.project_agent import update_project
|
|
|
|
result = await update_project.ainvoke({"project_id": "p1"})
|
|
|
|
fake_row = {"id": "p1", "name": "Alpha", "status": "active"}
|
|
|
|
data = json.loads(result)
|
|
|
|
with patch("app.agents.project_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
assert data["data"]["updates"] == {}
|
|
|
|
m.return_value = {"row": fake_row}
|
|
|
|
|
|
|
|
await update_project.ainvoke({"project_id": "p1"})
|
|
|
|
|
|
|
|
assert m.call_args.kwargs["data"]["updates"] == {}
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_delete_project(self) -> None:
|
|
|
|
async def test_delete_project(self) -> None:
|
|
|
|
from app.agents.project_agent import delete_project
|
|
|
|
from app.agents.project_agent import delete_project
|
|
|
|
|
|
|
|
with patch("app.agents.project_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
|
|
|
|
m.return_value = {"deleted": True}
|
|
|
|
result = await delete_project.ainvoke({"project_id": "p1"})
|
|
|
|
result = await delete_project.ainvoke({"project_id": "p1"})
|
|
|
|
data = json.loads(result)
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
assert data["action"] == "delete_record"
|
|
|
|
assert call_kwargs["action"] == "delete"
|
|
|
|
assert data["data"]["id"] == "p1"
|
|
|
|
assert call_kwargs["data"]["id"] == "p1"
|
|
|
|
|
|
|
|
assert "p1" in result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ── NoteAgent ─────────────────────────────────────────────────────────
|
|
|
|
# ── NoteAgent ─────────────────────────────────────────────────────────
|
|
|
|
@@ -543,78 +663,99 @@ class TestNoteAgentTools:
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_list_notes_no_project(self) -> None:
|
|
|
|
async def test_list_notes_no_project(self) -> None:
|
|
|
|
from app.agents.note_agent import list_notes
|
|
|
|
from app.agents.note_agent import list_notes
|
|
|
|
|
|
|
|
with patch("app.agents.note_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
|
|
|
|
m.return_value = {"rows": []}
|
|
|
|
result = await list_notes.ainvoke({})
|
|
|
|
result = await list_notes.ainvoke({})
|
|
|
|
data = json.loads(result)
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
assert data["action"] == "list"
|
|
|
|
assert call_kwargs["action"] == "select"
|
|
|
|
assert data["table"] == "notes"
|
|
|
|
assert call_kwargs["table"] == "notes"
|
|
|
|
assert data["filters"]["projectId"] is None
|
|
|
|
assert call_kwargs["filters"]["projectId"] is None
|
|
|
|
|
|
|
|
assert result == "No notes found."
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_list_notes_with_project(self) -> None:
|
|
|
|
async def test_list_notes_with_project(self) -> None:
|
|
|
|
from app.agents.note_agent import list_notes
|
|
|
|
from app.agents.note_agent import list_notes
|
|
|
|
result = await list_notes.ainvoke({"project_id": "p1"})
|
|
|
|
with patch("app.agents.note_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
data = json.loads(result)
|
|
|
|
m.return_value = {"rows": []}
|
|
|
|
assert data["filters"]["projectId"] == "p1"
|
|
|
|
await list_notes.ainvoke({"project_id": "p1"})
|
|
|
|
|
|
|
|
assert m.call_args.kwargs["filters"]["projectId"] == "p1"
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_get_note(self) -> None:
|
|
|
|
async def test_get_note(self) -> None:
|
|
|
|
from app.agents.note_agent import get_note
|
|
|
|
from app.agents.note_agent import get_note
|
|
|
|
|
|
|
|
fake_row = {"id": "n1", "title": "Daily log", "content": "# Today\nAll good."}
|
|
|
|
|
|
|
|
with patch("app.agents.note_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
|
|
|
|
m.return_value = {"row": fake_row}
|
|
|
|
result = await get_note.ainvoke({"note_id": "n1"})
|
|
|
|
result = await get_note.ainvoke({"note_id": "n1"})
|
|
|
|
data = json.loads(result)
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
assert data["action"] == "get"
|
|
|
|
assert call_kwargs["action"] == "get"
|
|
|
|
assert data["table"] == "notes"
|
|
|
|
assert call_kwargs["table"] == "notes"
|
|
|
|
assert data["data"]["id"] == "n1"
|
|
|
|
assert call_kwargs["data"]["id"] == "n1"
|
|
|
|
|
|
|
|
assert "Daily log" in result
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_create_note_minimal(self) -> None:
|
|
|
|
async def test_create_note_minimal(self) -> None:
|
|
|
|
from app.agents.note_agent import create_note
|
|
|
|
from app.agents.note_agent import create_note
|
|
|
|
result = await create_note.ainvoke({
|
|
|
|
fake_row = {"id": "n1", "title": "Daily log", "projectId": None}
|
|
|
|
"title": "Daily log",
|
|
|
|
with patch("app.agents.note_agent.execute_on_client", new_callable=AsyncMock) as m, \
|
|
|
|
"content": "# Today\nAll good.",
|
|
|
|
patch("app.agents.note_agent.embed", new_callable=AsyncMock) as me:
|
|
|
|
})
|
|
|
|
m.return_value = {"row": fake_row}
|
|
|
|
data = json.loads(result)
|
|
|
|
me.return_value = [0.0] * 1536
|
|
|
|
assert data["action"] == "create_record"
|
|
|
|
result = await create_note.ainvoke({"title": "Daily log", "content": "# Today\nAll good."})
|
|
|
|
assert data["table"] == "notes"
|
|
|
|
# First call: insert; second call: vector_upsert
|
|
|
|
assert data["data"]["title"] == "Daily log"
|
|
|
|
first_call = m.call_args_list[0].kwargs
|
|
|
|
assert data["data"]["content"] == "# Today\nAll good."
|
|
|
|
assert first_call["action"] == "insert"
|
|
|
|
assert data["data"]["projectId"] is None
|
|
|
|
assert first_call["table"] == "notes"
|
|
|
|
|
|
|
|
assert first_call["data"]["title"] == "Daily log"
|
|
|
|
|
|
|
|
assert first_call["data"]["content"] == "# Today\nAll good."
|
|
|
|
|
|
|
|
assert first_call["data"]["projectId"] is None
|
|
|
|
|
|
|
|
assert "Daily log" in result
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_create_note_with_project(self) -> None:
|
|
|
|
async def test_create_note_with_project(self) -> None:
|
|
|
|
from app.agents.note_agent import create_note
|
|
|
|
from app.agents.note_agent import create_note
|
|
|
|
result = await create_note.ainvoke({
|
|
|
|
fake_row = {"id": "n1", "title": "Sprint notes", "projectId": "p1"}
|
|
|
|
"title": "Sprint notes",
|
|
|
|
with patch("app.agents.note_agent.execute_on_client", new_callable=AsyncMock) as m, \
|
|
|
|
"content": "## Sprint 1",
|
|
|
|
patch("app.agents.note_agent.embed", new_callable=AsyncMock) as me:
|
|
|
|
"project_id": "p1",
|
|
|
|
m.return_value = {"row": fake_row}
|
|
|
|
})
|
|
|
|
me.return_value = [0.0] * 1536
|
|
|
|
data = json.loads(result)
|
|
|
|
await create_note.ainvoke({"title": "Sprint notes", "content": "## Sprint 1", "project_id": "p1"})
|
|
|
|
assert data["data"]["projectId"] == "p1"
|
|
|
|
first_call = m.call_args_list[0].kwargs
|
|
|
|
|
|
|
|
assert first_call["data"]["projectId"] == "p1"
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_update_note_content_only(self) -> None:
|
|
|
|
async def test_update_note_content_only(self) -> None:
|
|
|
|
from app.agents.note_agent import update_note
|
|
|
|
from app.agents.note_agent import update_note
|
|
|
|
result = await update_note.ainvoke({
|
|
|
|
fake_row = {"id": "n1", "title": "Daily log", "projectId": None}
|
|
|
|
"note_id": "n1",
|
|
|
|
with patch("app.agents.note_agent.execute_on_client", new_callable=AsyncMock) as m, \
|
|
|
|
"content": "# Updated content",
|
|
|
|
patch("app.agents.note_agent.embed", new_callable=AsyncMock) as me:
|
|
|
|
})
|
|
|
|
m.return_value = {"row": fake_row}
|
|
|
|
data = json.loads(result)
|
|
|
|
me.return_value = [0.0] * 1536
|
|
|
|
assert data["action"] == "update_record"
|
|
|
|
result = await update_note.ainvoke({"note_id": "n1", "content": "# Updated content"})
|
|
|
|
assert data["data"]["id"] == "n1"
|
|
|
|
first_call = m.call_args_list[0].kwargs
|
|
|
|
assert data["data"]["updates"]["content"] == "# Updated content"
|
|
|
|
assert first_call["action"] == "update"
|
|
|
|
assert "title" not in data["data"]["updates"]
|
|
|
|
assert first_call["data"]["id"] == "n1"
|
|
|
|
|
|
|
|
assert first_call["data"]["updates"]["content"] == "# Updated content"
|
|
|
|
|
|
|
|
assert "title" not in first_call["data"]["updates"]
|
|
|
|
|
|
|
|
assert "n1" in result
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_update_note_empty_updates(self) -> None:
|
|
|
|
async def test_update_note_empty_updates(self) -> None:
|
|
|
|
from app.agents.note_agent import update_note
|
|
|
|
from app.agents.note_agent import update_note
|
|
|
|
result = await update_note.ainvoke({"note_id": "n1"})
|
|
|
|
fake_row = {"id": "n1", "title": "Daily log", "projectId": None}
|
|
|
|
data = json.loads(result)
|
|
|
|
with patch("app.agents.note_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
assert data["data"]["updates"] == {}
|
|
|
|
m.return_value = {"row": fake_row}
|
|
|
|
|
|
|
|
await update_note.ainvoke({"note_id": "n1"})
|
|
|
|
|
|
|
|
assert m.call_args.kwargs["data"]["updates"] == {}
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_delete_note(self) -> None:
|
|
|
|
async def test_delete_note(self) -> None:
|
|
|
|
from app.agents.note_agent import delete_note
|
|
|
|
from app.agents.note_agent import delete_note
|
|
|
|
|
|
|
|
with patch("app.agents.note_agent.execute_on_client", new_callable=AsyncMock) as m:
|
|
|
|
|
|
|
|
m.return_value = {"deleted": True}
|
|
|
|
result = await delete_note.ainvoke({"note_id": "n1"})
|
|
|
|
result = await delete_note.ainvoke({"note_id": "n1"})
|
|
|
|
data = json.loads(result)
|
|
|
|
call_kwargs = m.call_args.kwargs
|
|
|
|
assert data["action"] == "delete_record"
|
|
|
|
assert call_kwargs["action"] == "delete"
|
|
|
|
assert data["table"] == "notes"
|
|
|
|
assert call_kwargs["table"] == "notes"
|
|
|
|
assert data["data"]["id"] == "n1"
|
|
|
|
assert call_kwargs["data"]["id"] == "n1"
|
|
|
|
|
|
|
|
assert "n1" in result
|
|
|
|
|