todo-mcp/tests/test_todo_mcp.py

109 lines
3.1 KiB
Python

"""End-to-end tests for the todo MCP server.
Each test runs the real (flattened) tool functions against a temp store via
``TODO_MCP_STORE`` so behavior is verified, not just imported.
"""
from __future__ import annotations
import json
import pytest
import todo.server as s
@pytest.fixture
def store(tmp_path, monkeypatch):
path = tmp_path / "todos.json"
monkeypatch.setenv("TODO_MCP_STORE", str(path))
return path
def _ids(items):
return sorted(t["id"] for t in items)
@pytest.mark.asyncio
async def test_add_and_list(store):
created = await s.todo_add(title="Buy milk", priority="high", tags="dairy, breakfast")
first = json.loads(created)
assert first["title"] == "Buy milk"
assert first["priority"] == "high"
assert first["status"] == "active"
assert first["tags"] == ["dairy", "breakfast"]
assert first["id"].startswith("t_")
listing = json.loads(await s.todo_list())
assert listing["total"] == 1
assert listing["tasks"][0]["id"] == first["id"]
@pytest.mark.asyncio
async def test_get_update_toggle_delete(store):
tid = json.loads(await s.todo_add(title="Task one"))["id"]
got = json.loads(await s.todo_get(id=tid))
assert got["id"] == tid
updated = json.loads(await s.todo_update(id=tid, status="done", priority="urgent"))
assert updated["status"] == "done"
assert updated["priority"] == "urgent"
toggled = json.loads(await s.todo_toggle(id=tid))
assert toggled["status"] == "active"
deleted = json.loads(await s.todo_delete(id=tid))
assert deleted["deleted"] == tid
missing = await s.todo_get(id="t_nope")
assert missing.startswith("Error")
@pytest.mark.asyncio
async def test_filters_and_stats(store):
await s.todo_add(title="A", priority="high", project="work")
await s.todo_add(title="B", priority="low", project="home")
await s.todo_add(title="C", priority="high", project="work")
only_work = json.loads(await s.todo_list(project="work"))
assert only_work["total"] == 2
stats = json.loads(await s.todo_stats())
assert stats["total"] == 3
assert stats["by_priority"]["high"] == 2
assert set(stats["projects"]) == {"work", "home"}
@pytest.mark.asyncio
async def test_clear_completed(store):
t1 = json.loads(await s.todo_add(title="keep"))
t2 = json.loads(await s.todo_add(title="done one"))
await s.todo_toggle(id=t2["id"])
cleared = json.loads(await s.todo_clear())
assert cleared["cleared"] == 1
assert cleared["remaining"] == 1
remaining = json.loads(await s.todo_list())
assert _ids(remaining["tasks"]) == [t1["id"]]
@pytest.mark.asyncio
async def test_markdown_format(store):
await s.todo_add(title="First", priority="high", due="2026-09-30")
md = await s.todo_list(format="markdown")
assert "**First**" in md
assert "📅 2026-09-30" in md
@pytest.mark.asyncio
async def test_bad_priority_rejected(store):
with pytest.raises(Exception):
await s.todo_add(title="x", priority="sideways")
@pytest.mark.asyncio
async def test_bad_due_rejected(store):
with pytest.raises(Exception):
await s.todo_add(title="x", due="not-a-date")