From bf53ea9f6a214bd544afd8218095537bafdadefa Mon Sep 17 00:00:00 2001 From: charlie4284 Date: Fri, 1 Mar 2024 12:35:48 +0800 Subject: [PATCH] test: decorate report functions --- repo_policy_compliance/check.py | 38 +++++++++++++- src-docs/check.py.md | 85 ++++++++++++++++++++++++++++++-- tests/unit/test_check.py | 23 +++++++++ tests/unit/test_github_client.py | 16 +++--- 4 files changed, 149 insertions(+), 13 deletions(-) diff --git a/repo_policy_compliance/check.py b/repo_policy_compliance/check.py index 6f8c8413..4893775d 100644 --- a/repo_policy_compliance/check.py +++ b/repo_policy_compliance/check.py @@ -4,7 +4,7 @@ """Individual checks used to compose job checks.""" from enum import Enum -from typing import NamedTuple +from typing import Callable, NamedTuple, ParamSpec, TypeVar from github import Github from github.Branch import Branch @@ -12,6 +12,7 @@ from repo_policy_compliance import log from repo_policy_compliance.comment import remove_quote_lines +from repo_policy_compliance.exceptions import GithubClientError from repo_policy_compliance.github_client import ( get_branch, get_collaborator_permission, @@ -65,6 +66,38 @@ class Report(NamedTuple): log.setup() +P = ParamSpec("P") +R = TypeVar("R") + + +def github_exceptions_to_fail_report(func: Callable[P, R]) -> Callable[P, R | Report]: + """Catch exceptions and convert to failed report with reason. + + Args: + func: The function to catch the GithubClient exceptions for. + + Returns: + The function where any exceptions raised would be converted to a failed result. + """ + + def wrapper(*args: P.args, **kwargs: P.kwargs) -> R | Report: + """Replace function. + + Args: + args: The positional arguments passed to the original method. + kwargs: The keywords arguments passed to the original method. + + Returns: + Failed result report if any exceptions were raised. The return value after calling the + wrapped function otherwise. + """ + try: + return func(*args, **kwargs) + except GithubClientError as exc: + return Report(result=Result.FAIL, reason=str(exc)) + + return wrapper + @log.call def branch_protected(branch: Branch) -> Report: @@ -84,6 +117,7 @@ def branch_protected(branch: Branch) -> Report: return Report(result=Result.PASS, reason=None) +@github_exceptions_to_fail_report @inject_github_client @log.call def target_branch_protection( @@ -133,6 +167,7 @@ def target_branch_protection( return Report(result=Result.PASS, reason=None) +@github_exceptions_to_fail_report @inject_github_client @log.call def collaborators(github_client: Github, repository_name: str) -> Report: @@ -195,6 +230,7 @@ def _branch_external_fork(repository: Repository, source_repository_name: str) - return True +@github_exceptions_to_fail_report @inject_github_client @log.call def execute_job( diff --git a/src-docs/check.py.md b/src-docs/check.py.md index ad9e110c..b1d2d89c 100644 --- a/src-docs/check.py.md +++ b/src-docs/check.py.md @@ -14,12 +14,14 @@ Individual checks used to compose job checks. --- - + -## function `exceptions_to_fail_report` +## function `github_exceptions_to_fail_report` ```python -exceptions_to_fail_report(func: Callable[~P, ~R]) → Callable[~P, ~R] +github_exceptions_to_fail_report( + func: Callable[~P, ~R] +) → Callable[~P, Union[~R, Report]] ``` Catch exceptions and convert to failed report with reason. @@ -28,7 +30,7 @@ Catch exceptions and convert to failed report with reason. **Args:** - - `func`: The function to catch the exceptions for. + - `func`: The function to catch the GithubClient exceptions for. @@ -36,6 +38,81 @@ Catch exceptions and convert to failed report with reason. The function where any exceptions raised would be converted to a failed result. +--- + + + +## function `wrapper` + +```python +wrapper(*args: args, **kwargs: kwargs) → Union[~R, Report] +``` + +Replace function. + + + +**Args:** + + - `args`: The positional arguments passed to the original method. + - `kwargs`: The keywords arguments passed to the original method. + + + +**Returns:** + Failed result report if any exceptions were raised. The return value after calling the wrapped function otherwise. + + +--- + + + +## function `wrapper` + +```python +wrapper(*args: args, **kwargs: kwargs) → Union[~R, Report] +``` + +Replace function. + + + +**Args:** + + - `args`: The positional arguments passed to the original method. + - `kwargs`: The keywords arguments passed to the original method. + + + +**Returns:** + Failed result report if any exceptions were raised. The return value after calling the wrapped function otherwise. + + +--- + + + +## function `wrapper` + +```python +wrapper(*args: args, **kwargs: kwargs) → Union[~R, Report] +``` + +Replace function. + + + +**Args:** + + - `args`: The positional arguments passed to the original method. + - `kwargs`: The keywords arguments passed to the original method. + + + +**Returns:** + Failed result report if any exceptions were raised. The return value after calling the wrapped function otherwise. + + --- ## class `Report` diff --git a/tests/unit/test_check.py b/tests/unit/test_check.py index e5f3976e..54d377cd 100644 --- a/tests/unit/test_check.py +++ b/tests/unit/test_check.py @@ -12,6 +12,29 @@ from github.Repository import Repository import repo_policy_compliance +from repo_policy_compliance.check import Result +from repo_policy_compliance.exceptions import GithubClientError + + +def test_github_exceptions_to_fail_report(): + """ + arrange: given a function that raises a GithubClient error. + act: when the function is called with github_exceptions_to_fail_report decorator. + assert: a failed report with exception as reason is returned. + """ + + @repo_policy_compliance.check.github_exceptions_to_fail_report + def github_client_error_raiser(): + """A mock function to raise github client error. + + Raises: + GithubClientError: always. + """ + raise GithubClientError("Exception message.") + + report = github_client_error_raiser() + assert report.result == Result.FAIL + assert report.reason == "Exception message." @pytest.mark.parametrize( diff --git a/tests/unit/test_github_client.py b/tests/unit/test_github_client.py index f590126b..b9a91ad3 100644 --- a/tests/unit/test_github_client.py +++ b/tests/unit/test_github_client.py @@ -10,7 +10,7 @@ from github.Repository import Repository import repo_policy_compliance.github_client -from repo_policy_compliance.check import target_branch_protection +from repo_policy_compliance.check import Result, target_branch_protection from repo_policy_compliance.exceptions import GithubClientError GITHUB_REPOSITORY_NAME = "test/repository" @@ -48,12 +48,12 @@ def test_github_error( repo_policy_compliance.github_client, "get", lambda *_args, **_kwargs: github_client ) - with pytest.raises(GithubClientError) as error: - # The github_client is injected - target_branch_protection( # pylint: disable=no-value-for-parameter - GITHUB_REPOSITORY_NAME, GITHUB_BRANCH_NAME, GITHUB_REPOSITORY_NAME - ) - assert expected_message in str(error.value) + # The github_client is injected + report = target_branch_protection( # pylint: disable=no-value-for-parameter + GITHUB_REPOSITORY_NAME, GITHUB_BRANCH_NAME, GITHUB_REPOSITORY_NAME + ) + assert report.result == Result.FAIL + assert expected_message in str(report.reason) def test_get_collaborator_permission_error(): @@ -67,7 +67,7 @@ def test_get_collaborator_permission_error(): with pytest.raises(GithubClientError) as error: # The github_client is injected - repo_policy_compliance.github_client.get_collaborator_permission( # pylint: disable=no-value-for-parameter + repo_policy_compliance.github_client.get_collaborator_permission( mock_repository, "test_user" ) assert "Invalid collaborator permission" in str(error.value)