Example #1
0
    def test_opens_issue_if_update_rejected(
        self, tmpdir, with_student_repos, extra_args
    ):
        master_repo = assignment_names[0]
        conflict_repo = plug.generate_repo_name(STUDENT_TEAMS[0], master_repo)
        filename = "superfile.super"
        text = "some epic content\nfor this file!"
        # update the master repo
        update_repo(master_repo, filename, text)
        # conflicting update in the student repo
        update_repo(conflict_repo, "somefile.txt", "some other content")

        issue = plug.Issue(title="Oops, push was rejected!", body="")
        issue_file = pathlib.Path(str(tmpdir)) / "issue.md"
        issue_file.write_text(issue.title)

        command = " ".join(
            [
                REPOBEE_GITLAB,
                *repobee_plug.cli.CoreCommand.repos.update.as_name_tuple(),
                *TEMPLATE_ORG_ARG,
                *BASE_ARGS,
                "-a",
                master_repo,
                *STUDENTS_ARG,
                "--issue",
                issue_file.name,
            ]
        )

        result = run_in_docker_with_coverage(command, extra_args=extra_args)

        assert result.returncode == 0
        assert_repos_contain(STUDENT_TEAMS[1:], [master_repo], filename, text)
        assert_issues_exist(STUDENT_TEAMS[0:1], [master_repo], issue)
Example #2
0
    def test_assign_to_nonexisting_students(
        self, with_student_repos, extra_args
    ):
        """If you try to assign reviews where one or more of the allocated
        student repos don't exist, there should be an error.
        """
        assignment_name = assignment_names[1]
        non_existing_group = "non-existing-group"
        student_team_names = STUDENT_TEAM_NAMES + [non_existing_group]

        command = " ".join(
            [
                REPOBEE_GITLAB,
                *repobee_plug.cli.CoreCommand.reviews.assign.as_name_tuple(),
                *BASE_ARGS_NO_TB,
                "-a",
                assignment_name,
                "-s",
                *student_team_names,
                "-n",
                "1",
            ]
        )

        result = run_in_docker_with_coverage(command, extra_args=extra_args)
        output = result.stdout.decode("utf-8")

        assert (
            "[ERROR] NotFoundError: Can't find repos: {}".format(
                plug.generate_repo_name(non_existing_group, assignment_name)
            )
            in output
        )
        assert result.returncode == 1
        assert_num_issues(STUDENT_TEAMS, [assignment_name], 0)
Example #3
0
    def test_closes_only_matched_issues(self, open_issues, extra_args):
        """Test that close-issues respects the regex."""
        assert len(open_issues) == 2, "expected there to be only 2 open issues"
        close_issue = open_issues[0]
        open_issue = open_issues[1]
        command = " ".join(
            [
                REPOBEE_GITLAB,
                *repobee_plug.cli.CoreCommand.issues.close.as_name_tuple(),
                *BASE_ARGS,
                *MASTER_REPOS_ARG,
                *STUDENTS_ARG,
                "-r",
                close_issue.title,
            ]
        )

        result = run_in_docker_with_coverage(command, extra_args=extra_args)

        assert result.returncode == 0
        assert_issues_exist(
            STUDENT_TEAM_NAMES,
            assignment_names,
            close_issue,
            expected_state="closed",
        )
        assert_issues_exist(
            STUDENT_TEAM_NAMES,
            assignment_names,
            open_issue,
            expected_state="opened",
        )
Example #4
0
    def test_setup_twice(self, extra_args):
        """Setting up twice should have the same effect as setting up once."""
        command = " ".join([
            REPOBEE_GITLAB,
            *repobee_plug.cli.CoreCommand.repos.setup.as_name_tuple(),
            *BASE_ARGS,
            *TEMPLATE_ORG_ARG,
            *MASTER_REPOS_ARG,
            *STUDENTS_ARG,
        ])

        result = run_in_docker_with_coverage(command, extra_args=extra_args)
        result = run_in_docker_with_coverage(command, extra_args=extra_args)
        assert result.returncode == 0
        assert_repos_exist(STUDENT_TEAMS, assignment_names)
        assert_on_groups(STUDENT_TEAMS)
