Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions create_pr_from_main/.gitmastery-exercise.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"exercise_name": "create-pr-from-main",
"tags": [],
"requires_git": true,
"requires_github": true,
"base_files": {},
"exercise_repo": {
"repo_type": "remote",
"repo_name": "languages",
"repo_title": "gm-languages",
"create_fork": true,
"init": null
}
}
1 change: 1 addition & 0 deletions create_pr_from_main/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
See https://git-mastery.github.io/lessons/prsCreate/exercise-create-pr-from-main.html
Empty file added create_pr_from_main/__init__.py
Empty file.
8 changes: 8 additions & 0 deletions create_pr_from_main/download.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from pathlib import Path

from exercise_utils.exercise_config import add_pr_config
from exercise_utils.gitmastery import create_start_tag

def setup(verbose: bool = False):
create_start_tag(verbose)
add_pr_config(pr_repo_full_name="git-mastery/gm-languages", config_path=Path("../"))
152 changes: 152 additions & 0 deletions create_pr_from_main/test_verify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import json
from contextlib import contextmanager
from pathlib import Path
from unittest.mock import PropertyMock, patch

import pytest
from exercise_utils.test import assert_output
from git.repo import Repo
from git_autograder import (
GitAutograderExercise,
GitAutograderStatus,
GitAutograderWrongAnswerException,
)
from git_autograder.pr import GitAutograderPr

from .verify import (
EXPECTED_CONTENT_STEP_3,
JAVA_FILE_MISSING,
JAVA_INVALID_CONTENT,
PR_MISSING,
WRONG_HEAD_BRANCH,
verify,
)


@pytest.fixture
def exercise(tmp_path: Path) -> GitAutograderExercise:
repo_dir = tmp_path / "ignore-me"
repo_dir.mkdir()
Repo.init(repo_dir)

with open(tmp_path / ".gitmastery-exercise.json", "a") as config_file:
config_file.write(
json.dumps(
{
"exercise_name": "create_pr_from_main",
"tags": [],
"requires_git": True,
"requires_github": True,
"base_files": {},
"exercise_repo": {
"repo_type": "local",
"repo_name": "ignore-me",
"init": True,
"create_fork": None,
"repo_title": "gm-shapes",
"pr_number": 1,
"pr_repo_full_name": "dummy/repo",
},
"downloaded_at": None,
}
)
)

with patch(
"git_autograder.pr.fetch_pull_request_data",
return_value={},
):
return GitAutograderExercise(exercise_path=tmp_path)


class FakeCommit:
def __init__(self, java_content: str | None) -> None:
self._java_content = java_content

@contextmanager
def file(self, file_path: str):
yield self._java_content


def _run_verify(
exercise: GitAutograderExercise,
pr_numbers: list[int] = [],
head_branch: str = "",
java_content: str | None = None,
):
fake_commit = FakeCommit(java_content)
with (
patch("create_pr_from_main.verify.get_github_username", return_value="dummy"),
patch(
"create_pr_from_main.verify.get_pr_numbers_by_author",
return_value=pr_numbers,
),
patch("create_pr_from_main.verify.add_pr_config"),
patch.object(exercise, "fetch_pr", return_value=None),
patch.object(
GitAutograderPr,
"head_branch",
new_callable=PropertyMock,
return_value=head_branch,
),
patch.object(
GitAutograderPr,
"last_user_commit",
new_callable=PropertyMock,
return_value=fake_commit,
),
):
return verify(exercise)


def test_success(exercise: GitAutograderExercise):
output = _run_verify(
exercise,
pr_numbers=[123],
head_branch="main",
java_content="\n".join(EXPECTED_CONTENT_STEP_3),
)

assert_output(output, GitAutograderStatus.SUCCESSFUL)


def test_pr_missing(exercise: GitAutograderExercise):
with pytest.raises(GitAutograderWrongAnswerException) as exception:
_run_verify(exercise)

assert exception.value.message == [PR_MISSING]


def test_wrong_head_branch(exercise: GitAutograderExercise):
with pytest.raises(GitAutograderWrongAnswerException) as exception:
_run_verify(
exercise,
pr_numbers=[1],
head_branch="feature/pr-branch"
)

assert exception.value.message == [WRONG_HEAD_BRANCH]


def test_java_file_missing(exercise: GitAutograderExercise):
with pytest.raises(GitAutograderWrongAnswerException) as exception:
_run_verify(
exercise,
pr_numbers=[1],
head_branch="main",
java_content=None,
)

assert exception.value.message == [JAVA_FILE_MISSING]


def test_java_content_invalid(exercise: GitAutograderExercise):
with pytest.raises(GitAutograderWrongAnswerException) as exception:
_run_verify(
exercise,
pr_numbers=[1],
head_branch="main",
java_content="wrong content\n",
)

assert exception.value.message == [JAVA_INVALID_CONTENT]
53 changes: 53 additions & 0 deletions create_pr_from_main/verify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from pathlib import Path

from git_autograder import (
GitAutograderOutput,
GitAutograderExercise,
GitAutograderStatus,
)

from exercise_utils.exercise_config import add_pr_config
from exercise_utils.github_cli import get_github_username, get_pr_numbers_by_author


JAVA_FILE_MISSING = "Java.txt file is missing in the latest commit on main branch."
JAVA_INVALID_CONTENT = "The content in Java.txt in main branch is not correct."
MUTIPLE_PRS = "Multiple PRs found. The lastest pr will be used in grading."
PR_MISSING = "No PR is found."
WRONG_HEAD_BRANCH = "The PR's head branch is not 'main'."


EXPECTED_CONTENT_STEP_3 = ["1955, by James Gosling"]


def verify(exercise: GitAutograderExercise) -> GitAutograderOutput:
username = get_github_username(False)
target_repo = f"git-mastery/{exercise.config.exercise_repo.repo_title}"
comments = []

pr_numbers = get_pr_numbers_by_author(username, target_repo, False)
if not pr_numbers:
raise exercise.wrong_answer([PR_MISSING])
if len(pr_numbers) > 1:
comments.append(MUTIPLE_PRS)
pr_number = pr_numbers[-1]

add_pr_config(pr_number=pr_number, config_path=Path("./"))
exercise.fetch_pr()

if exercise.repo.prs.pr.head_branch != "main":
comments.append(WRONG_HEAD_BRANCH)
raise exercise.wrong_answer(comments)

latest_user_commit = exercise.repo.prs.pr.last_user_commit
with latest_user_commit.file("Java.txt") as content:
if content is None:
comments.append(JAVA_FILE_MISSING)
raise exercise.wrong_answer(comments)
extracted_content = [line.strip() for line in content.splitlines() if line.strip() != ""]
if extracted_content != EXPECTED_CONTENT_STEP_3:
comments.append(JAVA_INVALID_CONTENT)
raise exercise.wrong_answer(comments)

comments.append("Good job creating the PR and pushing commits!")
return exercise.to_output(comments, GitAutograderStatus.SUCCESSFUL)
Loading