208 lines
8.1 KiB
Python
208 lines
8.1 KiB
Python
|
|
"""Unit tests for TodoParser."""
|
||
|
|
import pytest
|
||
|
|
from datetime import datetime
|
||
|
|
from src.parsers.todo_parser import TodoParser, Task
|
||
|
|
|
||
|
|
|
||
|
|
class TestTodoParser:
|
||
|
|
"""Tests for TodoParser class."""
|
||
|
|
|
||
|
|
def test_parse_simple_task(self) -> None:
|
||
|
|
"""Test parsing a simple task without metadata."""
|
||
|
|
line = "Buy milk"
|
||
|
|
task = TodoParser.parse_line(line)
|
||
|
|
|
||
|
|
assert task is not None
|
||
|
|
assert task.description == "Buy milk"
|
||
|
|
assert task.completed == False
|
||
|
|
assert task.priority is None
|
||
|
|
assert len(task.projects) == 0
|
||
|
|
assert len(task.contexts) == 0
|
||
|
|
|
||
|
|
def test_parse_task_with_priority(self) -> None:
|
||
|
|
"""Test parsing a task with priority."""
|
||
|
|
line = "(A) Write blog post"
|
||
|
|
task = TodoParser.parse_line(line)
|
||
|
|
|
||
|
|
assert task is not None
|
||
|
|
assert task.priority == "A"
|
||
|
|
assert task.description == "Write blog post"
|
||
|
|
|
||
|
|
def test_parse_task_with_creation_date(self) -> None:
|
||
|
|
"""Test parsing a task with creation date."""
|
||
|
|
line = "(B) 2026-01-31 Finish implementation"
|
||
|
|
task = TodoParser.parse_line(line)
|
||
|
|
|
||
|
|
assert task is not None
|
||
|
|
assert task.priority == "B"
|
||
|
|
assert task.creation_date == datetime(2026, 1, 31)
|
||
|
|
assert task.description == "Finish implementation"
|
||
|
|
|
||
|
|
def test_parse_task_with_projects(self) -> None:
|
||
|
|
"""Test parsing a task with project tags."""
|
||
|
|
line = "Write tests +myorg-assistant +testing"
|
||
|
|
task = TodoParser.parse_line(line)
|
||
|
|
|
||
|
|
assert task is not None
|
||
|
|
assert "myorg-assistant" in task.projects
|
||
|
|
assert "testing" in task.projects
|
||
|
|
assert task.description == "Write tests"
|
||
|
|
|
||
|
|
def test_parse_task_with_contexts(self) -> None:
|
||
|
|
"""Test parsing a task with context tags."""
|
||
|
|
line = "Call dentist @telefon @recados"
|
||
|
|
task = TodoParser.parse_line(line)
|
||
|
|
|
||
|
|
assert task is not None
|
||
|
|
assert "telefon" in task.contexts
|
||
|
|
assert "recados" in task.contexts
|
||
|
|
assert task.description == "Call dentist"
|
||
|
|
|
||
|
|
def test_parse_task_with_due_date(self) -> None:
|
||
|
|
"""Test parsing a task with due date metadata."""
|
||
|
|
line = "(A) Submit report +work due:2026-02-15"
|
||
|
|
task = TodoParser.parse_line(line)
|
||
|
|
|
||
|
|
assert task is not None
|
||
|
|
assert task.due_date == datetime(2026, 2, 15)
|
||
|
|
assert "due" in task.metadata
|
||
|
|
assert task.metadata["due"] == "2026-02-15"
|
||
|
|
|
||
|
|
def test_parse_completed_task(self) -> None:
|
||
|
|
"""Test parsing a completed task."""
|
||
|
|
line = "x 2026-01-31 Buy groceries"
|
||
|
|
task = TodoParser.parse_line(line)
|
||
|
|
|
||
|
|
assert task is not None
|
||
|
|
assert task.completed == True
|
||
|
|
assert task.completion_date == datetime(2026, 1, 31)
|
||
|
|
assert task.description == "Buy groceries"
|
||
|
|
|
||
|
|
def test_parse_complex_task(self) -> None:
|
||
|
|
"""Test parsing a complex task with all features."""
|
||
|
|
line = "(A) 2026-01-31 Write observability blog post +observability-blog @computer-deep due:2026-02-15 priority:high"
|
||
|
|
task = TodoParser.parse_line(line)
|
||
|
|
|
||
|
|
assert task is not None
|
||
|
|
assert task.priority == "A"
|
||
|
|
assert task.creation_date == datetime(2026, 1, 31)
|
||
|
|
assert task.description == "Write observability blog post"
|
||
|
|
assert "observability-blog" in task.projects
|
||
|
|
assert "computer-deep" in task.contexts
|
||
|
|
assert task.due_date == datetime(2026, 2, 15)
|
||
|
|
assert task.metadata["priority"] == "high"
|
||
|
|
|
||
|
|
def test_parse_empty_line(self) -> None:
|
||
|
|
"""Test parsing an empty line returns None."""
|
||
|
|
task = TodoParser.parse_line("")
|
||
|
|
assert task is None
|
||
|
|
|
||
|
|
def test_parse_comment_line(self) -> None:
|
||
|
|
"""Test parsing a comment line returns None."""
|
||
|
|
task = TodoParser.parse_line("# This is a comment")
|
||
|
|
assert task is None
|
||
|
|
|
||
|
|
def test_format_simple_task(self) -> None:
|
||
|
|
"""Test formatting a simple task."""
|
||
|
|
formatted = TodoParser.format_task(description="Buy milk")
|
||
|
|
assert formatted == "Buy milk"
|
||
|
|
|
||
|
|
def test_format_task_with_priority(self) -> None:
|
||
|
|
"""Test formatting a task with priority."""
|
||
|
|
formatted = TodoParser.format_task(
|
||
|
|
description="Write tests",
|
||
|
|
priority="A"
|
||
|
|
)
|
||
|
|
assert formatted == "(A) Write tests"
|
||
|
|
|
||
|
|
def test_format_task_with_all_features(self) -> None:
|
||
|
|
"""Test formatting a complex task."""
|
||
|
|
formatted = TodoParser.format_task(
|
||
|
|
description="Write blog post",
|
||
|
|
priority="B",
|
||
|
|
creation_date=datetime(2026, 1, 31),
|
||
|
|
projects=["observability-blog"],
|
||
|
|
contexts=["computer-deep"],
|
||
|
|
metadata={"due": "2026-02-15"}
|
||
|
|
)
|
||
|
|
assert "(B)" in formatted
|
||
|
|
assert "2026-01-31" in formatted
|
||
|
|
assert "Write blog post" in formatted
|
||
|
|
assert "+observability-blog" in formatted
|
||
|
|
assert "@computer-deep" in formatted
|
||
|
|
assert "due:2026-02-15" in formatted
|
||
|
|
|
||
|
|
def test_format_completed_task(self) -> None:
|
||
|
|
"""Test formatting a completed task."""
|
||
|
|
formatted = TodoParser.format_task(
|
||
|
|
description="Buy groceries",
|
||
|
|
completed=True,
|
||
|
|
completion_date=datetime(2026, 1, 31)
|
||
|
|
)
|
||
|
|
assert formatted.startswith("x 2026-01-31")
|
||
|
|
assert "Buy groceries" in formatted
|
||
|
|
|
||
|
|
def test_parse_file(self) -> None:
|
||
|
|
"""Test parsing multiple tasks from file content."""
|
||
|
|
content = """# Tasks
|
||
|
|
(A) Write tests +myorg-assistant
|
||
|
|
Buy milk @recados
|
||
|
|
x 2026-01-30 Completed task
|
||
|
|
|
||
|
|
(B) 2026-01-31 Another task +project due:2026-02-01"""
|
||
|
|
|
||
|
|
tasks = TodoParser.parse_file(content)
|
||
|
|
assert len(tasks) == 4
|
||
|
|
assert tasks[0].description == "Write tests"
|
||
|
|
assert tasks[1].description == "Buy milk"
|
||
|
|
assert tasks[2].completed == True
|
||
|
|
assert tasks[3].priority == "B"
|
||
|
|
|
||
|
|
def test_filter_tasks_by_completion(self) -> None:
|
||
|
|
"""Test filtering tasks by completion status."""
|
||
|
|
tasks = [
|
||
|
|
Task(raw_line="x Task 1", line_number=1, completed=True, description="Task 1"),
|
||
|
|
Task(raw_line="Task 2", line_number=2, completed=False, description="Task 2"),
|
||
|
|
Task(raw_line="Task 3", line_number=3, completed=False, description="Task 3"),
|
||
|
|
]
|
||
|
|
|
||
|
|
active = TodoParser.filter_tasks(tasks, completed=False)
|
||
|
|
assert len(active) == 2
|
||
|
|
|
||
|
|
completed = TodoParser.filter_tasks(tasks, completed=True)
|
||
|
|
assert len(completed) == 1
|
||
|
|
|
||
|
|
def test_filter_tasks_by_priority(self) -> None:
|
||
|
|
"""Test filtering tasks by priority."""
|
||
|
|
tasks = [
|
||
|
|
Task(raw_line="(A) Task 1", line_number=1, priority="A", description="Task 1"),
|
||
|
|
Task(raw_line="(B) Task 2", line_number=2, priority="B", description="Task 2"),
|
||
|
|
Task(raw_line="Task 3", line_number=3, description="Task 3"),
|
||
|
|
]
|
||
|
|
|
||
|
|
high_priority = TodoParser.filter_tasks(tasks, priority="A")
|
||
|
|
assert len(high_priority) == 1
|
||
|
|
assert high_priority[0].description == "Task 1"
|
||
|
|
|
||
|
|
def test_filter_tasks_by_project(self) -> None:
|
||
|
|
"""Test filtering tasks by project tag."""
|
||
|
|
tasks = [
|
||
|
|
Task(raw_line="Task 1 +project1", line_number=1, description="Task 1", projects=["project1"]),
|
||
|
|
Task(raw_line="Task 2 +project2", line_number=2, description="Task 2", projects=["project2"]),
|
||
|
|
Task(raw_line="Task 3 +project1", line_number=3, description="Task 3", projects=["project1"]),
|
||
|
|
]
|
||
|
|
|
||
|
|
project1_tasks = TodoParser.filter_tasks(tasks, project="project1")
|
||
|
|
assert len(project1_tasks) == 2
|
||
|
|
|
||
|
|
def test_filter_tasks_by_context(self) -> None:
|
||
|
|
"""Test filtering tasks by context tag."""
|
||
|
|
tasks = [
|
||
|
|
Task(raw_line="Task 1 @home", line_number=1, description="Task 1", contexts=["home"]),
|
||
|
|
Task(raw_line="Task 2 @work", line_number=2, description="Task 2", contexts=["work"]),
|
||
|
|
Task(raw_line="Task 3 @home", line_number=3, description="Task 3", contexts=["home"]),
|
||
|
|
]
|
||
|
|
|
||
|
|
home_tasks = TodoParser.filter_tasks(tasks, context="home")
|
||
|
|
assert len(home_tasks) == 2
|