diff --git a/README.md b/README.md index 9766c4de..9d7ce412 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,7 @@ jobs: ### Basic usage without external contributors If you don't expect external contributors, you don't need all the shenanigans -with the artifacts and the 2nd workflow. This is likely to be the most straightfoward +with the artifacts and the 2nd workflow. This is likely to be the most straightforward way to configure it for private repositories. It might look like this: ```yaml @@ -198,7 +198,6 @@ jobs: run: make test # This is the part where you put your own test command - name: Coverage comment - id: coverage_comment uses: py-cov-action/python-coverage-comment-action@v3 with: GITHUB_TOKEN: ${{ github.token }} @@ -327,14 +326,25 @@ jobs: COMMENT_ARTIFACT_NAME: python-coverage-comment-action # Name of the file in which the body of the comment to post on the PR is stored. - # You typically don't have to change this unless you're already using this name for something else. + # In monorepo setting, see SUBPROJECT_ID. COMMENT_FILENAME: python-coverage-comment-action.txt + # This setting is only necessary if you plan to run the action multiple times + # in the same repository. It will be appended to the value of all the + # settings that need to be unique, so as for the action to avoid mixing + # up results of multiple runs. + # Affects `COMMENT_FILENAME`, `COVERAGE_DATA_BRANCH`. + # Ideally, use dashes (`-`) rather than underscrores (`_`) to split words, + # for consistency + SUBPROJECT_ID: null / "lib-name" + # An alternative template for the comment for pull requests. See details below. COMMENT_TEMPLATE: The coverage rate is `{{ coverage.info.percent_covered | pct }}`{{ marker }} # Name of the branch in which coverage data will be stored on the repository. - # Please make sure that this branch is not protected. + # Default is 'python-coverage-comment-action-data'. Please make sure that this + # branch is not protected. + # In monorepo setting, see SUBPROJECT_ID. COVERAGE_DATA_BRANCH: python-coverage-comment-action-data # Deprecated, see https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/enabling-debug-logging @@ -383,6 +393,103 @@ coverage (percentage) of the whole project from the PR build: "Coverage: {{ coverage.info.percent_covered | pct }}{{ marker }}" ``` +## Monorepo setting + +In case you want to use the action multiple times with different parts of your +source (so you have multiple codebases into a single repo), you'll +need to use SUBPROJECT_ID with a different value for each launch. You may +still use the same step for storing all files as artifacts. You'll end up with +a different comment for each launch. Feel free to use the `COMMENT_TEMPLATE` if +you want each comment to clearly state what it relates to. + +```yaml +# .github/workflows/ci.yml +name: CI + +on: + pull_request: + push: + branches: + - "main" + +jobs: + test: + name: Run tests & display coverage + runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: write + steps: + - uses: actions/checkout@v3 + + - name: Test project 1 + run: make -C project_1 test + + - name: Test project 2 + run: make -C project_2 test + + - name: Coverage comment (project 1) + id: coverage_comment_1 + uses: py-cov-action/python-coverage-comment-action@v3 + with: + COVERAGE_PATH: project_1 + SUBPROJECT_ID: project-1 + GITHUB_TOKEN: ${{ github.token }} + + - name: Coverage comment (project 2) + id: coverage_comment_2 + uses: py-cov-action/python-coverage-comment-action@v3 + with: + COVERAGE_PATH: project_2/src + SUBPROJECT_ID: project-2 + GITHUB_TOKEN: ${{ github.token }} + + - name: Store Pull Request comment to be posted + uses: actions/upload-artifact@v3 + if: steps.coverage_comment_1.outputs.COMMENT_FILE_WRITTEN == 'true' || steps.coverage_comment_2.outputs.COMMENT_FILE_WRITTEN == 'true' + with: + name: python-coverage-comment-action + # Note the star + path: python-coverage-comment-action*.txt +``` + +```yaml +# .github/workflows/coverage.yml +name: Post coverage comment + +on: + workflow_run: + workflows: ["CI"] + types: + - completed + +jobs: + test: + name: Run tests & display coverage + runs-on: ubuntu-latest + if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' + permissions: + pull-requests: write + contents: write + actions: read + steps: + - name: Post comment + uses: py-cov-action/python-coverage-comment-action@v3 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_PR_RUN_ID: ${{ github.event.workflow_run.id }} + SUBPROJECT_ID: project-1 + COVERAGE_PATH: project_1 + + - name: Post comment + uses: py-cov-action/python-coverage-comment-action@v3 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_PR_RUN_ID: ${{ github.event.workflow_run.id }} + SUBPROJECT_ID: project-2 + COVERAGE_PATH: project_2/src +``` + # Other topics ## Pinning diff --git a/action.yml b/action.yml index ef328b71..42249d1b 100644 --- a/action.yml +++ b/action.yml @@ -27,6 +27,7 @@ inputs: Name of the branch in which coverage data will be stored on the repository. Default is 'python-coverage-comment-action-data'. Please make sure that this branch is not protected. + In monorepo setting, see SUBPROJECT_ID. default: python-coverage-comment-action-data required: false COVERAGE_PATH: @@ -38,13 +39,25 @@ inputs: COMMENT_ARTIFACT_NAME: description: > Name of the artifact in which the body of the comment to post on the PR is stored. + You typically don't have to change this unless you're already using this name for something else. default: python-coverage-comment-action required: false COMMENT_FILENAME: description: > Name of the file in which the body of the comment to post on the PR is stored. + In monorepo setting, see SUBPROJECT_ID. default: python-coverage-comment-action.txt required: false + SUBPROJECT_ID: + description: > + This setting is only necessary if you plan to run the action multiple + times in the same repository. It will be appended to the value of all the + settings that need to be unique, so as for the action to avoid mixing up + results of multiple runs. Ideally, use dashes (`-`) rather than + underscrores (`_`) to split words, for consistency. + Affects `COMMENT_FILENAME`, `COVERAGE_DATA_BRANCH`. + default: null + required: false MINIMUM_GREEN: description: > If the coverage percentage is above or equal to this value, the badge @@ -67,7 +80,7 @@ inputs: default: false ANNOTATION_TYPE: description: > - Only needed if ANNOTATE_MISSING_LINES is set to true. This parameter allows you to choose between + Only relevant if ANNOTATE_MISSING_LINES is set to true. This parameter allows you to choose between notice, warning and error as annotation type. For more information look here: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-a-notice-message default: warning @@ -78,8 +91,11 @@ inputs: outputs: COMMENT_FILE_WRITTEN: description: > - If "true", a comment file was written to COMMENT_FILENAME. If "false", - no comment file was written (because the comment was already posted). + This output is only set when running in PR mode. It's a boolean indicating + whether a comment file was written to COMMENT_FILENAME or not. If so, + you'll need to run the action in workflow_run mode to post it. If + "false", no comment file was written (likely because the comment was + already posted to the PR). runs: using: docker image: Dockerfile @@ -91,6 +107,7 @@ runs: COVERAGE_PATH: ${{ inputs.COVERAGE_PATH }} COMMENT_ARTIFACT_NAME: ${{ inputs.COMMENT_ARTIFACT_NAME }} COMMENT_FILENAME: ${{ inputs.COMMENT_FILENAME }} + SUBPROJECT_ID: ${{ inputs.SUBPROJECT_ID }} MINIMUM_GREEN: ${{ inputs.MINIMUM_GREEN }} MINIMUM_ORANGE: ${{ inputs.MINIMUM_ORANGE }} MERGE_COVERAGE_FILES: ${{ inputs.MERGE_COVERAGE_FILES }} diff --git a/coverage_comment/communication.py b/coverage_comment/communication.py index 33a5e6fb..bfe38d57 100644 --- a/coverage_comment/communication.py +++ b/coverage_comment/communication.py @@ -9,6 +9,7 @@ def get_readme_and_log( html_report_url: str, markdown_report: str, is_public: bool, + subproject_id: str | None = None, ) -> tuple[files.WriteFile, str]: readme_markdown = template.get_readme_markdown( is_public=is_public, @@ -18,6 +19,7 @@ def get_readme_and_log( direct_image_url=image_urls["direct"], endpoint_image_url=image_urls["endpoint"], dynamic_image_url=image_urls["dynamic"], + subproject_id=subproject_id, ) log_message = template.get_log_message( is_public=is_public, @@ -26,6 +28,7 @@ def get_readme_and_log( direct_image_url=image_urls["direct"], endpoint_image_url=image_urls["endpoint"], dynamic_image_url=image_urls["dynamic"], + subproject_id=subproject_id, ) readme = files.WriteFile( path=pathlib.Path("README.md"), diff --git a/coverage_comment/github.py b/coverage_comment/github.py index 5653a189..fb7b51bf 100644 --- a/coverage_comment/github.py +++ b/coverage_comment/github.py @@ -113,7 +113,7 @@ def get_pr_number_from_workflow_run( raise CannotDeterminePR(f"No open PR found for branch {full_branch}") -def get_my_login(github: github_client.GitHub): +def get_my_login(github: github_client.GitHub) -> str: try: response = github.user.get() except github_client.Forbidden: diff --git a/coverage_comment/main.py b/coverage_comment/main.py index f96d7b3b..93b68d3a 100644 --- a/coverage_comment/main.py +++ b/coverage_comment/main.py @@ -120,12 +120,13 @@ def generate_comment( previous_coverage_data_file = storage.get_datafile_contents( github=gh, repository=config.GITHUB_REPOSITORY, - branch=config.COVERAGE_DATA_BRANCH, + branch=config.FINAL_COVERAGE_DATA_BRANCH, ) previous_coverage = None if previous_coverage_data_file: previous_coverage = files.parse_datafile(contents=previous_coverage_data_file) + marker = template.get_marker(marker_id=config.SUBPROJECT_ID) try: comment = template.get_comment_markdown( coverage=coverage, @@ -133,6 +134,8 @@ def generate_comment( previous_coverage_rate=previous_coverage, base_template=template.read_template_file("comment.md.j2"), custom_template=config.COMMENT_TEMPLATE, + marker=marker, + subproject_id=config.SUBPROJECT_ID, ) except template.MissingMarker: log.error( @@ -167,7 +170,7 @@ def generate_comment( repository=config.GITHUB_REPOSITORY, pr_number=config.GITHUB_PR_NUMBER, contents=comment, - marker=template.MARKER, + marker=marker, ) except github.CannotPostComment: log.debug("Exception when posting comment", exc_info=True) @@ -178,7 +181,7 @@ def generate_comment( "on PR comments for external PRs)." ) comment_file.store_file( - filename=config.COMMENT_FILENAME, + filename=config.FINAL_COMMENT_FILENAME, content=comment, ) github.set_output(github_output=config.GITHUB_OUTPUT, COMMENT_FILE_WRITTEN=True) @@ -224,7 +227,7 @@ def post_comment(config: settings.Config, github_session: httpx.Client) -> int: repository=config.GITHUB_REPOSITORY, artifact_name=config.COMMENT_ARTIFACT_NAME, run_id=config.GITHUB_PR_RUN_ID, - filename=config.COMMENT_FILENAME, + filename=config.FINAL_COMMENT_FILENAME, ) except github.NoArtifact: log.info( @@ -240,7 +243,7 @@ def post_comment(config: settings.Config, github_session: httpx.Client) -> int: repository=config.GITHUB_REPOSITORY, pr_number=pr_number, contents=comment, - marker=template.MARKER, + marker=template.get_marker(marker_id=config.SUBPROJECT_ID), ) log.info("Comment posted in PR") @@ -294,14 +297,14 @@ def save_coverage_data_files( storage.get_raw_file_url, is_public=is_public, repository=config.GITHUB_REPOSITORY, - branch=config.COVERAGE_DATA_BRANCH, + branch=config.FINAL_COVERAGE_DATA_BRANCH, ) readme_url = storage.get_repo_file_url( - branch=config.COVERAGE_DATA_BRANCH, + branch=config.FINAL_COVERAGE_DATA_BRANCH, repository=config.GITHUB_REPOSITORY, ) html_report_url = storage.get_html_report_url( - branch=config.COVERAGE_DATA_BRANCH, + branch=config.FINAL_COVERAGE_DATA_BRANCH, repository=config.GITHUB_REPOSITORY, ) readme_file, log_message = communication.get_readme_and_log( @@ -310,12 +313,13 @@ def save_coverage_data_files( image_urls=files.get_urls(url_getter=url_getter), html_report_url=html_report_url, markdown_report=markdown_report, + subproject_id=config.SUBPROJECT_ID, ) operations.append(readme_file) storage.commit_operations( operations=operations, git=git, - branch=config.COVERAGE_DATA_BRANCH, + branch=config.FINAL_COVERAGE_DATA_BRANCH, ) log.info(log_message) diff --git a/coverage_comment/settings.py b/coverage_comment/settings.py index 94aadce1..13a56a2f 100644 --- a/coverage_comment/settings.py +++ b/coverage_comment/settings.py @@ -45,6 +45,7 @@ class Config: COVERAGE_PATH: pathlib.Path = pathlib.Path(".") COMMENT_ARTIFACT_NAME: str = "python-coverage-comment-action" COMMENT_FILENAME: pathlib.Path = pathlib.Path("python-coverage-comment-action.txt") + SUBPROJECT_ID: str | None = None GITHUB_OUTPUT: pathlib.Path | None = None MINIMUM_GREEN: decimal.Decimal = decimal.Decimal("100") MINIMUM_ORANGE: decimal.Decimal = decimal.Decimal("70") @@ -119,6 +120,20 @@ def GITHUB_PR_NUMBER(self) -> int | None: return int(self.GITHUB_REF.split("/")[2]) return None + @property + def FINAL_COMMENT_FILENAME(self): + filename = self.COMMENT_FILENAME + if self.SUBPROJECT_ID: + new_name = f"{filename.stem}-{self.SUBPROJECT_ID}{filename.suffix}" + return filename.parent / new_name + return filename + + @property + def FINAL_COVERAGE_DATA_BRANCH(self): + return self.COVERAGE_DATA_BRANCH + ( + f"-{self.SUBPROJECT_ID}" if self.SUBPROJECT_ID else "" + ) + # We need to type environ as a MutableMapping because that's what # os.environ is, and just saying `dict[str, str]` is not enough to make # mypy happy diff --git a/coverage_comment/template.py b/coverage_comment/template.py index 8cf83381..02f2df80 100644 --- a/coverage_comment/template.py +++ b/coverage_comment/template.py @@ -7,7 +7,9 @@ from coverage_comment import coverage as coverage_module -MARKER = """""" +MARKER = ( + """""" +) def uptodate(): @@ -39,11 +41,17 @@ class TemplateError(Exception): pass +def get_marker(marker_id: str | None): + return MARKER.format(id_part=f" (id: {marker_id})" if marker_id else "") + + def get_comment_markdown( coverage: coverage_module.Coverage, diff_coverage: coverage_module.DiffCoverage, previous_coverage_rate: decimal.Decimal | None, base_template: str, + marker: str, + subproject_id: str | None = None, custom_template: str | None = None, ): loader = CommentLoader(base_template=base_template, custom_template=custom_template) @@ -55,12 +63,13 @@ def get_comment_markdown( previous_coverage_rate=previous_coverage_rate, coverage=coverage, diff_coverage=diff_coverage, - marker=MARKER, + subproject_id=subproject_id, + marker=marker, ) except jinja2.exceptions.TemplateError as exc: raise TemplateError from exc - if MARKER not in comment: + if marker not in comment: raise MissingMarker() return comment @@ -74,6 +83,7 @@ def get_readme_markdown( html_report_url: str | None, dynamic_image_url: str | None, endpoint_image_url: str | None, + subproject_id: str | None = None, ): env = SandboxedEnvironment() template = jinja2.Template(read_template_file("readme.md.j2")) @@ -85,6 +95,7 @@ def get_readme_markdown( html_report_url=html_report_url, dynamic_image_url=dynamic_image_url, endpoint_image_url=endpoint_image_url, + subproject_id=subproject_id, ) @@ -95,6 +106,7 @@ def get_log_message( html_report_url: str | None, dynamic_image_url: str | None, endpoint_image_url: str | None, + subproject_id: str | None = None, ): env = SandboxedEnvironment() template = jinja2.Template(read_template_file("log.txt.j2")) @@ -105,6 +117,7 @@ def get_log_message( endpoint_image_url=endpoint_image_url, dynamic_image_url=dynamic_image_url, readme_url=readme_url, + subproject_id=subproject_id, ) diff --git a/coverage_comment/template_files/comment.md.j2 b/coverage_comment/template_files/comment.md.j2 index 95543145..5d7639a2 100644 --- a/coverage_comment/template_files/comment.md.j2 +++ b/coverage_comment/template_files/comment.md.j2 @@ -1,4 +1,4 @@ -{% block title %}## Coverage report{% endblock title %} +{% block title %}## Coverage report{% if subproject_id %} ({{ subproject_id }}){% endif %}{% endblock title %} {% block coverage_evolution -%} {% if previous_coverage_rate -%} {% block coverage_evolution_wording -%} diff --git a/coverage_comment/template_files/log.txt.j2 b/coverage_comment/template_files/log.txt.j2 index 0b69f097..b8d252c2 100644 --- a/coverage_comment/template_files/log.txt.j2 +++ b/coverage_comment/template_files/log.txt.j2 @@ -1,3 +1,6 @@ +{% if subproject_id %} Coverage info for {{ subproject_id }}: + +{% endif -%} {% if is_public -%} You can browse the full coverage report at: {{ html_report_url }} diff --git a/coverage_comment/template_files/readme.md.j2 b/coverage_comment/template_files/readme.md.j2 index 8f85075e..e7d7d69e 100644 --- a/coverage_comment/template_files/readme.md.j2 +++ b/coverage_comment/template_files/readme.md.j2 @@ -1,4 +1,4 @@ -# Repository Coverage +# Repository Coverage{% if subproject_id %} ({{ subproject_id }}){% endif %} {% if is_public -%} [Full report]({{ html_report_url }}) diff --git a/tests/end_to_end/conftest.py b/tests/end_to_end/conftest.py index 07e2f794..3f9e83bb 100644 --- a/tests/end_to_end/conftest.py +++ b/tests/end_to_end/conftest.py @@ -191,7 +191,7 @@ def gh_other_username(gh_other): @pytest.fixture -def git_repo(cd, git, action_ref, code_path): +def git_repo(cd, git, action_ref, code_path, subproject_id): with cd("repo") as repo: git("init", "-b", "main") # Copy .github @@ -213,6 +213,7 @@ def git_repo(cd, git, action_ref, code_path): file.read_text() .replace("__ACTION_REF__", action_ref) .replace("__ACTION_COVERAGE_PATH__", str(code_path)) + .replace("__ACTION_SUBPROJECT_ID__", json_module.dumps(subproject_id)) ) file.write_text(content) @@ -239,6 +240,12 @@ def code_path(request): return pathlib.Path(*mark.args) if mark else pathlib.Path(".") +@pytest.fixture +def subproject_id(request): + mark = request.node.get_closest_marker("subproject_id") + return mark.args[0] if mark else None + + @pytest.fixture def repo_full_name(repo_name, gh_me_username): return f"{gh_me_username}/{repo_name}" diff --git a/tests/end_to_end/repo/.github/workflows/ci.yml b/tests/end_to_end/repo/.github/workflows/ci.yml index 5d64e222..ca95b815 100644 --- a/tests/end_to_end/repo/.github/workflows/ci.yml +++ b/tests/end_to_end/repo/.github/workflows/ci.yml @@ -41,10 +41,11 @@ jobs: ANNOTATE_MISSING_LINES: true ANNOTATION_TYPE: notice COVERAGE_PATH: __ACTION_COVERAGE_PATH__ + SUBPROJECT_ID: __ACTION_SUBPROJECT_ID__ - name: Store Pull Request comment to be posted uses: actions/upload-artifact@v3 if: steps.coverage_comment.outputs.COMMENT_FILE_WRITTEN == 'true' with: name: python-coverage-comment-action - path: python-coverage-comment-action.txt + path: python-coverage-comment-action*.txt diff --git a/tests/end_to_end/repo/.github/workflows/coverage-comment.yml b/tests/end_to_end/repo/.github/workflows/coverage-comment.yml index 9db6642d..26668d71 100644 --- a/tests/end_to_end/repo/.github/workflows/coverage-comment.yml +++ b/tests/end_to_end/repo/.github/workflows/coverage-comment.yml @@ -22,3 +22,4 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_PR_RUN_ID: ${{ github.event.workflow_run.id }} COVERAGE_PATH: __ACTION_COVERAGE_PATH__ + SUBPROJECT_ID: __ACTION_SUBPROJECT_ID__ diff --git a/tests/end_to_end/test_all.py b/tests/end_to_end/test_all.py index 4b445295..69edae68 100644 --- a/tests/end_to_end/test_all.py +++ b/tests/end_to_end/test_all.py @@ -6,6 +6,7 @@ @pytest.mark.repo_suffix("public") @pytest.mark.code_path("subdir") +@pytest.mark.subproject_id("my-great-project") def test_public_repo( gh_create_repo, wait_for_run_to_start, @@ -71,15 +72,11 @@ def test_public_repo( assert number_of_svgs == 3 # Check that logs point to the branch that has the readme file. - data_branch_url = ( - f"https://github.com/{repo_full_name}/tree/python-coverage-comment-action-data" - ) + data_branch_url = f"https://github.com/{repo_full_name}/tree/python-coverage-comment-action-data-my-great-project" assert data_branch_url in links # Time to check the Readme contents - raw_url_prefix = ( - f"https://github.com/{repo_full_name}/raw/python-coverage-comment-action-data" - ) + raw_url_prefix = f"https://github.com/{repo_full_name}/raw/python-coverage-comment-action-data-my-great-project" readme_url = f"{raw_url_prefix}/README.md" response = client.get(readme_url, follow_redirects=True) @@ -124,6 +121,11 @@ def test_public_repo( fail_value="\n", ) assert ":arrow_up:" in comment + assert "## Coverage report (my-great-project)" in comment + assert ( + "This comment was produced by python-coverage-comment-action (id: my-great-project)" + in comment + ) # Let's merge the PR and see if everything works fine gh_me("pr", "merge", "1", "--merge") diff --git a/tests/unit/test_communication.py b/tests/unit/test_communication.py index 7fdb2b0a..033cdf77 100644 --- a/tests/unit/test_communication.py +++ b/tests/unit/test_communication.py @@ -35,6 +35,23 @@ def test_get_readme_and_log__public(): assert "https://c" in log +def test_get_readme_and_log__subproject(): + readme_file, log = communication.get_readme_and_log( + is_public=True, + image_urls={ + "direct": "https://a", + "endpoint": "https://b", + "dynamic": "https://c", + }, + readme_url="https://readme", + html_report_url="https://html_report", + markdown_report="**Hello report!**", + subproject_id="my-subproject", + ) + assert "Coverage info for my-subproject" in log + assert "# Repository Coverage (my-subproject)" in readme_file.contents + + def test_get_readme_and_log__private(): readme_file, log = communication.get_readme_and_log( is_public=False, diff --git a/tests/unit/test_settings.py b/tests/unit/test_settings.py index edab1716..428be88f 100644 --- a/tests/unit/test_settings.py +++ b/tests/unit/test_settings.py @@ -35,6 +35,7 @@ def test_config__from_environ__ok(): "GITHUB_STEP_SUMMARY": "step_summary", "COMMENT_ARTIFACT_NAME": "baz", "COMMENT_FILENAME": "qux", + "SUBPROJECT_ID": "subproject", "COMMENT_TEMPLATE": "footemplate", "COVERAGE_DATA_BRANCH": "branchname", "COVERAGE_PATH": "source_folder/", @@ -57,6 +58,7 @@ def test_config__from_environ__ok(): GITHUB_STEP_SUMMARY=pathlib.Path("step_summary"), COMMENT_ARTIFACT_NAME="baz", COMMENT_FILENAME=pathlib.Path("qux"), + SUBPROJECT_ID="subproject", COMMENT_TEMPLATE="footemplate", COVERAGE_DATA_BRANCH="branchname", COVERAGE_PATH=pathlib.Path("source_folder/"), @@ -156,3 +158,19 @@ def test_config__invalid_annotation_type(): ) def test_str_to_bool(input, output): assert settings.str_to_bool(input) is output + + +def test_final_comment_filename(config): + config_obj = config( + COMMENT_FILENAME=pathlib.Path("foo.txt"), + SUBPROJECT_ID="bar", + ) + assert config_obj.FINAL_COMMENT_FILENAME == pathlib.Path("foo-bar.txt") + + +def test_final_coverage_data_branch(config): + config_obj = config( + COVERAGE_DATA_BRANCH="foo", + SUBPROJECT_ID="bar", + ) + assert config_obj.FINAL_COVERAGE_DATA_BRANCH == "foo-bar" diff --git a/tests/unit/test_template.py b/tests/unit/test_template.py index 78a298b6..5ffe4f80 100644 --- a/tests/unit/test_template.py +++ b/tests/unit/test_template.py @@ -13,6 +13,7 @@ def test_get_comment_markdown(coverage_obj, diff_coverage_obj): coverage=coverage_obj, diff_coverage=diff_coverage_obj, previous_coverage_rate=decimal.Decimal("0.92"), + marker="", base_template=""" {{ previous_coverage_rate | pct }} {{ coverage.info.percent_covered | pct }} @@ -33,7 +34,7 @@ def test_get_comment_markdown(coverage_obj, diff_coverage_obj): "75%", "80%", "bar", - "", + "", ] assert result == expected @@ -45,11 +46,13 @@ def test_template(coverage_obj, diff_coverage_obj): diff_coverage=diff_coverage_obj, previous_coverage_rate=decimal.Decimal("0.92"), base_template=template.read_template_file("comment.md.j2"), + marker="", + subproject_id="foo", custom_template="""{% extends "base" %} {% block emoji_coverage_down %}:sob:{% endblock emoji_coverage_down %} """, ) - expected = """## Coverage report + expected = """## Coverage report (foo) The coverage rate went from `92%` to `75%` :sob: The branch rate is `50%`. @@ -63,7 +66,7 @@ def test_template(coverage_obj, diff_coverage_obj): Missing lines: `7`, `9` -""" +""" assert result == expected @@ -147,6 +150,7 @@ def test_template_full(): coverage=cov, diff_coverage=diff_cov, previous_coverage_rate=decimal.Decimal("1.0"), + marker="", base_template=template.read_template_file("comment.md.j2"), ) expected = """## Coverage report @@ -166,7 +170,7 @@ def test_template_full(): `100%` of new lines are covered (`100%` of the complete file). -""" +""" assert result == expected @@ -183,6 +187,7 @@ def test_template__no_new_lines_with_coverage(coverage_obj): coverage=coverage_obj, diff_coverage=diff_cov, previous_coverage_rate=decimal.Decimal("1.0"), + marker="", base_template=template.read_template_file("comment.md.j2"), ) expected = """## Coverage report @@ -192,7 +197,7 @@ def test_template__no_new_lines_with_coverage(coverage_obj): _None of the new lines are part of the tested code. Therefore, there is no coverage data about them._ -""" +""" assert result == expected @@ -201,6 +206,7 @@ def test_template__no_branch_no_previous(coverage_obj_no_branch, diff_coverage_o coverage=coverage_obj_no_branch, diff_coverage=diff_coverage_obj, previous_coverage_rate=None, + marker="", base_template=template.read_template_file("comment.md.j2"), ) expected = """## Coverage report @@ -219,13 +225,13 @@ def test_template__no_branch_no_previous(coverage_obj_no_branch, diff_coverage_o Missing lines: `7`, `9` -""" +""" assert result == expected def test_read_template_file(): assert template.read_template_file("comment.md.j2").startswith( - "{% block title %}## Coverage report{% endblock title %}" + "{% block title %}## Coverage report{% if subproject_id %}" ) @@ -236,6 +242,7 @@ def test_template__no_marker(coverage_obj, diff_coverage_obj): diff_coverage=diff_coverage_obj, previous_coverage_rate=decimal.Decimal("0.92"), base_template=template.read_template_file("comment.md.j2"), + marker="", custom_template="""foo bar""", ) @@ -247,6 +254,7 @@ def test_template__broken_template(coverage_obj, diff_coverage_obj): diff_coverage=diff_coverage_obj, previous_coverage_rate=decimal.Decimal("0.92"), base_template=template.read_template_file("comment.md.j2"), + marker="", custom_template="""{% extends "foo" %}""", ) @@ -268,3 +276,17 @@ def test_pct(value, displayed_coverage): def test_uptodate(): assert template.uptodate() is True + + +@pytest.mark.parametrize( + "marker_id, result", + [ + (None, ""), + ( + "foo", + "", + ), + ], +) +def test_get_marker(marker_id, result): + assert template.get_marker(marker_id=marker_id) == result