Example #5
0
    def test_clean_setup_in_subgroup(self, extra_args):
        """It should be possible to use a subgroup as the target org."""
        gl, template_group, target_group = gitlab_and_groups()
        subgroup_name = "bestgroup"
        subgroup_full_path = f"{target_group.path}/{subgroup_name}"
        gl.groups.create(
            dict(
                name=subgroup_name,
                path=subgroup_name,
                parent_id=target_group.id,
            ))

        base_args = [
            arg if arg != ORG_NAME else subgroup_full_path for arg in BASE_ARGS
        ]

        command = " ".join([
            REPOBEE_GITLAB,
            *repobee_plug.cli.CoreCommand.repos.setup.as_name_tuple(),
            *base_args,
            *TEMPLATE_ORG_ARG,
            *MASTER_REPOS_ARG,
            *STUDENTS_ARG,
        ])

        result = run_in_docker_with_coverage(command, extra_args=extra_args)
        assert result.returncode == 0
        assert_repos_exist(STUDENT_TEAMS,
                           assignment_names,
                           org_name=subgroup_full_path)
Example #6
0
    def test_end_all_reviews(self, with_reviews, extra_args):
        assignment_name, review_teams = with_reviews
        command = " ".join(
            [
                REPOBEE_GITLAB,
                *repobee_plug.cli.CoreCommand.reviews.end.as_name_tuple(),
                *BASE_ARGS,
                "-a",
                assignment_name,
                *STUDENTS_ARG,
            ]
        )

        result = run_in_docker_with_coverage(command, extra_args=extra_args)

        def assert_no_actual_groups(expected, actual):
            assert not actual

        assert result.returncode == 0
        # student teams should still exist
        assert_on_groups(STUDENT_TEAMS)
        # review teams should not
        assert_on_groups(
            review_teams, all_groups_assertion=assert_no_actual_groups
        )
Example #7
0
    def test_assign_one_review(self, with_student_repos, extra_args):
        assignment_name = assignment_names[1]
        expected_review_teams = [
            plug.StudentTeam(
                members=[],
                name=plug.generate_review_team_name(student_team_name,
                                                    assignment_name),
            ) for student_team_name in STUDENT_TEAM_NAMES
        ]
        command = " ".join([
            REPOBEE_GITLAB,
            *repobee_plug.cli.CoreCommand.reviews.assign.as_name_tuple(),
            *BASE_ARGS,
            "-a",
            assignment_name,
            *STUDENTS_ARG,
            "-n",
            "1",
        ])
        group_assertion = expected_num_members_group_assertion(
            expected_num_members=1)

        result = run_in_docker_with_coverage(command, extra_args=extra_args)

        assert result.returncode == 0
        assert_on_groups(expected_review_teams,
                         single_group_assertion=group_assertion)
        assert_num_issues(STUDENT_TEAMS, [assignment_name], 1)
        assert_issues_exist(
            STUDENT_TEAMS,
            [assignment_name],
            _repobee.command.peer.DEFAULT_REVIEW_ISSUE,
            expected_num_asignees=1,
        )
Example #8
0
    def test_clone_does_not_alter_existing_dirs(self, with_student_repos,
                                                tmpdir, extra_args):
        """Test that clone does not clobber existing directories."""
        team_with_local_repos = STUDENT_TEAMS[0]
        teams_without_local_repos = STUDENT_TEAMS[1:]

        expected_dir_hashes = []
        for template_repo_name in assignment_names:
            new_dir = plug.fileutils.generate_repo_path(
                str(tmpdir), team_with_local_repos.name, template_repo_name)
            new_dir.mkdir(parents=True)
            new_file = new_dir / "file"
            new_file.write_text(str(new_dir), encoding="utf-8")
            expected_dir_hashes.append((new_dir, hash_directory(new_dir)))
            repobee_testhelpers.funcs.initialize_repo(new_dir)

        command = " ".join([
            REPOBEE_GITLAB,
            *repobee_plug.cli.CoreCommand.repos.clone.as_name_tuple(),
            *BASE_ARGS,
            *MASTER_REPOS_ARG,
            *STUDENTS_ARG,
        ])

        result = run_in_docker_with_coverage(command, extra_args=extra_args)

        assert result.returncode == 0
        assert_cloned_repos(teams_without_local_repos, assignment_names,
                            tmpdir)
        for dirpath, expected_hash in expected_dir_hashes:
            dirhash = hash_directory(dirpath)
            assert dirhash == expected_hash, "hash mismatch for " + dirpath
