From 173aafc1464b5cce1340f019c5246e19d01a017b Mon Sep 17 00:00:00 2001 From: Jaspar Stach Date: Thu, 16 May 2024 12:19:50 +0200 Subject: [PATCH] WiP: Add tests, test functionallity --- pontos/github/api/api.py | 8 +++ pontos/github/api/foo.json | 54 ++++++++++++++++ pontos/github/api/packages.py | 11 ++-- pontos/github/models/packages.py | 29 +++++++-- tests/github/api/test_organizations.py | 88 +++++++++++++------------- tests/github/api/test_packages.py | 36 ++++------- 6 files changed, 147 insertions(+), 79 deletions(-) create mode 100644 pontos/github/api/foo.json diff --git a/pontos/github/api/api.py b/pontos/github/api/api.py index 11dbc6694..b6e9ecbc0 100644 --- a/pontos/github/api/api.py +++ b/pontos/github/api/api.py @@ -22,6 +22,7 @@ ) from pontos.github.api.labels import GitHubAsyncRESTLabels from pontos.github.api.organizations import GitHubAsyncRESTOrganizations +from pontos.github.api.packages import GitHubAsyncRESTPackages from pontos.github.api.pull_requests import GitHubAsyncRESTPullRequests from pontos.github.api.release import GitHubAsyncRESTReleases from pontos.github.api.repositories import GitHubAsyncRESTRepositories @@ -120,6 +121,13 @@ def labels(self) -> GitHubAsyncRESTLabels: """ return GitHubAsyncRESTLabels(self._client) + @property + def packages(self) -> GitHubAsyncRESTPackages: + """ + Packages related API + """ + return GitHubAsyncRESTPackages(self._client) + @property @deprecated( since="23.3.4", diff --git a/pontos/github/api/foo.json b/pontos/github/api/foo.json new file mode 100644 index 000000000..63eb46dfd --- /dev/null +++ b/pontos/github/api/foo.json @@ -0,0 +1,54 @@ +{ + 'id': 4917607, + 'name': 'asset-management-backend', + 'package_type': 'container', + 'owner': { + 'login': 'greenbone', + 'id': 31986857, + 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjMxOTg2ODU3', + 'avatar_url': 'https: //avatars.githubusercontent.com/u/31986857?v=4', + 'gravatar_id': '', + 'url': 'https://api.github.com/users/greenbone', + 'html_url': 'https://github.com/greenbone', + 'followers_url': 'https://api.github.com/users/greenbone/followers', + 'following_url': 'https://api.github.com/users/greenbone/following{/other_user}', + 'gists_url': 'https://api.github.com/users/greenbone/gists{/gist_id}', + 'starred_url': 'https://api.github.com/users/greenbone/starred{/owner}{/repo}', + 'subscriptions_url': 'https://api.github.com/users/greenbone/subscriptions', 'organizations_url': 'https://api.github.com/users/greenbone/orgs', 'repos_url': 'https://api.github.com/users/greenbone/repos', 'events_url': 'https://api.github.com/users/greenbone/events{/privacy}', 'received_events_url': 'https://api.github.com/users/greenbone/received_events', 'type': 'Organization', 'site_admin': False + }, + 'version_count': 7686, + 'visibility': 'public', + 'url': 'https://api.github.com/orgs/greenbone/packages/container/asset-management-backend', + 'created_at': '2023-03-16T14:39:23Z', + 'updated_at': '2024-05-06T07:46:34Z', + 'repository': { + 'id': 498688333, + 'node_id': 'R_kgDOHblhTQ', + 'name': 'asset-management-backend', + 'full_name': 'greenbone/asset-management-backend', + 'private': True, + 'owner': {'login': 'greenbone', 'id': 31986857, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjMxOTg2ODU3', 'avatar_url': 'https://avatars.githubusercontent.com/u/31986857?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/greenbone', 'html_url': 'https://github.com/greenbone', 'followers_url': 'https://api.github.com/users/greenbone/followers', 'following_url': 'https://api.github.com/users/greenbone/following{/other_user}', 'gists_url': 'https://api.github.com/users/greenbone/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/greenbone/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/greenbone/subscriptions', 'organizations_url': 'https://api.github.com/users/greenbone/orgs', 'repos_url': 'https://api.github.com/users/greenbone/repos', 'events_url': 'https://api.github.com/users/greenbone/events{/privacy}', 'received_events_url': 'https://api.github.com/users/greenbone/received_events', 'type': 'Organization', 'site_admin': False + }, + 'html_url': 'https://github.com/greenbone/asset-management-backend', + 'description': 'Backend for the asset-management-frontend that provides REST endpoints with the main functionality of the Opensight Asset product.', + 'fork': False, + 'url': 'https://api.github.com/repos/greenbone/asset-management-backend', + 'forks_url': 'https://api.github.com/repos/greenbone/asset-management-backend/forks', + 'keys_url': 'https://api.github.com/repos/greenbone/asset-management-backend/keys{/key_id}', + 'collaborators_url': 'https://api.github.com/repos/greenbone/asset-management-backend/collaborators{/collaborator}', + 'teams_url': 'https://api.github.com/repos/greenbone/asset-management-backend/teams', + 'hooks_url': 'https://api.github.com/repos/greenbone/asset-management-backend/hooks', + 'issue_events_url': 'https://api.github.com/repos/greenbone/asset-management-backend/issues/events{/number}', + 'events_url': 'https://api.github.com/repos/greenbone/asset-management-backend/events', + 'assignees_url': 'https://api.github.com/repos/greenbone/asset-management-backend/assignees{/user}', + 'branches_url': 'https://api.github.com/repos/greenbone/asset-management-backend/branches{/branch}', + 'tags_url': 'https://api.github.com/repos/greenbone/asset-management-backend/tags', 'blobs_url': 'https://api.github.com/repos/greenbone/asset-management-backend/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/greenbone/asset-management-backend/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/greenbone/asset-management-backend/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/greenbone/asset-management-backend/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/greenbone/asset-management-backend/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/greenbone/asset-management-backend/languages', 'stargazers_url': 'https://api.github.com/repos/greenbone/asset-management-backend/stargazers', 'contributors_url': 'https://api.github.com/repos/greenbone/asset-management-backend/contributors', + 'subscribers_url': 'https://api.github.com/repos/greenbone/asset-management-backend/subscribers', 'subscription_url': 'https://api.github.com/repos/greenbone/asset-management-backend/subscription', 'commits_url': 'https://api.github.com/repos/greenbone/asset-management-backend/commits{/sha}', + 'git_commits_url': 'https://api.github.com/repos/greenbone/asset-management-backend/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/greenbone/asset-management-backend/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/greenbone/asset-management-backend/issues/comments{/number}', + 'contents_url': 'https://api.github.com/repos/greenbone/asset-management-backend/contents/{+path}', 'compare_url': 'https://api.github.com/repos/greenbone/asset-management-backend/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/greenbone/asset-management-backend/merges', 'archive_url': 'https://api.github.com/repos/greenbone/asset-management-backend/{archive_format}{/ref}', + 'downloads_url': 'https://api.github.com/repos/greenbone/asset-management-backend/downloads', 'issues_url': 'https://api.github.com/repos/greenbone/asset-management-backend/issues{/number}', 'pulls_url': 'https://api.github.com/repos/greenbone/asset-management-backend/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/greenbone/asset-management-backend/milestones{/number}', + 'notifications_url': 'https://api.github.com/repos/greenbone/asset-management-backend/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/greenbone/asset-management-backend/labels{/name}', 'releases_url': 'https://api.github.com/repos/greenbone/asset-management-backend/releases{/id}', + 'deployments_url': 'https://api.github.com/repos/greenbone/asset-management-backend/deployments' + }, + 'html_url': 'https://github.com/orgs/greenbone/packages/container/package/asset-management-backend' +} \ No newline at end of file diff --git a/pontos/github/api/packages.py b/pontos/github/api/packages.py index 9bbe8ca9a..d6c2e5d5a 100644 --- a/pontos/github/api/packages.py +++ b/pontos/github/api/packages.py @@ -146,10 +146,11 @@ async def package_versions( print(package) """ api = f"/orgs/{organization}/packages/{package_type}/{package_name}/versions" - response = await self._client.get(api) - if not response.is_success: - raise GitHubApiError(response) - return response.json() + async for response in self._client.get_all(api): + response.raise_for_status() + + for version in response.json(): + yield PackageVersion.from_dict(version) async def package_version( self, @@ -194,7 +195,7 @@ async def package_version( response = await self._client.get(api) if not response.is_success: raise GitHubApiError(response) - return PackageVersion(**response.json()) + return PackageVersion.from_dict(response.json()) async def package_version_tags( self, diff --git a/pontos/github/models/packages.py b/pontos/github/models/packages.py index 0c942f419..1be7f33bb 100644 --- a/pontos/github/models/packages.py +++ b/pontos/github/models/packages.py @@ -3,12 +3,21 @@ # SPDX-License-Identifier: GPL-3.0-or-later # -from dataclasses import dataclass +from dataclasses import dataclass, field +from typing import Optional from pontos.github.models.base import GitHubModel, User +from pontos.github.models.organization import Repository from pontos.models import StrEnum -__all__ = ["PackageType"] +__all__ = [ + "PackageType", + "PackageVisibility", + "Package", + "Container", + "PackageVersionMetadata", + "PackageVersion", +] class PackageType(StrEnum): @@ -34,12 +43,23 @@ class Package(GitHubModel): version_count: int visibility: PackageVisibility url: str - tags: list[str] created_at: str updated_at: str + repository: Repository html_url: str +@dataclass +class Container(GitHubModel): + tags: list[str] + + +@dataclass +class PackageVersionMetadata(GitHubModel): + package_type: PackageType + container: Optional[Container] = field(default=None) + + @dataclass class PackageVersion(GitHubModel): id: int @@ -49,5 +69,4 @@ class PackageVersion(GitHubModel): created_at: str updated_at: str html_url: str - package_type: PackageType - tags: list[str] + metadata: PackageVersionMetadata diff --git a/tests/github/api/test_organizations.py b/tests/github/api/test_organizations.py index 10ffa3849..1b1f230b3 100644 --- a/tests/github/api/test_organizations.py +++ b/tests/github/api/test_organizations.py @@ -24,7 +24,7 @@ here = Path(__file__).parent -REPOSITORY_JSON = { +REPOSITORY_DICT = { "id": 1, "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", "name": "Hello-World", @@ -123,7 +123,7 @@ "watchers": 1, } -MEMBER_JSON = { +MEMBER_DICT = { "id": 1, "login": "octocat", "node_id": "MDQ6VXNlcjE=", @@ -166,13 +166,13 @@ async def test_not_exists(self): async def test_get_repositories(self): response1 = create_response() - response1.json.return_value = [REPOSITORY_JSON] + response1.json.return_value = [REPOSITORY_DICT] response2 = create_response() - repository_json2 = deepcopy(REPOSITORY_JSON) - repository_json2["id"] = 2 - repository_json3 = deepcopy(REPOSITORY_JSON) - repository_json3["id"] = 3 - response2.json.return_value = [repository_json2, repository_json3] + repository_dict2 = deepcopy(REPOSITORY_DICT) + repository_dict2["id"] = 2 + repository_dict3 = deepcopy(REPOSITORY_DICT) + repository_dict3["id"] = 3 + response2.json.return_value = [repository_dict2, repository_dict3] self.client.get_all.return_value = AsyncIteratorMock( [response1, response2] @@ -196,13 +196,13 @@ async def test_get_repositories(self): async def test_get_private_repositories(self): response1 = create_response() - response1.json.return_value = [REPOSITORY_JSON] + response1.json.return_value = [REPOSITORY_DICT] response2 = create_response() - repository_json2 = deepcopy(REPOSITORY_JSON) - repository_json2["id"] = 2 - repository_json3 = deepcopy(REPOSITORY_JSON) - repository_json3["id"] = 3 - response2.json.return_value = [repository_json2, repository_json3] + repository_dict2 = deepcopy(REPOSITORY_DICT) + repository_dict2["id"] = 2 + repository_dict3 = deepcopy(REPOSITORY_DICT) + repository_dict3["id"] = 3 + response2.json.return_value = [repository_dict2, repository_dict3] self.client.get_all.return_value = AsyncIteratorMock( [response1, response2] @@ -230,13 +230,13 @@ async def test_get_private_repositories(self): async def test_members(self): response1 = create_response() - response1.json.return_value = [MEMBER_JSON] + response1.json.return_value = [MEMBER_DICT] response2 = create_response() - member_json2 = deepcopy(MEMBER_JSON) - member_json2["id"] = 2 - member_json3 = deepcopy(MEMBER_JSON) - member_json3["id"] = 3 - response2.json.return_value = [member_json2, member_json3] + member_dict2 = deepcopy(MEMBER_DICT) + member_dict2["id"] = 2 + member_dict3 = deepcopy(MEMBER_DICT) + member_dict3["id"] = 3 + response2.json.return_value = [member_dict2, member_dict3] self.client.get_all.return_value = AsyncIteratorMock( [response1, response2] @@ -260,13 +260,13 @@ async def test_members(self): async def test_members_admins(self): response1 = create_response() - response1.json.return_value = [MEMBER_JSON] + response1.json.return_value = [MEMBER_DICT] response2 = create_response() - member_json2 = deepcopy(MEMBER_JSON) - member_json2["id"] = 2 - member_json3 = deepcopy(MEMBER_JSON) - member_json3["id"] = 3 - response2.json.return_value = [member_json2, member_json3] + member_dict2 = deepcopy(MEMBER_DICT) + member_dict2["id"] = 2 + member_dict3 = deepcopy(MEMBER_DICT) + member_dict3["id"] = 3 + response2.json.return_value = [member_dict2, member_dict3] self.client.get_all.return_value = AsyncIteratorMock( [response1, response2] @@ -290,13 +290,13 @@ async def test_members_admins(self): async def test_members_filter(self): response1 = create_response() - response1.json.return_value = [MEMBER_JSON] + response1.json.return_value = [MEMBER_DICT] response2 = create_response() - member_json2 = deepcopy(MEMBER_JSON) - member_json2["id"] = 2 - member_json3 = deepcopy(MEMBER_JSON) - member_json3["id"] = 3 - response2.json.return_value = [member_json2, member_json3] + member_dict2 = deepcopy(MEMBER_DICT) + member_dict2["id"] = 2 + member_dict3 = deepcopy(MEMBER_DICT) + member_dict3["id"] = 3 + response2.json.return_value = [member_dict2, member_dict3] self.client.get_all.return_value = AsyncIteratorMock( [response1, response2] @@ -411,13 +411,13 @@ async def test_remove_member_failure(self): async def test_outside_collaborators(self): response1 = create_response() - response1.json.return_value = [MEMBER_JSON] + response1.json.return_value = [MEMBER_DICT] response2 = create_response() - member_json2 = deepcopy(MEMBER_JSON) - member_json2["id"] = 2 - member_json3 = deepcopy(MEMBER_JSON) - member_json3["id"] = 3 - response2.json.return_value = [member_json2, member_json3] + member_dict2 = deepcopy(MEMBER_DICT) + member_dict2["id"] = 2 + member_dict3 = deepcopy(MEMBER_DICT) + member_dict3["id"] = 3 + response2.json.return_value = [member_dict2, member_dict3] self.client.get_all.return_value = AsyncIteratorMock( [response1, response2] @@ -441,13 +441,13 @@ async def test_outside_collaborators(self): async def test_outside_collaborators_filter(self): response1 = create_response() - response1.json.return_value = [MEMBER_JSON] + response1.json.return_value = [MEMBER_DICT] response2 = create_response() - member_json2 = deepcopy(MEMBER_JSON) - member_json2["id"] = 2 - member_json3 = deepcopy(MEMBER_JSON) - member_json3["id"] = 3 - response2.json.return_value = [member_json2, member_json3] + member_dict2 = deepcopy(MEMBER_DICT) + member_dict2["id"] = 2 + member_dict3 = deepcopy(MEMBER_DICT) + member_dict3["id"] = 3 + response2.json.return_value = [member_dict2, member_dict3] self.client.get_all.return_value = AsyncIteratorMock( [response1, response2] diff --git a/tests/github/api/test_packages.py b/tests/github/api/test_packages.py index f0dd2f798..947639285 100644 --- a/tests/github/api/test_packages.py +++ b/tests/github/api/test_packages.py @@ -7,31 +7,13 @@ from pontos.github.models.packages import ( Package, PackageType, + PackageVersion, PackageVisibility, ) from pontos.testing import AsyncIteratorMock from tests.github.api import GitHubAsyncRESTTestCase, create_response -OWNER_DICT = { - "login": "octocat", - "id": 1, - "node_id": "MDQ6VXNlcjE=", - "avatar_url": "https://github.com/images/error/octocat_happy.gif", - "gravatar_id": "", - "url": "https://api.github.com/users/octocat", - "html_url": "https://github.com/octocat", - "followers_url": "https://api.github.com/users/octocat/followers", - "following_url": "https://api.github.com/users/octocat/following{/other_user}", - "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", - "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", - "organizations_url": "https://api.github.com/users/octocat/orgs", - "repos_url": "https://api.github.com/users/octocat/repos", - "events_url": "https://api.github.com/users/octocat/events{/privacy}", - "received_events_url": "https://api.github.com/users/octocat/received_events", - "type": "User", - "site_admin": False, -} +from .test_organizations import MEMBER_DICT, REPOSITORY_DICT class GitHubAsyncRESTPackagesTestCase(GitHubAsyncRESTTestCase): @@ -59,13 +41,14 @@ async def test_package(self): "id": 1, "name": "bar", "package_type": "container", - "owner": OWNER_DICT, + "owner": MEMBER_DICT, "version_count": 1, "visibility": "public", "url": "https://api.github.com/orgs/foo/packages/container/bar", "tags": ["foo", "bar", "baz"], "created_at": "2022-01-01T00:00:00Z", "updated_at": "2022-01-01T00:00:00Z", + "repository": REPOSITORY_DICT, "html_url": "https://github.com/orgs/foo/packages/container/repo/bar", } @@ -101,13 +84,14 @@ async def test_packages(self): "id": 1, "name": "bar", "package_type": "container", - "owner": OWNER_DICT, + "owner": MEMBER_DICT, "version_count": 1, "visibility": "public", "url": "https://api.github.com/orgs/foo/packages/container/bar", "tags": ["foo", "bar", "baz"], "created_at": "2022-01-01T00:00:00Z", "updated_at": "2022-01-01T00:00:00Z", + "repository": REPOSITORY_DICT, "html_url": "https://github.com/orgs/foo/packages/container/repo/bar", } ] @@ -146,7 +130,7 @@ async def test_package_version(self): self.client.get.return_value = response - package_version = await self.api.package_version( + package_version: PackageVersion = await self.api.package_version( "foo", PackageType.CONTAINER, "bar", 1 ) @@ -170,5 +154,7 @@ async def test_package_version(self): package_version.html_url, "https://github.com/orgs/foo/packages/container/bar/1", ) - self.assertEqual(package_version.package_type, PackageType.CONTAINER) - self.assertEqual(package_version.tags, ["latest"]) + self.assertEqual( + package_version.metadata.package_type, PackageType.CONTAINER + ) + self.assertEqual(package_version.metadata.container.tags, ["latest"])