267 lines
9.6 KiB
Python
267 lines
9.6 KiB
Python
"""Unit tests for ProjectParser."""
|
|
import pytest
|
|
from datetime import datetime
|
|
from src.parsers.project_parser import ProjectParser, Project
|
|
|
|
|
|
class TestProjectParser:
|
|
"""Tests for ProjectParser class."""
|
|
|
|
def test_parse_simple_project(self) -> None:
|
|
"""Test parsing a simple active project."""
|
|
line = "+myorg-assistant MyOrg Personal Assistant [active]"
|
|
project = ProjectParser.parse_line(line)
|
|
|
|
assert project is not None
|
|
assert project.tag == "myorg-assistant"
|
|
assert project.description == "MyOrg Personal Assistant"
|
|
assert project.status == "active"
|
|
|
|
def test_parse_project_without_status(self) -> None:
|
|
"""Test parsing a project without explicit status defaults to active."""
|
|
line = "+blog-post Write new blog post"
|
|
project = ProjectParser.parse_line(line)
|
|
|
|
assert project is not None
|
|
assert project.status == "active"
|
|
assert project.description == "Write new blog post"
|
|
|
|
def test_parse_project_with_context(self) -> None:
|
|
"""Test parsing a project with context tags."""
|
|
line = "+home-office Setup home office @bcn [active]"
|
|
project = ProjectParser.parse_line(line)
|
|
|
|
assert project is not None
|
|
assert "bcn" in project.contexts
|
|
assert project.description == "Setup home office"
|
|
|
|
def test_parse_project_with_goal(self) -> None:
|
|
"""Test parsing a project with goal reference."""
|
|
line = "+observability-blog Write observability blog [active] goal:q1-2026"
|
|
project = ProjectParser.parse_line(line)
|
|
|
|
assert project is not None
|
|
assert "q1-2026" in project.goals
|
|
assert project.description == "Write observability blog"
|
|
|
|
def test_parse_project_with_due_date(self) -> None:
|
|
"""Test parsing a project with due date."""
|
|
line = "+renovation Kitchen renovation [waiting] due:2026-03-15"
|
|
project = ProjectParser.parse_line(line)
|
|
|
|
assert project is not None
|
|
assert project.status == "waiting"
|
|
assert project.due_date == datetime(2026, 3, 15)
|
|
assert project.metadata["due"] == "2026-03-15"
|
|
|
|
def test_parse_project_with_all_features(self) -> None:
|
|
"""Test parsing a complex project with all features."""
|
|
line = "+myorg-assistant MyOrg Personal Assistant [active] @computer-deep goal:q1-2026 priority:high due:2026-02-28"
|
|
project = ProjectParser.parse_line(line)
|
|
|
|
assert project is not None
|
|
assert project.tag == "myorg-assistant"
|
|
assert project.description == "MyOrg Personal Assistant"
|
|
assert project.status == "active"
|
|
assert "computer-deep" in project.contexts
|
|
assert "q1-2026" in project.goals
|
|
assert project.metadata["priority"] == "high"
|
|
assert project.due_date == datetime(2026, 2, 28)
|
|
|
|
def test_parse_waiting_project(self) -> None:
|
|
"""Test parsing a waiting project."""
|
|
line = "+house-docs House documentation [waiting]"
|
|
project = ProjectParser.parse_line(line)
|
|
|
|
assert project is not None
|
|
assert project.status == "waiting"
|
|
|
|
def test_parse_someday_project(self) -> None:
|
|
"""Test parsing a someday project."""
|
|
line = "+learn-rust Learn Rust programming [someday]"
|
|
project = ProjectParser.parse_line(line)
|
|
|
|
assert project is not None
|
|
assert project.status == "someday"
|
|
|
|
def test_parse_completed_project(self) -> None:
|
|
"""Test parsing a completed project."""
|
|
line = "+q4-review Q4 review [completed]"
|
|
project = ProjectParser.parse_line(line)
|
|
|
|
assert project is not None
|
|
assert project.status == "completed"
|
|
|
|
def test_parse_empty_line(self) -> None:
|
|
"""Test parsing an empty line returns None."""
|
|
project = ProjectParser.parse_line("")
|
|
assert project is None
|
|
|
|
def test_parse_comment_line(self) -> None:
|
|
"""Test parsing a comment line returns None."""
|
|
project = ProjectParser.parse_line("# This is a comment")
|
|
assert project is None
|
|
|
|
def test_parse_invalid_format(self) -> None:
|
|
"""Test parsing a line without project tag returns None."""
|
|
project = ProjectParser.parse_line("Not a valid project")
|
|
assert project is None
|
|
|
|
def test_format_simple_project(self) -> None:
|
|
"""Test formatting a simple project."""
|
|
formatted = ProjectParser.format_project(
|
|
tag="blog-post",
|
|
description="Write blog post",
|
|
status="active"
|
|
)
|
|
assert formatted == "+blog-post Write blog post [active]"
|
|
|
|
def test_format_project_with_all_features(self) -> None:
|
|
"""Test formatting a complex project."""
|
|
formatted = ProjectParser.format_project(
|
|
tag="myorg-assistant",
|
|
description="MyOrg Assistant",
|
|
status="active",
|
|
contexts=["computer-deep"],
|
|
goals=["q1-2026"],
|
|
metadata={"priority": "high", "due": "2026-02-28"}
|
|
)
|
|
assert "+myorg-assistant" in formatted
|
|
assert "MyOrg Assistant" in formatted
|
|
assert "[active]" in formatted
|
|
assert "@computer-deep" in formatted
|
|
assert "goal:q1-2026" in formatted
|
|
assert "priority:high" in formatted
|
|
assert "due:2026-02-28" in formatted
|
|
|
|
def test_parse_file(self) -> None:
|
|
"""Test parsing multiple projects from file content."""
|
|
content = """# Active Projects
|
|
+myorg-assistant Personal assistant [active] goal:q1-2026
|
|
+blog-post Write blog post [active] @computer-deep
|
|
+renovation Kitchen renovation [waiting] due:2026-03-15
|
|
|
|
+learn-rust Learn Rust [someday]"""
|
|
|
|
projects = ProjectParser.parse_file(content)
|
|
assert len(projects) == 4
|
|
assert projects[0].tag == "myorg-assistant"
|
|
assert projects[1].status == "active"
|
|
assert projects[2].status == "waiting"
|
|
assert projects[3].status == "someday"
|
|
|
|
def test_filter_projects_by_status(self) -> None:
|
|
"""Test filtering projects by status."""
|
|
projects = [
|
|
Project(
|
|
raw_line="+p1 Project 1 [active]",
|
|
line_number=1,
|
|
tag="p1",
|
|
description="Project 1",
|
|
status="active"
|
|
),
|
|
Project(
|
|
raw_line="+p2 Project 2 [waiting]",
|
|
line_number=2,
|
|
tag="p2",
|
|
description="Project 2",
|
|
status="waiting"
|
|
),
|
|
Project(
|
|
raw_line="+p3 Project 3 [active]",
|
|
line_number=3,
|
|
tag="p3",
|
|
description="Project 3",
|
|
status="active"
|
|
),
|
|
]
|
|
|
|
active = ProjectParser.filter_projects(projects, status="active")
|
|
assert len(active) == 2
|
|
|
|
waiting = ProjectParser.filter_projects(projects, status="waiting")
|
|
assert len(waiting) == 1
|
|
|
|
def test_filter_projects_by_context(self) -> None:
|
|
"""Test filtering projects by context."""
|
|
projects = [
|
|
Project(
|
|
raw_line="+p1 Project 1 @home",
|
|
line_number=1,
|
|
tag="p1",
|
|
description="Project 1",
|
|
contexts=["home"]
|
|
),
|
|
Project(
|
|
raw_line="+p2 Project 2 @work",
|
|
line_number=2,
|
|
tag="p2",
|
|
description="Project 2",
|
|
contexts=["work"]
|
|
),
|
|
]
|
|
|
|
home_projects = ProjectParser.filter_projects(projects, context="home")
|
|
assert len(home_projects) == 1
|
|
assert home_projects[0].tag == "p1"
|
|
|
|
def test_filter_projects_by_goal(self) -> None:
|
|
"""Test filtering projects by goal."""
|
|
projects = [
|
|
Project(
|
|
raw_line="+p1 Project 1 goal:q1-2026",
|
|
line_number=1,
|
|
tag="p1",
|
|
description="Project 1",
|
|
goals=["q1-2026"]
|
|
),
|
|
Project(
|
|
raw_line="+p2 Project 2 goal:q2-2026",
|
|
line_number=2,
|
|
tag="p2",
|
|
description="Project 2",
|
|
goals=["q2-2026"]
|
|
),
|
|
]
|
|
|
|
q1_projects = ProjectParser.filter_projects(projects, goal="q1-2026")
|
|
assert len(q1_projects) == 1
|
|
assert q1_projects[0].tag == "p1"
|
|
|
|
def test_get_active_projects(self) -> None:
|
|
"""Test getting only active projects."""
|
|
projects = [
|
|
Project(
|
|
raw_line="+p1 Project 1 [active]",
|
|
line_number=1,
|
|
tag="p1",
|
|
description="Project 1",
|
|
status="active"
|
|
),
|
|
Project(
|
|
raw_line="+p2 Project 2 [waiting]",
|
|
line_number=2,
|
|
tag="p2",
|
|
description="Project 2",
|
|
status="waiting"
|
|
),
|
|
Project(
|
|
raw_line="+p3 Project 3 [active]",
|
|
line_number=3,
|
|
tag="p3",
|
|
description="Project 3",
|
|
status="active"
|
|
),
|
|
Project(
|
|
raw_line="+p4 Project 4 [someday]",
|
|
line_number=4,
|
|
tag="p4",
|
|
description="Project 4",
|
|
status="someday"
|
|
),
|
|
]
|
|
|
|
active = ProjectParser.get_active_projects(projects)
|
|
assert len(active) == 2
|
|
assert all(p.status == "active" for p in active)
|