todo-backend / Chatbot /tests /unit /test_add_task_tool.py
Fizu123's picture
BACKEND FIX: Filter by credential provider during login
08af9fd
"""
Unit tests for add_task tool.
"""
import pytest
from unittest.mock import MagicMock, patch
from sqlmodel import Session
from backend.mcp_server.tools.add_task import add_task
from backend.mcp_server.schemas import ErrorCode
def test_add_task_valid_input():
"""Test adding a task with valid input."""
mock_task = MagicMock()
mock_task.id = 1
mock_task.title = "Buy groceries"
mock_task.description = "Milk, eggs, bread"
mock_task.completed = False
with patch('backend.mcp_server.tools.add_task.get_engine') as mock_get_engine, \
patch('backend.mcp_server.tools.add_task.Session') as mock_session_class:
mock_get_engine.return_value = MagicMock()
mock_session = MagicMock()
mock_session_class.return_value.__enter__.return_value = mock_session
mock_session_class.return_value.__exit__.return_value = False
mock_session.add.return_value = None
mock_session.commit.return_value = None
mock_session.refresh.side_effect = lambda task: setattr(task, 'id', 1)
result = add_task(
user_id="user_abc123",
title="Buy groceries",
description="Milk, eggs, bread"
)
assert result["success"] is True
assert result["data"]["task_id"] == 1
assert result["data"]["status"] == "created"
assert result["data"]["title"] == "Buy groceries"
assert result["error"] is None
def test_add_task_without_description():
"""Test adding a task without description (optional field)."""
mock_task = MagicMock()
mock_task.id = 1
mock_task.title = "Call mom"
mock_task.description = None
mock_task.completed = False
with patch('backend.mcp_server.tools.add_task.get_engine') as mock_get_engine, \
patch('backend.mcp_server.tools.add_task.Session') as mock_session_class:
mock_get_engine.return_value = MagicMock()
mock_session = MagicMock()
mock_session_class.return_value.__enter__.return_value = mock_session
mock_session_class.return_value.__exit__.return_value = False
mock_session.add.return_value = None
mock_session.commit.return_value = None
mock_session.refresh.side_effect = lambda task: setattr(task, 'id', 1)
result = add_task(
user_id="user_abc123",
title="Call mom"
)
assert result["success"] is True
assert result["data"]["status"] == "created"
assert result["data"]["title"] == "Call mom"
def test_add_task_title_validation_empty():
"""Test add_task rejects empty title."""
result = add_task(
user_id="user_abc123",
title=""
)
assert result["success"] is False
assert result["data"] is None
assert result["error"]["code"] == ErrorCode.INVALID_INPUT
assert "title" in result["error"]["message"].lower()
def test_add_task_title_validation_too_short():
"""Test add_task rejects title shorter than 1 character."""
result = add_task(
user_id="user_abc123",
title=""
)
assert result["success"] is False
assert result["error"]["code"] == ErrorCode.INVALID_INPUT
def test_add_task_title_validation_too_long():
"""Test add_task rejects title longer than 200 characters."""
long_title = "A" * 201
result = add_task(
user_id="user_abc123",
title=long_title
)
assert result["success"] is False
assert result["data"] is None
assert result["error"]["code"] == ErrorCode.INVALID_INPUT
assert "title" in result["error"]["message"].lower()
def test_add_task_description_validation_too_long():
"""Test add_task rejects description longer than 1000 characters."""
long_desc = "A" * 1001
result = add_task(
user_id="user_abc123",
title="Valid title",
description=long_desc
)
assert result["success"] is False
assert result["data"] is None
assert result["error"]["code"] == ErrorCode.INVALID_INPUT
def test_add_task_user_association():
"""Test task is created with correct user association."""
mock_task = MagicMock()
mock_task.id = 1
mock_task.title = "User specific task"
mock_task.user_id = "user_xyz456"
mock_task.completed = False
with patch('backend.mcp_server.tools.add_task.get_engine') as mock_get_engine, \
patch('backend.mcp_server.tools.add_task.Session') as mock_session_class:
mock_get_engine.return_value = MagicMock()
mock_session = MagicMock()
mock_session_class.return_value.__enter__.return_value = mock_session
mock_session_class.return_value.__exit__.return_value = False
mock_session.add.return_value = None
mock_session.commit.return_value = None
mock_session.refresh.side_effect = lambda task: setattr(task, 'id', 1)
result = add_task(
user_id="user_xyz456",
title="User specific task"
)
assert result["success"] is True
# The task should be associated with user_xyz456
def test_add_task_returns_correct_format():
"""Test add_task returns consistent response format."""
mock_task = MagicMock()
mock_task.id = 1
mock_task.title = "Test task"
mock_task.completed = False
with patch('backend.mcp_server.tools.add_task.get_engine') as mock_get_engine, \
patch('backend.mcp_server.tools.add_task.Session') as mock_session_class:
mock_get_engine.return_value = MagicMock()
mock_session = MagicMock()
mock_session_class.return_value.__enter__.return_value = mock_session
mock_session_class.return_value.__exit__.return_value = False
mock_session.add.return_value = None
mock_session.commit.return_value = None
mock_session.refresh.side_effect = lambda task: setattr(task, 'id', 1)
result = add_task(
user_id="user_abc123",
title="Test task"
)
# Response must have all three top-level keys
assert "success" in result
assert "data" in result
assert "error" in result
# Data must have required fields
assert result["data"]["task_id"] is not None
assert result["data"]["status"] is not None
assert result["data"]["title"] is not None
def test_add_task_special_characters_in_title():
"""Test add_task handles special characters in title."""
special_title = "Buy groceries! 🛒 (milk, eggs)"
mock_task = MagicMock()
mock_task.id = 1
mock_task.title = special_title
mock_task.completed = False
with patch('backend.mcp_server.tools.add_task.get_engine') as mock_get_engine, \
patch('backend.mcp_server.tools.add_task.Session') as mock_session_class:
mock_get_engine.return_value = MagicMock()
mock_session = MagicMock()
mock_session_class.return_value.__enter__.return_value = mock_session
mock_session_class.return_value.__exit__.return_value = False
mock_session.add.return_value = None
mock_session.commit.return_value = None
mock_session.refresh.side_effect = lambda task: setattr(task, 'id', 1)
result = add_task(
user_id="user_abc123",
title=special_title
)
assert result["success"] is True
assert result["data"]["title"] == special_title
def test_add_task_special_characters_in_description():
"""Test add_task handles special characters in description."""
special_desc = "Remember to: buy milk 🥛, eggs 🥚, bread 🍞"
mock_task = MagicMock()
mock_task.id = 1
mock_task.title = "Shopping list"
mock_task.description = special_desc
mock_task.completed = False
with patch('backend.mcp_server.tools.add_task.get_engine') as mock_get_engine, \
patch('backend.mcp_server.tools.add_task.Session') as mock_session_class:
mock_get_engine.return_value = MagicMock()
mock_session = MagicMock()
mock_session_class.return_value.__enter__.return_value = mock_session
mock_session_class.return_value.__exit__.return_value = False
mock_session.add.return_value = None
mock_session.commit.return_value = None
mock_session.refresh.side_effect = lambda task: setattr(task, 'id', 1)
result = add_task(
user_id="user_abc123",
title="Shopping list",
description=special_desc
)
assert result["success"] is True
assert result["data"]["title"] == "Shopping list"
def test_add_task_minimal_title():
"""Test add_task accepts minimum title length (1 character)."""
mock_task = MagicMock()
mock_task.id = 1
mock_task.title = "A"
mock_task.completed = False
with patch('backend.mcp_server.tools.add_task.get_engine') as mock_get_engine, \
patch('backend.mcp_server.tools.add_task.Session') as mock_session_class:
mock_get_engine.return_value = MagicMock()
mock_session = MagicMock()
mock_session_class.return_value.__enter__.return_value = mock_session
mock_session_class.return_value.__exit__.return_value = False
mock_session.add.return_value = None
mock_session.commit.return_value = None
mock_session.refresh.side_effect = lambda task: setattr(task, 'id', 1)
result = add_task(
user_id="user_abc123",
title="A"
)
assert result["success"] is True
assert result["data"]["title"] == "A"
def test_add_task_max_title():
"""Test add_task accepts maximum title length (200 characters)."""
max_title = "A" * 200
mock_task = MagicMock()
mock_task.id = 1
mock_task.title = max_title
mock_task.completed = False
with patch('backend.mcp_server.tools.add_task.get_engine') as mock_get_engine, \
patch('backend.mcp_server.tools.add_task.Session') as mock_session_class:
mock_get_engine.return_value = MagicMock()
mock_session = MagicMock()
mock_session_class.return_value.__enter__.return_value = mock_session
mock_session_class.return_value.__exit__.return_value = False
mock_session.add.return_value = None
mock_session.commit.return_value = None
mock_session.refresh.side_effect = lambda task: setattr(task, 'id', 1)
result = add_task(
user_id="user_abc123",
title=max_title
)
assert result["success"] is True
assert len(result["data"]["title"]) == 200
def test_add_task_max_description():
"""Test add_task accepts maximum description length (1000 characters)."""
max_desc = "A" * 1000
mock_task = MagicMock()
mock_task.id = 1
mock_task.title = "Task"
mock_task.description = max_desc
mock_task.completed = False
with patch('backend.mcp_server.tools.add_task.get_engine') as mock_get_engine, \
patch('backend.mcp_server.tools.add_task.Session') as mock_session_class:
mock_get_engine.return_value = MagicMock()
mock_session = MagicMock()
mock_session_class.return_value.__enter__.return_value = mock_session
mock_session_class.return_value.__exit__.return_value = False
mock_session.add.return_value = None
mock_session.commit.return_value = None
mock_session.refresh.side_effect = lambda task: setattr(task, 'id', 1)
result = add_task(
user_id="user_abc123",
title="Task",
description=max_desc
)
assert result["success"] is True