Files
myorg-assistant/tests/test_project_parser.py
Roger Oriol 87fb32b559 first commit
2026-02-03 23:50:19 +01:00

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)