def test_active_feature(): sub = subscription.Subscription( 123, True, "friend", {}, frozenset(), ) assert sub.has_feature(subscription.Features.PRIORITY_QUEUES) is False sub = subscription.Subscription( 123, False, "friend", {}, frozenset([subscription.Features.PRIORITY_QUEUES]), ) assert sub.has_feature(subscription.Features.PRIORITY_QUEUES) is False sub = subscription.Subscription( 123, True, "friend", {}, frozenset([subscription.Features.PRIORITY_QUEUES]), ) assert sub.has_feature(subscription.Features.PRIORITY_QUEUES) is True
async def test_disabled(redis_cache): action = request_reviews.RequestReviewsAction.get_schema()( { "random_count": 2, "teams": { "foobar": 2, "foobaz": 1, }, "users": { "jd": 2, "sileht": 1, }, }, ) client = mock.MagicMock() client.auth.installation.__getitem__.return_value = 123 sub = subscription.Subscription( redis_cache, 123, False, "No sub", {}, frozenset({}), ) installation = context.Installation(123, "Mergifyio", sub, client, redis_cache) repository = context.Repository(installation, "demo") ctxt = await context.Context.create( repository, { "number": 123, "state": None, "mergeable_state": "ok", "merged_by": None, "merged": None, "merged_at": None, "base": { "sha": "sha", "ref": "main", "user": { "login": { "Mergifyio", }, }, "repo": { "name": "demo", "private": False, "owner": { "login": "******", }, }, }, }, ) result = await action.run(ctxt, None) assert result.conclusion == check_api.Conclusion.ACTION_REQUIRED assert result.title == "Random request reviews are disabled" assert result.summary == ( "⚠ The [subscription](https://dashboard.mergify.io/github/Mergifyio/subscription) " "needs to be updated to enable this feature." )
def test_queue_summary_subscription(active, summary): ctxt = mock.Mock(subscription=subscription.Subscription( 123, active, "We're just testing", {}, frozenset({subscription.Features.PRIORITY_QUEUES}), )) ctxt.missing_feature_reason = subscription.Subscription.missing_feature_reason ctxt.pull = { "base": { "repo": { "owner": { "login": "******", }, }, }, } q = mock.Mock(installation_id=12345) q.get_pulls.return_value = [1, 2, 3, 4, 5, 6, 7, 8, 9] q.get_config.side_effect = gen_config( [4000, 3000, 3000, 3000, 2000, 2000, 1000, 1000, 1000]) with mock.patch.object(merge.queue.Queue, "from_context", return_value=q): action = merge.MergeAction( voluptuous.Schema(merge.MergeAction.validator)({})) assert summary == action.get_queue_summary(ctxt, q)
async def test_save_sub(features): owner_id = 1234 sub = subscription.Subscription(owner_id, True, "friend", {}, frozenset(features)) await sub.save_subscription_to_cache() rsub = await subscription.Subscription._retrieve_subscription_from_cache(owner_id) assert rsub == sub
async def test_get_mergify_config_location_from_cache( redis_cache: utils.RedisCache, ) -> None: client = mock.AsyncMock() client.auth.owner = "foo" client.item.side_effect = [ http.HTTPNotFound("Not Found", request=mock.Mock(), response=mock.Mock()), http.HTTPNotFound("Not Found", request=mock.Mock(), response=mock.Mock()), github_types.GitHubContentFile({ "content": encodebytes("whatever".encode()).decode(), "type": "file", "path": ".github/mergify.yml", "sha": github_types.SHAType("zeazeaze"), }), ] installation = context.Installation( github_types.GitHubAccountIdType(0), github_types.GitHubLogin("foo"), subscription.Subscription(redis_cache, 0, False, "", frozenset()), client, redis_cache, ) repository = context.Repository(installation, github_types.GitHubRepositoryName("bar")) await repository.get_mergify_config_file() assert client.item.call_count == 3 client.item.assert_has_calls([ mock.call("/repos/foo/bar/contents/.mergify.yml"), mock.call("/repos/foo/bar/contents/.mergify/config.yml"), mock.call("/repos/foo/bar/contents/.github/mergify.yml"), ]) client.item.reset_mock() client.item.side_effect = [ github_types.GitHubContentFile({ "content": encodebytes("whatever".encode()).decode(), "type": "file", "path": ".github/mergify.yml", "sha": github_types.SHAType("zeazeaze"), }), ] repository._cache = context.RepositoryCache() await repository.get_mergify_config_file() assert client.item.call_count == 1 client.item.assert_has_calls([ mock.call("/repos/foo/bar/contents/.github/mergify.yml"), ])
async def test_get_mergify_config_invalid( invalid: str, redis_cache: utils.RedisCache) -> None: with pytest.raises(InvalidRules): async def item(*args, **kwargs): return github_types.GitHubContentFile({ "content": encodebytes(invalid.encode()).decode(), "path": ".mergify.yml", "type": "file", "sha": "azertyu", }) client = mock.Mock() client.item.return_value = item() installation = context.Installation( github_types.GitHubAccountIdType(0), github_types.GitHubLogin("foobar"), subscription.Subscription(redis_cache, 0, False, "", frozenset()), client, redis_cache, ) repository = context.Repository( installation, github_types.GitHubRepositoryName("xyz"), github_types.GitHubRepositoryIdType(0), ) config_file = await repository.get_mergify_config_file() assert config_file is not None get_mergify_config(config_file)
async def test_init(redis_cache): subscription.Subscription( redis_cache, 123, True, "friend", frozenset({subscription.Features.PRIVATE_REPOSITORY}), )
async def prepare_context(client, redis_cache, subscribed=True): sub = subscription.Subscription( redis_cache, 123, subscribed, "sub or not to sub", frozenset( getattr(subscription.Features, f) for f in subscription.Features.__members__) if subscribed else frozenset(), ) installation = context.Installation(123, "Mergifyio", sub, client, redis_cache) repository = context.Repository(installation, "demo", 123) return await context.Context.create( repository, { "id": 12345, "number": 123, "state": None, "mergeable_state": "ok", "merged_by": None, "merged": None, "merged_at": None, "user": { "login": "******" }, "requested_reviewers": [{ "login": "******" }, { "login": "******" }], "requested_teams": [{ "slug": "foobar" }, { "slug": "foobaz" }], "base": { "sha": "sha", "ref": "main", "user": { "login": { "Mergifyio", }, }, "repo": { "full_name": "Mergifyio/demo", "name": "demo", "private": False, "owner": { "login": "******", }, }, }, }, )
async def fake_retrieve_subscription_from_db(redis_cache, owner_id): if owner_id == config.TESTING_ORGANIZATION_ID: return self.subscription return subscription.Subscription( redis_cache, owner_id, False, "We're just testing", set(), )
async def fake_subscription(redis_cache, owner_id): if owner_id == config.TESTING_ORGANIZATION_ID: return await real_get_subscription(redis_cache, owner_id) return subscription.Subscription( redis_cache, owner_id, False, "We're just testing", set(), )
async def _create_context(redis_cache, client): sub = subscription.Subscription( redis_cache, 123, True, "", {}, frozenset({}), ) installation = context.Installation(123, "Mergifyio", sub, client, redis_cache) repository = context.Repository(installation, "demo", 123) return await context.Context.create( repository, { "number": 789, "state": "open", "title": "Amazing new feature", "user": { "login": "******", "id": 1, }, "mergeable_state": "ok", "merged_by": None, "merged": None, "merged_at": None, "base": { "sha": "sha", "ref": "main", "user": { "login": { "Mergifyio", }, }, "repo": { "name": "demo", "private": False, "owner": { "login": "******", "id": 123, }, "permissions": { "admin": False, "push": False, "pull": True, }, }, }, }, [], )
def test_dict(): owner_id = 1234 sub = subscription.Subscription( owner_id, True, "friend", {}, frozenset({subscription.Features.PRIVATE_REPOSITORY}), ) assert sub.from_dict(owner_id, sub.to_dict()) == sub
async def test_dict(redis_cache): owner_id = 1234 sub = subscription.Subscription( redis_cache, owner_id, True, "friend", frozenset({subscription.Features.PRIVATE_REPOSITORY}), ) assert sub.from_dict(redis_cache, owner_id, sub.to_dict(), -2) == sub
async def test_from_dict_unknown_features(redis_cache): assert (subscription.Subscription.from_dict( redis_cache, 123, { "subscription_active": True, "subscription_reason": "friend", "features": ["unknown feature"], }, ) == subscription.Subscription(redis_cache, 123, True, "friend", frozenset(), -2))
async def test_queue_summary(redis_cache): repository = mock.Mock( get_pull_request_context=mock.AsyncMock( return_value=mock.Mock(pull={"title": "foo"}) ) ) ctxt = mock.Mock( repository=repository, subscription=subscription.Subscription( redis_cache, 123, True, "We're just testing", frozenset({subscription.Features.PRIORITY_QUEUES}), ), ) ctxt.missing_feature_reason = subscription.Subscription.missing_feature_reason ctxt.pull = { "base": { "repo": { "owner": { "login": "******", }, }, }, } q = mock.AsyncMock(installation_id=12345) q.get_pulls.return_value = [1, 2, 3, 4, 5, 6, 7, 8, 9] q.get_config.side_effect = gen_config( [4000, 3000, 3000, 3000, 2000, 2000, 1000, 1000, 1000] ) with mock.patch.object(merge.naive.Queue, "from_context", return_value=q): action = merge.MergeAction(voluptuous.Schema(merge.MergeAction.validator)({})) assert """**Required conditions for merge:** **The following pull requests are queued:** | | Pull request | Priority | | ---: | :--- | :--- | | 1 | foo #1 | 4000 | | 2 | foo #2 | high | | 3 | foo #3 | high | | 4 | foo #4 | high | | 5 | foo #5 | medium | | 6 | foo #6 | medium | | 7 | foo #7 | low | | 8 | foo #8 | low | | 9 | foo #9 | low | --- """ + constants.MERGIFY_PULL_REQUEST_DOC == await action._get_queue_summary( ctxt, mock.Mock(missing_conditions=[], conditions=[]), q )
def repository(redis_cache, fake_client): installation = context.Installation( github_types.GitHubAccountIdType(123), github_types.GitHubLogin("user"), subscription.Subscription(redis_cache, 0, False, "", frozenset()), fake_client, redis_cache, ) return context.Repository( installation, github_types.GitHubRepositoryName("name"), github_types.GitHubRepositoryIdType(123), )
async def test_active_feature(redis_cache): sub = subscription.Subscription( redis_cache, 123, True, "friend", frozenset(), ) assert sub.has_feature(subscription.Features.PRIORITY_QUEUES) is False sub = subscription.Subscription( redis_cache, 123, False, "friend", frozenset([subscription.Features.PRIORITY_QUEUES]), ) assert sub.has_feature(subscription.Features.PRIORITY_QUEUES) is False sub = subscription.Subscription( redis_cache, 123, True, "friend", frozenset([subscription.Features.PRIORITY_QUEUES]), ) assert sub.has_feature(subscription.Features.PRIORITY_QUEUES) is True sub = subscription.Subscription.from_dict( redis_cache, 123, { "subscription_active": True, "subscription_reason": "friend", "features": ["private_repository", "large_repository"], }, ) assert sub.has_feature(subscription.Features.PRIVATE_REPOSITORY) is True assert sub.has_feature(subscription.Features.LARGE_REPOSITORY) is True assert sub.has_feature(subscription.Features.PRIORITY_QUEUES) is False
async def test_save_sub(features, redis_cache): owner_id = 1234 sub = subscription.Subscription( redis_cache, owner_id, True, "friend", frozenset(features), ) await sub._save_subscription_to_cache() rsub = await subscription.Subscription._retrieve_subscription_from_cache( redis_cache, owner_id) assert rsub == sub
def test_queue_summary_subscription(active, summary): ctxt = mock.Mock(subscription=subscription.Subscription( 123, active, "We're just testing", {}, frozenset({subscription.Features.PRIORITY_QUEUES}), )) q = mock.Mock(installation_id=12345) q.get_pulls.return_value = [1, 2, 3, 4, 5, 6, 7, 8, 9] q.get_config.side_effect = gen_config( [4000, 3000, 3000, 3000, 2000, 2000, 1000, 1000, 1000]) with mock.patch.object(helpers.queue.Queue, "from_context", return_value=q): assert summary == helpers.get_queue_summary(ctxt)
async def test_subscription_db_unavailable(retrieve_subscription_from_db_mock, redis_cache): owner_id = 1234 sub = subscription.Subscription(redis_cache, owner_id, True, "friend", frozenset()) retrieve_subscription_from_db_mock.return_value = sub # no cache, no db -> reraise retrieve_subscription_from_db_mock.side_effect = http.HTTPServiceUnavailable( "boom!", response=mock.Mock(), request=mock.Mock()) with pytest.raises(http.HTTPServiceUnavailable): await subscription.Subscription.get_subscription(redis_cache, owner_id) retrieve_subscription_from_db_mock.assert_called_once() # no cache, but db -> got db sub retrieve_subscription_from_db_mock.reset_mock() retrieve_subscription_from_db_mock.side_effect = None rsub = await subscription.Subscription.get_subscription( redis_cache, owner_id) assert sub == rsub retrieve_subscription_from_db_mock.assert_called_once() # cache not expired and not db -> got cached sub retrieve_subscription_from_db_mock.reset_mock() rsub = await subscription.Subscription.get_subscription( redis_cache, owner_id) sub.ttl = 259200 assert rsub == sub retrieve_subscription_from_db_mock.assert_not_called() # cache expired and not db -> got cached sub retrieve_subscription_from_db_mock.reset_mock() retrieve_subscription_from_db_mock.side_effect = http.HTTPServiceUnavailable( "boom!", response=mock.Mock(), request=mock.Mock()) await redis_cache.expire(f"subscription-cache-owner-{owner_id}", 7200) rsub = await subscription.Subscription.get_subscription( redis_cache, owner_id) sub.ttl = 7200 assert rsub == sub retrieve_subscription_from_db_mock.assert_called_once() # cache expired and unexpected db issue -> reraise retrieve_subscription_from_db_mock.reset_mock() retrieve_subscription_from_db_mock.side_effect = Exception("WTF") await redis_cache.expire(f"subscription-cache-owner-{owner_id}", 7200) with pytest.raises(Exception): await subscription.Subscription.get_subscription(redis_cache, owner_id) retrieve_subscription_from_db_mock.assert_called_once()
def test_from_dict_unknown_features(): assert subscription.Subscription.from_dict( 123, { "subscription_active": True, "subscription_reason": "friend", "tokens": {}, "features": ["unknown feature"], }, ) == subscription.Subscription( 123, True, "friend", {}, frozenset(), )
def test_checks_feature_disabled(self): self.subscription = subscription.Subscription( config.INSTALLATION_ID, self.SUBSCRIPTION_ACTIVE, "You're not nice", {"mergify-test1": config.ORG_ADMIN_GITHUB_APP_OAUTH_TOKEN}, frozenset( getattr(subscription.Features, f) for f in subscription.Features.__members__ if f is not subscription.Features.CUSTOM_CHECKS) if self.SUBSCRIPTION_ACTIVE else frozenset(), ) loop = asyncio.get_event_loop() loop.run_until_complete(self.subscription.save_subscription_to_cache()) rules = { "pull_request_rules": [{ "name": "body need sentry ticket", "conditions": [ f"base={self.master_branch_name}", "#title>10", "#title<50", "#body<4096", "#files<100", "body~=(?m)^(Fixes|Related|Closes) (MERGIFY-ENGINE|MRGFY)-", "-label=ignore-guideline", ], "actions": { "post_check": {} }, }] } self.setup_repo(yaml.dump(rules)) p, _ = self.create_pr() self.run_engine() p.update() ctxt = context.Context(self.cli_integration, p.raw_data, {}) sorted_checks = list( sorted(ctxt.pull_engine_check_runs, key=operator.itemgetter("name"))) assert len(sorted_checks) == 2 check = sorted_checks[0] assert "action_required" == check["conclusion"] assert "Custom checks are disabled" == check["output"]["title"]
async def test_checks_feature_disabled(self): self.subscription = subscription.Subscription( utils.create_aredis_for_cache(max_idle_time=0), config.INSTALLATION_ID, self.SUBSCRIPTION_ACTIVE, "You're not nice", frozenset( getattr(subscription.Features, f) for f in subscription.Features.__members__ if f is not subscription.Features.CUSTOM_CHECKS ) if self.SUBSCRIPTION_ACTIVE else frozenset(), ) await self.subscription.save_subscription_to_cache() rules = { "pull_request_rules": [ { "name": "body need sentry ticket", "conditions": [ f"base={self.master_branch_name}", "#title>10", "#title<50", "#body<4096", "#files<100", "body~=(?m)^(Fixes|Related|Closes) (MERGIFY-ENGINE|MRGFY)-", "-label=ignore-guideline", ], "actions": {"post_check": {}}, } ] } await self.setup_repo(yaml.dump(rules)) p, _ = await self.create_pr() await self.run_engine() p = await self.get_pull(p["number"]) ctxt = await context.Context.create(self.repository_ctxt, p, []) sorted_checks = sorted( await ctxt.pull_engine_check_runs, key=operator.itemgetter("name") ) assert len(sorted_checks) == 2 check = sorted_checks[0] assert "action_required" == check["conclusion"] assert "Custom checks are disabled" == check["output"]["title"]
def test_disabled(): action = request_reviews.RequestReviewsAction.get_schema()( { "random_count": 2, "teams": { "foobar": 2, "foobaz": 1, }, "users": { "jd": 2, "sileht": 1, }, }, ) client = mock.MagicMock() client.auth.installation.__getitem__.return_value = 123 sub = subscription.Subscription( 123, False, "No sub", {}, frozenset({}), ) ctxt = context.Context( client, { "number": 123, "state": None, "mergeable_state": "ok", "merged_by": None, "merged": None, "merged_at": None, }, sub, ) assert action.run(ctxt, None, None) == ( "action_required", "Random request reviews are disabled", "⚠ The [subscription](https://dashboard.mergify.io/installation/123/subscription) needed to be updated to enable them.", )
def repository(redis_cache, fake_client): gh_owner = github_types.GitHubAccount({ "login": github_types.GitHubLogin("user"), "id": github_types.GitHubAccountIdType(0), "type": "User", "avatar_url": "", }) gh_repo = github_types.GitHubRepository({ "full_name": "user/name", "name": github_types.GitHubRepositoryName("name"), "private": False, "id": github_types.GitHubRepositoryIdType(0), "owner": gh_owner, "archived": False, "url": "", "html_url": "", "default_branch": github_types.GitHubRefType("ref"), }) installation = context.Installation( github_types.GitHubAccountIdType(123), github_types.GitHubLogin("user"), subscription.Subscription(redis_cache, 0, False, "", frozenset()), fake_client, redis_cache, ) return context.Repository(installation, gh_repo)
async def test_get_commits_to_cherry_pick_rebase( commits: mock.PropertyMock, redis_cache: utils.RedisCache, ) -> None: gh_owner = github_types.GitHubAccount({ "login": github_types.GitHubLogin("user"), "id": github_types.GitHubAccountIdType(0), "type": "User", "avatar_url": "", }) gh_repo = github_types.GitHubRepository({ "full_name": "user/name", "name": github_types.GitHubRepositoryName("name"), "private": False, "id": github_types.GitHubRepositoryIdType(0), "owner": gh_owner, "archived": False, "url": "", "html_url": "", "default_branch": github_types.GitHubRefType("ref"), }) c1 = github_types.GitHubBranchCommit({ "sha": github_types.SHAType("c1f"), "parents": [], "commit": { "message": "foobar" }, }) c2 = github_types.GitHubBranchCommit({ "sha": github_types.SHAType("c2"), "parents": [c1], "commit": { "message": "foobar" }, }) commits.return_value = [c1, c2] client = mock.Mock() client.auth.get_access_token.return_value = "<token>" client.items.side_effect = fake_get_github_pulls_from_sha installation = context.Installation( github_types.GitHubAccountIdType(123), github_types.GitHubLogin("user"), subscription.Subscription(redis_cache, 0, False, "", frozenset()), client, redis_cache, ) repository = context.Repository(installation, gh_repo) ctxt = await context.Context.create( repository, { "labels": [], "draft": False, "merge_commit_sha": github_types.SHAType(""), "title": "", "commits": 1, "rebaseable": False, "maintainer_can_modify": False, "id": github_types.GitHubPullRequestId(0), "number": github_types.GitHubPullRequestNumber(6), "merged": True, "state": "closed", "html_url": "<html_url>", "changed_files": 1, "base": { "label": "", "sha": github_types.SHAType("sha"), "user": { "login": github_types.GitHubLogin("user"), "id": github_types.GitHubAccountIdType(0), "type": "User", "avatar_url": "", }, "ref": github_types.GitHubRefType("ref"), "repo": { "full_name": "user/ref", "name": github_types.GitHubRepositoryName("name"), "private": False, "id": github_types.GitHubRepositoryIdType(0), "owner": { "login": github_types.GitHubLogin("user"), "id": github_types.GitHubAccountIdType(0), "type": "User", "avatar_url": "", }, "archived": False, "url": "", "html_url": "", "default_branch": github_types.GitHubRefType(""), }, }, "head": { "label": "", "sha": github_types.SHAType("sha"), "ref": github_types.GitHubRefType("fork"), "user": { "login": github_types.GitHubLogin("user"), "id": github_types.GitHubAccountIdType(0), "type": "User", "avatar_url": "", }, "repo": { "full_name": "fork/other", "name": github_types.GitHubRepositoryName("other"), "private": False, "archived": False, "url": "", "html_url": "", "default_branch": github_types.GitHubRefType(""), "id": github_types.GitHubRepositoryIdType(0), "owner": { "login": github_types.GitHubLogin("user"), "id": github_types.GitHubAccountIdType(0), "type": "User", "avatar_url": "", }, }, }, "user": { "login": github_types.GitHubLogin("user"), "id": github_types.GitHubAccountIdType(0), "type": "User", "avatar_url": "", }, "merged_by": None, "merged_at": None, "mergeable_state": "clean", }, ) base_branch = github_types.GitHubBranchCommitParent( {"sha": github_types.SHAType("base_branch")}) rebased_c1 = github_types.GitHubBranchCommit({ "sha": github_types.SHAType("rebased_c1"), "parents": [base_branch], "commit": { "message": "hello c1" }, }) rebased_c2 = github_types.GitHubBranchCommit({ "sha": github_types.SHAType("rebased_c2"), "parents": [rebased_c1], "commit": { "message": "hello c2" }, }) async def fake_get_github_commit_from_sha(url, api_version=None): if url.endswith("/commits/rebased_c1"): return rebased_c1 if url.endswith("/commits/rebased_c2"): return rebased_c2 raise RuntimeError(f"Unknown URL {url}") client.item.side_effect = fake_get_github_commit_from_sha assert await duplicate_pull._get_commits_to_cherrypick(ctxt, rebased_c2) == [ rebased_c1, rebased_c2, ]
async def test_get_commits_to_cherry_pick_merge( commits: mock.PropertyMock, redis_cache: utils.RedisCache, ) -> None: c1 = github_types.GitHubBranchCommit({ "sha": github_types.SHAType("c1f"), "parents": [], "commit": { "message": "foobar" }, }) c2 = github_types.GitHubBranchCommit({ "sha": github_types.SHAType("c2"), "parents": [c1], "commit": { "message": "foobar" }, }) async def fake_commits(): return [c1, c2] commits.return_value = fake_commits() client = mock.Mock() client.auth.get_access_token.return_value = "<token>" gh_owner = github_types.GitHubAccount({ "login": github_types.GitHubLogin("user"), "id": github_types.GitHubAccountIdType(0), "type": "User", "avatar_url": "", }) gh_repo = github_types.GitHubRepository({ "full_name": "user/name", "name": github_types.GitHubRepositoryName("name"), "private": False, "id": github_types.GitHubRepositoryIdType(0), "owner": gh_owner, "archived": False, "url": "", "html_url": "", "default_branch": github_types.GitHubRefType("ref"), }) installation = context.Installation( github_types.GitHubAccountIdType(123), github_types.GitHubLogin("user"), subscription.Subscription(redis_cache, 0, False, "", frozenset()), client, redis_cache, ) repository = context.Repository(installation, gh_repo) ctxt = await context.Context.create( repository, { "number": github_types.GitHubPullRequestNumber(6), "commits": 1, "merged": True, "state": "closed", "html_url": "<html_url>", "id": github_types.GitHubPullRequestId(0), "maintainer_can_modify": True, "labels": [], "rebaseable": True, "draft": True, "merge_commit_sha": None, "title": "foobar", "changed_files": 1, "base": { "label": "user:ref", "sha": github_types.SHAType("sha"), "ref": github_types.GitHubRefType("ref"), "user": gh_owner, "repo": github_types.GitHubRepository( { "full_name": "user/ref", "name": github_types.GitHubRepositoryName("name"), "private": False, "id": github_types.GitHubRepositoryIdType(0), "owner": gh_owner, "archived": False, "url": "", "html_url": "", "default_branch": github_types.GitHubRefType("ref"), }), }, "head": { "label": "user:ref", "sha": github_types.SHAType("sha"), "user": gh_owner, "ref": github_types.GitHubRefType("fork"), "repo": github_types.GitHubRepository( { "full_name": "fork/other", "name": github_types.GitHubRepositoryName("name"), "private": False, "id": github_types.GitHubRepositoryIdType(0), "owner": gh_owner, "archived": False, "url": "", "html_url": "", "default_branch": github_types.GitHubRefType("ref"), }), }, "user": gh_owner, "merged_at": None, "merged_by": None, "mergeable_state": "clean", }, ) base_branch = github_types.GitHubBranchCommit({ "sha": github_types.SHAType("base_branch"), "parents": [], "commit": { "message": "foobar" }, }) merge_commit = github_types.GitHubBranchCommit({ "sha": github_types.SHAType("merge_commit"), "parents": [base_branch, c2], "commit": { "message": "foobar" }, }) assert await duplicate_pull._get_commits_to_cherrypick(ctxt, merge_commit) == [ c1, c2, ]
async def test_signals(redis_cache): gh_owner = github_types.GitHubAccount({ "login": github_types.GitHubLogin("user"), "id": github_types.GitHubAccountIdType(0), "type": "User", "avatar_url": "", }) gh_repo = github_types.GitHubRepository({ "archived": False, "url": "", "default_branch": github_types.GitHubRefType(""), "id": github_types.GitHubRepositoryIdType(456), "full_name": "user/ref", "name": github_types.GitHubRepositoryName("name"), "private": False, "owner": gh_owner, }) client = mock.AsyncMock() client.auth.get_access_token.return_value = "<token>" sub = subscription.Subscription(redis_cache, 0, False, "", frozenset()) installation = context.Installation( gh_owner["id"], gh_owner["login"], sub, client, redis_cache, ) repository = context.Repository(installation, gh_repo["name"], gh_repo["id"]) ctxt = await context.Context.create( repository, { "title": "", "id": github_types.GitHubPullRequestId(0), "maintainer_can_modify": False, "rebaseable": False, "draft": False, "merge_commit_sha": None, "labels": [], "number": github_types.GitHubPullRequestNumber(6), "commits": 1, "merged": True, "state": "closed", "changed_files": 1, "html_url": "<html_url>", "base": { "label": "", "sha": github_types.SHAType("sha"), "user": { "login": github_types.GitHubLogin("user"), "id": github_types.GitHubAccountIdType(0), "type": "User", "avatar_url": "", }, "ref": github_types.GitHubRefType("ref"), "label": "", "repo": gh_repo, }, "head": { "label": "", "sha": github_types.SHAType("old-sha-one"), "ref": github_types.GitHubRefType("fork"), "user": { "login": github_types.GitHubLogin("user"), "id": github_types.GitHubAccountIdType(0), "type": "User", "avatar_url": "", }, "repo": { "archived": False, "url": "", "default_branch": github_types.GitHubRefType(""), "id": github_types.GitHubRepositoryIdType(123), "full_name": "fork/other", "name": github_types.GitHubRepositoryName("other"), "private": False, "owner": { "login": github_types.GitHubLogin("user"), "id": github_types.GitHubAccountIdType(0), "type": "User", "avatar_url": "", }, }, }, "user": { "login": github_types.GitHubLogin("user"), "id": github_types.GitHubAccountIdType(0), "type": "User", "avatar_url": "", }, "merged_by": None, "merged_at": None, "mergeable_state": "clean", }, ) assert len(signals.SIGNALS) == 0 signals.setup() assert len(signals.SIGNALS) == 1 with mock.patch( "mergify_engine_signals.noop.Signal.__call__") as signal_method: await signals.send(ctxt, "action.update") signal_method.assert_called_once_with(ctxt, "action.update")
def setUp(self): super(FunctionalTestBase, self).setUp() self.existing_labels = [] self.pr_counter = 0 self.git_counter = 0 self.cassette_library_dir = os.path.join(CASSETTE_LIBRARY_DIR_BASE, self.__class__.__name__, self._testMethodName) # Recording stuffs if RECORD: if os.path.exists(self.cassette_library_dir): shutil.rmtree(self.cassette_library_dir) os.makedirs(self.cassette_library_dir) self.recorder = vcr.VCR( cassette_library_dir=self.cassette_library_dir, record_mode="all" if RECORD else "none", match_on=["method", "uri"], filter_headers=[ ("Authorization", "<TOKEN>"), ("X-Hub-Signature", "<SIGNATURE>"), ("User-Agent", None), ("Accept-Encoding", None), ("Connection", None), ], before_record_response=self.response_filter, custom_patches=((pygithub.MainClass, "HTTPSConnection", vcr.stubs.VCRHTTPSConnection), ), ) if RECORD: github.CachedToken.STORAGE = {} else: # Never expire token during replay mock.patch.object(github_app, "get_or_create_jwt", return_value="<TOKEN>").start() mock.patch.object( github.GithubAppInstallationAuth, "get_access_token", return_value="<TOKEN>", ).start() # NOTE(sileht): httpx pyvcr stubs does not replay auth_flow as it directly patch client.send() # So anything occurring during auth_flow have to be mocked during replay def get_auth(owner=None, auth=None): if auth is None: auth = github.get_auth(owner) auth.installation = { "id": config.INSTALLATION_ID, } auth.permissions_need_to_be_updated = False auth.owner_id = config.TESTING_ORGANIZATION_ID return auth async def github_aclient(owner=None, auth=None): return github.AsyncGithubInstallationClient( get_auth(owner, auth)) def github_client(owner=None, auth=None): return github.GithubInstallationClient(get_auth(owner, auth)) mock.patch.object(github, "get_client", github_client).start() mock.patch.object(github, "aget_client", github_aclient).start() with open(engine.mergify_rule_path, "r") as f: engine.MERGIFY_RULE = yaml.safe_load(f.read().replace( "mergify[bot]", "mergify-test[bot]")) mock.patch.object(branch_updater.utils, "Gitter", self.get_gitter).start() mock.patch.object(duplicate_pull.utils, "Gitter", self.get_gitter).start() if not RECORD: # NOTE(sileht): Don't wait exponentialy during replay mock.patch.object(context.Context._ensure_complete.retry, "wait", None).start() # Web authentification always pass mock.patch("hmac.compare_digest", return_value=True).start() branch_prefix_path = os.path.join(self.cassette_library_dir, "branch_prefix") if RECORD: self.BRANCH_PREFIX = datetime.datetime.utcnow().strftime( "%Y%m%d%H%M%S") with open(branch_prefix_path, "w") as f: f.write(self.BRANCH_PREFIX) else: with open(branch_prefix_path, "r") as f: self.BRANCH_PREFIX = f.read() self.master_branch_name = self.get_full_branch_name("master") self.git = self.get_gitter(LOG) self.addCleanup(self.git.cleanup) self.loop = asyncio.get_event_loop() self.loop.run_until_complete(web.startup()) self.app = testclient.TestClient(web.app) # NOTE(sileht): Prepare a fresh redis self.redis_stream = redis.StrictRedis.from_url(config.STREAM_URL, decode_responses=False) self.redis_stream.flushall() self.redis_cache = utils.get_redis_for_cache() self.redis_cache.flushall() self.subscription = subscription.Subscription( config.INSTALLATION_ID, self.SUBSCRIPTION_ACTIVE, "You're not nice", { "mergify-test1": config.ORG_ADMIN_GITHUB_APP_OAUTH_TOKEN, "mergify-test3": config.ORG_USER_PERSONAL_TOKEN, }, frozenset( getattr(subscription.Features, f) for f in subscription.Features.__members__) if self.SUBSCRIPTION_ACTIVE else frozenset(), ) self.loop.run_until_complete( self.subscription.save_subscription_to_cache()) # Let's start recording cassette = self.recorder.use_cassette("http.json") cassette.__enter__() self.addCleanup(cassette.__exit__) integration = pygithub.GithubIntegration(config.INTEGRATION_ID, config.PRIVATE_KEY) self.installation_token = integration.get_access_token( config.INSTALLATION_ID).token base_url = config.GITHUB_API_URL self.g_integration = pygithub.Github(self.installation_token, base_url=base_url) self.g_admin = pygithub.Github(config.ORG_ADMIN_PERSONAL_TOKEN, base_url=base_url) self.g_fork = pygithub.Github(self.FORK_PERSONAL_TOKEN, base_url=base_url) self.o_admin = self.g_admin.get_organization( config.TESTING_ORGANIZATION) self.o_integration = self.g_integration.get_organization( config.TESTING_ORGANIZATION) self.u_fork = self.g_fork.get_user() assert self.o_admin.login == "mergifyio-testing" assert self.o_integration.login == "mergifyio-testing" assert self.u_fork.login in ["mergify-test2", "mergify-test3"] self.r_o_admin = self.o_admin.get_repo(self.REPO_NAME) self.r_o_integration = self.o_integration.get_repo(self.REPO_NAME) self.r_fork = self.u_fork.get_repo(self.REPO_NAME) self.url_main = f"{config.GITHUB_URL}/{self.r_o_integration.full_name}" self.url_fork = ( f"{config.GITHUB_URL}/{self.u_fork.login}/{self.r_o_integration.name}" ) self.cli_integration = github.get_client(config.TESTING_ORGANIZATION, ) real_get_subscription = subscription.Subscription.get_subscription async def fake_retrieve_subscription_from_db(owner_id): if owner_id == config.TESTING_ORGANIZATION_ID: return self.subscription return subscription.Subscription( owner_id, False, "We're just testing", {}, set(), ) async def fake_subscription(owner_id): if owner_id == config.TESTING_ORGANIZATION_ID: return await real_get_subscription(owner_id) return subscription.Subscription( owner_id, False, "We're just testing", {}, set(), ) mock.patch( "mergify_engine.subscription.Subscription._retrieve_subscription_from_db", side_effect=fake_retrieve_subscription_from_db, ).start() mock.patch( "mergify_engine.subscription.Subscription.get_subscription", side_effect=fake_subscription, ).start() mock.patch( "github.MainClass.Installation.Installation.get_repos", return_value=[self.r_o_integration], ).start() self._event_reader = EventReader(self.app) self._event_reader.drain() self.redis_stream.flushall()
async def asyncSetUp(self): super(FunctionalTestBase, self).setUp() self.existing_labels: typing.List[str] = [] self.protected_branches: typing.Set[str] = set() self.pr_counter: int = 0 self.git_counter: int = 0 self.cassette_library_dir = os.path.join(CASSETTE_LIBRARY_DIR_BASE, self.__class__.__name__, self._testMethodName) # Recording stuffs if RECORD: if os.path.exists(self.cassette_library_dir): shutil.rmtree(self.cassette_library_dir) os.makedirs(self.cassette_library_dir) self.recorder = vcr.VCR( cassette_library_dir=self.cassette_library_dir, record_mode="all" if RECORD else "none", match_on=["method", "uri"], ignore_localhost=True, filter_headers=[ ("Authorization", "<TOKEN>"), ("X-Hub-Signature", "<SIGNATURE>"), ("User-Agent", None), ("Accept-Encoding", None), ("Connection", None), ], before_record_response=self.response_filter, ) if RECORD: github.CachedToken.STORAGE = {} else: # Never expire token during replay mock.patch.object(github_app, "get_or_create_jwt", return_value="<TOKEN>").start() mock.patch.object( github.GithubAppInstallationAuth, "get_access_token", return_value="<TOKEN>", ).start() # NOTE(sileht): httpx pyvcr stubs does not replay auth_flow as it directly patch client.send() # So anything occurring during auth_flow have to be mocked during replay def get_auth(owner_name=None, owner_id=None, auth=None): if auth is None: auth = github.get_auth(owner_name, owner_id) auth.installation = { "id": config.INSTALLATION_ID, } auth.permissions_need_to_be_updated = False auth.owner_id = config.TESTING_ORGANIZATION_ID auth.owner = config.TESTING_ORGANIZATION return auth def github_aclient(owner_name=None, owner_id=None, auth=None): return github.AsyncGithubInstallationClient( get_auth(owner_name, owner_id, auth)) mock.patch.object(github, "aget_client", github_aclient).start() mock.patch.object(branch_updater.gitter, "Gitter", self.get_gitter).start() mock.patch.object(duplicate_pull.gitter, "Gitter", self.get_gitter).start() if not RECORD: # NOTE(sileht): Don't wait exponentialy during replay mock.patch.object(context.Context._ensure_complete.retry, "wait", None).start() # Web authentification always pass mock.patch("hmac.compare_digest", return_value=True).start() branch_prefix_path = os.path.join(self.cassette_library_dir, "branch_prefix") if RECORD: self.BRANCH_PREFIX = datetime.datetime.utcnow().strftime( "%Y%m%d%H%M%S") with open(branch_prefix_path, "w") as f: f.write(self.BRANCH_PREFIX) else: with open(branch_prefix_path, "r") as f: self.BRANCH_PREFIX = f.read() self.master_branch_name = self.get_full_branch_name("master") self.git = self.get_gitter(LOG) await self.git.init() self.addAsyncCleanup(self.git.cleanup) await root.startup() self.app = httpx.AsyncClient(app=root.app, base_url="http://localhost") await self.clear_redis_cache() self.redis_cache = utils.create_aredis_for_cache(max_idle_time=0) self.subscription = subscription.Subscription( self.redis_cache, config.TESTING_ORGANIZATION_ID, self.SUBSCRIPTION_ACTIVE, "You're not nice", frozenset( getattr(subscription.Features, f) for f in subscription.Features.__members__) if self.SUBSCRIPTION_ACTIVE else frozenset(), ) await self.subscription._save_subscription_to_cache() self.user_tokens = user_tokens.UserTokens( self.redis_cache, config.TESTING_ORGANIZATION_ID, { "mergify-test1": config.ORG_ADMIN_GITHUB_APP_OAUTH_TOKEN, "mergify-test3": config.ORG_USER_PERSONAL_TOKEN, }, ) await self.user_tokens.save_to_cache() # Let's start recording cassette = self.recorder.use_cassette("http.json") cassette.__enter__() self.addCleanup(cassette.__exit__) self.client_integration = github.aget_client( config.TESTING_ORGANIZATION, config.TESTING_ORGANIZATION_ID) self.client_admin = github.AsyncGithubInstallationClient( auth=github.GithubTokenAuth(token=config.ORG_ADMIN_PERSONAL_TOKEN)) self.client_fork = github.AsyncGithubInstallationClient( auth=github.GithubTokenAuth(token=self.FORK_PERSONAL_TOKEN)) self.addAsyncCleanup(self.client_integration.aclose) self.addAsyncCleanup(self.client_admin.aclose) self.addAsyncCleanup(self.client_fork.aclose) await self.client_admin.item("/user") await self.client_fork.item("/user") if RECORD: assert self.client_admin.auth.owner == "mergify-test1" assert self.client_fork.auth.owner == "mergify-test2" else: self.client_admin.auth.owner = "mergify-test1" self.client_fork.auth.owner = "mergify-test2" self.url_main = f"/repos/mergifyio-testing/{self.REPO_NAME}" self.url_fork = f"/repos/{self.client_fork.auth.owner}/{self.REPO_NAME}" self.git_main = f"{config.GITHUB_URL}/mergifyio-testing/{self.REPO_NAME}" self.git_fork = ( f"{config.GITHUB_URL}/{self.client_fork.auth.owner}/{self.REPO_NAME}" ) self.installation_ctxt = context.Installation( config.TESTING_ORGANIZATION_ID, config.TESTING_ORGANIZATION, self.subscription, self.client_integration, self.redis_cache, ) self.repository_ctxt = context.Repository(self.installation_ctxt, self.REPO_NAME, self.REPO_ID) real_get_subscription = subscription.Subscription.get_subscription async def fake_retrieve_subscription_from_db(redis_cache, owner_id): if owner_id == config.TESTING_ORGANIZATION_ID: return self.subscription return subscription.Subscription( redis_cache, owner_id, False, "We're just testing", set(), ) async def fake_subscription(redis_cache, owner_id): if owner_id == config.TESTING_ORGANIZATION_ID: return await real_get_subscription(redis_cache, owner_id) return subscription.Subscription( redis_cache, owner_id, False, "We're just testing", set(), ) mock.patch( "mergify_engine.subscription.Subscription._retrieve_subscription_from_db", side_effect=fake_retrieve_subscription_from_db, ).start() mock.patch( "mergify_engine.subscription.Subscription.get_subscription", side_effect=fake_subscription, ).start() async def fake_retrieve_user_tokens_from_db(redis_cache, owner_id): if owner_id == config.TESTING_ORGANIZATION_ID: return self.user_tokens return user_tokens.UserTokens(redis_cache, owner_id, {}) real_get_user_tokens = user_tokens.UserTokens.get async def fake_user_tokens(redis_cache, owner_id): if owner_id == config.TESTING_ORGANIZATION_ID: return await real_get_user_tokens(redis_cache, owner_id) return user_tokens.UserTokens(redis_cache, owner_id, {}) mock.patch( "mergify_engine.user_tokens.UserTokens._retrieve_from_db", side_effect=fake_retrieve_user_tokens_from_db, ).start() mock.patch( "mergify_engine.user_tokens.UserTokens.get", side_effect=fake_user_tokens, ).start() self._event_reader = EventReader(self.app) await self._event_reader.drain() # NOTE(sileht): Prepare a fresh redis await self.clear_redis_stream()