from pathlib import Path import pytest from rdb_discovery.tasks import ( _extract_test_commands_from_claude, generate_agent_prompt, get_next_task, ) def _write_claude_with_test_cmds(tmp_path: Path) -> None: """Write a CLAUDE.md with Test Commands section.""" (tmp_path / "CLAUDE.md").write_text( "# Claude Code Instructions\n\n" "## Test Commands\n\n" "Use the existing virtual environment.\n\n" "```bash\n" "source .venv/bin/activate\n" "python -m pytest\n" "```\n", encoding="utf-8", ) def test_generate_agent_prompt_includes_task_info(tmp_path: Path) -> None: (tmp_path / "TASKS.md").write_text( "# TASKS\n\n" "## TASK-001 — First\nStatus: Done\n\n" "Goal: First goal.\n\n" "Acceptance Criteria:\n" "- a\n\n" "## TASK-002 — Second\nStatus: Todo\n\n" "Goal: Second goal.\n\n" "Acceptance Criteria:\n" "- b\n" "- c\n", encoding="utf-8", ) prompt = generate_agent_prompt(tmp_path) assert "TASK-002" in prompt assert "Second" in prompt assert "Todo" in prompt assert "Second goal." in prompt def test_generate_agent_prompt_includes_acceptance_criteria(tmp_path: Path) -> None: (tmp_path / "TASKS.md").write_text( "# TASKS\n\n" "## TASK-001 — First\nStatus: Done\n\n" "Goal: x.\n\n" "Acceptance Criteria:\n" "- alpha\n\n" "## TASK-002 — Second\nStatus: Todo\n\n" "Goal: y.\n\n" "Acceptance Criteria:\n" "- bravo\n", encoding="utf-8", ) prompt = generate_agent_prompt(tmp_path) assert "- alpha" not in prompt assert "- bravo" in prompt def test_generate_agent_prompt_includes_read_instructions(tmp_path: Path) -> None: (tmp_path / "TASKS.md").write_text( "# TASKS\n\n" "## TASK-001 — First\nStatus: Done\n\n" "Goal: x.\n\n" "## TASK-002 — Second\nStatus: Todo\n\n" "Goal: y.\n", encoding="utf-8", ) prompt = generate_agent_prompt(tmp_path) assert "README.md" in prompt assert "TASKS.md" in prompt assert "PROJECT_STATE.md" in prompt assert "AGENT_HANDOFF.md" in prompt assert "context/agent-guidelines.md" in prompt def test_generate_agent_prompt_includes_one_step_constraint(tmp_path: Path) -> None: (tmp_path / "TASKS.md").write_text( "# TASKS\n\n" "## TASK-001 — First\nStatus: Done\n\n" "Goal: x.\n\n" "## TASK-002 — Second\nStatus: Todo\n\n" "Goal: y.\n", encoding="utf-8", ) prompt = generate_agent_prompt(tmp_path) assert "ONE small implementation step only" in prompt def test_generate_agent_prompt_includes_validation(tmp_path: Path) -> None: (tmp_path / "TASKS.md").write_text( "# TASKS\n\n" "## TASK-001 — First\nStatus: Done\n\n" "Goal: x.\n\n" "## TASK-002 — Second\nStatus: Todo\n\n" "Goal: y.\n", encoding="utf-8", ) prompt = generate_agent_prompt(tmp_path) assert "Validation" in prompt assert "pytest" in prompt def test_generate_agent_prompt_includes_reporting(tmp_path: Path) -> None: (tmp_path / "TASKS.md").write_text( "# TASKS\n\n" "## TASK-001 — First\nStatus: Done\n\n" "Goal: x.\n\n" "## TASK-002 — Second\nStatus: Todo\n\n" "Goal: y.\n", encoding="utf-8", ) prompt = generate_agent_prompt(tmp_path) assert "Reporting" in prompt def test_generate_agent_prompt_no_todo_task(tmp_path: Path) -> None: (tmp_path / "TASKS.md").write_text( "# TASKS\n\n" "## TASK-001 — First\nStatus: Done\n\n", encoding="utf-8", ) prompt = generate_agent_prompt(tmp_path) assert prompt == "No Todo task found." def test_generate_agent_prompt_no_tasks_file(tmp_path: Path) -> None: prompt = generate_agent_prompt(tmp_path) assert prompt == "No Todo task found." def test_generate_agent_prompt_includes_implementation_gap(tmp_path: Path) -> None: (tmp_path / "TASKS.md").write_text( "# TASKS\n\n" "## TASK-001 — First\nStatus: Done\n\n" "Goal: x.\n\n" "## TASK-002 — Second\nStatus: Todo\n\n" "Goal: y.\n" "Implementation Gap:\n" "Missing validation handler in cli.py\n", encoding="utf-8", ) prompt = generate_agent_prompt(tmp_path) assert "Implementation Gap:" in prompt assert "Missing validation handler in cli.py" in prompt def test_generate_agent_prompt_omits_gap_when_missing(tmp_path: Path) -> None: (tmp_path / "TASKS.md").write_text( "# TASKS\n\n" "## TASK-001 — First\nStatus: Done\n\n" "Goal: x.\n\n" "## TASK-002 — Second\nStatus: Todo\n\n" "Goal: y.\n", encoding="utf-8", ) prompt = generate_agent_prompt(tmp_path) assert "Implementation Gap:" not in prompt def test_generate_agent_prompt_includes_test_commands_from_claude(tmp_path: Path) -> None: (tmp_path / "TASKS.md").write_text( "# TASKS\n\n" "## TASK-001 — First\nStatus: Done\n\n" "## TASK-002 — Second\nStatus: Todo\n\n" "Goal: y.\n", encoding="utf-8", ) _write_claude_with_test_cmds(tmp_path) prompt = generate_agent_prompt(tmp_path) assert "Test Commands (from CLAUDE.md)" in prompt assert "source .venv/bin/activate" in prompt assert "python -m pytest" in prompt def test_generate_agent_prompt_includes_no_reread_constraint(tmp_path: Path) -> None: (tmp_path / "TASKS.md").write_text( "# TASKS\n\n" "## TASK-001 — First\nStatus: Done\n\n" "## TASK-002 — Second\nStatus: Todo\n\n" "Goal: y.\n", encoding="utf-8", ) prompt = generate_agent_prompt(tmp_path) assert "reread" in prompt.lower() or "read" in prompt.lower() def test_generate_agent_prompt_includes_inspect_first_constraint(tmp_path: Path) -> None: (tmp_path / "TASKS.md").write_text( "# TASKS\n\n" "## TASK-001 — First\nStatus: Done\n\n" "## TASK-002 — Second\nStatus: Todo\n\n" "Goal: y.\n", encoding="utf-8", ) prompt = generate_agent_prompt(tmp_path) assert "Inspect" in prompt or "inspect" in prompt def test_extract_test_commands_no_claude_file(tmp_path: Path) -> None: commands = _extract_test_commands_from_claude(tmp_path) assert commands == [] def test_extract_test_commands_from_claude(tmp_path: Path) -> None: (tmp_path / "CLAUDE.md").write_text( "# Instructions\n\n" "## Test Commands\n\n" "Run the following:\n\n" "```bash\n" "source .venv/bin/activate\n" "python -m pytest\n" "```\n", encoding="utf-8", ) commands = _extract_test_commands_from_claude(tmp_path) assert any(".venv/bin/activate" in cmd for cmd in commands) assert "python -m pytest" in commands def test_extract_test_commands_no_section(tmp_path: Path) -> None: (tmp_path / "CLAUDE.md").write_text( "# Instructions\n\n" "No test section here.\n", encoding="utf-8", ) commands = _extract_test_commands_from_claude(tmp_path) assert commands == []