109 lines
3.1 KiB
Python
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")
|