diff --git a/backend/tests/apps/github/api/internal/nodes/issue_test.py b/backend/tests/apps/github/api/internal/nodes/issue_test.py index e33f315bf7..c5d59aa981 100644 --- a/backend/tests/apps/github/api/internal/nodes/issue_test.py +++ b/backend/tests/apps/github/api/internal/nodes/issue_test.py @@ -1,5 +1,7 @@ """Test cases for IssueNode.""" +from unittest.mock import Mock + from apps.github.api.internal.nodes.issue import IssueNode @@ -22,3 +24,60 @@ def test_issue_node_fields(self): "repository_name", } assert field_names == expected_field_names + + def test_author_field(self): + """Test author field resolution.""" + mock_issue = Mock() + mock_author = Mock() + mock_issue.author = mock_author + + result = IssueNode.author(mock_issue) + assert result == mock_author + + def test_organization_name_with_organization(self): + """Test organization_name field when organization exists.""" + mock_issue = Mock() + mock_repository = Mock() + mock_organization = Mock() + mock_organization.login = "test-org" + mock_repository.organization = mock_organization + mock_issue.repository = mock_repository + + result = IssueNode.organization_name(mock_issue) + assert result == "test-org" + + def test_organization_name_without_organization(self): + """Test organization_name field when organization doesn't exist.""" + mock_issue = Mock() + mock_repository = Mock() + mock_repository.organization = None + mock_issue.repository = mock_repository + + result = IssueNode.organization_name(mock_issue) + assert result is None + + def test_organization_name_without_repository(self): + """Test organization_name field when repository doesn't exist.""" + mock_issue = Mock() + mock_issue.repository = None + + result = IssueNode.organization_name(mock_issue) + assert result is None + + def test_repository_name_with_repository(self): + """Test repository_name field when repository exists.""" + mock_issue = Mock() + mock_repository = Mock() + mock_repository.name = "test-repo" + mock_issue.repository = mock_repository + + result = IssueNode.repository_name(mock_issue) + assert result == "test-repo" + + def test_repository_name_without_repository(self): + """Test repository_name field when repository doesn't exist.""" + mock_issue = Mock() + mock_issue.repository = None + + result = IssueNode.repository_name(mock_issue) + assert result is None diff --git a/backend/tests/apps/github/api/internal/nodes/milestone_test.py b/backend/tests/apps/github/api/internal/nodes/milestone_test.py index 82e66128ec..a00b80169a 100644 --- a/backend/tests/apps/github/api/internal/nodes/milestone_test.py +++ b/backend/tests/apps/github/api/internal/nodes/milestone_test.py @@ -1,5 +1,9 @@ """Test cases for MilestoneNode.""" +from unittest.mock import Mock + +import pytest + from apps.github.api.internal.nodes.milestone import MilestoneNode @@ -26,3 +30,78 @@ def test_milestone_node_fields(self): "url", } assert field_names == expected_field_names + + def test_author_field(self): + """Test author field resolution.""" + mock_milestone = Mock() + mock_author = Mock() + mock_milestone.author = mock_author + + result = MilestoneNode.author(mock_milestone) + assert result == mock_author + + def test_organization_name_with_organization(self): + """Test organization_name field when organization exists.""" + mock_milestone = Mock() + mock_repository = Mock() + mock_organization = Mock() + mock_organization.login = "test-org" + mock_repository.organization = mock_organization + mock_milestone.repository = mock_repository + + result = MilestoneNode.organization_name(mock_milestone) + assert result == "test-org" + + def test_organization_name_without_organization(self): + """Test organization_name field when organization doesn't exist.""" + mock_milestone = Mock() + mock_repository = Mock() + mock_repository.organization = None + mock_milestone.repository = mock_repository + + result = MilestoneNode.organization_name(mock_milestone) + assert result is None + + def test_organization_name_without_repository(self): + """Test organization_name field when repository doesn't exist.""" + mock_milestone = Mock() + mock_milestone.repository = None + + result = MilestoneNode.organization_name(mock_milestone) + assert result is None + + def test_progress_with_issues(self): + """Test progress calculation with issues.""" + mock_milestone = Mock() + mock_milestone.closed_issues_count = 7 + mock_milestone.open_issues_count = 3 + + result = MilestoneNode.progress(mock_milestone) + assert result == pytest.approx(70.0) + + def test_progress_without_issues(self): + """Test progress calculation without issues.""" + mock_milestone = Mock() + mock_milestone.closed_issues_count = 0 + mock_milestone.open_issues_count = 0 + + result = MilestoneNode.progress(mock_milestone) + assert result == pytest.approx(0.0) + + def test_repository_name_with_repository(self): + """Test repository_name field when repository exists.""" + mock_milestone = Mock() + mock_repository = Mock() + mock_repository.name = "test-repo" + mock_milestone.repository = mock_repository + + result = MilestoneNode.repository_name(mock_milestone) + assert result == "test-repo" + + def test_repository_name_without_repository(self): + """Test repository_name field when repository doesn't exist.""" + mock_milestone = Mock() + mock_milestone.repository = None + + result = MilestoneNode.repository_name(mock_milestone) + assert result is None diff --git a/backend/tests/apps/github/api/internal/nodes/organization_test.py b/backend/tests/apps/github/api/internal/nodes/organization_test.py index ce61947a7d..10bc2804a4 100644 --- a/backend/tests/apps/github/api/internal/nodes/organization_test.py +++ b/backend/tests/apps/github/api/internal/nodes/organization_test.py @@ -1,5 +1,7 @@ """Test cases for OrganizationNode.""" +from unittest.mock import Mock, patch + from apps.github.api.internal.nodes.organization import ( OrganizationNode, OrganizationStatsNode, @@ -45,6 +47,82 @@ def test_resolve_url(self): assert url_field is not None assert url_field.type is str + @patch("apps.github.models.repository.Repository.objects") + @patch("apps.github.models.repository_contributor.RepositoryContributor.objects") + def test_stats_method_with_data(self, mock_repo_contributor_objects, mock_repository_objects): + """Test stats method with actual data.""" + # Mock repository queryset + mock_repositories = Mock() + mock_repositories.count.return_value = 5 + mock_repositories.aggregate.return_value = { + "total_stars": 100, + "total_forks": 50, + "total_issues": 25, + } + mock_repository_objects.filter.return_value = mock_repositories + + # Mock repository contributor queryset + mock_contributors = Mock() + mock_contributors.values.return_value.distinct.return_value.count.return_value = 15 + mock_repo_contributor_objects.filter.return_value = mock_contributors + + # Create mock organization + mock_organization = Mock() + + # Call stats method + result = OrganizationNode.stats(mock_organization) + + # Verify result + assert isinstance(result, OrganizationStatsNode) + assert result.total_repositories == 5 + assert result.total_contributors == 15 + assert result.total_stars == 100 + assert result.total_forks == 50 + assert result.total_issues == 25 + + @patch("apps.github.models.repository.Repository.objects") + @patch("apps.github.models.repository_contributor.RepositoryContributor.objects") + def test_stats_method_with_none_values( + self, mock_repo_contributor_objects, mock_repository_objects + ): + """Test stats method when aggregate returns None values.""" + # Mock repository queryset + mock_repositories = Mock() + mock_repositories.count.return_value = 3 + mock_repositories.aggregate.return_value = { + "total_stars": None, + "total_forks": None, + "total_issues": None, + } + mock_repository_objects.filter.return_value = mock_repositories + + # Mock repository contributor queryset + mock_contributors = Mock() + mock_contributors.values.return_value.distinct.return_value.count.return_value = 8 + mock_repo_contributor_objects.filter.return_value = mock_contributors + + # Create mock organization + mock_organization = Mock() + + # Call stats method + result = OrganizationNode.stats(mock_organization) + + # Verify result with default values + assert isinstance(result, OrganizationStatsNode) + assert result.total_repositories == 3 + assert result.total_contributors == 8 + assert result.total_stars == 0 + assert result.total_forks == 0 + assert result.total_issues == 0 + + def test_url_method(self): + """Test url method.""" + mock_organization = Mock() + mock_organization.url = "https://github.com/test-org" + + result = OrganizationNode.url(mock_organization) + assert result == "https://github.com/test-org" + class TestOrganizationStatsNode: def test_organization_stats_node(self): @@ -59,3 +137,19 @@ def test_organization_stats_node(self): field.name for field in OrganizationStatsNode.__strawberry_definition__.fields } assert field_names == expected_fields + + def test_organization_stats_node_creation(self): + """Test creating OrganizationStatsNode with values.""" + stats = OrganizationStatsNode( + total_repositories=10, + total_contributors=25, + total_stars=150, + total_forks=75, + total_issues=30, + ) + + assert stats.total_repositories == 10 + assert stats.total_contributors == 25 + assert stats.total_stars == 150 + assert stats.total_forks == 75 + assert stats.total_issues == 30 diff --git a/backend/tests/apps/github/api/internal/nodes/pull_request_test.py b/backend/tests/apps/github/api/internal/nodes/pull_request_test.py new file mode 100644 index 0000000000..90d54cdcff --- /dev/null +++ b/backend/tests/apps/github/api/internal/nodes/pull_request_test.py @@ -0,0 +1,98 @@ +"""Test cases for PullRequestNode.""" + +from unittest.mock import Mock + +from apps.github.api.internal.nodes.pull_request import PullRequestNode + + +class TestPullRequestNode: + """Test cases for PullRequestNode class.""" + + def test_pull_request_node_type(self): + assert hasattr(PullRequestNode, "__strawberry_definition__") + + def test_meta_configuration(self): + field_names = {field.name for field in PullRequestNode.__strawberry_definition__.fields} + expected_field_names = { + "_id", + "author", + "created_at", + "organization_name", + "repository_name", + "title", + "url", + } + assert field_names == expected_field_names + + def test_author_field(self): + """Test author field resolution.""" + mock_pr = Mock() + mock_author = Mock() + mock_pr.author = mock_author + + result = PullRequestNode.author(mock_pr) + assert result == mock_author + + def test_organization_name_with_organization(self): + """Test organization_name field when organization exists.""" + mock_pr = Mock() + mock_repository = Mock() + mock_organization = Mock() + mock_organization.login = "test-org" + mock_repository.organization = mock_organization + mock_pr.repository = mock_repository + + result = PullRequestNode.organization_name(mock_pr) + assert result == "test-org" + + def test_organization_name_without_organization(self): + """Test organization_name field when organization doesn't exist.""" + mock_pr = Mock() + mock_repository = Mock() + mock_repository.organization = None + mock_pr.repository = mock_repository + + result = PullRequestNode.organization_name(mock_pr) + assert result is None + + def test_organization_name_without_repository(self): + """Test organization_name field when repository doesn't exist.""" + mock_pr = Mock() + mock_pr.repository = None + + result = PullRequestNode.organization_name(mock_pr) + assert result is None + + def test_repository_name_with_repository(self): + """Test repository_name field when repository exists.""" + mock_pr = Mock() + mock_repository = Mock() + mock_repository.name = "test-repo" + mock_pr.repository = mock_repository + + result = PullRequestNode.repository_name(mock_pr) + assert result == "test-repo" + + def test_repository_name_without_repository(self): + """Test repository_name field when repository doesn't exist.""" + mock_pr = Mock() + mock_pr.repository = None + + result = PullRequestNode.repository_name(mock_pr) + assert result is None + + def test_url_with_url(self): + """Test url field when URL exists.""" + mock_pr = Mock() + mock_pr.url = "https://github.com/test-org/test-repo/pull/123" + + result = PullRequestNode.url(mock_pr) + assert result == "https://github.com/test-org/test-repo/pull/123" + + def test_url_without_url(self): + """Test url field when URL doesn't exist.""" + mock_pr = Mock() + mock_pr.url = None + + result = PullRequestNode.url(mock_pr) + assert result == "" diff --git a/backend/tests/apps/github/api/internal/nodes/release_test.py b/backend/tests/apps/github/api/internal/nodes/release_test.py index 91979d0878..ef45238ffd 100644 --- a/backend/tests/apps/github/api/internal/nodes/release_test.py +++ b/backend/tests/apps/github/api/internal/nodes/release_test.py @@ -1,5 +1,7 @@ """Test cases for ReleaseNode.""" +from unittest.mock import Mock + from apps.github.api.internal.nodes.release import ReleaseNode from apps.github.api.internal.nodes.user import UserNode @@ -31,3 +33,98 @@ def test_author_field(self): author_field = next((field for field in fields if field.name == "author"), None) assert author_field is not None assert author_field.type.of_type is UserNode + + def test_author_resolution(self): + """Test author field resolution.""" + mock_release = Mock() + mock_author = Mock() + mock_release.author = mock_author + + result = ReleaseNode.author(mock_release) + assert result == mock_author + + def test_organization_name_with_organization(self): + """Test organization_name field when organization exists.""" + mock_release = Mock() + mock_repository = Mock() + mock_organization = Mock() + mock_organization.login = "test-org" + mock_repository.organization = mock_organization + mock_release.repository = mock_repository + + result = ReleaseNode.organization_name(mock_release) + assert result == "test-org" + + def test_organization_name_without_organization(self): + """Test organization_name field when organization doesn't exist.""" + mock_release = Mock() + mock_repository = Mock() + mock_repository.organization = None + mock_release.repository = mock_repository + + result = ReleaseNode.organization_name(mock_release) + assert result is None + + def test_organization_name_without_repository(self): + """Test organization_name field when repository doesn't exist.""" + mock_release = Mock() + mock_release.repository = None + + result = ReleaseNode.organization_name(mock_release) + assert result is None + + def test_project_name_with_project(self): + """Test project_name field when project exists.""" + mock_release = Mock() + mock_repository = Mock() + mock_project = Mock() + mock_project.name = "OWASP Test Project" + mock_repository.project = mock_project + mock_release.repository = mock_repository + + result = ReleaseNode.project_name(mock_release) + assert result == " Test Project" # OWASP prefix stripped + + def test_project_name_without_project(self): + """Test project_name field when project doesn't exist.""" + mock_release = Mock() + mock_repository = Mock() + mock_repository.project = None + mock_release.repository = mock_repository + + result = ReleaseNode.project_name(mock_release) + assert result is None + + def test_project_name_without_repository(self): + """Test project_name field when repository doesn't exist.""" + mock_release = Mock() + mock_release.repository = None + + result = ReleaseNode.project_name(mock_release) + assert result is None + + def test_repository_name_with_repository(self): + """Test repository_name field when repository exists.""" + mock_release = Mock() + mock_repository = Mock() + mock_repository.name = "test-repo" + mock_release.repository = mock_repository + + result = ReleaseNode.repository_name(mock_release) + assert result == "test-repo" + + def test_repository_name_without_repository(self): + """Test repository_name field when repository doesn't exist.""" + mock_release = Mock() + mock_release.repository = None + + result = ReleaseNode.repository_name(mock_release) + assert result is None + + def test_url_field(self): + """Test url field resolution.""" + mock_release = Mock() + mock_release.url = "https://github.com/test-org/test-repo/releases/tag/v1.0.0" + + result = ReleaseNode.url(mock_release) + assert result == "https://github.com/test-org/test-repo/releases/tag/v1.0.0" diff --git a/backend/tests/apps/github/api/internal/nodes/repository_test.py b/backend/tests/apps/github/api/internal/nodes/repository_test.py index b3129e4603..4b1a167eba 100644 --- a/backend/tests/apps/github/api/internal/nodes/repository_test.py +++ b/backend/tests/apps/github/api/internal/nodes/repository_test.py @@ -1,95 +1,204 @@ -"""Test cases for ProjectNode.""" +"""Test cases for RepositoryNode.""" + +from unittest.mock import Mock from apps.github.api.internal.nodes.issue import IssueNode from apps.github.api.internal.nodes.milestone import MilestoneNode -from apps.github.api.internal.nodes.pull_request import PullRequestNode +from apps.github.api.internal.nodes.organization import OrganizationNode from apps.github.api.internal.nodes.release import ReleaseNode from apps.github.api.internal.nodes.repository import RepositoryNode -from apps.owasp.api.internal.nodes.project import ProjectNode +from apps.github.api.internal.nodes.repository_contributor import RepositoryContributorNode -class TestProjectNode: - def test_project_node_inheritance(self): - assert hasattr(ProjectNode, "__strawberry_definition__") +class TestRepositoryNode: + def test_repository_node_inheritance(self): + assert hasattr(RepositoryNode, "__strawberry_definition__") def test_meta_configuration(self): - field_names = {field.name for field in ProjectNode.__strawberry_definition__.fields} + field_names = {field.name for field in RepositoryNode.__strawberry_definition__.fields} expected_field_names = { "_id", + "commits_count", "contributors_count", "created_at", + "description", "forks_count", - "is_active", - "level", - "name", - "open_issues_count", - "stars_count", - "summary", - "type", - "issues_count", + "issues", "key", "languages", - "recent_issues", + "latest_release", + "license", + "name", + "open_issues_count", + "organization", + "owner_key", + "project", "recent_milestones", - "recent_pull_requests", - "recent_releases", - "repositories", - "repositories_count", + "releases", + "size", + "stars_count", + "subscribers_count", + "top_contributors", "topics", + "updated_at", + "url", } assert expected_field_names.issubset(field_names) def _get_field_by_name(self, name): return next( - (f for f in ProjectNode.__strawberry_definition__.fields if f.name == name), None + (f for f in RepositoryNode.__strawberry_definition__.fields if f.name == name), None ) - def test_resolve_issues_count(self): - field = self._get_field_by_name("issues_count") + def test_resolve_issues(self): + field = self._get_field_by_name("issues") assert field is not None - assert field.type is int - - def test_resolve_key(self): - field = self._get_field_by_name("key") - assert field is not None - assert field.type is str + assert field.type.of_type is IssueNode def test_resolve_languages(self): field = self._get_field_by_name("languages") assert field is not None assert field.type == list[str] - def test_resolve_recent_issues(self): - field = self._get_field_by_name("recent_issues") + def test_resolve_latest_release(self): + field = self._get_field_by_name("latest_release") assert field is not None - assert field.type.of_type is IssueNode + assert field.type is str - def test_resolve_recent_milestones(self): - field = self._get_field_by_name("recent_milestones") + def test_resolve_organization(self): + field = self._get_field_by_name("organization") assert field is not None - assert field.type.of_type is MilestoneNode + assert field.type.of_type is OrganizationNode - def test_resolve_recent_pull_requests(self): - field = self._get_field_by_name("recent_pull_requests") + def test_resolve_owner_key(self): + field = self._get_field_by_name("owner_key") assert field is not None - assert field.type.of_type is PullRequestNode + assert field.type is str - def test_resolve_recent_releases(self): - field = self._get_field_by_name("recent_releases") + def test_resolve_recent_milestones(self): + field = self._get_field_by_name("recent_milestones") assert field is not None - assert field.type.of_type is ReleaseNode + assert field.type.of_type is MilestoneNode - def test_resolve_repositories(self): - field = self._get_field_by_name("repositories") + def test_resolve_releases(self): + field = self._get_field_by_name("releases") assert field is not None - assert field.type.of_type is RepositoryNode + assert field.type.of_type is ReleaseNode - def test_resolve_repositories_count(self): - field = self._get_field_by_name("repositories_count") + def test_resolve_top_contributors(self): + field = self._get_field_by_name("top_contributors") assert field is not None - assert field.type is int + assert field.type.of_type is RepositoryContributorNode def test_resolve_topics(self): field = self._get_field_by_name("topics") assert field is not None assert field.type == list[str] + + def test_resolve_url(self): + field = self._get_field_by_name("url") + assert field is not None + assert field.type is str + + def test_issues_method(self): + """Test issues method resolution.""" + mock_repository = Mock() + mock_issues = Mock() + mock_issues.select_related.return_value.order_by.return_value.__getitem__ = Mock( + return_value=[] + ) + mock_repository.issues = mock_issues + + RepositoryNode.issues(mock_repository) + mock_issues.select_related.assert_called_with("author") + mock_issues.select_related.return_value.order_by.assert_called_with("-created_at") + + def test_languages_method(self): + """Test languages method resolution.""" + mock_repository = Mock() + mock_repository.languages = {"Python": 1000, "JavaScript": 500} + + result = RepositoryNode.languages(mock_repository) + assert result == ["Python", "JavaScript"] + + def test_latest_release_method(self): + """Test latest_release method resolution.""" + mock_repository = Mock() + mock_repository.latest_release = "v1.0.0" + + result = RepositoryNode.latest_release(mock_repository) + assert result == "v1.0.0" + + def test_organization_method(self): + """Test organization method resolution.""" + mock_repository = Mock() + mock_organization = Mock() + mock_repository.organization = mock_organization + + result = RepositoryNode.organization(mock_repository) + assert result == mock_organization + + def test_owner_key_method(self): + """Test owner_key method resolution.""" + mock_repository = Mock() + mock_repository.owner_key = "test-owner" + + result = RepositoryNode.owner_key(mock_repository) + assert result == "test-owner" + + def test_project_method(self): + """Test project method resolution.""" + mock_repository = Mock() + mock_project = Mock() + mock_repository.project = mock_project + + result = RepositoryNode.project(mock_repository) + assert result == mock_project + + def test_recent_milestones_method(self): + """Test recent_milestones method resolution.""" + mock_repository = Mock() + mock_milestones = Mock() + mock_milestones.select_related.return_value.order_by.return_value.__getitem__ = Mock( + return_value=[] + ) + mock_repository.recent_milestones = mock_milestones + + RepositoryNode.recent_milestones(mock_repository, limit=3) + mock_milestones.select_related.assert_called_with("repository") + mock_milestones.select_related.return_value.order_by.assert_called_with("-created_at") + + def test_releases_method(self): + """Test releases method resolution.""" + mock_repository = Mock() + mock_releases = Mock() + mock_releases.order_by.return_value.__getitem__ = Mock(return_value=[]) + mock_repository.published_releases = mock_releases + + RepositoryNode.releases(mock_repository) + mock_releases.order_by.assert_called_with("-published_at") + + def test_top_contributors_method(self): + """Test top_contributors method resolution.""" + mock_repository = Mock() + mock_contributors = [Mock(), Mock()] + mock_repository.idx_top_contributors = mock_contributors + + result = RepositoryNode.top_contributors(mock_repository) + assert result == mock_contributors + + def test_topics_method(self): + """Test topics method resolution.""" + mock_repository = Mock() + mock_repository.topics = ["security", "python", "django"] + + result = RepositoryNode.topics(mock_repository) + assert result == ["security", "python", "django"] + + def test_url_method(self): + """Test url method resolution.""" + mock_repository = Mock() + mock_repository.url = "https://github.com/test-org/test-repo" + + result = RepositoryNode.url(mock_repository) + assert result == "https://github.com/test-org/test-repo" diff --git a/backend/tests/apps/github/api/internal/nodes/user_test.py b/backend/tests/apps/github/api/internal/nodes/user_test.py index aff34ec8b0..01237ad925 100644 --- a/backend/tests/apps/github/api/internal/nodes/user_test.py +++ b/backend/tests/apps/github/api/internal/nodes/user_test.py @@ -1,5 +1,9 @@ """Test cases for UserNode.""" +from unittest.mock import Mock + +import pytest + from apps.github.api.internal.nodes.user import UserNode @@ -34,3 +38,43 @@ def test_meta_configuration(self): "url", } assert field_names == expected_field_names + + def test_created_at_field(self): + """Test created_at field resolution.""" + mock_user = Mock() + mock_user.idx_created_at = 1234567890.0 + + result = UserNode.created_at(mock_user) + assert result == pytest.approx(1234567890.0) + + def test_issues_count_field(self): + """Test issues_count field resolution.""" + mock_user = Mock() + mock_user.idx_issues_count = 42 + + result = UserNode.issues_count(mock_user) + assert result == 42 + + def test_releases_count_field(self): + """Test releases_count field resolution.""" + mock_user = Mock() + mock_user.idx_releases_count = 15 + + result = UserNode.releases_count(mock_user) + assert result == 15 + + def test_updated_at_field(self): + """Test updated_at field resolution.""" + mock_user = Mock() + mock_user.idx_updated_at = 1234567890.0 + + result = UserNode.updated_at(mock_user) + assert result == pytest.approx(1234567890.0) + + def test_url_field(self): + """Test url field resolution.""" + mock_user = Mock() + mock_user.url = "https://github.com/testuser" + + result = UserNode.url(mock_user) + assert result == "https://github.com/testuser"