Example #1
0
    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")
Example #2
0
    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)
Example #3
0
    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)
Example #4
0
    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)
Example #5
0
    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)
Example #6
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)
Example #7
0
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
Example #8
0
    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)
Example #9
0
 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)
Example #10
0
    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)
Example #11
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()
Example #12
0
    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)
Example #13
0
    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)
Example #14
0
    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)
Example #15
0
    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()
Example #16
0
    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)
Example #17
0
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)
Example #18
0
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)
Example #19
0
    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)
Example #20
0
    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
Example #21
0
# 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"
Example #22
0
    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)
Example #23
0
# 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)
Example #24
0
    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)