""" Unit tests for Issue domain models. Tests pure business logic with no external dependencies. """ import pytest from datetime import datetime, timedelta, timezone from domain.issues.models import Issue, Label, IssueState, LabelCategories from domain.issues.exceptions import IssueStateError class TestLabel: """Test Label value object.""" def test_label_creation(self): # Arrange & Act label = Label(name="bug", color="#ff0000", description="Bug label") # Assert assert label.name == "bug" assert label.color == "#ff0000" assert label.description == "Bug label" def test_is_state_label(self): # Arrange state_label = Label("status:in-progress") regular_label = Label("bug") # Act & Assert assert state_label.is_state_label() is True assert regular_label.is_state_label() is False def test_is_priority_label(self): # Arrange priority_label = Label("priority:high") regular_label = Label("bug") # Act & Assert assert priority_label.is_priority_label() is True assert regular_label.is_priority_label() is False def test_is_type_label(self): # Arrange type_label = Label("bug") priority_label = Label("priority:high") # Act & Assert assert type_label.is_type_label() is True assert priority_label.is_type_label() is False @pytest.mark.parametrize("label_name,expected", [ ("bug", True), ("enhancement", True), ("feature", True), ("documentation", True), ("custom-label", False), ("priority:high", False) ]) def test_type_label_recognition(self, label_name, expected): # Arrange label = Label(label_name) # Act & Assert assert label.is_type_label() == expected class TestIssue: """Test Issue aggregate root.""" def test_issue_creation_with_valid_data(self): # Arrange created_at = datetime.now(timezone.utc) updated_at = datetime.now(timezone.utc) labels = [Label("bug"), Label("priority:high")] # Act issue = Issue( number=123, title="Test Issue", state=IssueState.OPEN, labels=labels, created_at=created_at, updated_at=updated_at ) # Assert assert issue.number == 123 assert issue.title == "Test Issue" assert issue.state == IssueState.OPEN assert len(issue.labels) == 2 assert issue.created_at == created_at assert issue.updated_at == updated_at def test_categorize_labels_correctly_separates_types(self): # Arrange labels = [ Label("bug"), # type label Label("priority:high"), # priority label Label("status:in-progress"), # state label Label("documentation"), # type label Label("custom-label") # other label ] issue = Issue( number=1, title="Test", state=IssueState.OPEN, labels=labels, created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc) ) # Act categories = issue.categorize_labels() # Assert assert "bug" in categories.type_labels assert "documentation" in categories.type_labels assert "priority:high" in categories.priority_labels assert "status:in-progress" in categories.state_labels assert "custom-label" in categories.other_labels def test_close_issue_changes_state_and_sets_closed_at(self): # Arrange issue = Issue( number=1, title="Test", state=IssueState.OPEN, labels=[], created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc) ) # Act issue.close() # Assert assert issue.state == IssueState.CLOSED assert issue.closed_at is not None assert isinstance(issue.closed_at, datetime) def test_close_already_closed_issue_raises_error(self): # Arrange issue = Issue( number=1, title="Test", state=IssueState.CLOSED, labels=[], created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc), closed_at=datetime.now(timezone.utc) ) # Act & Assert with pytest.raises(IssueStateError) as exc_info: issue.close() assert "Issue is already closed" in str(exc_info.value) assert exc_info.value.current_state == "closed" assert exc_info.value.attempted_state == "closed" def test_reopen_closed_issue_changes_state_and_clears_closed_at(self): # Arrange issue = Issue( number=1, title="Test", state=IssueState.CLOSED, labels=[], created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc), closed_at=datetime.now(timezone.utc) ) # Act issue.reopen() # Assert assert issue.state == IssueState.OPEN assert issue.closed_at is None def test_reopen_open_issue_raises_error(self): # Arrange issue = Issue( number=1, title="Test", state=IssueState.OPEN, labels=[], created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc) ) # Act & Assert with pytest.raises(IssueStateError) as exc_info: issue.reopen() assert "Issue is not closed" in str(exc_info.value) def test_add_label_to_issue(self): # Arrange issue = Issue( number=1, title="Test", state=IssueState.OPEN, labels=[Label("bug")], created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc) ) new_label = Label("priority:high") # Act issue.add_label(new_label) # Assert assert len(issue.labels) == 2 assert new_label in issue.labels def test_add_duplicate_label_does_not_duplicate(self): # Arrange label = Label("bug") issue = Issue( number=1, title="Test", state=IssueState.OPEN, labels=[label], created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc) ) # Act issue.add_label(label) # Assert assert len(issue.labels) == 1 def test_remove_label_from_issue(self): # Arrange issue = Issue( number=1, title="Test", state=IssueState.OPEN, labels=[Label("bug"), Label("priority:high")], created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc) ) # Act issue.remove_label("bug") # Assert assert len(issue.labels) == 1 assert not any(label.name == "bug" for label in issue.labels) def test_has_label_returns_correct_value(self): # Arrange issue = Issue( number=1, title="Test", state=IssueState.OPEN, labels=[Label("bug"), Label("priority:high")], created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc) ) # Act & Assert assert issue.has_label("bug") is True assert issue.has_label("priority:high") is True assert issue.has_label("enhancement") is False class TestLabelCategories: """Test LabelCategories value object.""" def test_label_categories_creation(self): # Arrange & Act categories = LabelCategories( state_labels=["status:open"], priority_labels=["priority:high"], type_labels=["bug"], other_labels=["custom"] ) # Assert assert categories.state_labels == ["status:open"] assert categories.priority_labels == ["priority:high"] assert categories.type_labels == ["bug"] assert categories.other_labels == ["custom"]