from __future__ import annotations
import textwrap
import pytest
from runner.task_loader import (
TaskLoadError,
load_all_tasks,
load_task,
load_task_by_id,
)
def _write_yaml(tmp_path, filename, content):
p = tmp_path / filename
p.write_text(textwrap.dedent(content), encoding="utf-8")
return p
VALID_YAML = """\
id: ruff-001
repo: astral-sh/ruff
commit: f14fd5d88507553830d78cf3cfae625a17297ebd
description: |
Fix the formatter to preserve parentheses.
test_command: "cargo test -p ruff_python_formatter"
language: rust
difficulty: medium
tags: [formatter, bug-fix]
"""
class TestLoadTask:
def test_load_valid_task(self, tmp_path):
path = _write_yaml(tmp_path, "ruff-001.yaml", VALID_YAML)
task = load_task(path)
assert task["id"] == "ruff-001"
assert task["repo"] == "astral-sh/ruff"
assert task["commit"] == "f14fd5d88507553830d78cf3cfae625a17297ebd"
assert "Fix the formatter" in task["description"]
assert task["test_command"] == "cargo test -p ruff_python_formatter"
assert task["language"] == "rust"
assert task["difficulty"] == "medium"
assert task["tags"] == ["formatter", "bug-fix"]
def test_load_minimal_task(self, tmp_path):
yaml_content = """\
id: flask-001
repo: pallets/flask
commit: abc1234567
description: Fix session handling
test_command: pytest tests/
"""
path = _write_yaml(tmp_path, "flask-001.yaml", yaml_content)
task = load_task(path)
assert task["id"] == "flask-001"
assert task.get("language") is None
assert task.get("difficulty") is None
assert task.get("tags") is None
def test_missing_required_field(self, tmp_path):
yaml_content = """\
id: bad-001
repo: owner/repo
description: Missing commit and test_command
"""
path = _write_yaml(tmp_path, "bad.yaml", yaml_content)
with pytest.raises(TaskLoadError, match="missing required fields"):
load_task(path)
def test_empty_id(self, tmp_path):
yaml_content = """\
id: ""
repo: owner/repo
commit: abc1234567
description: Test
test_command: pytest
"""
path = _write_yaml(tmp_path, "empty-id.yaml", yaml_content)
with pytest.raises(TaskLoadError, match="non-empty string"):
load_task(path)
def test_bad_repo_format(self, tmp_path):
yaml_content = """\
id: test-001
repo: not-a-slug
commit: abc1234567
description: Test
test_command: pytest
"""
path = _write_yaml(tmp_path, "bad-repo.yaml", yaml_content)
with pytest.raises(TaskLoadError, match="GitHub slug"):
load_task(path)
def test_short_commit(self, tmp_path):
yaml_content = """\
id: test-001
repo: owner/repo
commit: abc
description: Test
test_command: pytest
"""
path = _write_yaml(tmp_path, "short-commit.yaml", yaml_content)
with pytest.raises(TaskLoadError, match="hex hash"):
load_task(path)
def test_invalid_difficulty(self, tmp_path):
yaml_content = """\
id: test-001
repo: owner/repo
commit: abc1234567
description: Test
test_command: pytest
difficulty: impossible
"""
path = _write_yaml(tmp_path, "bad-diff.yaml", yaml_content)
with pytest.raises(TaskLoadError, match="difficulty"):
load_task(path)
def test_tags_not_list(self, tmp_path):
yaml_content = """\
id: test-001
repo: owner/repo
commit: abc1234567
description: Test
test_command: pytest
tags: "not-a-list"
"""
path = _write_yaml(tmp_path, "bad-tags.yaml", yaml_content)
with pytest.raises(TaskLoadError, match="tags.*list"):
load_task(path)
def test_file_not_found(self, tmp_path):
with pytest.raises(TaskLoadError, match="not found"):
load_task(tmp_path / "nonexistent.yaml")
def test_invalid_yaml_syntax(self, tmp_path):
p = tmp_path / "bad.yaml"
p.write_text("{{invalid: yaml: [", encoding="utf-8")
with pytest.raises(TaskLoadError, match="Invalid YAML"):
load_task(p)
def test_yaml_not_mapping(self, tmp_path):
p = tmp_path / "list.yaml"
p.write_text("- item1\n- item2\n", encoding="utf-8")
with pytest.raises(TaskLoadError, match="expected a YAML mapping"):
load_task(p)
class TestLoadTaskById:
def test_find_by_filename(self, tmp_path):
_write_yaml(tmp_path, "ruff-001.yaml", VALID_YAML)
task = load_task_by_id("ruff-001", tmp_path)
assert task["id"] == "ruff-001"
def test_find_by_scanning(self, tmp_path):
yaml_content = """\
id: custom-id
repo: owner/repo
commit: abc1234567
description: Test task
test_command: pytest
"""
_write_yaml(tmp_path, "renamed-file.yaml", yaml_content)
task = load_task_by_id("custom-id", tmp_path)
assert task["id"] == "custom-id"
def test_not_found(self, tmp_path):
_write_yaml(tmp_path, "ruff-001.yaml", VALID_YAML)
with pytest.raises(TaskLoadError, match="No task found"):
load_task_by_id("nonexistent", tmp_path)
class TestLoadAllTasks:
def test_load_multiple(self, tmp_path):
_write_yaml(tmp_path, "ruff-001.yaml", VALID_YAML)
yaml2 = """\
id: flask-001
repo: pallets/flask
commit: def4567890
description: Fix flask
test_command: pytest tests/
"""
_write_yaml(tmp_path, "flask-001.yaml", yaml2)
tasks = load_all_tasks(tmp_path)
assert len(tasks) == 2
assert tasks[0]["id"] == "flask-001" assert tasks[1]["id"] == "ruff-001"
def test_empty_directory(self, tmp_path):
with pytest.raises(TaskLoadError, match="No .yaml files"):
load_all_tasks(tmp_path)
def test_directory_not_found(self, tmp_path):
with pytest.raises(TaskLoadError, match="not found"):
load_all_tasks(tmp_path / "nope")
def test_duplicate_ids(self, tmp_path):
_write_yaml(tmp_path, "a.yaml", VALID_YAML)
_write_yaml(tmp_path, "b.yaml", VALID_YAML) with pytest.raises(TaskLoadError, match="Duplicate"):
load_all_tasks(tmp_path)
def test_loads_real_tasks(self):
tasks_dir = Path(__file__).parent.parent / "tasks"
if not tasks_dir.is_dir():
pytest.skip("tasks/ directory not available")
tasks = load_all_tasks(tasks_dir)
assert len(tasks) >= 1
for t in tasks:
assert "id" in t
assert "repo" in t
from pathlib import Path