async def send_refresh( redis_cache: utils.RedisCache, redis_stream: utils.RedisStream, repository: github_types.GitHubRepository, pull_request_number: typing.Optional[ github_types.GitHubPullRequestNumber] = None, ref: typing.Optional[github_types.GitHubRefType] = None, action: github_types.GitHubEventRefreshActionType = "user", ) -> None: data = github_types.GitHubEventRefresh({ "action": action, "ref": ref, "repository": repository, "pull_request_number": pull_request_number, "sender": { "login": github_types.GitHubLogin("<internal>"), "id": github_types.GitHubAccountIdType(0), "type": "User", "avatar_url": "", }, "organization": repository["owner"], "installation": { "id": github_types.GitHubInstallationIdType(0), "account": repository["owner"], }, }) await filter_and_dispatch(redis_cache, redis_stream, "refresh", str(uuid.uuid4()), data)
def fake_repository( redis_links: redis_utils.RedisLinks, fake_subscription: subscription.Subscription, ) -> context.Repository: gh_owner = github_types.GitHubAccount({ "login": github_types.GitHubLogin("Mergifyio"), "id": github_types.GitHubAccountIdType(0), "type": "User", "avatar_url": "", }) gh_repo = github_types.GitHubRepository({ "full_name": "Mergifyio/mergify-engine", "name": github_types.GitHubRepositoryName("mergify-engine"), "private": False, "id": github_types.GitHubRepositoryIdType(0), "owner": gh_owner, "archived": False, "url": "", "html_url": "", "default_branch": github_types.GitHubRefType("main"), }) installation_json = github_types.GitHubInstallation({ "id": github_types.GitHubInstallationIdType(12345), "target_type": gh_owner["type"], "permissions": {}, "account": gh_owner, }) fake_client = mock.Mock() installation = context.Installation(installation_json, fake_subscription, fake_client, redis_links) return context.Repository(installation, gh_repo)
async def _send_refresh( redis_stream: "redis_utils.RedisStream", repository: github_types.GitHubRepository, action: github_types.GitHubEventRefreshActionType, source: str, pull_request_number: typing.Optional[ github_types.GitHubPullRequestNumber] = None, ref: typing.Optional[github_types.GitHubRefType] = None, score: typing.Optional[str] = None, ) -> None: # TODO(sileht): move refresh stuff into it's own file # Break circular import from mergify_engine import github_events from mergify_engine import worker data = github_types.GitHubEventRefresh({ "action": action, "source": source, "ref": ref, "pull_request_number": pull_request_number, "repository": repository, "sender": { "login": github_types.GitHubLogin("<internal>"), "id": github_types.GitHubAccountIdType(0), "type": "User", "avatar_url": "", }, "organization": repository["owner"], "installation": { "id": github_types.GitHubInstallationIdType(0), "account": repository["owner"], "target_type": repository["owner"]["type"], "permissions": {}, }, }) slim_event = github_events._extract_slim_event("refresh", data) await worker.push( redis_stream, repository["owner"]["id"], repository["owner"]["login"], repository["id"], repository["name"], pull_request_number, "refresh", slim_event, None, )
def fake_repository( redis_cache: utils.RedisCache, fake_subscription: subscription.Subscription, ) -> context.Repository: gh_owner = github_types.GitHubAccount( { "login": github_types.GitHubLogin("Mergifyio"), "id": github_types.GitHubAccountIdType(0), "type": "User", "avatar_url": "", } ) gh_repo = github_types.GitHubRepository( { "full_name": "Mergifyio/mergify-engine", "name": github_types.GitHubRepositoryName("mergify-engine"), "private": False, "id": github_types.GitHubRepositoryIdType(0), "owner": gh_owner, "archived": False, "url": "", "html_url": "", "default_branch": github_types.GitHubRefType("main"), } ) installation_json = github_types.GitHubInstallation( { "id": github_types.GitHubInstallationIdType(12345), "target_type": gh_owner["type"], "permissions": {}, "account": gh_owner, } ) fake_client = redis_queue = mock.Mock() # NOTE(Syffe): Since redis_queue is not used in fake_repository, we simply mock it, # otherwise a fixture is needed for it. This might change with future use of redis_queue. installation = context.Installation( installation_json, fake_subscription, fake_client, redis_cache, redis_queue ) return context.Repository(installation, gh_repo)
async def _do_test_event_to_pull_check_run( redis_cache: utils.RedisCache, filename: str, expected_pulls: typing.List[int]) -> None: with open( os.path.join(os.path.dirname(__file__), "events", filename), "r", ) as f: data = json.loads(f.read().replace("https://github.com", config.GITHUB_URL).replace( "https://api.github.com", config.GITHUB_REST_API_URL)) gh_owner = github_types.GitHubAccount({ "type": "User", "id": github_types.GitHubAccountIdType(12345), "login": github_types.GitHubLogin("CytopiaTeam"), "avatar_url": "", }) installation_json = github_types.GitHubInstallation({ "id": github_types.GitHubInstallationIdType(12345), "target_type": gh_owner["type"], "permissions": {}, "account": gh_owner, }) installation = context.Installation(installation_json, mock.Mock(), mock.Mock(), redis_cache, mock.Mock()) pulls = await github_events.extract_pull_numbers_from_event( installation, "check_run", data, [], ) assert pulls == expected_pulls
async def _send_refresh( redis_cache: RedisCache, redis_stream: RedisStream, repository: github_types.GitHubRepository, action: github_types.GitHubEventRefreshActionType, source: str, pull_request_number: typing.Optional[ github_types.GitHubPullRequestNumber] = None, ref: typing.Optional[github_types.GitHubRefType] = None, score: typing.Optional[str] = None, ) -> None: # Break circular import from mergify_engine import github_events data = github_types.GitHubEventRefresh({ "action": action, "source": source, "ref": ref, "pull_request_number": pull_request_number, "repository": repository, "sender": { "login": github_types.GitHubLogin("<internal>"), "id": github_types.GitHubAccountIdType(0), "type": "User", "avatar_url": "", }, "organization": repository["owner"], "installation": { "id": github_types.GitHubInstallationIdType(0), "account": repository["owner"], "target_type": repository["owner"]["type"], "permissions": {}, }, }) await github_events.filter_and_dispatch(redis_cache, redis_stream, "refresh", str(uuid.uuid4()), data)
async def test_team_permission_cache(redis_cache: utils.RedisCache) -> None: class FakeClient(github.AsyncGithubInstallationClient): called: int def __init__(self, owner: str, repo: str) -> None: super().__init__(auth=None) # type: ignore[arg-type] self.owner = owner self.repo = repo self.called = 0 async def get(self, url: str, *args: typing.Any, **kwargs: typing.Any) -> typing.Any: # type: ignore[override] self.called += 1 if ( url == f"/orgs/{self.owner}/teams/team-ok/repos/{self.owner}/{self.repo}" ): return {} elif ( url == f"/orgs/{self.owner}/teams/team-nok/repos/{self.owner}/{self.repo}" ): raise http.HTTPNotFound( message="Not found", request=mock.ANY, response=mock.ANY ) elif ( url == f"/orgs/{self.owner}/teams/team-also-nok/repos/{self.owner}/{self.repo}" ): raise http.HTTPNotFound( message="Not found", request=mock.ANY, response=mock.ANY ) raise ValueError(f"Unknown test URL `{url}`") gh_owner = github_types.GitHubAccount( { "id": github_types.GitHubAccountIdType(123), "login": github_types.GitHubLogin("jd"), "type": "User", "avatar_url": "", } ) gh_repo = github_types.GitHubRepository( { "id": github_types.GitHubRepositoryIdType(0), "owner": gh_owner, "full_name": "", "archived": False, "url": "", "html_url": "", "default_branch": github_types.GitHubRefType(""), "name": github_types.GitHubRepositoryName("test"), "private": False, } ) installation_json = github_types.GitHubInstallation( { "id": github_types.GitHubInstallationIdType(12345), "target_type": gh_owner["type"], "permissions": {}, "account": gh_owner, } ) team_slug1 = github_types.GitHubTeamSlug("team-ok") team_slug2 = github_types.GitHubTeamSlug("team-nok") team_slug3 = github_types.GitHubTeamSlug("team-also-nok") sub = subscription.Subscription( redis_cache, 0, "", frozenset([subscription.Features.PUBLIC_REPOSITORY]) ) client = FakeClient(gh_owner["login"], gh_repo["name"]) installation = context.Installation( installation_json, sub, client, redis_cache, mock.Mock() ) repository = context.Repository(installation, gh_repo) assert client.called == 0 assert await repository.team_has_read_permission(team_slug1) assert client.called == 1 assert await repository.team_has_read_permission(team_slug1) assert client.called == 1 assert not await repository.team_has_read_permission(team_slug2) assert client.called == 2 assert not await repository.team_has_read_permission(team_slug2) assert client.called == 2 assert not await repository.team_has_read_permission(team_slug3) assert client.called == 3 gh_repo = github_types.GitHubRepository( { "id": github_types.GitHubRepositoryIdType(1), "owner": gh_owner, "full_name": "", "archived": False, "url": "", "html_url": "", "default_branch": github_types.GitHubRefType(""), "name": github_types.GitHubRepositoryName("test2"), "private": False, } ) client = FakeClient(gh_owner["login"], gh_repo["name"]) installation = context.Installation( installation_json, sub, client, redis_cache, mock.Mock() ) repository = context.Repository(installation, gh_repo) assert client.called == 0 assert not await repository.team_has_read_permission(team_slug2) assert client.called == 1 # From local cache assert not await repository.team_has_read_permission(team_slug2) assert client.called == 1 # From redis repository._caches.team_has_read_permission.clear() assert not await repository.team_has_read_permission(team_slug2) assert client.called == 1 assert await repository.team_has_read_permission(team_slug1) assert client.called == 2 await context.Repository.clear_team_permission_cache_for_repo( redis_cache, gh_owner, gh_repo ) repository._caches.team_has_read_permission.clear() assert await repository.team_has_read_permission(team_slug1) assert client.called == 3 assert not await repository.team_has_read_permission(team_slug3) assert client.called == 4 await context.Repository.clear_team_permission_cache_for_org(redis_cache, gh_owner) repository._caches.team_has_read_permission.clear() assert not await repository.team_has_read_permission(team_slug3) assert client.called == 5 assert not await repository.team_has_read_permission(team_slug2) assert client.called == 6 # From local cache assert not await repository.team_has_read_permission(team_slug2) assert client.called == 6 # From redis repository._caches.team_has_read_permission.clear() assert not await repository.team_has_read_permission(team_slug2) assert client.called == 6 repository._caches.team_has_read_permission.clear() await context.Repository.clear_team_permission_cache_for_team( redis_cache, gh_owner, team_slug2 ) repository._caches.team_has_read_permission.clear() assert not await repository.team_has_read_permission(team_slug2) assert client.called == 7
async def test_user_permission_cache(redis_cache: utils.RedisCache) -> None: class FakeClient(github.AsyncGithubInstallationClient): called: int def __init__(self, owner: str, repo: str) -> None: super().__init__(auth=None) # type: ignore[arg-type] self.owner = owner self.repo = repo self.called = 0 async def item(self, url, *args, **kwargs): self.called += 1 if self.repo == "test": if ( url == f"/repos/{self.owner}/{self.repo}/collaborators/foo/permission" ): return {"permission": "admin"} elif url.startswith(f"/repos/{self.owner}/{self.repo}/collaborators/"): return {"permission": "loser"} elif self.repo == "test2": if ( url == f"/repos/{self.owner}/{self.repo}/collaborators/bar/permission" ): return {"permission": "admin"} elif url.startswith(f"/repos/{self.owner}/{self.repo}/collaborators/"): return {"permission": "loser"} raise ValueError(f"Unknown test URL `{url}` for repo {self.repo}") gh_owner = github_types.GitHubAccount( { "id": github_types.GitHubAccountIdType(123), "login": github_types.GitHubLogin("jd"), "type": "User", "avatar_url": "", } ) gh_repo = github_types.GitHubRepository( { "id": github_types.GitHubRepositoryIdType(0), "owner": gh_owner, "full_name": "", "archived": False, "url": "", "html_url": "", "default_branch": github_types.GitHubRefType(""), "name": github_types.GitHubRepositoryName("test"), "private": False, } ) user_1 = github_types.GitHubAccount( { "id": github_types.GitHubAccountIdType(1), "login": github_types.GitHubLogin("foo"), "type": "User", "avatar_url": "", } ) user_2 = github_types.GitHubAccount( { "id": github_types.GitHubAccountIdType(2), "login": github_types.GitHubLogin("bar"), "type": "User", "avatar_url": "", } ) user_3 = github_types.GitHubAccount( { "id": github_types.GitHubAccountIdType(3), "login": github_types.GitHubLogin("baz"), "type": "User", "avatar_url": "", } ) installation_json = github_types.GitHubInstallation( { "id": github_types.GitHubInstallationIdType(12345), "target_type": gh_owner["type"], "permissions": {}, "account": gh_owner, } ) sub = subscription.Subscription( redis_cache, 0, "", frozenset([subscription.Features.PUBLIC_REPOSITORY]) ) client = FakeClient(gh_owner["login"], gh_repo["name"]) installation = context.Installation( installation_json, sub, client, redis_cache, mock.Mock() ) repository = context.Repository(installation, gh_repo) assert client.called == 0 assert await repository.has_write_permission(user_1) assert client.called == 1 assert await repository.has_write_permission(user_1) assert client.called == 1 assert not await repository.has_write_permission(user_2) assert client.called == 2 # From local cache assert not await repository.has_write_permission(user_2) assert client.called == 2 # From redis repository._caches.user_permissions.clear() assert not await repository.has_write_permission(user_2) assert client.called == 2 assert not await repository.has_write_permission(user_3) assert client.called == 3 gh_repo = github_types.GitHubRepository( { "id": github_types.GitHubRepositoryIdType(1), "owner": gh_owner, "full_name": "", "archived": False, "url": "", "html_url": "", "default_branch": github_types.GitHubRefType(""), "name": github_types.GitHubRepositoryName("test2"), "private": False, } ) client = FakeClient(gh_owner["login"], gh_repo["name"]) installation = context.Installation( installation_json, sub, client, redis_cache, mock.Mock() ) repository = context.Repository(installation, gh_repo) assert client.called == 0 assert await repository.has_write_permission(user_2) assert client.called == 1 # From local cache assert await repository.has_write_permission(user_2) assert client.called == 1 # From redis repository._caches.user_permissions.clear() assert await repository.has_write_permission(user_2) assert client.called == 1 assert not await repository.has_write_permission(user_1) assert client.called == 2 await context.Repository.clear_user_permission_cache_for_repo( redis_cache, gh_owner, gh_repo ) repository._caches.user_permissions.clear() assert not await repository.has_write_permission(user_1) assert client.called == 3 assert not await repository.has_write_permission(user_3) assert client.called == 4 await context.Repository.clear_user_permission_cache_for_org(redis_cache, gh_owner) repository._caches.user_permissions.clear() assert not await repository.has_write_permission(user_3) assert client.called == 5 assert await repository.has_write_permission(user_2) assert client.called == 6 # From local cache assert await repository.has_write_permission(user_2) assert client.called == 6 # From redis repository._caches.user_permissions.clear() assert await repository.has_write_permission(user_2) assert client.called == 6 await context.Repository.clear_user_permission_cache_for_user( redis_cache, gh_owner, gh_repo, user_2 ) repository._caches.user_permissions.clear() assert await repository.has_write_permission(user_2) assert client.called == 7
async def test_team_members_cache(redis_cache: utils.RedisCache) -> None: class FakeClient(github.AsyncGithubInstallationClient): called: int def __init__(self, owner: str) -> None: super().__init__(auth=None) # type: ignore[arg-type] self.owner = owner self.called = 0 async def items(self, url, *args, **kwargs): self.called += 1 if url == f"/orgs/{self.owner}/teams/team1/members": yield {"login": "******"} yield {"login": "******"} elif url == f"/orgs/{self.owner}/teams/team2/members": yield {"login": "******"} yield {"login": "******"} elif url == f"/orgs/{self.owner}/teams/team3/members": return else: raise ValueError(f"Unknown test URL `{url}` for repo {self.repo}") gh_owner = github_types.GitHubAccount( { "id": github_types.GitHubAccountIdType(123), "login": github_types.GitHubLogin("jd"), "type": "User", "avatar_url": "", } ) installation_json = github_types.GitHubInstallation( { "id": github_types.GitHubInstallationIdType(12345), "target_type": gh_owner["type"], "permissions": {}, "account": gh_owner, } ) team_slug1 = github_types.GitHubTeamSlug("team1") team_slug2 = github_types.GitHubTeamSlug("team2") team_slug3 = github_types.GitHubTeamSlug("team3") sub = subscription.Subscription( redis_cache, 0, "", frozenset([subscription.Features.PUBLIC_REPOSITORY]) ) client = FakeClient(gh_owner["login"]) installation = context.Installation( installation_json, sub, client, redis_cache, mock.Mock() ) assert client.called == 0 assert (await installation.get_team_members(team_slug1)) == ["member1", "member2"] assert client.called == 1 assert (await installation.get_team_members(team_slug1)) == ["member1", "member2"] assert client.called == 1 assert (await installation.get_team_members(team_slug2)) == ["member3", "member4"] assert client.called == 2 # From local cache assert (await installation.get_team_members(team_slug2)) == ["member3", "member4"] assert client.called == 2 # From redis installation._caches.team_members.clear() assert (await installation.get_team_members(team_slug2)) == ["member3", "member4"] assert client.called == 2 assert (await installation.get_team_members(team_slug3)) == [] assert client.called == 3 # From local cache assert (await installation.get_team_members(team_slug3)) == [] assert client.called == 3 # From redis installation._caches.team_members.clear() assert (await installation.get_team_members(team_slug3)) == [] assert client.called == 3 await installation.clear_team_members_cache_for_team( redis_cache, gh_owner, github_types.GitHubTeamSlug(team_slug2) ) installation._caches.team_members.clear() assert (await installation.get_team_members(team_slug2)) == ["member3", "member4"] assert client.called == 4 # From local cache assert (await installation.get_team_members(team_slug2)) == ["member3", "member4"] assert client.called == 4 # From redis installation._caches.team_members.clear() assert (await installation.get_team_members(team_slug2)) == ["member3", "member4"] assert client.called == 4 await installation.clear_team_members_cache_for_org(redis_cache, gh_owner) installation._caches.team_members.clear() assert (await installation.get_team_members(team_slug1)) == ["member1", "member2"] assert client.called == 5 # From local cache assert (await installation.get_team_members(team_slug1)) == ["member1", "member2"] assert client.called == 5 # From redis installation._caches.team_members.clear() assert (await installation.get_team_members(team_slug1)) == ["member1", "member2"] assert client.called == 5 assert (await installation.get_team_members(team_slug2)) == ["member3", "member4"] assert client.called == 6 # From local cache assert (await installation.get_team_members(team_slug2)) == ["member3", "member4"] assert client.called == 6 # From redis installation._caches.team_members.clear() assert (await installation.get_team_members(team_slug2)) == ["member3", "member4"] assert client.called == 6 assert (await installation.get_team_members(team_slug3)) == [] assert client.called == 7 # From local cache assert (await installation.get_team_members(team_slug3)) == [] assert client.called == 7 # From redis installation._caches.team_members.clear() assert (await installation.get_team_members(team_slug3)) == [] assert client.called == 7