Example #9
0
    def test_expect_too_many_reviews(self, with_reviews, extra_args):
        """Test that warnings are printed if a student is assigned to fewer
        review teams than expected.
        """
        assignment_name, _ = with_reviews
        num_reviews = 0
        actual_assigned_reviews = 1
        num_expected_reviews = 2
        assignment_name = assignment_names[1]
        warning_template = (
            r"\[WARNING\] Expected {} to be assigned to {} review teams, but "
            "found {}. Review teams may have been tampered with."
        )
        pattern_template = r"{}.*{}.*{}.*\w+-{}.*"
        expected_output_patterns = [
            pattern_template.format(
                team_name,
                str(num_reviews),
                str(actual_assigned_reviews - num_reviews),
                assignment_name,
            )
            for team_name in STUDENT_TEAM_NAMES
        ] + [
            warning_template.format(
                team_name,
                str(num_expected_reviews),
                str(actual_assigned_reviews),
            )
            for team_name in STUDENT_TEAM_NAMES
        ]
        unexpected_output_patterns = [r"\[ERROR\]"]

        command = " ".join(
            [
                REPOBEE_GITLAB,
                *repobee_plug.cli.CoreCommand.reviews.check.as_name_tuple(),
                *BASE_ARGS,
                "-a",
                assignment_name,
                *STUDENTS_ARG,
                "--num-reviews",
                str(num_expected_reviews),
                "--title-regex",
                "Review",
            ]
        )

        result = run_in_docker_with_coverage(command, extra_args=extra_args)
        output = result.stdout.decode("utf-8")

        assert result.returncode == 0
        search_flags = re.MULTILINE
        for expected_pattern in expected_output_patterns:
            assert re.search(expected_pattern, output, search_flags)
        for unexpected_pattern in unexpected_output_patterns:
            assert not re.search(unexpected_pattern, output, search_flags)
Example #10
0
    def test_clone_twice(self, with_student_repos, tmpdir, extra_args):
        """Cloning twice in a row should have the same effect as cloning
        once.
        """
        command = " ".join([
            REPOBEE_GITLAB,
            *repobee_plug.cli.CoreCommand.repos.clone.as_name_tuple(),
            *BASE_ARGS,
            *MASTER_REPOS_ARG,
            *STUDENTS_ARG,
        ])

        first_result = run_in_docker_with_coverage(command,
                                                   extra_args=extra_args)
        second_result = run_in_docker_with_coverage(command,
                                                    extra_args=extra_args)

        assert first_result.returncode == 0
        assert second_result.returncode == 0
        assert_cloned_repos(STUDENT_TEAMS, assignment_names, tmpdir)
Example #11
0
    def test_discover_repos(self, with_student_repos, tmpdir, extra_args):
        """Test that the --discover-repos option finds all student repos."""
        command = " ".join([
            REPOBEE_GITLAB,
            *repobee_plug.cli.CoreCommand.repos.clone.as_name_tuple(),
            *BASE_ARGS,
            *STUDENTS_ARG,
            "--discover-repos",
        ])

        result = run_in_docker_with_coverage(command, extra_args=extra_args)

        assert result.returncode == 0
        assert_cloned_repos(STUDENT_TEAMS, assignment_names, tmpdir)
Example #12
0
    def test_clean_setup(self, extra_args):
        """Test a first-time setup with master repos in the master org."""
        command = " ".join([
            REPOBEE_GITLAB,
            *repobee_plug.cli.CoreCommand.repos.setup.as_name_tuple(),
            *BASE_ARGS,
            *TEMPLATE_ORG_ARG,
            *MASTER_REPOS_ARG,
            *STUDENTS_ARG,
        ])

        result = run_in_docker_with_coverage(command, extra_args=extra_args)
        assert result.returncode == 0
        assert_repos_exist(STUDENT_TEAMS, assignment_names)
        assert_on_groups(STUDENT_TEAMS)
Example #13
0
    def test_clean_clone(self, with_student_repos, tmpdir, extra_args):
        """Test cloning student repos when there are no repos in the current
        working directory.
        """
        command = " ".join([
            REPOBEE_GITLAB,
            *repobee_plug.cli.CoreCommand.repos.clone.as_name_tuple(),
            *BASE_ARGS,
            *MASTER_REPOS_ARG,
            *STUDENTS_ARG,
        ])

        result = run_in_docker_with_coverage(command, extra_args=extra_args)

        assert result.returncode == 0
        assert_cloned_repos(STUDENT_TEAMS, assignment_names, tmpdir)
Example #14
0
    def test_happy_path(self, local_master_repos, extra_args):
        """Migrate a few repos from the existing master repo into the target
        organization.
        """
        command = " ".join([
            REPOBEE_GITLAB,
            *repobee_plug.cli.CoreCommand.repos.migrate.as_name_tuple(),
            *BASE_ARGS,
            *MASTER_REPOS_ARG,
            "--allow-local-templates",
        ])

        result = run_in_docker_with_coverage(command, extra_args=extra_args)

        assert result.returncode == 0
        assert_template_repos_exist(local_master_repos, ORG_NAME)
Example #15
0
    def test_lists_matching_issues(
        self, open_issues, extra_args, discover_repos
    ):
        # arrange
        assert len(open_issues) == 2, "expected there to be only 2 open issues"
        matched = open_issues[0]
        unmatched = open_issues[1]
        repo_names = plug.generate_repo_names(STUDENT_TEAMS, assignment_names)

        issue_pattern_template = r"^.*{}/#\d:\s+{}.*by {}.?$"
        expected_issue_output_patterns = [
            issue_pattern_template.format(repo_name, matched.title, TEACHER)
            for repo_name in repo_names
        ]
        unexpected_issue_output_patterns = [
            issue_pattern_template.format(repo_name, unmatched.title, TEACHER)
            for repo_name in repo_names
        ] + [
            r"\[ERROR\]"
        ]  # any kind of error is bad

        repo_arg = ["--discover-repos"] if discover_repos else MASTER_REPOS_ARG
        command = " ".join(
            [
                REPOBEE_GITLAB,
                *repobee_plug.cli.CoreCommand.issues.list.as_name_tuple(),
                *BASE_ARGS,
                *repo_arg,
                *STUDENTS_ARG,
                "-r",
                matched.title,
            ]
        )

        # act
        result = run_in_docker_with_coverage(command, extra_args=extra_args)
        output = result.stdout.decode("utf-8")

        # assert
        assert result.returncode == 0
        search_flags = re.MULTILINE
        for expected_pattern in expected_issue_output_patterns:
            assert re.search(expected_pattern, output, search_flags)
        for unexpected_pattern in unexpected_issue_output_patterns:
            assert not re.search(unexpected_pattern, output, search_flags)
Example #16
0
    def test_setup_with_token_owner_as_student(self, extra_args):
        """Setting up with the token owner as a student should not cause
        a crash (see #812)
        """
        command = " ".join([
            REPOBEE_GITLAB,
            *repobee_plug.cli.CoreCommand.repos.setup.as_name_tuple(),
            *BASE_ARGS,
            *TEMPLATE_ORG_ARG,
            *MASTER_REPOS_ARG,
            "--students",
            TEACHER,
        ])

        result = run_in_docker_with_coverage(command, extra_args=extra_args)
        assert result.returncode == 0
        assert_repos_exist([plug.StudentTeam(members=[TEACHER])],
                           assignment_names)
Example #17
0
    def test_happy_path(self, with_student_repos, extra_args):
        master_repo = assignment_names[0]
        filename = "superfile.super"
        text = "some epic content\nfor this file!"
        update_repo(master_repo, filename, text)

        command = " ".join([
            REPOBEE_GITLAB,
            *repobee_plug.cli.CoreCommand.repos.update.as_name_tuple(),
            *TEMPLATE_ORG_ARG,
            *BASE_ARGS,
            "-a",
            master_repo,
            *STUDENTS_ARG,
        ])

        result = run_in_docker_with_coverage(command, extra_args=extra_args)
        assert result.returncode == 0
        assert_repos_contain(STUDENT_TEAMS, [master_repo], filename, text)
Example #18
0
    def test_clone_does_not_create_dirs_on_fail(self, with_student_repos,
                                                tmpdir, extra_args):
        """Test that no local directories are created for repos that RepoBee
        fails to pull.
        """
        non_existing_assignment_names = ["non-existing-1", "non-existing-2"]
        command = " ".join([
            REPOBEE_GITLAB,
            *repobee_plug.cli.CoreCommand.repos.clone.as_name_tuple(),
            *BASE_ARGS,
            *STUDENTS_ARG,
            "-a",
            " ".join(non_existing_assignment_names),
        ])

        result = run_in_docker_with_coverage(command, extra_args=extra_args)

        assert result.returncode == 0
        assert [dir for dir in os.listdir(str(tmpdir))
                if os.path.isdir(dir)] == []
