utils/todo_hooks/tests/test_session_start.py
Krzysztof kuhy Rudnicki 1fb04ce59d
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file length / file-length (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
palette drift / palette (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
shared gate tests / gate unit tests (push) Waiting to run
todo_hooks: session-start, canonical token path
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 12:24:29 +01:00

141 lines
5.0 KiB
Python

"""Tests for the SessionStart task summary."""
from __future__ import annotations
import json
import time
from typing import TYPE_CHECKING
from todo_hooks import __main__ as cli
from todo_hooks.tests._fakes import TOKEN, FakeMcp, line
if TYPE_CHECKING:
from collections.abc import Callable
from pathlib import Path
import pytest
ORIGIN = "git@github.com:kuhyx/demo.git"
def _rows(count: int) -> str:
return "\n".join(
line(f"{i:08x}-x", "kuhyx/demo", f"# Task {i}") for i in range(count)
)
def _env(mcp: FakeMcp) -> dict[str, str]:
return {"TODO_MCP_URL": mcp.url, "TODO_MCP_TOKEN": TOKEN}
def _stdin(cwd: Path) -> str:
return json.dumps({"cwd": str(cwd), "hook_event_name": "SessionStart"})
def test_prints_header_and_tasks(
mcp: FakeMcp, make_repo: Callable[..., Path], capsys: pytest.CaptureFixture[str]
) -> None:
root = make_repo("demo", origin=ORIGIN)
seen: list[dict[str, object]] = []
mcp.tools["list_tasks"] = lambda a: seen.append(a) or _rows(2)
assert cli.main(["session-start"], env=_env(mcp), stdin=_stdin(root)) == 0
assert capsys.readouterr().out.splitlines() == [
"Open todo tasks for kuhyx/demo (2):",
"- 00000000 Task 0",
"- 00000001 Task 1",
]
assert seen == [{"repo": "kuhyx/demo", "status": "open"}]
def test_nine_tasks_fit_ten_lines(
mcp: FakeMcp, make_repo: Callable[..., Path], capsys: pytest.CaptureFixture[str]
) -> None:
root = make_repo("demo", origin=ORIGIN)
mcp.tools["list_tasks"] = lambda _a: _rows(9)
cli.main(["session-start"], env=_env(mcp), stdin=_stdin(root))
assert len(capsys.readouterr().out.splitlines()) == 10
def test_more_than_nine_is_truncated(
mcp: FakeMcp, make_repo: Callable[..., Path], capsys: pytest.CaptureFixture[str]
) -> None:
root = make_repo("demo", origin=ORIGIN)
mcp.tools["list_tasks"] = lambda _a: _rows(15)
cli.main(["session-start"], env=_env(mcp), stdin=_stdin(root))
lines = capsys.readouterr().out.splitlines()
assert len(lines) == 10
assert lines[0] == "Open todo tasks for kuhyx/demo (15):"
assert lines[-1] == '… +7 more: list_tasks(repo="kuhyx/demo")'
def test_no_tasks_prints_nothing(
mcp: FakeMcp, make_repo: Callable[..., Path], capsys: pytest.CaptureFixture[str]
) -> None:
root = make_repo("demo", origin=ORIGIN)
mcp.tools["list_tasks"] = lambda _a: "no tasks match"
assert cli.main(["session-start"], env=_env(mcp), stdin=_stdin(root)) == 0
assert capsys.readouterr().out == ""
def test_cwd_fallback_when_stdin_is_not_json(
mcp: FakeMcp, make_repo: Callable[..., Path], capsys: pytest.CaptureFixture[str]
) -> None:
root = make_repo("demo", origin=ORIGIN)
mcp.tools["list_tasks"] = lambda _a: _rows(1)
cli.main(["session-start"], env=_env(mcp), cwd=root, stdin="not json")
assert "kuhyx/demo" in capsys.readouterr().out
cli.main(["session-start"], env=_env(mcp), cwd=root, stdin="[1]")
assert "kuhyx/demo" in capsys.readouterr().out
cli.main(["session-start"], env=_env(mcp), cwd=root, stdin="{}")
assert "kuhyx/demo" in capsys.readouterr().out
def test_silent_without_git_or_origin(
mcp: FakeMcp,
make_repo: Callable[..., Path],
tmp_path: Path,
capsys: pytest.CaptureFixture[str],
) -> None:
mcp.tools["list_tasks"] = lambda _a: _rows(1)
plain = tmp_path / "plain"
plain.mkdir()
assert cli.main(["session-start"], env=_env(mcp), stdin=_stdin(plain)) == 0
local = make_repo("local")
assert cli.main(["session-start"], env=_env(mcp), stdin=_stdin(local)) == 0
assert capsys.readouterr().out == ""
assert mcp.requests == []
def test_silent_when_server_down_or_token_bad(
mcp: FakeMcp, make_repo: Callable[..., Path], capsys: pytest.CaptureFixture[str]
) -> None:
root = make_repo("demo", origin=ORIGIN)
bad = {**_env(mcp), "TODO_MCP_TOKEN": "wrong"}
assert cli.main(["session-start"], env=bad, stdin=_stdin(root)) == 0
dead = {**_env(mcp), "TODO_MCP_URL": "http://127.0.0.1:1/mcp"}
assert cli.main(["session-start"], env=dead, stdin=_stdin(root)) == 0
no_token = {"TODO_MCP_URL": mcp.url, "TODO_MCP_TOKEN_FILE": "/nonexistent"}
assert cli.main(["session-start"], env=no_token, stdin=_stdin(root)) == 0
assert capsys.readouterr().out == ""
def test_silent_on_unexpected_error(
mcp: FakeMcp, make_repo: Callable[..., Path], capsys: pytest.CaptureFixture[str]
) -> None:
root = make_repo("demo", origin=ORIGIN)
mcp.mode = "garbage"
assert cli.main(["session-start"], env=_env(mcp), stdin=_stdin(root)) == 0
assert capsys.readouterr().out == ""
def test_slow_server_stays_within_budget(
mcp: FakeMcp, make_repo: Callable[..., Path], capsys: pytest.CaptureFixture[str]
) -> None:
root = make_repo("demo", origin=ORIGIN)
mcp.delay = 3.0
mcp.tools["list_tasks"] = lambda _a: _rows(1)
start = time.monotonic()
assert cli.main(["session-start"], env=_env(mcp), stdin=_stdin(root)) == 0
assert time.monotonic() - start < 1.6
assert capsys.readouterr().out == ""