Files
markitect-main/tests/unit/domain/projects/test_project_models.py
tegwick 1fa0f1e84a
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
fix: Eliminate all 111 test warnings by fixing root causes
- Replace deprecated datetime.utcnow() with datetime.now(timezone.utc)
  across all domain models, services, infrastructure, and test files
- Add missing timezone imports to all affected files
- Fix pytest.ini configuration format from [tool:pytest] to [pytest]
- Remove warning suppressions to expose actual issues
- Ensure proper pytest marker registration for smoke tests

Results:
- 305 passed, 2 skipped, 0 warnings (down from 111 warnings)
- All functionality preserved with modern datetime API usage
- Improved code quality by addressing root causes vs suppression

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-27 20:14:22 +02:00

607 lines
17 KiB
Python

"""
Unit tests for Project domain models.
Tests pure business logic with no external dependencies.
"""
import pytest
from datetime import datetime, timedelta, timezone
from domain.projects.models import Project, Milestone, ProjectState
from domain.projects.exceptions import MilestoneError
class TestMilestone:
"""Test Milestone entity."""
def test_milestone_creation(self):
# Arrange
due_date = datetime.now(timezone.utc) + timedelta(days=30)
# Act
milestone = Milestone(
id=1,
title="Version 1.0",
description="First release",
due_date=due_date,
state="open",
open_issues=5,
closed_issues=3
)
# Assert
assert milestone.id == 1
assert milestone.title == "Version 1.0"
assert milestone.description == "First release"
assert milestone.due_date == due_date
assert milestone.state == "open"
assert milestone.open_issues == 5
assert milestone.closed_issues == 3
def test_completion_percentage_calculation(self):
# Arrange
milestone = Milestone(
id=1,
title="Test",
description=None,
due_date=None,
state="open",
open_issues=2,
closed_issues=8
)
# Act
percentage = milestone.completion_percentage
# Assert
assert percentage == 80.0 # 8/(2+8) * 100
def test_completion_percentage_with_no_issues(self):
# Arrange
milestone = Milestone(
id=1,
title="Test",
description=None,
due_date=None,
state="open",
open_issues=0,
closed_issues=0
)
# Act
percentage = milestone.completion_percentage
# Assert
assert percentage == 0.0
def test_total_issues_property(self):
# Arrange
milestone = Milestone(
id=1,
title="Test",
description=None,
due_date=None,
state="open",
open_issues=3,
closed_issues=7
)
# Act & Assert
assert milestone.total_issues == 10
def test_is_overdue_with_past_due_date(self):
# Arrange
past_date = datetime.now(timezone.utc) - timedelta(days=1)
milestone = Milestone(
id=1,
title="Test",
description=None,
due_date=past_date,
state="open",
open_issues=1,
closed_issues=0
)
# Act & Assert
assert milestone.is_overdue() is True
def test_is_overdue_with_future_due_date(self):
# Arrange
future_date = datetime.now(timezone.utc) + timedelta(days=1)
milestone = Milestone(
id=1,
title="Test",
description=None,
due_date=future_date,
state="open",
open_issues=1,
closed_issues=0
)
# Act & Assert
assert milestone.is_overdue() is False
def test_is_overdue_with_no_due_date(self):
# Arrange
milestone = Milestone(
id=1,
title="Test",
description=None,
due_date=None,
state="open",
open_issues=1,
closed_issues=0
)
# Act & Assert
assert milestone.is_overdue() is False
def test_is_overdue_with_closed_milestone(self):
# Arrange
past_date = datetime.now(timezone.utc) - timedelta(days=1)
milestone = Milestone(
id=1,
title="Test",
description=None,
due_date=past_date,
state="closed",
open_issues=0,
closed_issues=5
)
# Act & Assert
assert milestone.is_overdue() is False
def test_is_completed_with_closed_state(self):
# Arrange
milestone = Milestone(
id=1,
title="Test",
description=None,
due_date=None,
state="closed",
open_issues=0,
closed_issues=5
)
# Act & Assert
assert milestone.is_completed() is True
def test_is_completed_with_100_percent_completion(self):
# Arrange
milestone = Milestone(
id=1,
title="Test",
description=None,
due_date=None,
state="open",
open_issues=0,
closed_issues=5
)
# Act & Assert
assert milestone.is_completed() is True
def test_is_completed_with_partial_completion(self):
# Arrange
milestone = Milestone(
id=1,
title="Test",
description=None,
due_date=None,
state="open",
open_issues=2,
closed_issues=3
)
# Act & Assert
assert milestone.is_completed() is False
def test_add_issue_increments_open_count(self):
# Arrange
milestone = Milestone(
id=1,
title="Test",
description=None,
due_date=None,
state="open",
open_issues=3,
closed_issues=2
)
# Act
milestone.add_issue()
# Assert
assert milestone.open_issues == 4
assert milestone.closed_issues == 2
def test_close_issue_moves_from_open_to_closed(self):
# Arrange
milestone = Milestone(
id=1,
title="Test",
description=None,
due_date=None,
state="open",
open_issues=3,
closed_issues=2
)
# Act
milestone.close_issue()
# Assert
assert milestone.open_issues == 2
assert milestone.closed_issues == 3
def test_close_issue_with_no_open_issues_raises_error(self):
# Arrange
milestone = Milestone(
id=1,
title="Test",
description=None,
due_date=None,
state="open",
open_issues=0,
closed_issues=5
)
# Act & Assert
with pytest.raises(MilestoneError) as exc_info:
milestone.close_issue()
assert "no open issues" in str(exc_info.value)
assert exc_info.value.milestone_id == 1
def test_reopen_issue_moves_from_closed_to_open(self):
# Arrange
milestone = Milestone(
id=1,
title="Test",
description=None,
due_date=None,
state="open",
open_issues=2,
closed_issues=3
)
# Act
milestone.reopen_issue()
# Assert
assert milestone.open_issues == 3
assert milestone.closed_issues == 2
def test_reopen_issue_with_no_closed_issues_raises_error(self):
# Arrange
milestone = Milestone(
id=1,
title="Test",
description=None,
due_date=None,
state="open",
open_issues=5,
closed_issues=0
)
# Act & Assert
with pytest.raises(MilestoneError) as exc_info:
milestone.reopen_issue()
assert "no closed issues" in str(exc_info.value)
assert exc_info.value.milestone_id == 1
class TestProject:
"""Test Project aggregate root."""
def test_project_creation(self):
# Arrange
created_at = datetime.now(timezone.utc)
updated_at = datetime.now(timezone.utc)
milestones = [
Milestone(1, "M1", None, None, "open", 2, 1),
Milestone(2, "M2", None, None, "closed", 0, 3)
]
# Act
project = Project(
name="Test Project",
description="A test project",
state=ProjectState.ACTIVE,
milestones=milestones,
kanban_columns=["Todo", "In Progress", "Done"],
created_at=created_at,
updated_at=updated_at
)
# Assert
assert project.name == "Test Project"
assert project.description == "A test project"
assert project.state == ProjectState.ACTIVE
assert len(project.milestones) == 2
assert project.kanban_columns == ["Todo", "In Progress", "Done"]
def test_get_active_milestones(self):
# Arrange
milestones = [
Milestone(1, "M1", None, None, "open", 2, 1),
Milestone(2, "M2", None, None, "closed", 0, 3),
Milestone(3, "M3", None, None, "open", 1, 0)
]
project = Project(
name="Test",
description="",
state=ProjectState.ACTIVE,
milestones=milestones,
kanban_columns=[],
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
# Act
active_milestones = project.get_active_milestones()
# Assert
assert len(active_milestones) == 2
assert all(m.state == "open" for m in active_milestones)
def test_get_completed_milestones(self):
# Arrange
milestones = [
Milestone(1, "M1", None, None, "open", 2, 1), # Not completed
Milestone(2, "M2", None, None, "closed", 0, 3), # Completed (closed)
Milestone(3, "M3", None, None, "open", 0, 5) # Completed (100%)
]
project = Project(
name="Test",
description="",
state=ProjectState.ACTIVE,
milestones=milestones,
kanban_columns=[],
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
# Act
completed_milestones = project.get_completed_milestones()
# Assert
assert len(completed_milestones) == 2
def test_get_overdue_milestones(self):
# Arrange
past_date = datetime.now(timezone.utc) - timedelta(days=1)
future_date = datetime.now(timezone.utc) + timedelta(days=1)
milestones = [
Milestone(1, "M1", None, past_date, "open", 2, 1), # Overdue
Milestone(2, "M2", None, future_date, "open", 1, 0), # Not overdue
Milestone(3, "M3", None, None, "open", 1, 0) # No due date
]
project = Project(
name="Test",
description="",
state=ProjectState.ACTIVE,
milestones=milestones,
kanban_columns=[],
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
# Act
overdue_milestones = project.get_overdue_milestones()
# Assert
assert len(overdue_milestones) == 1
assert overdue_milestones[0].id == 1
def test_calculate_overall_progress(self):
# Arrange
milestones = [
Milestone(1, "M1", None, None, "open", 1, 4), # 80% complete
Milestone(2, "M2", None, None, "open", 3, 2) # 40% complete
]
project = Project(
name="Test",
description="",
state=ProjectState.ACTIVE,
milestones=milestones,
kanban_columns=[],
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
# Act
progress = project.calculate_overall_progress()
# Assert
assert progress == 60.0 # (80 + 40) / 2
def test_calculate_overall_progress_with_no_milestones(self):
# Arrange
project = Project(
name="Test",
description="",
state=ProjectState.ACTIVE,
milestones=[],
kanban_columns=[],
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
# Act
progress = project.calculate_overall_progress()
# Assert
assert progress == 0.0
def test_get_total_issues(self):
# Arrange
milestones = [
Milestone(1, "M1", None, None, "open", 2, 3), # 5 total
Milestone(2, "M2", None, None, "open", 1, 4) # 5 total
]
project = Project(
name="Test",
description="",
state=ProjectState.ACTIVE,
milestones=milestones,
kanban_columns=[],
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
# Act & Assert
assert project.get_total_issues() == 10
assert project.get_total_open_issues() == 3
assert project.get_total_closed_issues() == 7
def test_archive_project(self):
# Arrange
project = Project(
name="Test",
description="",
state=ProjectState.ACTIVE,
milestones=[],
kanban_columns=[],
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
# Act
project.archive()
# Assert
assert project.state == ProjectState.ARCHIVED
assert project.archived_at is not None
def test_activate_project(self):
# Arrange
project = Project(
name="Test",
description="",
state=ProjectState.ARCHIVED,
milestones=[],
kanban_columns=[],
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc),
archived_at=datetime.now(timezone.utc)
)
# Act
project.activate()
# Assert
assert project.state == ProjectState.ACTIVE
assert project.archived_at is None
def test_add_milestone(self):
# Arrange
project = Project(
name="Test",
description="",
state=ProjectState.ACTIVE,
milestones=[],
kanban_columns=[],
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
milestone = Milestone(1, "New Milestone", None, None, "open", 0, 0)
# Act
project.add_milestone(milestone)
# Assert
assert len(project.milestones) == 1
assert project.milestones[0] == milestone
def test_add_duplicate_milestone_raises_error(self):
# Arrange
milestone1 = Milestone(1, "Milestone 1", None, None, "open", 0, 0)
milestone2 = Milestone(1, "Milestone 2", None, None, "open", 0, 0) # Same ID
project = Project(
name="Test",
description="",
state=ProjectState.ACTIVE,
milestones=[milestone1],
kanban_columns=[],
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
# Act & Assert
with pytest.raises(ValueError, match="Milestone with ID 1 already exists"):
project.add_milestone(milestone2)
def test_remove_milestone(self):
# Arrange
milestone = Milestone(1, "Milestone", None, None, "open", 0, 0)
project = Project(
name="Test",
description="",
state=ProjectState.ACTIVE,
milestones=[milestone],
kanban_columns=[],
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
# Act
project.remove_milestone(1)
# Assert
assert len(project.milestones) == 0
def test_remove_nonexistent_milestone_raises_error(self):
# Arrange
project = Project(
name="Test",
description="",
state=ProjectState.ACTIVE,
milestones=[],
kanban_columns=[],
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
# Act & Assert
with pytest.raises(ValueError, match="Milestone with ID 999 not found"):
project.remove_milestone(999)
def test_get_milestone(self):
# Arrange
milestone = Milestone(1, "Milestone", None, None, "open", 0, 0)
project = Project(
name="Test",
description="",
state=ProjectState.ACTIVE,
milestones=[milestone],
kanban_columns=[],
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
# Act
found_milestone = project.get_milestone(1)
# Assert
assert found_milestone == milestone
def test_get_nonexistent_milestone_returns_none(self):
# Arrange
project = Project(
name="Test",
description="",
state=ProjectState.ACTIVE,
milestones=[],
kanban_columns=[],
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
# Act
found_milestone = project.get_milestone(999)
# Assert
assert found_milestone is None