From 1c66f0ee66abef5916147e703a6b1ad7f063d921 Mon Sep 17 00:00:00 2001 From: "Jiahao, Woo" Date: Thu, 2 Oct 2025 08:43:59 +0100 Subject: [PATCH 1/6] [mix-messy-docs] Initial template --- mix_messy_docs/.gitmastery-exercise.json | 16 ++++++++++++++ mix_messy_docs/README.md | 27 ++++++++++++++++++++++++ mix_messy_docs/__init__.py | 0 mix_messy_docs/download.py | 2 ++ mix_messy_docs/tests/__init__.py | 0 mix_messy_docs/tests/specs/base.yml | 6 ++++++ mix_messy_docs/tests/test_verify.py | 12 +++++++++++ mix_messy_docs/verify.py | 11 ++++++++++ 8 files changed, 74 insertions(+) create mode 100644 mix_messy_docs/.gitmastery-exercise.json create mode 100644 mix_messy_docs/README.md create mode 100644 mix_messy_docs/__init__.py create mode 100644 mix_messy_docs/download.py create mode 100644 mix_messy_docs/tests/__init__.py create mode 100644 mix_messy_docs/tests/specs/base.yml create mode 100644 mix_messy_docs/tests/test_verify.py create mode 100644 mix_messy_docs/verify.py diff --git a/mix_messy_docs/.gitmastery-exercise.json b/mix_messy_docs/.gitmastery-exercise.json new file mode 100644 index 0000000..58935b1 --- /dev/null +++ b/mix_messy_docs/.gitmastery-exercise.json @@ -0,0 +1,16 @@ +{ + "exercise_name": "mix-messy-docs", + "tags": [ + "git-branch" + ], + "requires_git": true, + "requires_github": true, + "base_files": {}, + "exercise_repo": { + "repo_type": "remote", + "repo_name": "user-docs", + "repo_title": "gm-user-docs", + "create_fork": false, + "init": null + } +} \ No newline at end of file diff --git a/mix_messy_docs/README.md b/mix_messy_docs/README.md new file mode 100644 index 0000000..06fc669 --- /dev/null +++ b/mix_messy_docs/README.md @@ -0,0 +1,27 @@ +# mix-messy-docs + +You are writing user documentation for a product. You have already written documentation for a few new features, each in a separate branch. You wish to accumulate this work in a separate branch called `development` until the next product release. + +## Task + +1. Create a new branch `development`, starting from the commit tagged `v1.0` +2. Merge the `feature-search` branch onto the `development` branch, without using fast-forwarding (i.e., create a merge commit). Delete the `feature-search` branch. +3. Similarly, merge the `feature-delete` branch onto the `development` branch. Resolve any merge conflicts -- in the `features.md`, the delete feature should appear after the search feature (see below). Delete the `feature-delete` branch. + ``` + # Features + + ## Create Book + + Allows creating one book at a time. + + ## Searching for Books + + Allows searching for books by keywords. + Works only for book titles. + + ## Deleting Books + + Allows deleting books. + ``` +5. The `list` branch is not yet ready to be merged but rename it as `feature-list`, to be consistent with the naming convention you have been following in this repo. + diff --git a/mix_messy_docs/__init__.py b/mix_messy_docs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mix_messy_docs/download.py b/mix_messy_docs/download.py new file mode 100644 index 0000000..35b8011 --- /dev/null +++ b/mix_messy_docs/download.py @@ -0,0 +1,2 @@ +def setup(verbose: bool = False): + pass diff --git a/mix_messy_docs/tests/__init__.py b/mix_messy_docs/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mix_messy_docs/tests/specs/base.yml b/mix_messy_docs/tests/specs/base.yml new file mode 100644 index 0000000..00c3a53 --- /dev/null +++ b/mix_messy_docs/tests/specs/base.yml @@ -0,0 +1,6 @@ +initialization: + steps: + - type: commit + empty: true + message: Empty commit + id: start diff --git a/mix_messy_docs/tests/test_verify.py b/mix_messy_docs/tests/test_verify.py new file mode 100644 index 0000000..e4d452d --- /dev/null +++ b/mix_messy_docs/tests/test_verify.py @@ -0,0 +1,12 @@ +from git_autograder import GitAutograderTestLoader + +from ..verify import verify + +REPOSITORY_NAME = "mix-messy-docs" + +loader = GitAutograderTestLoader(__file__, REPOSITORY_NAME, verify) + + +def test_base(): + with loader.load("specs/base.yml", "start"): + pass diff --git a/mix_messy_docs/verify.py b/mix_messy_docs/verify.py new file mode 100644 index 0000000..1288d3d --- /dev/null +++ b/mix_messy_docs/verify.py @@ -0,0 +1,11 @@ +from git_autograder import ( + GitAutograderOutput, + GitAutograderExercise, + GitAutograderStatus, +) + + +def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: + # INSERT YOUR GRADING CODE HERE + + return exercise.to_output([], GitAutograderStatus.SUCCESSFUL) From 00cb404167ef6ea5893491151a135731b8a3b3d6 Mon Sep 17 00:00:00 2001 From: "Jiahao, Woo" Date: Thu, 2 Oct 2025 09:02:40 +0100 Subject: [PATCH 2/6] [tooling] Add function to track remote branches --- exercise_utils/git.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/exercise_utils/git.py b/exercise_utils/git.py index c93af2c..fe5fc84 100644 --- a/exercise_utils/git.py +++ b/exercise_utils/git.py @@ -68,3 +68,8 @@ def init(verbose: bool) -> None: def push(remote: str, branch: str, verbose: bool) -> None: """Push the given branch on the remote.""" run_command(["git", "push", remote, branch], verbose) + + +def track_remote_branch(remote: str, branch: str, verbose: bool) -> None: + """Tracks a remote branch locally using the same name.""" + run_command(["git", "branch", branch, f"{remote}/{branch}"], verbose) From f11c8db5fd9f75429672495d45071acaab0300bf Mon Sep 17 00:00:00 2001 From: "Jiahao, Woo" Date: Thu, 2 Oct 2025 09:02:48 +0100 Subject: [PATCH 3/6] [mix-messy-docs] Add remote branch tracking --- mix_messy_docs/download.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mix_messy_docs/download.py b/mix_messy_docs/download.py index 35b8011..1071347 100644 --- a/mix_messy_docs/download.py +++ b/mix_messy_docs/download.py @@ -1,2 +1,8 @@ +from exercise_utils.git import track_remote_branch + + def setup(verbose: bool = False): - pass + remote_name = "origin" + remote_branches = ["feature-search", "feature-delete", "list"] + for remote_branch_name in remote_branches: + track_remote_branch(remote_name, remote_branch_name, verbose) From df7f6e5460adbf4ab77fcf34b8c6aace4a4b48b1 Mon Sep 17 00:00:00 2001 From: "Jiahao, Woo" Date: Thu, 2 Oct 2025 22:25:28 +0100 Subject: [PATCH 4/6] [mix-messy-docs] Implement verification --- mix_messy_docs/verify.py | 87 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/mix_messy_docs/verify.py b/mix_messy_docs/verify.py index 1288d3d..04ebd1d 100644 --- a/mix_messy_docs/verify.py +++ b/mix_messy_docs/verify.py @@ -1,11 +1,94 @@ +from os import EX_TEMPFAIL from git_autograder import ( GitAutograderOutput, GitAutograderExercise, GitAutograderStatus, ) +MISSING_DEVELOPMENT_BRANCH = "You are missing the 'development' branch!" +WRONG_BRANCH_POINT = "You did not branch from the commit with tag v1.0!" +FEATURE_SEARCH_BRANCH_STILL_EXISTS = ( + "Branch 'feature-search' still exists! Remember to delete it after merging!" +) +FEATURE_DELETE_BRANCH_STILL_EXISTS = ( + "Branch 'feature-delete' still exists! Remember to delete it after merging!" +) +LIST_BRANCH_STILL_EXISTS = ( + "Branch 'list' still exists! Remember to rename it to 'feature-list'!" +) +FEATURE_LIST_BRANCH_MISSING = "Branch 'feature-list' is missing. Did you misspell it?" +MERGE_FEATURE_SEARCH_FIRST = "You need to merge 'feature-search' first!" +MERGE_FEATURE_DELETE_SECOND = "You need to merge 'feature-delete' second!" +MISSING_FEATURES_FILE = "You are missing 'features.md'!" +FEATURES_FILE_CONTENT_INVALID = "Contents of 'features.md' is not valid! Try again!" + +RESET_MESSAGE = 'Reset the repository using "gitmastery progress reset" and start again' + + +EXPECTED_LINES = [ + "# Features", + "## Create Book", + "Allows creating one book at a time.", + "## Searching for Books", + "Allows searching for books by keywords.", + "Works only for book titles.", + "## Deleting Books", + "Allows deleting books.", +] + def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: - # INSERT YOUR GRADING CODE HERE + development_branch = exercise.repo.branches.branch_or_none("development") + if development_branch is None: + raise exercise.wrong_answer([MISSING_DEVELOPMENT_BRANCH]) + + tag_commit = exercise.repo.repo.tags["v1.0"].commit + development_commit = development_branch.latest_commit + branched_from_tag_hexsha = exercise.repo.repo.git.merge_base( + tag_commit.hexsha, development_commit.hexsha + ) + # Alternative is to use reflog which states where the branch is created from + if branched_from_tag_hexsha != tag_commit.hexsha: + # Not branched from this but maybe somewhere earlier + raise exercise.wrong_answer([WRONG_BRANCH_POINT]) + + reflog = development_branch.reflog + merge_logs = [log for log in reflog if "merge" in log.action] + # reflog returns it in reverse order + has_feature_delete_commit_merge = ( + len(merge_logs) >= 1 + and merge_logs[0].action == "commit (merge)" + and "feature-delete" in merge_logs[0].message + ) + has_feature_search_merge = ( + len(merge_logs) >= 2 and merge_logs[1].action == "merge feature-search" + ) + if not has_feature_search_merge: + raise exercise.wrong_answer([MERGE_FEATURE_SEARCH_FIRST, RESET_MESSAGE]) + + if not has_feature_delete_commit_merge: + raise exercise.wrong_answer([MERGE_FEATURE_DELETE_SECOND, RESET_MESSAGE]) + + feature_list_branch = exercise.repo.branches.branch_or_none("feature-list") + list_branch = exercise.repo.branches.branch_or_none("list") + if list_branch is not None and feature_list_branch is None: + raise exercise.wrong_answer([LIST_BRANCH_STILL_EXISTS]) + elif list_branch is None and feature_list_branch is None: + raise exercise.wrong_answer([FEATURE_LIST_BRANCH_MISSING]) + + with exercise.repo.files.file_or_none("features.md") as features_file: + if features_file is None: + raise exercise.wrong_answer([MISSING_FEATURES_FILE]) + + contents = [ + line.strip() for line in features_file.readlines() if line.strip() != "" + ] + if contents != EXPECTED_LINES: + raise exercise.wrong_answer([FEATURES_FILE_CONTENT_INVALID]) - return exercise.to_output([], GitAutograderStatus.SUCCESSFUL) + return exercise.to_output( + [ + "Great work using all of the concepts you've learnt about branching to mix the messy documentation!" + ], + GitAutograderStatus.SUCCESSFUL, + ) From 1b0db27e87da6b58136f68e3570672e3ea4aec4b Mon Sep 17 00:00:00 2001 From: "Jiahao, Woo" Date: Thu, 2 Oct 2025 22:25:39 +0100 Subject: [PATCH 5/6] [mix-messy-docs] Add simple tests --- mix_messy_docs/tests/specs/right_order.yml | 87 +++++++++++++++++++ .../tests/specs/wrong_branch_point.yml | 18 ++++ mix_messy_docs/tests/test_verify.py | 15 +++- 3 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 mix_messy_docs/tests/specs/right_order.yml create mode 100644 mix_messy_docs/tests/specs/wrong_branch_point.yml diff --git a/mix_messy_docs/tests/specs/right_order.yml b/mix_messy_docs/tests/specs/right_order.yml new file mode 100644 index 0000000..16d4207 --- /dev/null +++ b/mix_messy_docs/tests/specs/right_order.yml @@ -0,0 +1,87 @@ +initialization: + steps: + - type: commit + empty: true + message: Empty commit + id: start + - type: new-file + filename: conflict.txt + contents: | + Hello world + - type: add + files: + - conflict.txt + - type: commit + message: Expected branch point + - type: tag + tag-name: v1.0 + + - type: branch + branch-name: feature-search + - type: edit-file + filename: conflict.txt + contents: | + Hello world! + - type: add + files: + - conflict.txt + - type: commit + message: Feature search changes + - type: checkout + branch-name: main + + - type: branch + branch-name: feature-delete + - type: edit-file + filename: conflict.txt + contents: | + Hello world? + - type: add + files: + - conflict.txt + - type: commit + message: Feature delete changes + - type: checkout + branch-name: main + + - type: branch + branch-name: feature-list + - type: commit + empty: true + message: Feature list changes + + - type: checkout + branch-name: main + - type: branch + branch-name: development + - type: commit + empty: true + message: Commit on development + - type: merge + branch-name: feature-search + no-ff: true + - type: bash + runs: | + # Controlling everything through Bash to simplify workflow + (git merge feature-delete || true) > /dev/null + echo 'New contents' > conflict.txt + git add conflict.txt + (git commit --no-edit) > /dev/null + + - type: new-file + filename: features.md + contents: | + # Features + + ## Create Book + + Allows creating one book at a time. + + ## Searching for Books + + Allows searching for books by keywords. + Works only for book titles. + + ## Deleting Books + + Allows deleting books. diff --git a/mix_messy_docs/tests/specs/wrong_branch_point.yml b/mix_messy_docs/tests/specs/wrong_branch_point.yml new file mode 100644 index 0000000..ab794c1 --- /dev/null +++ b/mix_messy_docs/tests/specs/wrong_branch_point.yml @@ -0,0 +1,18 @@ +initialization: + steps: + - type: commit + empty: true + message: Empty commit + id: start + - type: branch + branch-name: development + - type: commit + empty: true + message: Commit on development + - type: checkout + branch-name: main + - type: commit + empty: true + message: Expected branch point + - type: tag + tag-name: v1.0 diff --git a/mix_messy_docs/tests/test_verify.py b/mix_messy_docs/tests/test_verify.py index e4d452d..99adea4 100644 --- a/mix_messy_docs/tests/test_verify.py +++ b/mix_messy_docs/tests/test_verify.py @@ -1,6 +1,7 @@ -from git_autograder import GitAutograderTestLoader +from git_autograder import GitAutograderStatus, GitAutograderTestLoader +from git_autograder.test_utils import assert_output -from ..verify import verify +from ..verify import WRONG_BRANCH_POINT, verify REPOSITORY_NAME = "mix-messy-docs" @@ -10,3 +11,13 @@ def test_base(): with loader.load("specs/base.yml", "start"): pass + + +def test_wrong_branch_point(): + with loader.load("specs/wrong_branch_point.yml") as output: + assert_output(output, GitAutograderStatus.UNSUCCESSFUL, [WRONG_BRANCH_POINT]) + + +def test_right_order(): + with loader.load("specs/right_order.yml") as output: + assert_output(output, GitAutograderStatus.SUCCESSFUL) From 2fa491749b56566fb0f3930ad8b5f76b4a96f320 Mon Sep 17 00:00:00 2001 From: "Jiahao, Woo" Date: Fri, 3 Oct 2025 20:07:19 +0100 Subject: [PATCH 6/6] [mix-messy-docs] Add tests --- mix_messy_docs/tests/specs/contents_wrong.yml | 88 +++++++++++++++++++ .../specs/feature_list_branch_missing.yml | 87 ++++++++++++++++++ .../tests/specs/list_branch_exists.yml | 87 ++++++++++++++++++ .../{base.yml => missing_development.yml} | 0 .../tests/specs/no_merge_feature_delete.yml | 80 +++++++++++++++++ .../tests/specs/no_merge_feature_search.yml | 87 ++++++++++++++++++ mix_messy_docs/tests/test_verify.py | 66 +++++++++++++- mix_messy_docs/verify.py | 24 ++--- 8 files changed, 503 insertions(+), 16 deletions(-) create mode 100644 mix_messy_docs/tests/specs/contents_wrong.yml create mode 100644 mix_messy_docs/tests/specs/feature_list_branch_missing.yml create mode 100644 mix_messy_docs/tests/specs/list_branch_exists.yml rename mix_messy_docs/tests/specs/{base.yml => missing_development.yml} (100%) create mode 100644 mix_messy_docs/tests/specs/no_merge_feature_delete.yml create mode 100644 mix_messy_docs/tests/specs/no_merge_feature_search.yml diff --git a/mix_messy_docs/tests/specs/contents_wrong.yml b/mix_messy_docs/tests/specs/contents_wrong.yml new file mode 100644 index 0000000..65cf434 --- /dev/null +++ b/mix_messy_docs/tests/specs/contents_wrong.yml @@ -0,0 +1,88 @@ +initialization: + steps: + - type: commit + empty: true + message: Empty commit + id: start + - type: new-file + filename: conflict.txt + contents: | + Hello world + - type: add + files: + - conflict.txt + - type: commit + message: Expected branch point + - type: tag + tag-name: v1.0 + + - type: branch + branch-name: feature-search + - type: edit-file + filename: conflict.txt + contents: | + Hello world! + - type: add + files: + - conflict.txt + - type: commit + message: Feature search changes + - type: checkout + branch-name: main + + - type: branch + branch-name: feature-delete + - type: edit-file + filename: conflict.txt + contents: | + Hello world? + - type: add + files: + - conflict.txt + - type: commit + message: Feature delete changes + - type: checkout + branch-name: main + + - type: branch + branch-name: feature-list + - type: commit + empty: true + message: Feature list changes + + - type: checkout + branch-name: main + - type: branch + branch-name: development + - type: commit + empty: true + message: Commit on development + - type: merge + branch-name: feature-search + no-ff: true + - type: bash + runs: | + # Controlling everything through Bash to simplify workflow + (git merge feature-delete || true) > /dev/null + echo 'New contents' > conflict.txt + git add conflict.txt + (git commit --no-edit) > /dev/null + + - type: new-file + filename: features.md + contents: | + # Features + + ## Searching for Books + + Allows searching for books by keywords. + Works only for book titles. + + ## Create Book + + Allows creating one book at a time. + + + ## Deleting Books + + Allows deleting books. diff --git a/mix_messy_docs/tests/specs/feature_list_branch_missing.yml b/mix_messy_docs/tests/specs/feature_list_branch_missing.yml new file mode 100644 index 0000000..83babe7 --- /dev/null +++ b/mix_messy_docs/tests/specs/feature_list_branch_missing.yml @@ -0,0 +1,87 @@ +initialization: + steps: + - type: commit + empty: true + message: Empty commit + id: start + - type: new-file + filename: conflict.txt + contents: | + Hello world + - type: add + files: + - conflict.txt + - type: commit + message: Expected branch point + - type: tag + tag-name: v1.0 + + - type: branch + branch-name: feature-search + - type: edit-file + filename: conflict.txt + contents: | + Hello world! + - type: add + files: + - conflict.txt + - type: commit + message: Feature search changes + - type: checkout + branch-name: main + + - type: branch + branch-name: feature-delete + - type: edit-file + filename: conflict.txt + contents: | + Hello world? + - type: add + files: + - conflict.txt + - type: commit + message: Feature delete changes + - type: checkout + branch-name: main + + - type: branch + branch-name: other-list + - type: commit + empty: true + message: Feature list changes + + - type: checkout + branch-name: main + - type: branch + branch-name: development + - type: commit + empty: true + message: Commit on development + - type: merge + branch-name: feature-search + no-ff: true + - type: bash + runs: | + # Controlling everything through Bash to simplify workflow + (git merge feature-delete || true) > /dev/null + echo 'New contents' > conflict.txt + git add conflict.txt + (git commit --no-edit) > /dev/null + + - type: new-file + filename: features.md + contents: | + # Features + + ## Create Book + + Allows creating one book at a time. + + ## Searching for Books + + Allows searching for books by keywords. + Works only for book titles. + + ## Deleting Books + + Allows deleting books. diff --git a/mix_messy_docs/tests/specs/list_branch_exists.yml b/mix_messy_docs/tests/specs/list_branch_exists.yml new file mode 100644 index 0000000..fd02357 --- /dev/null +++ b/mix_messy_docs/tests/specs/list_branch_exists.yml @@ -0,0 +1,87 @@ +initialization: + steps: + - type: commit + empty: true + message: Empty commit + id: start + - type: new-file + filename: conflict.txt + contents: | + Hello world + - type: add + files: + - conflict.txt + - type: commit + message: Expected branch point + - type: tag + tag-name: v1.0 + + - type: branch + branch-name: feature-search + - type: edit-file + filename: conflict.txt + contents: | + Hello world! + - type: add + files: + - conflict.txt + - type: commit + message: Feature search changes + - type: checkout + branch-name: main + + - type: branch + branch-name: feature-delete + - type: edit-file + filename: conflict.txt + contents: | + Hello world? + - type: add + files: + - conflict.txt + - type: commit + message: Feature delete changes + - type: checkout + branch-name: main + + - type: branch + branch-name: list + - type: commit + empty: true + message: Feature list changes + + - type: checkout + branch-name: main + - type: branch + branch-name: development + - type: commit + empty: true + message: Commit on development + - type: merge + branch-name: feature-search + no-ff: true + - type: bash + runs: | + # Controlling everything through Bash to simplify workflow + (git merge feature-delete || true) > /dev/null + echo 'New contents' > conflict.txt + git add conflict.txt + (git commit --no-edit) > /dev/null + + - type: new-file + filename: features.md + contents: | + # Features + + ## Create Book + + Allows creating one book at a time. + + ## Searching for Books + + Allows searching for books by keywords. + Works only for book titles. + + ## Deleting Books + + Allows deleting books. diff --git a/mix_messy_docs/tests/specs/base.yml b/mix_messy_docs/tests/specs/missing_development.yml similarity index 100% rename from mix_messy_docs/tests/specs/base.yml rename to mix_messy_docs/tests/specs/missing_development.yml diff --git a/mix_messy_docs/tests/specs/no_merge_feature_delete.yml b/mix_messy_docs/tests/specs/no_merge_feature_delete.yml new file mode 100644 index 0000000..3643efb --- /dev/null +++ b/mix_messy_docs/tests/specs/no_merge_feature_delete.yml @@ -0,0 +1,80 @@ +initialization: + steps: + - type: commit + empty: true + message: Empty commit + id: start + - type: new-file + filename: conflict.txt + contents: | + Hello world + - type: add + files: + - conflict.txt + - type: commit + message: Expected branch point + - type: tag + tag-name: v1.0 + + - type: branch + branch-name: feature-search + - type: edit-file + filename: conflict.txt + contents: | + Hello world! + - type: add + files: + - conflict.txt + - type: commit + message: Feature search changes + - type: checkout + branch-name: main + + - type: branch + branch-name: feature-delete + - type: edit-file + filename: conflict.txt + contents: | + Hello world? + - type: add + files: + - conflict.txt + - type: commit + message: Feature delete changes + - type: checkout + branch-name: main + + - type: branch + branch-name: feature-list + - type: commit + empty: true + message: Feature list changes + + - type: checkout + branch-name: main + - type: branch + branch-name: development + - type: commit + empty: true + message: Commit on development + - type: merge + branch-name: feature-search + no-ff: true + + - type: new-file + filename: features.md + contents: | + # Features + + ## Create Book + + Allows creating one book at a time. + + ## Searching for Books + + Allows searching for books by keywords. + Works only for book titles. + + ## Deleting Books + + Allows deleting books. diff --git a/mix_messy_docs/tests/specs/no_merge_feature_search.yml b/mix_messy_docs/tests/specs/no_merge_feature_search.yml new file mode 100644 index 0000000..22b33c9 --- /dev/null +++ b/mix_messy_docs/tests/specs/no_merge_feature_search.yml @@ -0,0 +1,87 @@ +initialization: + steps: + - type: commit + empty: true + message: Empty commit + id: start + - type: new-file + filename: conflict.txt + contents: | + Hello world + - type: add + files: + - conflict.txt + - type: commit + message: Expected branch point + - type: tag + tag-name: v1.0 + + - type: branch + branch-name: feature-search + - type: edit-file + filename: conflict.txt + contents: | + Hello world! + - type: add + files: + - conflict.txt + - type: commit + message: Feature search changes + - type: checkout + branch-name: main + + - type: branch + branch-name: feature-delete + - type: edit-file + filename: conflict.txt + contents: | + Hello world? + - type: add + files: + - conflict.txt + - type: commit + message: Feature delete changes + - type: checkout + branch-name: main + + - type: branch + branch-name: feature-list + - type: commit + empty: true + message: Feature list changes + + - type: checkout + branch-name: main + - type: branch + branch-name: development + - type: commit + empty: true + message: Commit on development + - type: merge + branch-name: feature-delete + no-ff: true + - type: bash + runs: | + # Controlling everything through Bash to simplify workflow + (git merge feature-search || true) > /dev/null + echo 'New contents' > conflict.txt + git add conflict.txt + (git commit --no-edit) > /dev/null + + - type: new-file + filename: features.md + contents: | + # Features + + ## Create Book + + Allows creating one book at a time. + + ## Searching for Books + + Allows searching for books by keywords. + Works only for book titles. + + ## Deleting Books + + Allows deleting books. diff --git a/mix_messy_docs/tests/test_verify.py b/mix_messy_docs/tests/test_verify.py index 99adea4..3391ee9 100644 --- a/mix_messy_docs/tests/test_verify.py +++ b/mix_messy_docs/tests/test_verify.py @@ -1,16 +1,29 @@ from git_autograder import GitAutograderStatus, GitAutograderTestLoader from git_autograder.test_utils import assert_output -from ..verify import WRONG_BRANCH_POINT, verify +from ..verify import ( + FEATURE_LIST_BRANCH_MISSING, + FEATURES_FILE_CONTENT_INVALID, + LIST_BRANCH_STILL_EXISTS, + MERGE_FEATURE_DELETE_SECOND, + MERGE_FEATURE_SEARCH_FIRST, + MISSING_DEVELOPMENT_BRANCH, + MISSING_FEATURES_FILE, + RESET_MESSAGE, + WRONG_BRANCH_POINT, + verify, +) REPOSITORY_NAME = "mix-messy-docs" loader = GitAutograderTestLoader(__file__, REPOSITORY_NAME, verify) -def test_base(): - with loader.load("specs/base.yml", "start"): - pass +def test_missing_development(): + with loader.load("specs/missing_development.yml") as output: + assert_output( + output, GitAutograderStatus.UNSUCCESSFUL, [MISSING_DEVELOPMENT_BRANCH] + ) def test_wrong_branch_point(): @@ -21,3 +34,48 @@ def test_wrong_branch_point(): def test_right_order(): with loader.load("specs/right_order.yml") as output: assert_output(output, GitAutograderStatus.SUCCESSFUL) + + +def test_no_merge_feature_search(): + with loader.load("specs/no_merge_feature_search.yml") as output: + assert_output( + output, + GitAutograderStatus.UNSUCCESSFUL, + [MERGE_FEATURE_SEARCH_FIRST, RESET_MESSAGE], + ) + + +def test_no_merge_feature_delete(): + with loader.load("specs/no_merge_feature_delete.yml") as output: + assert_output( + output, + GitAutograderStatus.UNSUCCESSFUL, + [MERGE_FEATURE_DELETE_SECOND, RESET_MESSAGE], + ) + + +def test_list_branch_exists(): + with loader.load("specs/list_branch_exists.yml") as output: + assert_output( + output, + GitAutograderStatus.UNSUCCESSFUL, + [LIST_BRANCH_STILL_EXISTS], + ) + + +def test_feature_list_branch_missing(): + with loader.load("specs/feature_list_branch_missing.yml") as output: + assert_output( + output, + GitAutograderStatus.UNSUCCESSFUL, + [FEATURE_LIST_BRANCH_MISSING], + ) + + +def test_contents_wrong(): + with loader.load("specs/contents_wrong.yml") as output: + assert_output( + output, + GitAutograderStatus.UNSUCCESSFUL, + [FEATURES_FILE_CONTENT_INVALID], + ) diff --git a/mix_messy_docs/verify.py b/mix_messy_docs/verify.py index 04ebd1d..3f2fec5 100644 --- a/mix_messy_docs/verify.py +++ b/mix_messy_docs/verify.py @@ -53,15 +53,14 @@ def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: raise exercise.wrong_answer([WRONG_BRANCH_POINT]) reflog = development_branch.reflog - merge_logs = [log for log in reflog if "merge" in log.action] - # reflog returns it in reverse order - has_feature_delete_commit_merge = ( - len(merge_logs) >= 1 - and merge_logs[0].action == "commit (merge)" - and "feature-delete" in merge_logs[0].message - ) + merge_logs = [log for log in reflog if "merge" in log.action][::-1] has_feature_search_merge = ( - len(merge_logs) >= 2 and merge_logs[1].action == "merge feature-search" + len(merge_logs) >= 1 and merge_logs[0].action == "merge feature-search" + ) + has_feature_delete_commit_merge = ( + len(merge_logs) >= 2 + and merge_logs[1].action == "commit (merge)" + and "feature-delete" in merge_logs[1].message ) if not has_feature_search_merge: raise exercise.wrong_answer([MERGE_FEATURE_SEARCH_FIRST, RESET_MESSAGE]) @@ -71,10 +70,11 @@ def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: feature_list_branch = exercise.repo.branches.branch_or_none("feature-list") list_branch = exercise.repo.branches.branch_or_none("list") - if list_branch is not None and feature_list_branch is None: - raise exercise.wrong_answer([LIST_BRANCH_STILL_EXISTS]) - elif list_branch is None and feature_list_branch is None: - raise exercise.wrong_answer([FEATURE_LIST_BRANCH_MISSING]) + if feature_list_branch is None: + if list_branch is not None: + raise exercise.wrong_answer([LIST_BRANCH_STILL_EXISTS]) + else: + raise exercise.wrong_answer([FEATURE_LIST_BRANCH_MISSING]) with exercise.repo.files.file_or_none("features.md") as features_file: if features_file is None: