def test_run_with_extended_students_syntax(self, platform_url, tmp_path): students_file = tmp_path / "students.yml" students_file.write_text( """ some-team: members: [simon] other-team: members: [eve, alice] """.strip(), encoding=sys.getdefaultencoding(), ) expected_teams = [ plug.StudentTeam(name="some-team", members=["simon"]), plug.StudentTeam(name="other-team", members=["eve", "alice"]), ] funcs.run_repobee( f"{plug.cli.CoreCommand.teams.create} --base-url {platform_url} " f"--students-file {students_file}", plugins=[_repobee.ext.studentsyml], ) assert sorted(funcs.get_student_teams(platform_url)) == sorted( expected_teams )
def _extract_groups(args: argparse.Namespace) -> List[plug.StudentTeam]: """Extract groups from args namespace.` Args: args: A namespace object. Returns: a list of student usernames, or None of neither `students` or `students_file` is in the namespace. """ if "students" in args and args.students: students = [plug.StudentTeam(members=[s]) for s in args.students] elif "students_file" in args and args.students_file: students_file = pathlib.Path(args.students_file).resolve() if not students_file.is_file(): raise exception.FileError( "'{!s}' is not a file".format(students_file)) if not students_file.stat().st_size: raise exception.FileError("'{!s}' is empty".format(students_file)) students = [ plug.StudentTeam(members=[s for s in group.strip().split()]) for group in students_file.read_text( encoding=sys.getdefaultencoding()).split(os.linesep) if group # skip blank lines ] else: students = [] return students
def test_student_groups_parsed_correcly( self, empty_students_file, read_issue_mock, action, extra_args ): """Test that putting multiple students on the same line in the students file results in them being in the same group. """ # arrange groupings = ( ["study"], ["buddy", "shuddy"], ["grape"], ["cat", "dog", "mouse"], ) expected_groups = sorted( plug.StudentTeam(members=group) for group in groupings ) empty_students_file.write( os.linesep.join([" ".join(group) for group in groupings]) ) sys_args = [ *action.as_name_tuple(), *BASE_ARGS, "--sf", str(empty_students_file), *extra_args, ] # act parsed_args, _ = _repobee.cli.parsing.handle_args(sys_args) # assert assert sorted(parsed_args.students) == expected_groups
def with_reviews(with_student_repos, tmpdir): 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([ *str(repobee_plug.cli.CoreCommand.reviews.assign).split(), *BASE_ARGS, "-a", assignment_name, *STUDENTS_ARG, "-n", "1", ]) repobee_testhelpers.funcs.run_repobee(command, workdir=tmpdir, plugins=[_repobee.ext.gitlab]) assert_on_groups( expected_review_teams, single_group_assertion=expected_num_members_group_assertion(1), ) return (assignment_name, expected_review_teams)
def test_setup_with_wrong_case_on_student(self, tmpdir): """User names are case insensitive on GitLab, and so setup should work fine even if the case of some character in a student's username is "incorrect". See https://github.com/repobee/repobee/issues/900 """ student = STUDENT_TEAMS[0].members[0] student_wrong_case = student.upper() assert (student != student_wrong_case), "cases match, test is pointless :(" command = " ".join([ *repobee_plug.cli.CoreCommand.repos.setup.as_name_tuple(), *BASE_ARGS, *TEMPLATE_ORG_ARG, *MASTER_REPOS_ARG, "--students", student_wrong_case, ]) run_repobee(command, workdir=tmpdir, plugins=[_repobee.ext.gitlab]) assert_repos_exist([plug.StudentTeam(members=[student])], assignment_names)
def test_assign_one_review(self, with_student_repos, tmpdir): 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_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) run_repobee(command, workdir=tmpdir, plugins=[_repobee.ext.gitlab]) 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, )
def with_reviews(with_student_repos): 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, *str(repobee_plug.cli.CoreCommand.reviews.assign).split(), *BASE_ARGS, "-a", assignment_name, *STUDENTS_ARG, "-n", "1", ] ) result = run_in_docker(command) assert result.returncode == 0 assert_on_groups( expected_review_teams, single_group_assertion=expected_num_members_group_assertion(1), ) return (assignment_name, expected_review_teams)
def _post_setup( pts: List[PushSpec], newly_created: bool, api: plug.PlatformAPI ) -> Mapping[Any, Any]: teams_and_repos = [ (pt.metadata["team"], pt.metadata["repo"]) for pt in pts ] student_repos = [ plug.StudentRepo( name=repo.name, url=repo.url, team=plug.StudentTeam(name=team.name, members=team.members), ) for team, repo in teams_and_repos ] student_repos_iter = plug.cli.io.progress_bar( student_repos, desc="Executing post_setup hooks" ) return plugin.execute_tasks( student_repos_iter, plug.manager.hook.post_setup, api, cwd=None, copy_repos=False, extra_kwargs=dict(newly_created=newly_created), )
def _execute_post_setup_hook(push_tuples: List[git.Push], api: plug.PlatformAPI) -> Mapping[Any, Any]: """Execute the post_setup hook on the given push tuples. Note that the push tuples are expected to have the "team" and "repo" keys set in the metadata. """ post_setup_exists = any( ["post_setup" in dir(p) for p in plug.manager.get_plugins()]) if not post_setup_exists or not push_tuples: return {} teams_and_repos = [(pt.metadata["team"], pt.metadata["repo"]) for pt in push_tuples] student_repos = [ plug.StudentRepo( name=repo.name, url=repo.url, team=plug.StudentTeam(members=team.members), ) for team, repo in teams_and_repos ] student_repos_iter = plug.cli.io.progress_bar( student_repos, desc="Executing post_setup hooks") return plugin.execute_tasks( student_repos_iter, plug.manager.hook.post_setup, api, cwd=None, copy_repos=False, )
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, )
def _extract_groups(args: argparse.Namespace) -> List[plug.StudentTeam]: """Extract groups from args namespace.` Args: args: A namespace object. Returns: a list of student usernames, or None of neither `students` or `students_file` is in the namespace. """ if "students" in args and args.students: students = [plug.StudentTeam(members=[s]) for s in args.students] elif "students_file" in args and args.students_file: students_file = pathlib.Path(args.students_file).resolve() if not students_file.is_file(): raise exception.FileError(f"'{students_file}' is not a file") if not students_file.stat().st_size: raise exception.FileError(f"'{students_file}' is empty") students = list( plug.manager.hook.parse_students_file(students_file=students_file)) else: students = [] return students
def parse_students_file( students_file: pathlib.Path, ) -> Iterable[plug.StudentTeam]: return [ plug.StudentTeam(members=[s for s in group.strip().split()]) for group in students_file.read_text( encoding=sys.getdefaultencoding()).split(os.linesep) if group # skip blank lines ]
def test_setup_with_token_owner_as_student(self, tmpdir): """Setting up with the token owner as a student should not cause a crash (see #812) """ command = " ".join([ *repobee_plug.cli.CoreCommand.repos.setup.as_name_tuple(), *BASE_ARGS, *TEMPLATE_ORG_ARG, *MASTER_REPOS_ARG, "--students", TEACHER, ]) run_repobee(command, workdir=tmpdir, plugins=[_repobee.ext.gitlab]) assert_repos_exist([plug.StudentTeam(members=[TEACHER])], assignment_names)
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)
def get_student_teams( platform_url: str, org_name: str = const.TARGET_ORG_NAME) -> List[plug.StudentTeam]: """Like :py:func:`get_platform_teams`, but converts each team to a :py:class:`~repobee_plug.StudentTeam`. for easier comparison. Args: platform_url: URL to the directory used by the :py:class:`fakeapi.FakeAPI`. org_name: The organization to get repos from. Returns: A list of student teams. """ api = get_api(platform_url, org_name=org_name) return [ plug.StudentTeam(members=[usr.username for usr in team.members]) for team in api._teams[org_name].values() ]
def _clone_to_student_repos( team_repo_tuples: List[Tuple[plug.Team, List[plug.Repo]]], workdir: pathlib.Path, clone_dir: pathlib.Path, api: plug.PlatformAPI, ) -> List[plug.StudentRepo]: student_repos = [ plug.StudentRepo( name=repo.name, team=plug.StudentTeam(name=team.name, members=list(team.members)), url=repo.url, _path=workdir / team.name / repo.name, ) for team, repos in team_repo_tuples for repo in repos ] list( _repobee.git.clone_student_repos(student_repos, clone_dir, update_local=False, api=api)) return student_repos
def test_setup_with_twice_with_wrong_case_on_second_setup(self, tmpdir): """User names on GitLab are case insensitive, and so setting up repos for the same student with two different cases of characters should work the same as setting up just once See https://github.com/repobee/repobee/issues/900#issuecomment-830075510 for a bug where this was not the case. """ student = STUDENT_TEAMS[0].members[0] student_lowercase = STUDENT_TEAMS[0].members[0].lower() # the original should be lowercase assert (student == student_lowercase ), "expected real student username to be lowercase" student_uppercase = student_lowercase.upper() base_command = [ *repobee_plug.cli.CoreCommand.repos.setup.as_name_tuple(), *BASE_ARGS, *TEMPLATE_ORG_ARG, *MASTER_REPOS_ARG, "--students", ] def _cmd(student): return " ".join(base_command + [student]) run_repobee( _cmd(student_lowercase), workdir=tmpdir, plugins=[_repobee.ext.gitlab], ) run_repobee( _cmd(student_uppercase), workdir=tmpdir, plugins=[_repobee.ext.gitlab], ) assert_repos_exist([plug.StudentTeam(members=[student])], assignment_names)
def test_students_missing_from_grades_file_causes_crash( self, tmp_grades_file, mocked_hook_results): """Test that if a specified student is missing from the grades file, there is a crash. """ missing_team = plug.StudentTeam(members=["randomdude"]) args = argparse.Namespace( students=list(TEAMS) + [missing_team], hook_results_file="", # don't care, read_results_file is mocked grades_file=tmp_grades_file, assignments="week-1 week-2 week-4 week-6".split(), edit_msg_file=str(tmp_grades_file.parent / "editmsg.txt"), teachers=list(TEACHERS), grade_specs=[PASS_GRADESPEC_FORMAT], allow_other_states=False, ) with pytest.raises(_exception.FileError) as exc_info: csvgrades.callback(args=args) assert "student(s) {} missing from the grades file".format( missing_team.members[0]) in str(exc_info.value)
def assign_peer_reviews( assignment_names: Iterable[str], teams: Iterable[plug.StudentTeam], num_reviews: int, issue: Optional[plug.Issue], double_blind_key: Optional[str], api: plug.PlatformAPI, ) -> None: """Assign peer reviewers among the students to each student repo. Each student is assigned to review num_reviews repos, and consequently, each repo gets reviewed by num_reviews reviewers. In practice, each student repo has a review team generated (called <student-repo-name>-review), to which num_reviews _other_ students are assigned. The team itself is given pull-access to the student repo, so that reviewers can view code and open issues, but cannot modify the contents of the repo. Args: assignment_names: Names of assginments. teams: Team objects specifying student groups. num_reviews: Amount of reviews each student should perform (consequently, the amount of reviews of each repo) issue: An issue with review instructions to be opened in the considered repos. double_blind_key: If provided, use key to make double-blind review allocation. api: An implementation of :py:class:`repobee_plug.PlatformAPI` used to interface with the platform (e.g. GitHub or GitLab) instance. """ issue = issue or DEFAULT_REVIEW_ISSUE expected_repo_names = set(plug.generate_repo_names(teams, assignment_names)) fetched_teams = progresswrappers.get_teams(teams, api, desc="Fetching teams and repos") team_repo_tuples = [(team, list(api.get_team_repos(team))) for team in fetched_teams] fetched_repos = list( itertools.chain.from_iterable(repos for _, repos in team_repo_tuples)) fetched_repo_dict = {r.name: r for r in fetched_repos} missing = expected_repo_names - fetched_repo_dict.keys() if missing: raise plug.NotFoundError(f"Can't find repos: {', '.join(missing)}") if double_blind_key: plug.log.info(f"Creating anonymous repos with key: {double_blind_key}") fetched_repo_dict = _create_anonymized_repos( [(team, _only_expected_repos(repos, expected_repo_names)) for team, repos in team_repo_tuples], double_blind_key, api, ) allocations_for_output = [] for assignment_name in assignment_names: plug.echo("Allocating reviews") allocations = plug.manager.hook.generate_review_allocations( teams=teams, num_reviews=num_reviews) # adjust names of review teams review_team_specs, reviewed_team_names = list( zip(*[( plug.StudentTeam( members=alloc.review_team.members, name=_review_team_name( alloc.reviewed_team, assignment_name, key=double_blind_key, ), ), alloc.reviewed_team, ) for alloc in allocations])) review_teams = _repobee.command.teams.create_teams( review_team_specs, plug.TeamPermission.PULL, api) review_teams_progress = plug.cli.io.progress_bar( review_teams, desc="Creating review teams", total=len(review_team_specs), ) for review_team, reviewed_team_name in zip(review_teams_progress, reviewed_team_names): reviewed_repo = fetched_repo_dict[plug.generate_repo_name( reviewed_team_name, assignment_name)] review_teams_progress.write( # type: ignore f"Assigning {' and '.join(review_team.members)} " f"to review {reviewed_repo.name}") api.assign_repo(review_team, reviewed_repo, plug.TeamPermission.PULL) api.create_issue( issue.title, issue.body, reviewed_repo, # It's not possible to assign users with read-access in Gitea # FIXME redesign so Gitea does not require special handling assignees=review_team.members if not isinstance(api, _repobee.ext.gitea.GiteaAPI) else None, ) allocations_for_output.append({ "reviewed_repo": { "name": reviewed_repo.name, "url": reviewed_repo.url, }, "review_team": { "name": review_team.name, "members": review_team.members, }, }) if featflags.is_feature_enabled( featflags.FeatureFlag.REPOBEE_4_REVIEW_COMMANDS): output = dict(allocations=allocations_for_output, num_reviews=num_reviews) pathlib.Path("review_allocations.json").write_text( json.dumps(output, indent=4), encoding=sys.getdefaultencoding(), )
DIR_PATHS_WITH_SPACES.exists()), "test pre-reference error, dir must exit" assert ABSTRACT_TEST_REPO.exists(), "test pre-reference error, dir must exit" JUNIT_PATH = str(envvars.JUNIT_PATH) HAMCREST_PATH = str(envvars.HAMCREST_PATH) RTD = str(CUR_DIR / "reference-tests") IGNORE_TESTS = ["FiboTest"] CLASSPATH = "some-stuf:nice/path:path/to/unimportant/lib.jar" CLASSPATH_WITH_JARS = CLASSPATH + ":{}:{}".format(JUNIT_PATH, HAMCREST_PATH) NUM_PRIME_CHECKER_TESTS = 3 NUM_FIBO_TESTS = 2 DUMMY_TEAM = plug.StudentTeam(members=[], name="dummy") def setup_hooks( reference_tests_dir=RTD, assignments=ASSIGNMENT_NAMES, ignore_tests=[], hamcrest_path=HAMCREST_PATH, junit_path=JUNIT_PATH, verbose=False, very_verbose=False, disable_security=False, run_student_tests=False, timeout=10, ): """Return an instance of JUnit4Hooks with pre-configured arguments."""
"docker-compose down", "docker network rm development", f"rm -rf {str(DOCKER_VOLUME)}", f"git checkout {DOCKER_VOLUME}", ] TEACHER_USER = "******" TEACHER_TOKEN = ((CURRENT_DIR / "teacher_token.txt").read_text(encoding="utf8").strip()) ADMIN_USER = "******" ADMIN_TOKEN = ((CURRENT_DIR / "admin_token.txt").read_text(encoding="utf8").strip()) STUDENT_TEAMS = [ plug.StudentTeam(members=line.strip().split()) for line in (CURRENT_DIR / "students.txt").read_text("utf8").strip().split("\n") ] def main(args: List[str]) -> None: def _usage(): print("usage: python giteamanager.py <prime|setup|teardown>") sys.exit() if len(args) != 2: _usage() cmd = args[1] if cmd == "prime":
import argparse import sys import pathlib import random from unittest import mock import pytest from _repobee import plugin import repobee_plug as plug from repobee_feedback import feedback ASSIGNMENT_NAMES = ("task-1", "task-2") STUDENT_TEAMS = tuple([ plug.StudentTeam(members=members) for members in (["slarse"], ["glassey"], ["grundb", "glennol"]) ]) STUDENT_TEAM_NAMES = tuple(map(str, STUDENT_TEAMS)) PASS_ISSUE = plug.Issue(title="Pass", body="Well done!\nAbsolutely flawless!") KOMP_ISSUE = plug.Issue(title="Komplettering", body="Not perfect, you need to fix this.") FAIL_ISSUE = plug.Issue(title="Fail", body="Unfortunately, there are severe errors.") ISSUES = (PASS_ISSUE, KOMP_ISSUE, FAIL_ISSUE) random.seed(512) def _write_issue(issue: plug.Issue, path: pathlib.Path): text = "{}\n{}".format(issue.title, issue.body)
import pathlib import repobee_plug as plug from repobee_testhelpers._internal.templates import TEMPLATE_REPOS_DIR DIR = pathlib.Path(__file__).resolve().parent TOKEN = (DIR.parent / "token").read_text(encoding="utf-8").strip() ADMIN_TOKEN = "".join(reversed(TOKEN)) OAUTH_USER = "******" BASE_DOMAIN = "localhost:3000" BASE_URL = "https://" + BASE_DOMAIN ORG_NAME = "dd1337-fall2020" TEMPLATE_ORG_NAME = "dd1337-master" TEACHER = "ric" assignment_names = [p.name for p in TEMPLATE_REPOS_DIR.iterdir() if p.is_dir()] TEMPLATE_REPO_PATHS = list(dir_.absolute() for dir_ in TEMPLATE_REPOS_DIR.iterdir() if dir_.is_dir()) STUDENT_TEAMS = [ plug.StudentTeam(members=[s.strip()]) for s in (DIR.parent / "students.txt").read_text().strip().split("\n") ] STUDENT_TEAM_NAMES = [str(t) for t in STUDENT_TEAMS] STUDENT_REPO_NAMES = plug.generate_repo_names(STUDENT_TEAMS, assignment_names) BASE_ARGS_NO_TB = ["--bu", BASE_URL, "-o", ORG_NAME, "-t", TOKEN] BASE_ARGS = [*BASE_ARGS_NO_TB, "--tb"] STUDENTS_ARG = ["-s", " ".join(STUDENT_TEAM_NAMES)] MASTER_REPOS_ARG = ["-a", " ".join(assignment_names)] TEMPLATE_ORG_ARG = ["--template-org-name", TEMPLATE_ORG_NAME]
def merge_teams(teams): members = list( itertools.chain.from_iterable([team.members for team in teams]) ) return plug.StudentTeam(members=members)
def _to_student_team(name: str, data: dict) -> plug.StudentTeam: if _MEMBERS_KEY not in data: raise plug.PlugError(f"Missing members mapping for '{name}'") return plug.StudentTeam(name=name, members=data[_MEMBERS_KEY])
def assign_peer_reviews( assignment_names: Iterable[str], teams: Iterable[plug.StudentTeam], num_reviews: int, issue: Optional[plug.Issue], api: plug.PlatformAPI, ) -> None: """Assign peer reviewers among the students to each student repo. Each student is assigned to review num_reviews repos, and consequently, each repo gets reviewed by num_reviews reviewers. In practice, each student repo has a review team generated (called <student-repo-name>-review), to which num_reviews _other_ students are assigned. The team itself is given pull-access to the student repo, so that reviewers can view code and open issues, but cannot modify the contents of the repo. Args: assignment_names: Names of assginments. teams: Team objects specifying student groups. num_reviews: Amount of reviews each student should perform (consequently, the amount of reviews of each repo) issue: An issue with review instructions to be opened in the considered repos. api: An implementation of :py:class:`repobee_plug.PlatformAPI` used to interface with the platform (e.g. GitHub or GitLab) instance. """ issue = issue or DEFAULT_REVIEW_ISSUE expected_repo_names = plug.generate_repo_names(teams, assignment_names) fetched_teams = progresswrappers.get_teams(teams, api, desc="Fetching teams and repos") fetched_repos = list( itertools.chain.from_iterable(map(api.get_team_repos, fetched_teams))) fetched_repo_dict = {r.name: r for r in fetched_repos} missing = set(expected_repo_names) - set(fetched_repo_dict.keys()) if missing: raise plug.NotFoundError(f"Can't find repos: {', '.join(missing)}") for assignment_name in assignment_names: plug.echo("Allocating reviews") allocations = plug.manager.hook.generate_review_allocations( teams=teams, num_reviews=num_reviews) # adjust names of review teams review_team_specs, reviewed_team_names = list( zip(*[( plug.StudentTeam( members=alloc.review_team.members, name=plug.generate_review_team_name( str(alloc.reviewed_team), assignment_name), ), alloc.reviewed_team, ) for alloc in allocations])) review_teams = _repobee.command.teams.create_teams( review_team_specs, plug.TeamPermission.PULL, api) review_teams_progress = plug.cli.io.progress_bar( review_teams, desc="Creating review teams", total=len(review_team_specs), ) for review_team, reviewed_team_name in zip(review_teams_progress, reviewed_team_names): reviewed_repo = fetched_repo_dict[plug.generate_repo_name( reviewed_team_name, assignment_name)] review_teams_progress.write( # type: ignore f"Assigning {' and '.join(review_team.members)} " f"to review {reviewed_repo.name}") api.assign_repo(review_team, reviewed_repo, plug.TeamPermission.PULL) api.create_issue( issue.title, issue.body, reviewed_repo, assignees=review_team.members, )
def review_student_teams(): return [ plug.StudentTeam(members=[student]) for student in ("ham", "spam", "bacon", "eggs") ]
"""Module for constants used throughout the test suite.""" import string import collections from datetime import datetime from itertools import permutations import repobee_plug as plug USER = "******" ORG_NAME = "test-org" TEMPLATE_ORG_NAME = "test-master-org" HOST_URL = "https://some_enterprise_host" BASE_URL = "{}/api/v3".format(HOST_URL) # 5! = 120 different students STUDENTS = tuple( plug.StudentTeam(members=["".join(perm)]) for perm in permutations(string.ascii_lowercase[:5])) ISSUE_PATH = "some/issue/path" ISSUE = plug.Issue(title="Best title", body="This is the body of the issue.") PLUGINS = ["javac", "pylint"] TOKEN = "besttoken1337" CONFIG_TOKEN = "bestconfigtoken" FIXED_DATETIME = datetime(2009, 11, 22) User = collections.namedtuple("User", ("login", ))
def test_constructor_lowercases_member_names(self): members = ["siMON", "alIce", "EVE"] members_lowercase = ["simon", "alice", "eve"] team = plug.StudentTeam(members=members) assert team.members == members_lowercase