Example #19
0
    def test_end_non_existing_reviews(self, with_reviews, extra_args):
        _, review_teams = with_reviews
        assignment_name = assignment_names[0]
        command = " ".join([
            REPOBEE_GITLAB,
            *repobee_plug.cli.CoreCommand.reviews.end.as_name_tuple(),
            *BASE_ARGS,
            "-a",
            assignment_name,
            *STUDENTS_ARG,
        ])

        result = run_in_docker_with_coverage(command, extra_args=extra_args)

        assert result.returncode == 0
        assert_on_groups(STUDENT_TEAMS)
        assert_on_groups(
            review_teams,
            single_group_assertion=expected_num_members_group_assertion(1),
        )
Example #20
0
    def test_no_reviews_opened(self, with_reviews, extra_args):
        assignment_name, _ = with_reviews
        num_reviews = 0
        num_expected_reviews = 1
        assignment_name = assignment_names[1]
        pattern_template = r"{}.*{}.*{}.*\w+-{}.*"
        expected_output_patterns = [
            pattern_template.format(
                team_name,
                str(num_reviews),
                str(num_expected_reviews - num_reviews),
                assignment_name,
            )
            for team_name in STUDENT_TEAM_NAMES
        ]
        unexpected_output_patterns = [r"\[ERROR\]"]

        command = " ".join(
            [
                REPOBEE_GITLAB,
                *repobee_plug.cli.CoreCommand.reviews.check.as_name_tuple(),
                *BASE_ARGS,
                "-a",
                assignment_name,
                *STUDENTS_ARG,
                "--num-reviews",
                str(num_expected_reviews),
                "--title-regex",
                "Review",
            ]
        )

        result = run_in_docker_with_coverage(command, extra_args=extra_args)
        output = result.stdout.decode("utf-8")

        assert result.returncode == 0
        search_flags = re.MULTILINE
        for expected_pattern in expected_output_patterns:
            assert re.search(expected_pattern, output, search_flags)
        for unexpected_pattern in unexpected_output_patterns:
            assert not re.search(unexpected_pattern, output, search_flags)
Example #21
0
    def test_happy_path(self, tmpdir_volume_arg, tmpdir, extra_args):
        """Test opening an issue in each student repo."""
        filename = "issue.md"
        text = "{}\n{}".format(self._ISSUE.title, self._ISSUE.body)
        tmpdir.join(filename).write_text(text, encoding="utf-8")

        command = " ".join([
            REPOBEE_GITLAB,
            *repobee_plug.cli.CoreCommand.issues.open.as_name_tuple(),
            *BASE_ARGS,
            *MASTER_REPOS_ARG,
            *STUDENTS_ARG,
            "-i",
            "{}/{}".format(VOLUME_DST, filename),
        ])

        result = run_in_docker_with_coverage(command, extra_args=extra_args)

        assert result.returncode == 0
        assert_num_issues(STUDENT_TEAMS, assignment_names, 1)
        assert_issues_exist(STUDENT_TEAMS, assignment_names, self._ISSUE)
Example #22
0
    def test_setup_with_default_branch_protection_does_not_carry_over(
            self, extra_args):
        """Student repositories created when global default branch
        protection is enabled on the GitLab instance, should still not have
        default branch protection.
        """
        # arrange
        gl = gitlab.Gitlab(url=LOCAL_BASE_URL,
                           private_token=ADMIN_TOKEN,
                           ssl_verify=False)
        gl.auth()
        settings = gl.settings.get()
        settings.default_branch_protection = (
            _repobee.ext.gitlab.DefaultBranchProtection.FULL.value)
        settings.save()
        command = " ".join([
            REPOBEE_GITLAB,
            *repobee_plug.cli.CoreCommand.repos.setup.as_name_tuple(),
            *BASE_ARGS,
            *TEMPLATE_ORG_ARG,
            *MASTER_REPOS_ARG,
            *STUDENTS_ARG,
        ])

        # act
        result = run_in_docker_with_coverage(command, extra_args=extra_args)

        # assert
        assert result.returncode == 0
        api = api_instance(ORG_NAME)
        loop_ran = False
        for repo in api.get_repos():
            loop_ran = True
            assert not repo.implementation.protectedbranches.list()

        assert loop_ran, "assertion loop did not execute"