From 01e5c811abe309f71519e2ca3c65292b44d25f29 Mon Sep 17 00:00:00 2001 From: tegwick Date: Wed, 17 Dec 2025 15:40:30 +0100 Subject: [PATCH] fix: move Gitea integration tests to issue-facade capability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Corrected the location of Gitea integration tests. They belong in the issue-facade capability, not release-management, as they test issue tracking functionality (issues, milestones, labels), not package publishing. Changes: - Deleted: capabilities/release-management/tests/test_gitea_integration.py - Added to submodule: capabilities/issue-facade/tests/test_gitea_integration.py - Updated submodule reference for issue-facade Capability Separation Clarified: - **issue-facade**: Issue tracking backends (Gitea, GitHub, GitLab, JIRA, etc.) - Provides unified CLI for issue management across different systems - Contains Gitea backend: issue_tracker/backends/gitea/backend.py - **release-management**: Package building, versioning, registry publishing - Handles version management with setuptools-scm - Publishes packages to registries (Gitea package registry, PyPI, etc.) Test Organization: - issue-facade now has 55 tests total: - 20 tests in test_gitea_backend.py (passing - current backend) - 35 tests in test_gitea_integration.py (skipped - needs architecture update) Main markitect test suite: 1,158 passed, 3 skipped (unchanged) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- capabilities/issue-facade | 2 +- .../tests/test_gitea_integration.py | 529 ------------------ 2 files changed, 1 insertion(+), 530 deletions(-) delete mode 100644 capabilities/release-management/tests/test_gitea_integration.py diff --git a/capabilities/issue-facade b/capabilities/issue-facade index 34a8bc7d..2dfe5130 160000 --- a/capabilities/issue-facade +++ b/capabilities/issue-facade @@ -1 +1 @@ -Subproject commit 34a8bc7d4c76ea19fe55bde5f74e15142f11cd65 +Subproject commit 2dfe5130a3d613622af3109929099fae38b456b7 diff --git a/capabilities/release-management/tests/test_gitea_integration.py b/capabilities/release-management/tests/test_gitea_integration.py deleted file mode 100644 index eda4f412..00000000 --- a/capabilities/release-management/tests/test_gitea_integration.py +++ /dev/null @@ -1,529 +0,0 @@ -""" -Tests for Gitea Issue/Milestone/Label Management (Future Functionality) - -IMPORTANT: These tests are for Gitea issue tracker integration functionality -that is NOT YET IMPLEMENTED in the release-management capability. - -The current release-management capability focuses on: -- Package registry operations (GiteaRegistry) -- Version management and tagging -- Package building and publishing - -This test suite covers future Gitea API operations for: -- Issue management (create, update, close issues) -- Milestone tracking -- Label operations - -These tests serve as: -1. Specification for future issue management features -2. Documentation of expected API behavior -3. Placeholder for when issue tracking integration is added - -Current Status: SKIPPED - Functionality not implemented -Related: capabilities/release-management/src/release_management/registries/gitea/ -""" - -import pytest -from unittest.mock import Mock, MagicMock, patch -from datetime import datetime - -# Skip all tests - this functionality doesn't exist in the capability yet -# Remove this skip marker when implementing Gitea issue management features -pytestmark = pytest.mark.skip( - reason="Gitea issue/milestone/label management not yet implemented in release-management capability. " - "Current capability only supports package registry operations." -) - - -class TestGiteaConfig: - """Test GiteaConfig functionality.""" - - def test_config_creation(self): - """Test basic config creation.""" - config = GiteaConfig( - gitea_url="https://gitea.example.com", - repo_owner="test_owner", - repo_name="test_repo", - auth_token="test_token" - ) - - assert config.gitea_url == "https://gitea.example.com" - assert config.repo_owner == "test_owner" - assert config.repo_name == "test_repo" - assert config.auth_token == "test_token" - - def test_api_url_properties(self): - """Test API URL property generation.""" - config = GiteaConfig( - gitea_url="https://gitea.example.com", - repo_owner="test_owner", - repo_name="test_repo" - ) - - assert config.base_api_url == "https://gitea.example.com/api/v1" - assert config.repo_api_url == "https://gitea.example.com/api/v1/repos/test_owner/test_repo" - assert config.issues_api_url == "https://gitea.example.com/api/v1/repos/test_owner/test_repo/issues" - - @patch('gitea.config.subprocess.run') - def test_from_git_repository(self, mock_run): - """Test config creation from git repository.""" - mock_run.return_value = Mock( - stdout="https://gitea.example.com/owner/repo.git", - returncode=0 - ) - - config = GiteaConfig.from_git_repository() - - assert config.gitea_url == "https://gitea.example.com" - assert config.repo_owner == "owner" - assert config.repo_name == "repo" - - def test_config_validation(self): - """Test config validation.""" - # Valid config should not raise - config = GiteaConfig( - gitea_url="https://gitea.example.com", - repo_owner="owner", - repo_name="repo" - ) - config.validate() # Should not raise - - # Invalid URL should raise - invalid_config = GiteaConfig( - gitea_url="invalid-url", - repo_owner="owner", - repo_name="repo" - ) - with pytest.raises(Exception): - invalid_config.validate() - - -class TestIssuesClient: - """Test IssuesClient functionality.""" - - def setup_method(self): - """Set up test fixtures.""" - self.mock_api = Mock() - self.client = IssuesClient(self.mock_api) - - # Mock issue for responses - self.mock_issue = Mock(spec=Issue) - self.mock_issue.number = 1 - self.mock_issue.title = "Test Issue" - self.mock_issue.body = "Test body" - self.mock_issue.state = "open" - self.mock_issue.html_url = "https://gitea.example.com/owner/repo/issues/1" - self.mock_issue.created_at = datetime(2023, 1, 1, 12, 0, 0) - self.mock_issue.updated_at = datetime(2023, 1, 1, 12, 0, 0) - self.mock_issue.assignee = None - self.mock_issue.labels = [] - self.mock_issue.milestone = None - - def test_get_issue(self): - """Test getting a single issue.""" - self.mock_api.get_issue.return_value = self.mock_issue - - result = self.client.get(1) - - assert result == self.mock_issue - self.mock_api.get_issue.assert_called_once_with(1) - - def test_list_issues(self): - """Test listing issues.""" - self.mock_api.list_issues.return_value = [self.mock_issue] - - result = self.client.list() - - assert result == [self.mock_issue] - self.mock_api.list_issues.assert_called_once_with("all", 1, 50) - - def test_list_issues_with_filters(self): - """Test listing issues with filters.""" - self.mock_api.list_issues.return_value = [self.mock_issue] - - result = self.client.list(state="open", page=2, per_page=25) - - assert result == [self.mock_issue] - self.mock_api.list_issues.assert_called_once_with("open", 2, 25) - - def test_create_issue(self): - """Test creating an issue.""" - self.mock_api.create_issue.return_value = self.mock_issue - - result = self.client.create("Test Title", "Test Body") - - assert result == self.mock_issue - self.mock_api.create_issue.assert_called_once() - - def test_create_issue_with_options(self): - """Test creating an issue with optional fields.""" - self.mock_api.create_issue.return_value = self.mock_issue - - result = self.client.create( - "Test Title", - "Test Body", - assignees=["user1"], - milestone=1, - labels=["bug", "priority:high"] - ) - - assert result == self.mock_issue - self.mock_api.create_issue.assert_called_once() - - def test_update_issue(self): - """Test updating an issue.""" - self.mock_api.update_issue.return_value = self.mock_issue - - result = self.client.update(1, title="New Title") - - assert result == self.mock_issue - self.mock_api.update_issue.assert_called_once() - - def test_close_issue(self): - """Test closing an issue.""" - closed_issue = Mock(spec=Issue) - closed_issue.state = "closed" - self.mock_api.update_issue.return_value = closed_issue - - result = self.client.close(1) - - assert result.state == "closed" - self.mock_api.update_issue.assert_called_once() - - def test_reopen_issue(self): - """Test reopening an issue.""" - opened_issue = Mock(spec=Issue) - opened_issue.state = "open" - self.mock_api.update_issue.return_value = opened_issue - - result = self.client.reopen(1) - - assert result.state == "open" - self.mock_api.update_issue.assert_called_once() - - def test_add_labels(self): - """Test adding labels to an issue.""" - # Mock getting current issue - self.mock_issue.labels = [Mock(name="existing")] - self.mock_api.get_issue.return_value = self.mock_issue - - # Mock update result - updated_issue = Mock(spec=Issue) - updated_issue.labels = [Mock(name="existing"), Mock(name="new")] - self.mock_api.update_issue.return_value = updated_issue - - result = self.client.add_labels(1, ["new"]) - - assert len(result.labels) == 2 - self.mock_api.get_issue.assert_called_once_with(1) - self.mock_api.update_issue.assert_called_once() - - def test_remove_labels(self): - """Test removing labels from an issue.""" - # Mock getting current issue - label1 = Mock(name="keep") - label2 = Mock(name="remove") - self.mock_issue.labels = [label1, label2] - self.mock_api.get_issue.return_value = self.mock_issue - - # Mock update result - updated_issue = Mock(spec=Issue) - updated_issue.labels = [label1] - self.mock_api.update_issue.return_value = updated_issue - - result = self.client.remove_labels(1, ["remove"]) - - assert len(result.labels) == 1 - self.mock_api.get_issue.assert_called_once_with(1) - self.mock_api.update_issue.assert_called_once() - - def test_assign_to_milestone(self): - """Test assigning issue to milestone.""" - self.mock_api.update_issue.return_value = self.mock_issue - - result = self.client.assign_to_milestone(1, 5) - - assert result == self.mock_issue - self.mock_api.update_issue.assert_called_once() - - def test_remove_from_milestone(self): - """Test removing issue from milestone.""" - self.mock_api.update_issue.return_value = self.mock_issue - - result = self.client.remove_from_milestone(1) - - assert result == self.mock_issue - self.mock_api.update_issue.assert_called_once() - - def test_set_labels(self): - """Test replacing all labels on an issue.""" - self.mock_api.update_issue.return_value = self.mock_issue - - result = self.client.set_labels(1, ["bug", "priority:high"]) - - assert result == self.mock_issue - self.mock_api.update_issue.assert_called_once() - - def test_update_title(self): - """Test updating only issue title.""" - self.mock_api.update_issue.return_value = self.mock_issue - - result = self.client.update_title(1, "New Title") - - assert result == self.mock_issue - self.mock_api.update_issue.assert_called_once() - - def test_update_body(self): - """Test updating only issue body.""" - self.mock_api.update_issue.return_value = self.mock_issue - - result = self.client.update_body(1, "New Body") - - assert result == self.mock_issue - self.mock_api.update_issue.assert_called_once() - - def test_set_priority(self): - """Test setting issue priority.""" - # Mock getting current issue - self.mock_issue.labels = [Mock(name="bug")] - self.mock_api.get_issue.return_value = self.mock_issue - self.mock_api.update_issue.return_value = self.mock_issue - - result = self.client.set_priority(1, Priority.HIGH) - - assert result == self.mock_issue - self.mock_api.get_issue.assert_called_once_with(1) - self.mock_api.update_issue.assert_called_once() - - def test_set_status(self): - """Test setting issue status.""" - # Mock getting current issue - self.mock_issue.labels = [Mock(name="bug")] - self.mock_api.get_issue.return_value = self.mock_issue - self.mock_api.update_issue.return_value = self.mock_issue - - result = self.client.set_status(1, ProjectState.ACTIVE) - - assert result == self.mock_issue - self.mock_api.get_issue.assert_called_once_with(1) - self.mock_api.update_issue.assert_called_once() - - def test_to_dict(self): - """Test converting issue to dictionary.""" - result = self.client.to_dict(self.mock_issue) - - expected_keys = ['number', 'title', 'body', 'state', 'html_url', - 'created_at', 'updated_at', 'assignee', 'labels', 'milestone'] - - assert all(key in result for key in expected_keys) - assert result['number'] == 1 - assert result['title'] == "Test Issue" - assert result['state'] == "open" - - -class TestMilestonesClient: - """Test MilestonesClient functionality.""" - - def setup_method(self): - """Set up test fixtures.""" - self.mock_api = Mock() - self.client = MilestonesClient(self.mock_api) - - self.mock_milestone = Mock(spec=Milestone) - self.mock_milestone.id = 1 - self.mock_milestone.title = "Test Milestone" - - def test_list_milestones(self): - """Test listing milestones.""" - self.mock_api.list_milestones.return_value = [self.mock_milestone] - - result = self.client.list() - - assert result == [self.mock_milestone] - self.mock_api.list_milestones.assert_called_once_with("all") - - def test_list_open_milestones(self): - """Test listing open milestones.""" - self.mock_api.list_milestones.return_value = [self.mock_milestone] - - result = self.client.list_open() - - assert result == [self.mock_milestone] - self.mock_api.list_milestones.assert_called_once_with("open") - - def test_create_milestone(self): - """Test creating a milestone.""" - self.mock_api.create_milestone.return_value = self.mock_milestone - - result = self.client.create("Test Milestone", "Description") - - assert result == self.mock_milestone - self.mock_api.create_milestone.assert_called_once() - - -class TestLabelsClient: - """Test LabelsClient functionality.""" - - def setup_method(self): - """Set up test fixtures.""" - self.mock_api = Mock() - self.client = LabelsClient(self.mock_api) - - self.mock_label = Mock(spec=Label) - self.mock_label.id = 1 - self.mock_label.name = "bug" - - def test_list_labels(self): - """Test listing labels.""" - self.mock_api.list_labels.return_value = [self.mock_label] - - result = self.client.list() - - assert result == [self.mock_label] - self.mock_api.list_labels.assert_called_once() - - def test_create_label(self): - """Test creating a label.""" - self.mock_api.create_label.return_value = self.mock_label - - result = self.client.create("bug", "red", "Bug reports") - - assert result == self.mock_label - self.mock_api.create_label.assert_called_once() - - -class TestGiteaClient: - """Test the main GiteaClient facade.""" - - @patch('gitea.client.GiteaApiClient') - def test_client_initialization(self, mock_api_client): - """Test GiteaClient initialization.""" - config = GiteaConfig( - gitea_url="https://gitea.example.com", - repo_owner="test_owner", - repo_name="test_repo" - ) - - client = GiteaClient(config) - - assert isinstance(client.issues, IssuesClient) - assert isinstance(client.milestones, MilestonesClient) - assert isinstance(client.labels, LabelsClient) - mock_api_client.assert_called_once_with(config) - - @patch('gitea.client.GiteaConfig.from_git_repository') - @patch('gitea.client.GiteaApiClient') - def test_client_auto_config(self, mock_api_client, mock_from_git): - """Test GiteaClient with auto-detected config.""" - mock_config = Mock() - mock_from_git.return_value = mock_config - - client = GiteaClient() - - mock_from_git.assert_called_once() - mock_api_client.assert_called_once_with(mock_config) - - -class TestErrorHandling: - """Test error handling throughout the facade.""" - - def setup_method(self): - """Set up test fixtures.""" - self.mock_api = Mock() - self.client = IssuesClient(self.mock_api) - - def test_gitea_error_propagation(self): - """Test that GiteaError is properly propagated.""" - self.mock_api.get_issue.side_effect = GiteaError("API Error") - - with pytest.raises(GiteaError): - self.client.get(1) - - def test_not_found_error_propagation(self): - """Test that GiteaNotFoundError is properly propagated.""" - self.mock_api.get_issue.side_effect = GiteaNotFoundError("Issue not found") - - with pytest.raises(GiteaNotFoundError): - self.client.get(999) - - def test_auth_error_propagation(self): - """Test that GiteaAuthError is properly propagated.""" - self.mock_api.create_issue.side_effect = GiteaAuthError("Unauthorized") - - with pytest.raises(GiteaAuthError): - self.client.create("Title", "Body") - - -class TestIntegrationPatterns: - """Test integration patterns and best practices.""" - - @patch('gitea.client.GiteaApiClient') - def test_consistent_interface(self, mock_api_client): - """Test that the facade provides consistent interfaces.""" - config = GiteaConfig(gitea_url="https://gitea.example.com", - repo_owner="owner", repo_name="repo") - client = GiteaClient(config) - - # All sub-clients should be available - assert hasattr(client, 'issues') - assert hasattr(client, 'milestones') - assert hasattr(client, 'labels') - - # All should have consistent method patterns - assert hasattr(client.issues, 'list') - assert hasattr(client.issues, 'get') - assert hasattr(client.issues, 'create') - assert hasattr(client.issues, 'update') - - assert hasattr(client.milestones, 'list') - assert hasattr(client.milestones, 'create') - - assert hasattr(client.labels, 'list') - assert hasattr(client.labels, 'create') - - def test_backward_compatibility_dict_conversion(self): - """Test that to_dict provides backward compatibility.""" - mock_api = Mock() - client = IssuesClient(mock_api) - - # Create a mock issue with all expected attributes - mock_issue = Mock(spec=Issue) - mock_issue.number = 1 - mock_issue.title = "Test" - mock_issue.body = "Body" - mock_issue.state = "open" - mock_issue.html_url = "https://example.com" - mock_issue.created_at = datetime(2023, 1, 1) - mock_issue.updated_at = datetime(2023, 1, 1) - mock_issue.assignee = None - mock_issue.labels = [] - mock_issue.milestone = None - - result = client.to_dict(mock_issue) - - # Should contain all expected fields for backward compatibility - required_fields = ['number', 'title', 'body', 'state', 'html_url', - 'created_at', 'updated_at', 'assignee', 'labels', 'milestone'] - - for field in required_fields: - assert field in result, f"Missing required field: {field}" - - def test_label_operations_consistency(self): - """Test that label operations work consistently.""" - mock_api = Mock() - client = IssuesClient(mock_api) - - # Mock issue with labels - mock_issue = Mock() - mock_issue.labels = [Mock(name="bug"), Mock(name="priority:high")] - mock_api.get_issue.return_value = mock_issue - mock_api.update_issue.return_value = mock_issue - - # Test all label operations - client.add_labels(1, ["new-label"]) - client.remove_labels(1, ["old-label"]) - client.set_labels(1, ["label1", "label2"]) - - # Should have made appropriate API calls - assert mock_api.get_issue.call_count == 2 # add_labels and remove_labels - assert mock_api.update_issue.call_count == 3 # all three operations \ No newline at end of file