def test_view_client(self): user = utils.get_test_user() with self.settings(INSTALLED_GITSERVERS=[utils.github_config(authorized_users=[])]): url = reverse('ci:view_client', args=[1000,]) response = self.client.get(url) self.assertEqual(response.status_code, 404) client = utils.create_client() # not logged in url = reverse('ci:view_client', args=[client.pk,]) response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertContains(response, "You are not allowed") # logged in but not on the authorized list utils.simulate_login(self.client.session, user) response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertContains(response, "You are not allowed") with self.settings(INSTALLED_GITSERVERS=[utils.github_config(authorized_users=[user.name])]): # logged in and on the authorized list response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertNotContains(response, "You are not allowed") # Should be cached response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertNotContains(response, "You are not allowed")
def test_add_comment(self, mock_post): j = utils.create_job() j.event.cause = models.Event.PUSH j.event.save() api = j.event.build_user.api() # wrong cause UpdateRemoteStatus.add_comment(api, j.event.build_user, j) self.assertEqual(mock_post.call_count, 0) j.event.cause = models.Event.PULL_REQUEST j.event.save() # no comments_url UpdateRemoteStatus.add_comment(api, j.event.build_user, j) self.assertEqual(mock_post.call_count, 0) j.event.comments_url = 'url' j.event.save() with self.settings(INSTALLED_GITSERVERS=[ utils.github_config(post_job_status=False, remote_update=True) ]): # not posting job status UpdateRemoteStatus.add_comment(api, j.event.build_user, j) self.assertEqual(mock_post.call_count, 0) self.assertEqual(api._errors, []) with self.settings(INSTALLED_GITSERVERS=[ utils.github_config(post_job_status=True, remote_update=True) ]): # OK api = j.event.build_user.api() UpdateRemoteStatus.add_comment(api, j.event.build_user, j) self.assertEqual(mock_post.call_count, 1)
def test_check_automerge(self, mock_get): mock_get.return_value = utils.Response() e0 = utils.create_event(cause=models.Event.PUSH) e0.cause = models.Event.PULL_REQUEST e0.status = models.JobStatus.SUCCESS e0.pull_request = utils.create_pr() e0.save() with self.settings( INSTALLED_GITSERVERS=[utils.github_config( remote_update=True)]): # Not configured for automerge UpdateRemoteStatus.check_automerge(e0) self.assertEqual(mock_get.call_count, 0) auto_merge_settings = { "auto_merge_label": "Auto Merge", "auto_merge_require_review": False, "auto_merge_enabled": True, } repo = e0.base.branch.repository repo_settings = { "%s/%s" % (repo.user.name, repo.name): auto_merge_settings } git_config = utils.github_config(remote_update=True, repo_settings=repo_settings) with self.settings(INSTALLED_GITSERVERS=[git_config]): # Only works for pull requests e0.cause = models.Event.PUSH e0.save() UpdateRemoteStatus.check_automerge(e0) self.assertEqual(mock_get.call_count, 0) # Only works if the event status is SUCCESS e0.cause = models.Event.PULL_REQUEST e0.status = models.JobStatus.FAILED_OK e0.pull_request = utils.create_pr() e0.save() UpdateRemoteStatus.check_automerge(e0) self.assertEqual(mock_get.call_count, 0) e0.status = models.JobStatus.SUCCESS e0.save() # Should try to auto merge UpdateRemoteStatus.check_automerge(e0) self.assertEqual(mock_get.call_count, 1)
def test_failed_but_allowed_label(self, mock_label): # Make sure any failed but allowed label is removed # on pushes c1_data, c2_data, pr = self.create_pr_data() self.set_counts() pr.save() self.compare_counts(events=1, jobs=2, ready=1, prs=1, active=2, active_repos=1) # Doesn't get called when a PR is first created self.assertEqual(mock_label.call_count, 0) with self.settings(INSTALLED_GITSERVERS=[ utils.github_config(failed_but_allowed_label_name="foo") ]): # We have labels now, so the new event should only have the default plus the matched pr.head_commit.sha = "123" self.set_counts() pr.save() self.compare_counts(jobs=2, ready=1, events=1, commits=1, active=2, canceled=2, events_canceled=1, num_changelog=2, num_events_completed=1, num_jobs_completed=2) self.assertEqual(mock_label.call_count, 2)
def test_with_no_matched(self): # No labels setup, should just do the normal c1_data, c2_data, pr = self.create_pr_data() self.set_counts() pr.changed_files = ["bar/foo", 'foo/bar'] pr.save() self.compare_counts(events=1, jobs=2, ready=1, prs=1, active=2, active_repos=1) # We have labels now, so the new event should only have the default plus the matched with self.settings(INSTALLED_GITSERVERS=[ utils.github_config( recipe_label_activation=utils.default_labels()) ]): alt = self.set_label_on_recipes() pr.head_commit.sha = "123" self.set_counts() pr.save() self.compare_counts(jobs=2, ready=1, events=1, commits=1, active=2, canceled=2, events_canceled=1, num_changelog=2, num_events_completed=1, num_jobs_completed=2) self.assertEqual(alt[0].jobs.count(), 0)
def test_authorized_fail(self, mock_is_collaborator, mock_comment): """ Recipe with automatic=authorized Try out the case where the user IS NOT a collaborator """ mock_is_collaborator.return_value = False c1_data, c2_data, pr = self.create_pr_data() pr_recipe = models.Recipe.objects.filter( cause=models.Recipe.CAUSE_PULL_REQUEST).last() pr_recipe.automatic = models.Recipe.AUTO_FOR_AUTHORIZED pr_recipe.save() with self.settings(INSTALLED_GITSERVERS=[ utils.github_config(post_job_status=True) ]): self.set_counts() pr.save() self.compare_counts(events=1, jobs=2, ready=1, active=1, prs=1, active_repos=1) ev = models.Event.objects.order_by('-created').first() self.assertEqual(ev.jobs.count(), 2) self.assertEqual(ev.jobs.filter(ready=False).count(), 1) self.assertEqual(ev.jobs.filter(active=False).count(), 1) self.assertEqual(mock_comment.call_count, 1)
class Command(BaseCommand): help = "Generate the claim_response.json file that the client testing uses." @override_settings(INSTALLED_GITSERVERS=[utils.github_config()]) def handle(self, *args, **options): factory = RequestFactory() client = utils.create_client() job = utils.create_job() user = job.recipe.build_user orig_recipe_dir = settings.RECIPE_BASE_DIR recipe_dir = utils.create_recipe_dir() settings.RECIPE_BASE_DIR = recipe_dir try: args = [user.build_key, client.name] request = factory.get(reverse("ci:client:ready_jobs", args=args)) reply = views.ready_jobs(request, user.build_key, client.name) args = [user.build_key, job.config.name, client.name] claim_job_url = reverse("ci:client:claim_job", args=args) data = json.dumps({"job_id": job.pk}) request = factory.post(claim_job_url, data, content_type="application/json") reply = views.claim_job(request, user.build_key, job.config.name, client.name) if reply.status_code == 200: this_file = os.path.realpath(__file__) test_dir = os.path.join(os.path.dirname(this_file), "..", "..", "..", "client", "tests") fname = os.path.join(os.path.abspath(test_dir), "claim_response.json") # to makes diffs better, hardcode job and recipe ids data = json.loads(reply.content) data["job_id"] = 1 data["job_info"]["job_id"] = 1 data["job_info"]["environment"]["job_id"] = 1 data["job_info"]["environment"]["recipe_id"] = 1 data["job_info"]["environment"]["CIVET_JOB_ID"] = 1 data["job_info"]["environment"]["CIVET_RECIPE_ID"] = 1 self.stdout.write("Writing: %s" % fname) with open(fname, "w") as f: json.dump(data, f, indent=2, sort_keys=True) f.write("\n") else: self.stderr.write(reply.status_code) self.stderr.write(reply.content) except Exception as e: self.stderr.write(traceback.format_exc()) self.stderr.write("Error occurred: %s" % e) job.recipe.delete() shutil.rmtree(recipe_dir) settings.RECIPE_BASE_DIR = orig_recipe_dir settings.RECIPE_BASE_DIR = orig_recipe_dir
def test_create_event_summary(self, mock_get, mock_post): mock_get.return_value = utils.Response() mock_post.return_value = utils.Response() ev = utils.create_event() ev.comments_url = 'url' ev.save() j0 = utils.create_job(event=ev) config = utils.create_build_config("config1") j0.recipe.build_configs.add(config) utils.create_job(event=ev, recipe=j0.recipe, config=config) r1 = utils.create_recipe(name="r1") j1 = utils.create_job(recipe=r1, event=ev) j0.recipe.depends_on.add(r1) with self.settings(INSTALLED_GITSERVERS=[ utils.github_config(post_event_summary=False, remote_update=True) ]): # Not posting the summary so we should not do anything UpdateRemoteStatus.create_event_summary(ev) self.assertEqual(mock_post.call_count, 0) self.assertEqual(mock_get.call_count, 0) with self.settings(INSTALLED_GITSERVERS=[ utils.github_config(post_event_summary=True, remote_update=True) ]): # Configured to post the summary UpdateRemoteStatus.create_event_summary(ev) self.assertEqual(mock_post.call_count, 1) # 1 for adding comment self.assertEqual(mock_get.call_count, 1) # 1 for getting current comments utils.update_job(j1, status=models.JobStatus.FAILED, complete=True, invalidated=True) utils.create_step_result(job=j1, status=models.JobStatus.FAILED) self.assertEqual(len(ev.get_unrunnable_jobs()), 2) UpdateRemoteStatus.create_event_summary(ev) self.assertEqual(mock_post.call_count, 2) self.assertEqual(mock_get.call_count, 2)
def test_install_webhooks(self, mock_install): mock_install.side_effect = Exception("Bam!") with test_utils.RecipeDir() as recipes_dir: self.create_valid_recipes(recipes_dir) creator = RecipeCreator.RecipeCreator(recipes_dir) creator.install_webhooks() self.assertEqual(mock_install.call_count, 0) with self.settings(INSTALLED_GITSERVERS=[ test_utils.github_config(install_webhook=True) ]): creator.install_webhooks() self.assertEqual(mock_install.call_count, 1)
def test_create_issue_on_fail(self, mock_get, mock_post, mock_patch): j = utils.create_job() get_data = [{"title": "foo", "number": 1}] mock_get.return_value = utils.Response(get_data) mock_post.return_value = utils.Response({"html_url": "<html_url>"}) mock_patch.return_value = utils.Response({"html_url": "<html_url>"}) git_config = utils.github_config(remote_update=True) with self.settings(INSTALLED_GITSERVERS=[git_config]): utils.update_job(j, status=models.JobStatus.SUCCESS) j.event.cause = models.Event.PULL_REQUEST j.event.save() # Don't do anything on a PR UpdateRemoteStatus.create_issue_on_fail(j) self.assertEqual(mock_get.call_count, 0) self.assertEqual(mock_post.call_count, 0) self.assertEqual(mock_patch.call_count, 0) j.event.cause = models.Event.PUSH j.event.save() # Don't do anything unless it is a failure UpdateRemoteStatus.create_issue_on_fail(j) self.assertEqual(mock_get.call_count, 0) self.assertEqual(mock_post.call_count, 0) self.assertEqual(mock_patch.call_count, 0) utils.update_job(j, status=models.JobStatus.FAILED) # Don't do anything unless the recipe wants to create an issue UpdateRemoteStatus.create_issue_on_fail(j) self.assertEqual(mock_get.call_count, 0) self.assertEqual(mock_post.call_count, 0) self.assertEqual(mock_patch.call_count, 0) j.recipe.create_issue_on_fail = True j.recipe.create_issue_on_fail_message = "Additional message" j.recipe.save() # Should create new issue UpdateRemoteStatus.create_issue_on_fail(j) self.assertEqual(mock_get.call_count, 1) self.assertEqual(mock_post.call_count, 1) self.assertEqual(mock_patch.call_count, 0)
def test_view_pr_matched(self, mock_collab): user = utils.get_test_user() utils.simulate_login(self.client.session, user) mock_collab.return_value = True with self.settings(INSTALLED_GITSERVERS=[utils.github_config(recipe_label_activation=utils.default_labels())]): self.set_label_on_recipes() changed_files = ["docs/foo", "docs/bar"] self.set_counts() self.create_pr_data(pr_num=2, changed_files=changed_files) self.compare_counts(jobs=2, active=2, events=1, ready=1, prs=1, num_pr_alts=1, active_repos=1) pr = models.PullRequest.objects.get(number=2) self.assertEqual(pr.alternate_recipes.count(), 1) url = reverse('ci:view_pr', args=[pr.pk,]) # try adding a default recipe recipes = models.Recipe.objects.order_by('created').filter(cause = models.Recipe.CAUSE_PULL_REQUEST) self.assertEqual(recipes.count(), 2) self.set_counts() data = {"recipes": [pr.alternate_recipes.first().pk], "default_recipes": [recipes[1].pk,]} response = self.client.post(url, data) self.assertEqual(response.status_code, 200) self.compare_counts(jobs=1, active=1) # shouldn't be able to remove one of the default self.set_counts() data["default_recipes"] = [] response = self.client.post(url, data) self.assertEqual(response.status_code, 200) self.compare_counts() # try the original again, should give a form error (which we can't detect) data["default_recipes"] = [recipes[1].pk,] self.set_counts() response = self.client.post(url, data) self.assertEqual(response.status_code, 200) self.compare_counts()
def test_step_start_pr_status(self, mock_post): user = utils.get_test_user() job = utils.create_job(user=user) utils.update_job(job, status=models.JobStatus.CANCELED) results = utils.create_step_result(job=job) results.exit_status = 1 results.save() job.event.cause = models.Event.PUSH job.event.save() with self.settings( INSTALLED_GITSERVERS=[utils.github_config( remote_update=True)]): # Wrong cause UpdateRemoteStatus.step_start_pr_status(results, job) self.assertEqual(mock_post.call_count, 0) job.event.cause = models.Event.PULL_REQUEST job.event.save() # OK UpdateRemoteStatus.step_start_pr_status(results, job) self.assertEqual(mock_post.call_count, 1)
def test_sync_badges(self): # Nothing configured out = StringIO() with self.settings(INSTALLED_GITSERVERS=[ utils.github_config(repo_settings={"owner/repo": {}}) ]): self.set_counts() management.call_command("sync_badges", stdout=out) self.compare_counts() with self.settings(INSTALLED_GITSERVERS=[ utils.github_config(repo_settings={ "owner/repo": { "badges": [{ "recipe": "foo", "name": "badge" }] } }) ]): # Does not match any recipes self.set_counts() management.call_command("sync_badges", stdout=out) self.compare_counts() # Match but no jobs r = models.Recipe.objects.first() r.filename = "foo" r.save() self.set_counts() management.call_command("sync_badges", stdout=out) self.compare_counts() j = utils.create_job(recipe=r, ) utils.update_job(j, status=models.JobStatus.FAILED_OK) j.event.cause = models.Event.PUSH j.event.save() j.save() # Should create a new badge self.set_counts() management.call_command("sync_badges", "--dryrun", stdout=out) self.compare_counts() self.set_counts() management.call_command("sync_badges", stdout=out) self.compare_counts(badges=1) # doing it again shouldn't change anything self.set_counts() management.call_command("sync_badges", stdout=out) self.compare_counts() # Now it should delete the one we just created since it no longer matches r.filename = "bar" r.save() self.set_counts() management.call_command("sync_badges", "--dryrun", stdout=out) self.compare_counts() self.set_counts() management.call_command("sync_badges", stdout=out) self.compare_counts(badges=-1)
def test_get_active_labels(self): with self.settings(INSTALLED_GITSERVERS=[ utils.github_config( recipe_label_activation=utils.default_labels()) ]): all_docs = ["docs/foo", "docs/bar", "docs/foobar"] some_docs = all_docs[:] + ["tutorials/foo", "tutorials/bar"] matched, match_all = event.get_active_labels(self.repo, all_docs) self.assertEqual(matched, ["DOCUMENTATION"]) self.assertEqual(match_all, True) matched, match_all = event.get_active_labels(self.repo, some_docs) self.assertEqual(matched, ["DOCUMENTATION", "TUTORIAL"]) self.assertEqual(match_all, False) # No labels are configured other_docs = ["common/foo", "common/bar"] matched, match_all = event.get_active_labels(self.repo, other_docs) self.assertEqual(matched, []) self.assertEqual(match_all, True) # One of the labels matches all the files labels = {"LABEL0": "^common", "LABEL1": "^common/no_exist"} with self.settings(INSTALLED_GITSERVERS=[ utils.github_config(recipe_label_activation=labels) ]): matched, match_all = event.get_active_labels(self.repo, other_docs) self.assertEqual(matched, ["LABEL0"]) self.assertEqual(match_all, True) # One of the labels matches but not all the files labels = {"LABEL0": "^common/foo", "LABEL1": "^common/no_exist"} with self.settings(INSTALLED_GITSERVERS=[ utils.github_config(recipe_label_activation=labels) ]): matched, match_all = event.get_active_labels(self.repo, other_docs) self.assertEqual(matched, ["LABEL0"]) self.assertEqual(match_all, False) # Old syntax is no longer supported with self.settings(INSTALLED_GITSERVERS=[ utils.github_config( recipe_label_activation_additive=["ADDITIVE"]) ]): matched, match_all = event.get_active_labels(self.repo, other_docs) self.assertEqual(matched, []) self.assertEqual(match_all, True) # Anything that matches an additive label automatically sets matched_all to false labels = {"ADDITIVE": "^common/foo"} with self.settings(INSTALLED_GITSERVERS=[ utils.github_config(recipe_label_activation_additive=labels) ]): matched, match_all = event.get_active_labels(self.repo, other_docs) self.assertEqual(matched, ["ADDITIVE"]) self.assertEqual(match_all, False) # A normal label matches everything but the additive label also matches labels = {"LABEL": "^common/"} add_labels = {"ADDITIVE": "^common/foo"} git_config = utils.github_config( recipe_label_activation_additive=add_labels, recipe_label_activation=labels) with self.settings(INSTALLED_GITSERVERS=[git_config]): matched, match_all = event.get_active_labels(self.repo, other_docs) self.assertEqual(matched, ["ADDITIVE", "LABEL"]) self.assertEqual(match_all, False)
def test_start_canceled_on_fail(self): user = utils.get_test_user() r0 = utils.create_recipe(name='recipe0', user=user) r1 = utils.create_recipe(name='recipe1', user=user) r2 = utils.create_recipe(name='recipe2', user=user) e0 = utils.create_event(user=user, cause=models.Event.PUSH) j0 = utils.create_job(recipe=r0, event=e0, user=user) utils.update_job(j0, status=models.JobStatus.SUCCESS, complete=True) j1 = utils.create_job(recipe=r1, event=e0, user=user) utils.update_job(j1, status=models.JobStatus.CANCELED, complete=True) j2 = utils.create_job(recipe=r2, event=e0, user=user) utils.update_job(j2, status=models.JobStatus.RUNNING, complete=True) e1 = utils.create_event(user=user, cause=models.Event.PUSH, commit1='12345') j3 = utils.create_job(recipe=r0, event=e1, user=user) utils.update_job(j3, status=models.JobStatus.SUCCESS, complete=True) j4 = utils.create_job(recipe=r1, event=e1, user=user) utils.update_job(j4, status=models.JobStatus.CANCELED, complete=True) j5 = utils.create_job(recipe=r2, event=e1, user=user) utils.update_job(j5, status=models.JobStatus.RUNNING, complete=True) e2 = utils.create_event(user=user, cause=models.Event.PUSH, commit1='123456') j6 = utils.create_job(recipe=r0, event=e2, user=user) utils.update_job(j6, status=models.JobStatus.SUCCESS, complete=True) j7 = utils.create_job(recipe=r1, event=e2, user=user) utils.update_job(j7, status=models.JobStatus.FAILED, complete=True) j8 = utils.create_job(recipe=r2, event=e2, user=user) utils.update_job(j8, status=models.JobStatus.FAILED_OK, complete=True) # If the job isn't a fail then it shouldn't do anything self.set_counts() UpdateRemoteStatus.start_canceled_on_fail(j6) self.compare_counts() # Normal behavior, a job fails and doesn't do anything to the previous event self.set_counts() UpdateRemoteStatus.start_canceled_on_fail(j7) self.compare_counts() repo_name = "%s/%s" % (e0.base.branch.repository.user.name, e0.base.branch.repository.name) branch_name = e0.base.branch.name branch_settings = { "auto_cancel_push_events_except_current": True, "auto_uncancel_previous_event": True } repo_settings = { repo_name: { "branch_settings": { branch_name: branch_settings } } } with self.settings(INSTALLED_GITSERVERS=[ utils.github_config(repo_settings=repo_settings) ]): # If the job isn't a fail then it shouldn't do anything self.set_counts() UpdateRemoteStatus.start_canceled_on_fail(j3) self.compare_counts() # A job fails and should go to the previous event and uncancel any jobs self.set_counts() UpdateRemoteStatus.start_canceled_on_fail(j7) self.compare_counts(ready=1, active_branches=1, canceled=-1, invalidated=1, num_changelog=1, num_jobs_completed=-1) j0.refresh_from_db() self.assertEqual(j0.status, models.JobStatus.SUCCESS) j1.refresh_from_db() self.assertEqual(j1.status, models.JobStatus.CANCELED) self.assertTrue(j1.complete) j2.refresh_from_db() self.assertEqual(j2.status, models.JobStatus.RUNNING) j3.refresh_from_db() self.assertEqual(j3.status, models.JobStatus.SUCCESS) j4.refresh_from_db() self.assertEqual(j4.status, models.JobStatus.NOT_STARTED) self.assertFalse(j4.complete) j5.refresh_from_db() self.assertEqual(j5.status, models.JobStatus.RUNNING) # If another job on the same event fails, it shouldn't try to uncancel previous events utils.update_job(j8, status=models.JobStatus.FAILED) utils.update_job(j4, status=models.JobStatus.CANCELED, complete=True) self.set_counts() UpdateRemoteStatus.start_canceled_on_fail(j7) self.compare_counts()
def test_event_complete(self, mock_del, mock_get, mock_post): ev = utils.create_event(cause=models.Event.PUSH) ev.comments_url = 'url' ev.save() git_config = utils.github_config(post_event_summary=False, failed_but_allowed_label_name=None, remote_update=True) with self.settings(INSTALLED_GITSERVERS=[git_config]): # Not complete, shouldn't do anything UpdateRemoteStatus.event_complete(ev) self.assertEqual(mock_get.call_count, 0) self.assertEqual(mock_post.call_count, 0) self.assertEqual(mock_del.call_count, 0) ev.complete = True ev.save() # event isn't a pull request, so we shouldn't do anything UpdateRemoteStatus.event_complete(ev) self.assertEqual(mock_get.call_count, 0) self.assertEqual(mock_post.call_count, 0) self.assertEqual(mock_del.call_count, 0) ev.cause = models.Event.PULL_REQUEST ev.status = models.JobStatus.SUCCESS ev.pull_request = utils.create_pr() ev.save() # No label so we shouldn't do anything UpdateRemoteStatus.event_complete(ev) self.assertEqual(mock_get.call_count, 0) self.assertEqual(mock_post.call_count, 0) self.assertEqual(mock_del.call_count, 0) git_config = utils.github_config(post_event_summary=False, failed_but_allowed_label_name='foo', remote_update=True) with self.settings(INSTALLED_GITSERVERS=[git_config]): # event is SUCCESS, so we shouldn't add a label but # we will try to remove an existing label UpdateRemoteStatus.event_complete(ev) self.assertEqual(mock_get.call_count, 0) self.assertEqual(mock_post.call_count, 0) self.assertEqual(mock_del.call_count, 1) # removing the label ev.status = models.JobStatus.FAILED ev.save() # Don't put anything if the event is FAILED UpdateRemoteStatus.event_complete(ev) self.assertEqual(mock_get.call_count, 0) self.assertEqual(mock_post.call_count, 0) self.assertEqual(mock_del.call_count, 2) ev.status = models.JobStatus.FAILED_OK ev.save() # should try to add a label UpdateRemoteStatus.event_complete(ev) self.assertEqual(mock_get.call_count, 0) self.assertEqual(mock_post.call_count, 1) # add the label self.assertEqual(mock_del.call_count, 2)
class Tests(DBTester.DBTester): def setUp(self): super(Tests, self).setUp() self.create_default_recipes() def create_commit_data(self): c1 = utils.create_commit(sha='1', branch=self.branch, user=self.owner) c2 = utils.create_commit(sha='2', branch=self.branch, user=self.owner) c1_data = GitCommitData.GitCommitData(self.owner.name, c1.repo().name, c1.branch.name, c1.sha, '', c1.server()) c2_data = GitCommitData.GitCommitData(self.owner.name, c2.repo().name, c2.branch.name, c2.sha, '', c2.server()) return c1, c1_data, c2, c2_data def create_data(self): c1, c1_data, c2, c2_data = self.create_commit_data() push = PushEvent.PushEvent() push.build_user = self.build_user push.full_text = '' push.base_commit = c1_data push.head_commit = c2_data return c1_data, c2_data, push def test_no_recipes(self): # Make sure if there is a push and there are no recipes, we don't leave anything around # This shouldn't create an event or any jobs. c1_data, c2_data, push = self.create_data() c1_data = GitCommitData.GitCommitData("no_exist", "no_exist", "no_exist", "1", "", self.build_user.server) push.base_commit = c1_data self.set_counts() push.save() self.compare_counts() def test_bad_user(self): other_build_user = utils.create_user(name="bad_build_user") # Make sure we only get recipes for the correct build user # This shouldn't create an event or any jobs. c1_data, c2_data, push = self.create_data() push.build_user = other_build_user self.set_counts() push.save() self.compare_counts() def test_valid(self): c1_data, c2_data, push = self.create_data() # a valid Push, should just create an event and 2 jobs. # 1 job depends on the other so only 1 job should be ready self.set_counts() push.save() self.compare_counts(events=1, jobs=2, ready=1, active=2, active_repos=1) # save again shouldn't do anything self.set_counts() push.save() self.compare_counts() def test_multiple(self): c1_data, c2_data, push = self.create_data() self.set_counts() push.save() self.compare_counts(events=1, jobs=2, ready=1, active=2, active_repos=1) # now try another event on the Push # it should just create more jobs old_ev = models.Event.objects.first() c2_data.sha = '10' push.head_commit = c2_data self.set_counts() push.save() self.compare_counts(events=1, jobs=2, ready=1, commits=1, active=2) old_ev.refresh_from_db() self.assertEqual(old_ev.status, models.JobStatus.NOT_STARTED) self.assertFalse(old_ev.complete) def test_manual(self): c1_data, c2_data, push = self.create_data() q = models.Recipe.objects.filter(cause=models.Recipe.CAUSE_PUSH) self.assertEqual(q.count(), 2) r = q.exclude(depends_on=None).first() r.active = False r.save() r = q.exclude(pk=r.pk).first() r.automatic = models.Recipe.MANUAL r.save() self.set_counts() push.save() self.compare_counts(events=1, jobs=1, active_repos=1) j = models.Job.objects.first() self.assertEqual(j.active, False) self.assertEqual(j.status, models.JobStatus.ACTIVATION_REQUIRED) def test_recipe(self): c1_data, c2_data, push = self.create_data() self.set_counts() push.save() self.compare_counts(events=1, jobs=2, ready=1, active=2, active_repos=1) # now try another event on the Push but with a new recipe. push_recipe = models.Recipe.objects.filter( cause=models.Recipe.CAUSE_PUSH).latest() new_recipe = utils.create_recipe(name="New recipe", user=self.build_user, repo=self.repo, branch=self.branch, cause=models.Recipe.CAUSE_PUSH) new_recipe.filename = push_recipe.filename new_recipe.save() push_recipe.current = False push_recipe.save() c2_data.sha = '10' push.head_commit = c2_data self.set_counts() push.save() self.compare_counts(events=1, jobs=2, ready=2, commits=1, active=2) # save the same push and make sure the jobs haven't changed # and no new events were created. self.set_counts() push.save() self.compare_counts() def test_change_recipe(self): c1_data, c2_data, push = self.create_data() self.set_counts() push.save() self.compare_counts(events=1, jobs=2, ready=1, active=2, active_repos=1) # This scenario is one where the event already exists but # for some reason the same push event gets called and the recipes have changed. # Nothing should have changed push_recipe = models.Recipe.objects.filter( cause=models.Recipe.CAUSE_PUSH).latest() new_recipe = utils.create_recipe(name="New recipe", user=self.build_user, repo=self.repo, branch=self.branch, cause=models.Recipe.CAUSE_PUSH) new_recipe.filename = push_recipe.filename new_recipe.save() push_recipe.current = False push_recipe.save() self.assertEqual(push_recipe.jobs.count(), 1) self.set_counts() push.save() self.compare_counts() push_recipe.refresh_from_db() new_recipe.refresh_from_db() self.assertEqual(push_recipe.jobs.count(), 1) self.assertEqual(new_recipe.jobs.count(), 0) def test_save(self): c1_data, c2_data, push = self.create_data() base = models.Recipe.objects.filter(cause=models.Recipe.CAUSE_PUSH, depends_on=None) self.assertEqual(base.count(), 1) base = base.first() with_dep = models.Recipe.objects.filter( cause=models.Recipe.CAUSE_PUSH).exclude(depends_on=None) self.assertEqual(with_dep.count(), 1) with_dep = with_dep.first() self.assertEqual(with_dep.depends_on.first(), base) def test_auto_cancel_on_push(self): c1_data, c2_data, push = self.create_data() push_rs = models.Recipe.objects.filter(cause=models.Recipe.CAUSE_PUSH) self.assertEqual(push_rs.count(), 2) push_first = push_rs.first() push_first.auto_cancel_on_push = True push_first.save() self.set_counts() push.save() self.compare_counts(events=1, jobs=2, ready=1, active=2, active_repos=1) old_ev = models.Event.objects.latest() self.assertEqual(old_ev.status, models.JobStatus.NOT_STARTED) self.assertFalse(old_ev.complete) self.assertEqual(old_ev.base.branch.status, models.JobStatus.NOT_STARTED) # This scenario is one where an event exists on the branch, with one job # set to auto_cancel_on_push. # Then another event comes along on the branch, the first job should be canceled. old_ev = models.Event.objects.latest() c2_data.sha = '10' push.head_commit = c2_data self.set_counts() push.save() self.compare_counts(events=1, jobs=2, ready=1, commits=1, active=2, canceled=1, events_canceled=1, num_changelog=1, num_jobs_completed=1, num_events_completed=1, active_branches=1) push_first.refresh_from_db() self.assertEqual(push_first.jobs.count(), 2) js_status = sorted([j.status for j in push_first.jobs.all()]) self.assertEqual( [models.JobStatus.NOT_STARTED, models.JobStatus.CANCELED], js_status) old_ev.refresh_from_db() self.assertEqual(old_ev.status, models.JobStatus.CANCELED) self.assertTrue(old_ev.complete) self.assertEqual(old_ev.base.branch.status, models.JobStatus.CANCELED) # A finished job shouldn't be canceled j = push_first.jobs.get(status=models.JobStatus.NOT_STARTED) j.status = models.JobStatus.FAILED j.complete = True j.save() c2_data.sha = '11' push.head_commit = c2_data self.set_counts() push.save() self.compare_counts(events=1, jobs=2, ready=1, commits=1, active=2) def get_ready_job_pks(self, expected): request = self.factory.get("/") ready_jobs = client_views.ready_jobs(request, self.build_user.build_key, "some_client") jobs_json = json.loads(ready_jobs.content) ready_pks = [] for job in jobs_json["jobs"]: ready_pks.append(job["id"]) self.assertEqual(len(ready_pks), expected) return ready_pks @override_settings(INSTALLED_GITSERVERS=[ utils.github_config( repo_settings={ "owner/repo": { "branch_settings": { "devel": { "auto_cancel_push_events_except_current": True } } } }) ]) def test_auto_cancel_except_current(self): """ This scenario is one where an event exists on the branch, and the branch is configured to be auto_cancel_except_current Another event comes along and we make sure the current event is not cancelled (if it is running) and the new event jobs won't run before the current event. Then another event comes in and the second event gets cancelled but the original event doesn't. """ c1_data, c2_data, push = self.create_data() push_rs = models.Recipe.objects.filter(cause=models.Recipe.CAUSE_PUSH) # remove the dependencies to make the testing easier for r in push_rs.all(): r.depends_on.clear() self.assertEqual(push_rs.count(), 2) push_last = push_rs.last() push_last.auto_cancel_on_push = True push_last.save() self.set_counts() push.save() self.compare_counts(events=1, jobs=2, ready=2, active=2, active_repos=1) e0 = models.Event.objects.latest() self.assertEqual(e0.status, models.JobStatus.NOT_STARTED) self.assertFalse(e0.complete) self.assertEqual(e0.base.branch.status, models.JobStatus.NOT_STARTED) # We have an event that isn't running yet. So when a new event comes # in we can cancel it. e0 = models.Event.objects.latest() e0.save() j0 = e0.jobs.filter(recipe__auto_cancel_on_push=True).first() j1 = e0.jobs.filter(recipe__auto_cancel_on_push=False).first() ready_pks = self.get_ready_job_pks(2) c2_data.sha = '10' push.head_commit = c2_data self.set_counts() push.save() self.compare_counts(events=1, jobs=2, ready=2, commits=1, active=2, canceled=1, num_changelog=1, num_jobs_completed=1) e0.refresh_from_db() self.assertEqual(e0.jobs.count(), 2) js_status = sorted([j.status for j in e0.jobs.all()]) self.assertEqual( [models.JobStatus.NOT_STARTED, models.JobStatus.CANCELED], js_status) self.assertEqual(e0.status, models.JobStatus.NOT_STARTED) self.assertFalse(e0.complete) self.assertEqual(e0.base.branch.status, models.JobStatus.NOT_STARTED) ready_pks = self.get_ready_job_pks(3) self.assertIn(j1.pk, ready_pks) self.assertNotIn(j0.pk, ready_pks) # We set this new event to running so that it becomes the "current" event # and won't get cancelled. e0 = models.Event.objects.latest() j0 = e0.jobs.filter(recipe__auto_cancel_on_push=True).first() j0.status = models.JobStatus.RUNNING j0.save() j1 = e0.jobs.filter(recipe__auto_cancel_on_push=False).first() e0.status = models.JobStatus.RUNNING e0.save() ready_pks = self.get_ready_job_pks(2) self.assertIn(j1.pk, ready_pks) c2_data.sha = '100' push.head_commit = c2_data self.set_counts() push.save() self.compare_counts(events=1, jobs=2, ready=2, commits=1, active=2) e0.refresh_from_db() self.assertEqual(e0.jobs.count(), 2) js_status = sorted([j.status for j in e0.jobs.all()]) self.assertEqual( [models.JobStatus.NOT_STARTED, models.JobStatus.RUNNING], js_status) self.assertEqual(e0.status, models.JobStatus.RUNNING) self.assertFalse(e0.complete) self.assertEqual(e0.base.branch.status, models.JobStatus.NOT_STARTED) ready_pks = self.get_ready_job_pks(4) self.assertIn(j1.pk, ready_pks) self.assertNotIn(j0.pk, ready_pks) e1 = models.Event.objects.latest() self.assertEqual(e1.status, models.JobStatus.NOT_STARTED) self.assertFalse(e1.complete) j2 = e1.jobs.filter(recipe__auto_cancel_on_push=True).first() j3 = e1.jobs.filter(recipe__auto_cancel_on_push=False).first() self.assertIn(j2.pk, ready_pks) self.assertIn(j3.pk, ready_pks) # Set this new event to running, it should still get # cancelled when a newer event comes in. e1.status = models.JobStatus.RUNNING e1.save() c2_data.sha = '1000' push.head_commit = c2_data self.set_counts() push.save() self.compare_counts( events=1, jobs=2, ready=2, commits=1, active=2, canceled=1, num_changelog=1, num_jobs_completed=1, ) e1.refresh_from_db() self.assertEqual(e1.status, models.JobStatus.RUNNING) self.assertFalse(e1.complete) # One of the jobs still needs to run js_status = sorted([j.status for j in e1.jobs.all()]) self.assertEqual( [models.JobStatus.NOT_STARTED, models.JobStatus.CANCELED], js_status) e2 = models.Event.objects.latest() self.assertEqual(e2.status, models.JobStatus.NOT_STARTED) self.assertFalse(e2.complete) ready_pks = self.get_ready_job_pks(5) self.assertNotIn(j0.pk, ready_pks) self.assertIn(j1.pk, ready_pks) self.assertNotIn(j2.pk, ready_pks) j2.refresh_from_db() self.assertEqual(j2.status, models.JobStatus.CANCELED) self.assertIn(j3.pk, ready_pks) for j in e2.jobs.all(): self.assertIn(j.pk, ready_pks) @override_settings(INSTALLED_GITSERVERS=[ utils.github_config( repo_settings={ "owner/repo": { "branch_settings": { "devel": { "auto_cancel_push_events_except_current": True, "auto_uncancel_previous_event": True, } } } }) ]) def test_auto_uncancel_event(self): """ E0 - starts running E1 - waits for E0 to finish E2 - waits for E0 to finish, cancels E1 E0 is still running but clients start on E2 which gets a job failure. Uncancels E1. E3 - waits for E0 to finish. Cancels E2 and E1. At this point E3 will eventually fail. If E0 is still running then it needs to see that E2 is already failed so there is no need to uncancel. Instead, it needs to uncancel E1. """ print("Branch: %s" % self.branch) c1_data, c2_data, push = self.create_data() push_rs = models.Recipe.objects.filter(cause=models.Recipe.CAUSE_PUSH) # remove the dependencies to make the testing easier for r in push_rs.all(): r.depends_on.clear() r.auto_cancel_on_push = True r.save() self.assertEqual(push_rs.count(), 2) self.set_counts() push.save() self.compare_counts(events=1, jobs=2, ready=2, active=2, active_repos=1) e0 = models.Event.objects.latest() e0.status = models.JobStatus.RUNNING e0.complete = False e0.save() # We have an event that isn't running yet. So when a new event comes # in we can cancel it. # E0 is running for j in e0.jobs.all(): utils.update_job(j, status=models.JobStatus.RUNNING) # A new event E1 comes in. E0 is still running so nothing is canceled. # Jobs on E1 can be run though c2_data.sha = '3' push.head_commit = c2_data self.set_counts() push.save() self.compare_counts(events=1, jobs=2, ready=2, commits=1, active=2) e0.refresh_from_db() for j in e0.jobs.all(): self.assertEqual(j.status, models.JobStatus.RUNNING) e1 = models.Event.objects.latest() for j in e1.jobs.all(): self.assertEqual(j.status, models.JobStatus.NOT_STARTED) self.assertEqual(j.ready, True) # A new event E2 comes in. E0 is still running so we need to cancel E1. # Jobs on E2 can be run. c2_data.sha = '4' push.head_commit = c2_data self.set_counts() push.save() self.compare_counts(events=1, jobs=2, ready=2, commits=1, active=2, active_branches=1, canceled=2, events_canceled=1, num_events_completed=1, num_jobs_completed=2, num_changelog=2) e0.refresh_from_db() for j in e0.jobs.all(): self.assertEqual(j.status, models.JobStatus.RUNNING) e1.refresh_from_db() for j in e1.jobs.all(): self.assertEqual(j.status, models.JobStatus.CANCELED) self.assertEqual(j.complete, True) e2 = models.Event.objects.latest() for j in e2.jobs.all(): self.assertEqual(j.status, models.JobStatus.NOT_STARTED) self.assertEqual(j.complete, False) # Make a job failure on E2. Should uncancel E1. e2_j0 = e2.jobs.first() e2_j1 = e2.jobs.last() utils.update_job(e2_j0, status=models.JobStatus.FAILED, complete=True) self.set_counts() UpdateRemoteStatus.start_canceled_on_fail(e2_j0) self.compare_counts( active_branches=-1, canceled=-2, events_canceled=-1, invalidated=2, num_changelog=2, num_events_completed=-1, num_jobs_completed=-2, ) e0.refresh_from_db() for j in e0.jobs.all(): self.assertEqual(j.status, models.JobStatus.RUNNING) e1.refresh_from_db() for j in e1.jobs.all(): self.assertEqual(j.status, models.JobStatus.NOT_STARTED) self.assertEqual(j.complete, False) # A new event E3 comes in. E0, E1, and E2 all are running # so we need to cancel E1 and E2. # Jobs on E3 can be run. c2_data.sha = '5' push.head_commit = c2_data self.set_counts() push.save() self.compare_counts(events=1, jobs=2, ready=2, commits=1, active=2, active_branches=1, canceled=3, events_canceled=1, num_events_completed=2, num_jobs_completed=3, num_changelog=3) e0.refresh_from_db() for j in e0.jobs.all(): self.assertEqual(j.status, models.JobStatus.RUNNING) e1.refresh_from_db() for j in e1.jobs.all(): self.assertEqual(j.status, models.JobStatus.CANCELED) self.assertEqual(j.complete, True) e2.refresh_from_db() e2_j0.refresh_from_db() self.assertEqual(e2_j0.status, models.JobStatus.FAILED) self.assertEqual(e2_j0.complete, True) e2_j1.refresh_from_db() self.assertEqual(e2_j1.status, models.JobStatus.CANCELED) self.assertEqual(e2_j1.complete, True) e3 = models.Event.objects.latest() for j in e3.jobs.all(): self.assertEqual(j.status, models.JobStatus.NOT_STARTED) self.assertEqual(j.complete, False) # Make a job failure on E3. Should uncancel E1 and leave E2 canceled. e3_j0 = e3.jobs.first() utils.update_job(e3_j0, status=models.JobStatus.FAILED, complete=True) self.set_counts() UpdateRemoteStatus.start_canceled_on_fail(e3_j0) self.compare_counts( active_branches=-1, canceled=-2, events_canceled=-1, num_changelog=2, num_events_completed=-1, num_jobs_completed=-2, ) e0.refresh_from_db() for j in e0.jobs.all(): self.assertEqual(j.status, models.JobStatus.RUNNING) e1.refresh_from_db() for j in e1.jobs.all(): self.assertEqual(j.status, models.JobStatus.NOT_STARTED) self.assertEqual(j.complete, False) e2.refresh_from_db() e2_j0.refresh_from_db() self.assertEqual(e2_j0.status, models.JobStatus.FAILED) self.assertEqual(e2_j0.complete, True) e2_j1.refresh_from_db() self.assertEqual(e2_j1.status, models.JobStatus.CANCELED) self.assertEqual(e2_j1.complete, True)
class Tests(DBTester.DBTester): def setUp(self): super(Tests, self).setUp() self.create_default_recipes() def create_commit_data(self): c1 = utils.create_commit(sha='1', branch=self.branch, user=self.owner) c2 = utils.create_commit(sha='2', branch=self.branch, user=self.owner) c1_data = GitCommitData.GitCommitData(self.owner.name, c1.repo().name, c1.branch.name, c1.sha, '', c1.server()) c2_data = GitCommitData.GitCommitData(self.owner.name, c2.repo().name, c2.branch.name, c2.sha, '', c2.server()) return c1, c1_data, c2, c2_data def create_pr_data(self): c1, c1_data, c2, c2_data = self.create_commit_data() pr = PullRequestEvent.PullRequestEvent() pr.pr_number = 1 pr.action = PullRequestEvent.PullRequestEvent.OPENED pr.build_user = self.build_user pr.title = 'PR 1' pr.html_url = 'url' pr.full_text = '' pr.base_commit = c1_data pr.head_commit = c2_data pr.trigger_user = c2.user().name return c1_data, c2_data, pr def test_bad_user(self): """ Make sure we only get recipes for the correct build user This shouldn't create an event or any jobs. """ c1_data, c2_data, pr = self.create_pr_data() other_build_user = utils.create_user_with_token(name="bad_build_user") pr.build_user = other_build_user self.set_counts() pr.save() self.compare_counts() def test_valid(self): """ a valid PR, should just create an event, a PR, and 2 jobs """ c1_data, c2_data, pr = self.create_pr_data() self.set_counts() pr.changed_files = ["docs/foo"] pr.save() self.compare_counts(events=1, jobs=2, ready=1, prs=1, active=2, active_repos=1) # save the same pull request and make sure the jobs haven't changed # and no new events were created. self.set_counts() pr.save() self.compare_counts() # save the same pull request and make sure the jobs haven't changed # and no new events were created. self.set_counts() pr.save() self.compare_counts() # new sha should create new event and cancel old one pr.head_commit.sha = "5678" self.set_counts() pr.save() self.compare_counts(jobs=2, ready=1, events=1, commits=1, active=2, canceled=2, events_canceled=1, num_changelog=2, num_events_completed=1, num_jobs_completed=2) # should now add the alternative job automatically with self.settings(INSTALLED_GITSERVERS=[ utils.github_config( recipe_label_activation=utils.default_labels()) ]): alt = self.set_label_on_recipes() pr.changed_files = ["docs/foo", 'other/bar'] pr.head_commit.sha = "6789" self.set_counts() pr.save() self.compare_counts(jobs=3, ready=1, events=1, commits=1, active=3, canceled=2, events_canceled=1, num_changelog=2, num_events_completed=1, num_jobs_completed=2, num_pr_alts=1) self.assertEqual(alt[0].jobs.count(), 1) # new commit should add the previously added alternate job pr.changed_files = [] pr.head_commit.sha = "789" self.set_counts() pr.save() self.compare_counts(jobs=3, ready=1, events=1, commits=1, active=3, canceled=3, events_canceled=1, num_changelog=3, num_events_completed=1, num_jobs_completed=3) self.assertEqual(alt[0].jobs.count(), 2) # new commit should only add the alt job and its dependency pr.changed_files = ["docs/foo"] pr.head_commit.sha = "89" self.set_counts() pr.save() self.compare_counts(jobs=2, ready=1, events=1, commits=1, active=2, canceled=3, events_canceled=1, num_changelog=3, num_events_completed=1, num_jobs_completed=3) self.assertEqual(alt[0].jobs.count(), 3) def test_cancel(self): c1_data, c2_data, pr = self.create_pr_data() self.set_counts() pr.save() self.compare_counts(events=1, jobs=2, ready=1, prs=1, active=2, active_repos=1) alt_recipe = models.Recipe.objects.filter( cause=models.Recipe.CAUSE_PULL_REQUEST_ALT).first() pr_rec = models.PullRequest.objects.first() pr_rec.alternate_recipes.add(alt_recipe) # now try another event on the PR # it should cancel previous events and jobs # the alt_recipe job and another pr recipe depend on the same recipe # so only one job will be ready old_ev = models.Event.objects.first() c2_data.sha = '10' pr.head_commit = c2_data self.set_counts() pr.save() self.compare_counts(jobs=3, ready=1, events=1, commits=1, canceled=2, active=3, num_events_completed=1, num_jobs_completed=2, events_canceled=1, num_changelog=2) old_ev.refresh_from_db() self.assertEqual(old_ev.status, models.JobStatus.CANCELED) self.assertTrue(old_ev.complete) new_ev = models.Event.objects.first() self.assertEqual(new_ev.status, models.JobStatus.NOT_STARTED) self.assertFalse(new_ev.complete) for j in new_ev.jobs.all(): self.assertEqual(j.status, models.JobStatus.NOT_STARTED) self.assertFalse(j.complete) for j in old_ev.jobs.all(): self.assertEqual(j.status, models.JobStatus.CANCELED) self.assertTrue(j.complete) # save the same pull request and make sure the jobs haven't changed # and no new events were created. self.set_counts() pr.save() self.compare_counts() def test_change_recipe(self): """ Try saving the same pull request but the recipe repo has changed. This scenario is one where the event already exists but the user might have just changed something cosmetic about the PR. So we don't change the current recipes on the event or the jobs either. But a recipe does get created """ c1_data, c2_data, pr = self.create_pr_data() c1_data, c2_data, pr = self.create_pr_data() self.set_counts() pr.save() self.compare_counts(events=1, jobs=2, ready=1, prs=1, active=2, active_repos=1) new_recipe = utils.create_recipe( name="New recipe", user=self.build_user, repo=self.repo, branch=self.branch, cause=models.Recipe.CAUSE_PULL_REQUEST) pr_recipe = models.Recipe.objects.filter( cause=models.Recipe.CAUSE_PULL_REQUEST).latest() new_recipe.filename = pr_recipe.filename new_recipe.save() for dep in pr_recipe.depends_on.all(): new_recipe.depends_on.add(dep) pr_recipe.current = False pr_recipe.save() self.set_counts() pr.save() self.compare_counts() def test_not_active(self): """ with only one PR active and one not active """ c1_data, c2_data, pr = self.create_pr_data() pr_recipe = models.Recipe.objects.filter( cause=models.Recipe.CAUSE_PULL_REQUEST).last() pr_recipe.active = False pr_recipe.save() self.set_counts() pr.save() self.compare_counts(events=1, jobs=1, ready=1, active=1, prs=1, active_repos=1) ev = models.Event.objects.order_by('-created').first() self.assertEqual(ev.jobs.count(), 1) self.assertEqual(ev.jobs.filter(ready=False).count(), 0) self.assertEqual(ev.jobs.filter(active=False).count(), 0) def test_manual(self): """ one PR marked as manual """ c1_data, c2_data, pr = self.create_pr_data() pr_recipe = models.Recipe.objects.filter( cause=models.Recipe.CAUSE_PULL_REQUEST).last() pr_recipe.automatic = models.Recipe.MANUAL pr_recipe.save() self.set_counts() pr.save() self.compare_counts(events=1, jobs=2, ready=1, active=1, prs=1, active_repos=1) ev = models.Event.objects.order_by('-created').first() self.assertEqual(ev.jobs.count(), 2) self.assertEqual(ev.jobs.filter(ready=False).count(), 1) self.assertEqual(ev.jobs.filter(active=False).count(), 1) @patch.object(api.GitHubAPI, 'pr_comment') @patch.object(api.GitHubAPI, 'is_collaborator') def test_authorized_fail(self, mock_is_collaborator, mock_comment): """ Recipe with automatic=authorized Try out the case where the user IS NOT a collaborator """ mock_is_collaborator.return_value = False c1_data, c2_data, pr = self.create_pr_data() pr_recipe = models.Recipe.objects.filter( cause=models.Recipe.CAUSE_PULL_REQUEST).last() pr_recipe.automatic = models.Recipe.AUTO_FOR_AUTHORIZED pr_recipe.save() with self.settings(INSTALLED_GITSERVERS=[ utils.github_config(post_job_status=True) ]): self.set_counts() pr.save() self.compare_counts(events=1, jobs=2, ready=1, active=1, prs=1, active_repos=1) ev = models.Event.objects.order_by('-created').first() self.assertEqual(ev.jobs.count(), 2) self.assertEqual(ev.jobs.filter(ready=False).count(), 1) self.assertEqual(ev.jobs.filter(active=False).count(), 1) self.assertEqual(mock_comment.call_count, 1) @patch.object(api.GitHubAPI, 'is_collaborator') def test_authorized_success(self, mock_is_collaborator): """ Recipe with automatic=authorized Try out the case where the user IS a collaborator """ mock_is_collaborator.return_value = True c1_data, c2_data, pr = self.create_pr_data() c1_data, c2_data, pr = self.create_pr_data() pr_recipe = models.Recipe.objects.filter( cause=models.Recipe.CAUSE_PULL_REQUEST).last() pr_recipe.automatic = models.Recipe.AUTO_FOR_AUTHORIZED pr_recipe.save() self.set_counts() pr.save() # one PR depends on the other so only 1 ready self.compare_counts(events=1, jobs=2, ready=1, active=2, prs=1, active_repos=1) ev = models.Event.objects.order_by('-created').first() self.assertEqual(ev.jobs.count(), 2) self.assertEqual(ev.jobs.filter(ready=True).count(), 1) self.assertEqual(ev.jobs.filter(active=True).count(), 2) @patch.object(api.GitHubAPI, 'is_collaborator') def test_authorized_no_user(self, mock_is_collaborator): """ Recipe with automatic=authorized Try out the case where the user isn't in the database """ mock_is_collaborator.return_value = False c1_data, c2_data, pr = self.create_pr_data() c1_data, c2_data, pr = self.create_pr_data() pr_recipe = models.Recipe.objects.filter( cause=models.Recipe.CAUSE_PULL_REQUEST).last() pr_recipe.automatic = models.Recipe.AUTO_FOR_AUTHORIZED pr_recipe.save() pr.trigger_user = "" self.set_counts() pr.save() # one PR depends on the other so only 1 ready self.compare_counts(events=1, jobs=2, ready=1, active=1, prs=1, active_repos=1) ev = models.Event.objects.order_by('-created').first() self.assertEqual(ev.jobs.count(), 2) self.assertEqual(ev.jobs.filter(ready=True).count(), 1) self.assertEqual(ev.jobs.filter(active=True).count(), 1) @patch.object(api.GitHubAPI, 'is_collaborator') def test_authorized_new_user(self, mock_is_collaborator): """ Recipe with automatic=authorized Try out the case where the user isn't in the database """ mock_is_collaborator.return_value = False c1_data, c2_data, pr = self.create_pr_data() c1_data, c2_data, pr = self.create_pr_data() pr_recipe = models.Recipe.objects.filter( cause=models.Recipe.CAUSE_PULL_REQUEST).last() pr_recipe.automatic = models.Recipe.AUTO_FOR_AUTHORIZED pr_recipe.save() pr.trigger_user = "******" self.set_counts() pr.save() # one PR depends on the other so only 1 ready self.compare_counts(events=1, jobs=2, ready=1, active=1, prs=1, active_repos=1) ev = models.Event.objects.order_by('-created').first() self.assertEqual(ev.jobs.count(), 2) self.assertEqual(ev.jobs.filter(ready=True).count(), 1) self.assertEqual(ev.jobs.filter(active=True).count(), 1) def test_close(self): c1_data, c2_data, pr = self.create_pr_data() self.set_counts() pr.save() self.compare_counts(events=1, jobs=2, ready=1, prs=1, active=2, active_repos=1) self.set_counts() pr.action = PullRequestEvent.PullRequestEvent.CLOSED pr.save() self.compare_counts(pr_closed=True) self.set_counts() pr.pr_number = 1000 pr.save() self.compare_counts(pr_closed=True) def test_create_pr_alternates(self): c1_data, c2_data, pr = self.create_pr_data() pr.save() pr_rec = models.PullRequest.objects.latest() self.set_counts() pr.create_pr_alternates(pr_rec) self.compare_counts() alt_recipe = models.Recipe.objects.filter( cause=models.Recipe.CAUSE_PULL_REQUEST_ALT).first() pr_rec.alternate_recipes.add(alt_recipe) self.set_counts() pr.create_pr_alternates(pr_rec) self.compare_counts(jobs=1, active=1) @override_settings(INSTALLED_GITSERVERS=[ utils.github_config(recipe_label_activation=utils.default_labels()) ]) def test_get_recipes(self): alt = self.set_label_on_recipes() c1_data, c2_data, pr = self.create_pr_data() base = models.Recipe.objects.filter( cause=models.Recipe.CAUSE_PULL_REQUEST, depends_on=None) self.assertEqual(base.count(), 1) base = base.first() with_dep = models.Recipe.objects.filter( cause=models.Recipe.CAUSE_PULL_REQUEST).exclude(depends_on=None) self.assertEqual(with_dep.count(), 1) with_dep = with_dep.first() matched = ["DOCUMENTATION"] matched_all = True c1_data.create() recipes = pr._get_recipes(c1_data.commit_record, matched, matched_all) self.assertEqual(len(recipes), 2) # The ALT recipe and its dependency self.assertIn(alt[0], recipes) self.assertIn(base, recipes) matched_all = False recipes = pr._get_recipes(c1_data.commit_record, matched, matched_all) self.assertEqual(len(recipes), 3) # The normal recipes plus the ALT self.assertIn(alt[0], recipes) self.assertIn(base, recipes) self.assertIn(with_dep, recipes) self.assertEqual(recipes.count(base), 1) matched = [] recipes = pr._get_recipes(c1_data.commit_record, matched, matched_all) self.assertEqual(len(recipes), 2) # Just the normal recipes self.assertIn(with_dep, recipes) self.assertIn(base, recipes) self.assertNotIn(alt[0], recipes) @override_settings(INSTALLED_GITSERVERS=[ utils.github_config(recipe_label_activation=utils.default_labels()) ]) def test_get_recipes_with_deps(self): alt = self.set_label_on_recipes() c1_data, c2_data, pr = self.create_pr_data() alt = models.Recipe.objects.filter( cause=models.Recipe.CAUSE_PULL_REQUEST_ALT) self.assertEqual(alt.count(), 1) recipes = pr._get_recipes_with_deps(alt.all()) self.assertEqual(len(recipes), 2) def test_long_titles(self): c1_data, c2_data, pr = self.create_pr_data() pr.title = 'a' * 200 self.set_counts() pr.save() self.compare_counts(events=1, jobs=2, ready=1, prs=1, active=2, active_repos=1) pr_rec = models.PullRequest.objects.first() self.assertEqual(pr_rec.title, 'a' * 120) def test_with_only_matched(self): # No labels setup, should just do the normal c1_data, c2_data, pr = self.create_pr_data() self.set_counts() pr.changed_files = ["docs/foo", 'docs/bar'] pr.save() self.compare_counts(events=1, jobs=2, ready=1, prs=1, active=2, active_repos=1) # We have labels now, so the new event should only have the matched jobs (and dependencies) with self.settings(INSTALLED_GITSERVERS=[ utils.github_config( recipe_label_activation=utils.default_labels()) ]): alt = self.set_label_on_recipes() pr.head_commit.sha = "123" self.set_counts() pr.save() self.compare_counts(jobs=2, ready=1, events=1, commits=1, active=2, canceled=2, events_canceled=1, num_changelog=2, num_events_completed=1, num_jobs_completed=2, num_pr_alts=1) self.assertEqual(alt[0].jobs.count(), 1) def test_with_mixed_matched(self): # No labels setup, should just do the normal c1_data, c2_data, pr = self.create_pr_data() self.set_counts() pr.changed_files = ["docs/foo", 'foo/bar'] pr.save() self.compare_counts(events=1, jobs=2, ready=1, prs=1, active=2, active_repos=1) # We have labels now, so the new event should only have the default plus the matched with self.settings(INSTALLED_GITSERVERS=[ utils.github_config( recipe_label_activation=utils.default_labels()) ]): alt = self.set_label_on_recipes() pr.head_commit.sha = "123" self.set_counts() pr.save() self.compare_counts(jobs=3, ready=1, events=1, commits=1, active=3, canceled=2, events_canceled=1, num_changelog=2, num_events_completed=1, num_jobs_completed=2, num_pr_alts=1) self.assertEqual(alt[0].jobs.count(), 1) @override_settings(INSTALLED_GITSERVERS=[ utils.github_config(recipe_label_activation=utils.default_labels()) ]) def test_matched_with_no_labels(self): # No labels setup, should just do the normal c1_data, c2_data, pr = self.create_pr_data() self.set_counts() pr.changed_files = ["docs/foo", 'docs/bar'] pr.save() self.compare_counts(events=1, jobs=2, ready=1, prs=1, active=2, active_repos=1) def test_with_no_matched(self): # No labels setup, should just do the normal c1_data, c2_data, pr = self.create_pr_data() self.set_counts() pr.changed_files = ["bar/foo", 'foo/bar'] pr.save() self.compare_counts(events=1, jobs=2, ready=1, prs=1, active=2, active_repos=1) # We have labels now, so the new event should only have the default plus the matched with self.settings(INSTALLED_GITSERVERS=[ utils.github_config( recipe_label_activation=utils.default_labels()) ]): alt = self.set_label_on_recipes() pr.head_commit.sha = "123" self.set_counts() pr.save() self.compare_counts(jobs=2, ready=1, events=1, commits=1, active=2, canceled=2, events_canceled=1, num_changelog=2, num_events_completed=1, num_jobs_completed=2) self.assertEqual(alt[0].jobs.count(), 0) @patch.object(api.GitHubAPI, 'remove_pr_label') def test_failed_but_allowed_label(self, mock_label): # Make sure any failed but allowed label is removed # on pushes c1_data, c2_data, pr = self.create_pr_data() self.set_counts() pr.save() self.compare_counts(events=1, jobs=2, ready=1, prs=1, active=2, active_repos=1) # Doesn't get called when a PR is first created self.assertEqual(mock_label.call_count, 0) with self.settings(INSTALLED_GITSERVERS=[ utils.github_config(failed_but_allowed_label_name="foo") ]): # We have labels now, so the new event should only have the default plus the matched pr.head_commit.sha = "123" self.set_counts() pr.save() self.compare_counts(jobs=2, ready=1, events=1, commits=1, active=2, canceled=2, events_canceled=1, num_changelog=2, num_events_completed=1, num_jobs_completed=2) self.assertEqual(mock_label.call_count, 2)
def test_valid(self): """ a valid PR, should just create an event, a PR, and 2 jobs """ c1_data, c2_data, pr = self.create_pr_data() self.set_counts() pr.changed_files = ["docs/foo"] pr.save() self.compare_counts(events=1, jobs=2, ready=1, prs=1, active=2, active_repos=1) # save the same pull request and make sure the jobs haven't changed # and no new events were created. self.set_counts() pr.save() self.compare_counts() # save the same pull request and make sure the jobs haven't changed # and no new events were created. self.set_counts() pr.save() self.compare_counts() # new sha should create new event and cancel old one pr.head_commit.sha = "5678" self.set_counts() pr.save() self.compare_counts(jobs=2, ready=1, events=1, commits=1, active=2, canceled=2, events_canceled=1, num_changelog=2, num_events_completed=1, num_jobs_completed=2) # should now add the alternative job automatically with self.settings(INSTALLED_GITSERVERS=[ utils.github_config( recipe_label_activation=utils.default_labels()) ]): alt = self.set_label_on_recipes() pr.changed_files = ["docs/foo", 'other/bar'] pr.head_commit.sha = "6789" self.set_counts() pr.save() self.compare_counts(jobs=3, ready=1, events=1, commits=1, active=3, canceled=2, events_canceled=1, num_changelog=2, num_events_completed=1, num_jobs_completed=2, num_pr_alts=1) self.assertEqual(alt[0].jobs.count(), 1) # new commit should add the previously added alternate job pr.changed_files = [] pr.head_commit.sha = "789" self.set_counts() pr.save() self.compare_counts(jobs=3, ready=1, events=1, commits=1, active=3, canceled=3, events_canceled=1, num_changelog=3, num_events_completed=1, num_jobs_completed=3) self.assertEqual(alt[0].jobs.count(), 2) # new commit should only add the alt job and its dependency pr.changed_files = ["docs/foo"] pr.head_commit.sha = "89" self.set_counts() pr.save() self.compare_counts(jobs=2, ready=1, events=1, commits=1, active=2, canceled=3, events_canceled=1, num_changelog=3, num_events_completed=1, num_jobs_completed=3) self.assertEqual(alt[0].jobs.count(), 3)
def test_pull_request(self, mock_del, mock_get, mock_post): url = reverse('ci:github:webhook', args=[self.build_user.build_key]) changed_files = utils.Response([{"filename": "foo"}]) mock_get.return_value = changed_files mock_del.return_value = utils.Response() mock_post.return_value = utils.Response() data = self.get_data('pr_open_01.json') py_data = json.loads(data) py_data['pull_request']['base']['repo']['owner'][ 'login'] = self.owner.name py_data['pull_request']['base']['repo']['name'] = self.repo.name py_data['pull_request']['title'] = '[WIP] testTitle' # no events or jobs on a work in progress self.set_counts() response = self.client_post_json(url, py_data) self.assertEqual(response.status_code, 200) self.assertEqual(response.content, b"OK") self.compare_counts() self.assertEqual(mock_get.call_count, 0) self.assertEqual(mock_del.call_count, 0) self.assertEqual(mock_post.call_count, 0) # no events or jobs on a work in progress py_data['pull_request']['title'] = 'WIP: testTitle' self.set_counts() response = self.client_post_json(url, py_data) self.assertEqual(response.status_code, 200) self.assertEqual(response.content, b"OK") self.compare_counts() self.assertEqual(mock_get.call_count, 0) self.assertEqual(mock_del.call_count, 0) self.assertEqual(mock_post.call_count, 0) # should produce a job and an event py_data['pull_request']['title'] = 'testTitle' self.set_counts() mock_get.call_count = 0 response = self.client_post_json(url, py_data) self.assertEqual(response.status_code, 200) self.assertEqual(response.content, b"OK") self.compare_counts( jobs=2, ready=1, events=1, commits=2, users=1, repos=1, branches=1, prs=1, active=2, active_repos=1, ) ev = models.Event.objects.latest() self.assertEqual(ev.trigger_user, py_data['pull_request']['user']['login']) self.assertEqual(mock_get.call_count, 1) # for changed files self.assertEqual(mock_del.call_count, 0) self.assertEqual(mock_post.call_count, 0) # should just close the event py_data['action'] = 'closed' mock_get.call_count = 0 self.set_counts() response = self.client_post_json(url, py_data) self.assertEqual(response.status_code, 200) self.assertEqual(response.content, b"OK") self.compare_counts(pr_closed=True) self.assertEqual(mock_get.call_count, 1) # for changed files self.assertEqual(mock_del.call_count, 0) self.assertEqual(mock_post.call_count, 0) # should just open the same event py_data['action'] = 'reopened' mock_get.call_count = 0 self.set_counts() response = self.client_post_json(url, py_data) self.assertEqual(response.status_code, 200) self.assertEqual(response.content, b"OK") self.compare_counts() self.assertEqual(mock_get.call_count, 1) # for changed files self.assertEqual(mock_del.call_count, 0) self.assertEqual(mock_post.call_count, 0) # nothing should change py_data['action'] = 'labeled' mock_get.call_count = 0 self.set_counts() response = self.client_post_json(url, py_data) self.assertEqual(response.status_code, 200) self.assertEqual(response.content, b"OK") self.compare_counts() self.assertEqual(mock_get.call_count, 0) self.assertEqual(mock_del.call_count, 0) self.assertEqual(mock_post.call_count, 0) # nothing should change py_data['action'] = 'bad_action' self.set_counts() response = self.client_post_json(url, py_data) self.assertEqual(response.status_code, 400) self.assertIn(b"bad_action", response.content) self.compare_counts() self.assertEqual(mock_get.call_count, 0) self.assertEqual(mock_del.call_count, 0) self.assertEqual(mock_post.call_count, 0) # on synchronize we also remove labels on the PR py_data['action'] = 'synchronize' with self.settings( INSTALLED_GITSERVERS=[utils.github_config( remote_update=True)]): label_name = self.server.server_config( )["remove_pr_label_prefix"][0] mock_get.return_value = None remove_label = utils.Response([{"name": label_name}]) mock_get.side_effect = [remove_label, changed_files] mock_del.return_value = utils.Response() self.set_counts() response = self.client_post_json(url, py_data) self.assertEqual(response.status_code, 200) self.assertEqual(response.content, b"OK") self.compare_counts() self.assertEqual( mock_get.call_count, 2) # 1 for changed files, 1 in remove_pr_todo_labels self.assertEqual(mock_del.call_count, 1) # for remove_pr_todo_labels self.assertEqual(mock_post.call_count, 0) # new sha, new event py_data['pull_request']['head']['sha'] = '2345' mock_get.side_effect = [remove_label, changed_files] mock_get.call_count = 0 mock_del.call_count = 0 self.set_counts() response = self.client_post_json(url, py_data) self.assertEqual(response.status_code, 200) self.assertEqual(response.content, b"OK") self.compare_counts( jobs=2, ready=1, events=1, commits=1, active=2, canceled=2, events_canceled=1, num_changelog=2, num_events_completed=1, num_jobs_completed=2, ) self.assertEqual(mock_del.call_count, 1) self.assertEqual( mock_get.call_count, 2) # 1 for changed files, 1 in remove_pr_todo_labels self.assertEqual(mock_post.call_count, 2) # 2 new jobs pending status
# limitations under the License. from __future__ import unicode_literals, absolute_import from ci.client.tests import ClientTester from django.test import override_settings from ci.client import ProcessCommands from ci.tests import utils from ci import models from ci.github import api from requests_oauthlib import OAuth2Session from mock import patch import re @override_settings( INSTALLED_GITSERVERS=[utils.github_config(remote_update=True)]) class Tests(ClientTester.ClientTester): def test_find_in_output(self): s = " Foo=some value" r = ProcessCommands.find_in_output(s, "Foo") self.assertEqual(r, None) s = s.strip() r = ProcessCommands.find_in_output(s, "Foo") s = "Foo = bar" r = ProcessCommands.find_in_output(s, "Foo") self.assertEqual(r, None) s = "Foo=" r = ProcessCommands.find_in_output(s, "Foo") self.assertEqual(r, "") s = "Another line\nFoo=bar"
def test_get_user_repos_info(self): request = self.factory.get('/') request.session = self.client.session repos = [] for i in range(3): repo = utils.create_repo(name="repo%s" % i) repo.active = True repo.save() branch = utils.create_branch(name="branch0", user=repo.user, repo=repo) branch.status = models.JobStatus.SUCCESS branch.save() ev = utils.create_event(branch1=branch, branch2=branch, user=repo.user) utils.create_job(event=ev, user=repo.user) repos.append(repo) # user not logged in repo_status, evinfo, default = views.get_user_repos_info(request) self.assertEqual(len(repo_status), 3) self.assertEqual(len(evinfo), 3) self.assertFalse(default) # user not logged in, default enforced request = self.factory.get('/?default') repo_status, evinfo, default = views.get_user_repos_info(request) self.assertEqual(len(repo_status), 3) self.assertEqual(len(evinfo), 3) self.assertTrue(default) request = self.factory.get('/') user = repos[0].user utils.simulate_login(self.client.session, user) request.session = self.client.session # user is logged in but no prefs set repo_status, evinfo, default = views.get_user_repos_info(request) self.assertEqual(len(repo_status), 3) self.assertEqual(len(evinfo), 3) self.assertFalse(default) # user is logged in, add repos to prefs for i in range(3): user.preferred_repos.add(repos[i]) repo_status, evinfo, default = views.get_user_repos_info(request) self.assertEqual(len(repo_status), i+1) self.assertEqual(len(evinfo), i+1) self.assertFalse(default) # user has one pref but default is enforced user.preferred_repos.clear() user.preferred_repos.add(repos[0]) request = self.factory.get('/?default') repo_status, evinfo, default = views.get_user_repos_info(request) self.assertEqual(len(repo_status), 3) self.assertEqual(len(evinfo), 3) self.assertTrue(default) with self.settings(INSTALLED_GITSERVERS=[utils.github_config(hostname="server_does_not_exist")]): user.preferred_repos.clear() user.preferred_repos.add(repos[0]) request = self.factory.get('/') repo_status, evinfo, default = views.get_user_repos_info(request) self.assertEqual(len(repo_status), 3) self.assertEqual(len(evinfo), 3) self.assertFalse(default)
# Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import unicode_literals, absolute_import from django.test import SimpleTestCase from django.test import override_settings from ci.tests import utils as test_utils from client import inl_client, BaseClient import os, shutil, tempfile, pwd from mock import patch @override_settings(INSTALLED_GITSERVERS=[test_utils.github_config()]) class CommandlineINLClientTests(SimpleTestCase): def setUp(self): self.log_dir = tempfile.mkdtemp() self.orig_home_env = os.environ['HOME'] self.orig_civet_home_env = os.environ.get('CIVET_HOME', None) if self.orig_civet_home_env: del os.environ['CIVET_HOME'] os.environ['HOME'] = self.log_dir self.civet_dir = '{}/civet'.format(self.log_dir) os.mkdir(self.civet_dir) logs_dir = self.civet_dir + '/logs' os.mkdir(logs_dir) def tearDown(self): shutil.rmtree(self.log_dir)
def test_ready_jobs_with_current_event(self): """ If a branch is specified with "auto_cancel_push_events_except_current" then jobs on the "current" event get priority over subsequent events. Basically the normal sort of (-priority, created) gets changed to (created, -priority) for those jobs. """ user = utils.get_test_user() r0 = utils.create_recipe(name='recipe0', user=user) r0.priority = 30 r0.save() r1 = utils.create_recipe(name='recipe1', user=user) r1.priority = 20 r1.save() r2 = utils.create_recipe(name='recipe2', user=user) r2.priority = 10 r2.save() e0 = utils.create_event(user=user, cause=models.Event.PUSH) j0 = utils.create_job(recipe=r0, event=e0, user=user) j1 = utils.create_job(recipe=r1, event=e0, user=user) j2 = utils.create_job(recipe=r2, event=e0, user=user) e1 = utils.create_event(user=user, cause=models.Event.PUSH, commit1='12345') j3 = utils.create_job(recipe=r0, event=e1, user=user) j4 = utils.create_job(recipe=r1, event=e1, user=user) j5 = utils.create_job(recipe=r2, event=e1, user=user) for j in models.Job.objects.all(): j.active = True j.ready = True j.save() url = reverse('ci:client:ready_jobs', args=[user.build_key, 'client']) self.set_counts() response = self.client.get(url) self.compare_counts(num_clients=1) self.assertEqual(response.status_code, 200) data = response.json() self.assertIn('jobs', data) jobs = data["jobs"] self.assertEqual(len(jobs), 6) # Normally jobs are sorted by (-priority, created) self.assertEqual(jobs[0]["id"], j0.pk) self.assertEqual(jobs[1]["id"], j3.pk) self.assertEqual(jobs[2]["id"], j1.pk) self.assertEqual(jobs[3]["id"], j4.pk) self.assertEqual(jobs[4]["id"], j2.pk) self.assertEqual(jobs[5]["id"], j5.pk) repo_name = "%s/%s" % (e0.base.branch.repository.user.name, e0.base.branch.repository.name) branch_name = e0.base.branch.name repo_settings = { repo_name: { "branch_settings": { branch_name: { "auto_cancel_push_events_except_current": True } } } } with self.settings(INSTALLED_GITSERVERS=[ utils.github_config(repo_settings=repo_settings) ]): response = self.client.get(url) self.compare_counts(num_clients=1) self.assertEqual(response.status_code, 200) data = response.json() self.assertIn('jobs', data) jobs = data["jobs"] self.assertEqual(len(jobs), 6) # Jobs now are (created, -priority) self.assertEqual(jobs[0]["id"], j0.pk) self.assertEqual(jobs[1]["id"], j1.pk) self.assertEqual(jobs[2]["id"], j2.pk) self.assertEqual(jobs[3]["id"], j3.pk) self.assertEqual(jobs[4]["id"], j4.pk) self.assertEqual(jobs[5]["id"], j5.pk)