diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index ac978c86..22474318 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -66,7 +66,7 @@ def main(initialise_git_repository: str, deploy_docs_to_github_pages: str) -> in '-d "{{cookiecutter.project_short_description}}" ' "--public " "-r origin " - "--source {{cookiecutter.project_slug}}\n" + "--source {{cookiecutter.project_slug}}\n", ) except FileNotFoundError: # GitHub CLI isn't installed @@ -78,7 +78,7 @@ def main(initialise_git_repository: str, deploy_docs_to_github_pages: str) -> in "https://docs.github.com/en/get-started/quickstart/create-a-repo.\n\n" "Then run:\n\n" "git remote add origin git@github.com:" - "{{cookiecutter.__repo_name}}.git\n" + "{{cookiecutter.__repo_name}}.git\n", ) except subprocess.CalledProcessError as e: # some other error @@ -98,7 +98,7 @@ def main(initialise_git_repository: str, deploy_docs_to_github_pages: str) -> in "{{cookiecutter.__repo_url}}/settings/pages\n\n" "and under 'Built and deployment' selecting 'Deploy from a branch' for " "the 'Source' drop-down and 'gh-pages' for the 'Branch' drop-down, " - "leaving the branch path drop-down with its default value of '/ (root)'." + "leaving the branch path drop-down with its default value of '/ (root)'.", ) return _EXIT_SUCCESS @@ -109,5 +109,5 @@ def main(initialise_git_repository: str, deploy_docs_to_github_pages: str) -> in main( "{{ cookiecutter.initialise_git_repository }}", "{{ cookiecutter.deploy_docs_to_github_pages }}", - ) + ), ) diff --git a/tests/conftest.py b/tests/conftest.py index c95247ed..5fd2c878 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,20 +7,50 @@ import pytest +@pytest.fixture(scope="function") # noqa: PT003 +def default_config() -> dict[str, str]: + """ + Get the minimal default configuration for cutting a cookie in tests. + + This is used if `generate_package` is called without arguments. + """ + return { + "github_owner": "test-user", + "project_short_description": "description", + "project_name": "Cookiecutter Test", + "project_slug": "cookiecutter-test", + } + + +@pytest.fixture(scope="function") # noqa: PT003 +def default_config_with(default_config: dict[str, str]) -> typing.Callable: + """Extend or modify the default configuration with one additional value.""" + + def _wrapped_with(**kwargs: str) -> dict[str, str]: + return default_config | kwargs + + return _wrapped_with + + def _generate_package( config: dict[str, str], path: pathlib.Path -) -> subprocess.CompletedProcess[str]: +) -> tuple[subprocess.CompletedProcess[str], pathlib.Path]: """ Generate a project from the cookiecutter template. - Arguments: - --------- - config: dict + Parameters + ---------- + config A dictionary with values for the cookiecutter template, as defined in the cookiecutter.json - path: Path + path Directory to create package in. + Returns + ------- + subprocess.CompletedProcess, pathlib.Path + The result of the cookiecutter command and the path to the generated package. + """ args = [f"{key}={val}" for key, val in config.items()] cmd = ["cookiecutter", ".", "--no-input", "--output-dir", f"{path}"] @@ -30,10 +60,18 @@ def _generate_package( shell=False, capture_output=True, text=True, - ) + ), path / config["project_slug"] @pytest.fixture -def generate_package() -> typing.Callable: +def generate_package( + default_config: dict[str, str], tmp_path: pathlib.Path +) -> typing.Callable: """Generate project from cookiecutter template.""" - return _generate_package + + def _wrapped_with_tmp_path( + config: dict[str, str] = default_config, + ) -> tuple[subprocess.CompletedProcess, pathlib.Path]: + return _generate_package(config, tmp_path) + + return _wrapped_with_tmp_path diff --git a/tests/test_git_init.py b/tests/test_git_init.py index 3918ee78..60f59a38 100644 --- a/tests/test_git_init.py +++ b/tests/test_git_init.py @@ -1,29 +1,21 @@ """Checks that the git repo initialisation works.""" -import pathlib import subprocess import typing import pytest -@pytest.mark.parametrize("initialise_git_repository", [True, False]) +@pytest.mark.parametrize("init", [True, False]) def test_initialisation_of_git_repo( - initialise_git_repository: bool, # noqa: FBT001 + init: bool, # noqa: FBT001 + default_config_with: typing.Callable, generate_package: typing.Callable, - tmp_path: pathlib.Path, ) -> None: """Checks to see if git was correctly initialised if desired.""" - test_config = { - "github_owner": "test-user", - "project_short_description": "description", - "project_name": "Cookiecutter Test", - "initialise_git_repository": initialise_git_repository, - } # Run cookiecutter with initialise_git_repository - result = generate_package(config=test_config, path=tmp_path) - - test_project_dir = tmp_path / "cookiecutter-test" + config = default_config_with(initialise_git_repository=str(init)) + result, test_project_dir = generate_package(config=config) # check if git is initialised git_status = subprocess.run( # noqa: S603 @@ -37,7 +29,7 @@ def test_initialisation_of_git_repo( text=True, ) - if not initialise_git_repository: + if not init: # should not have found git assert "fatal: not a git repository" in git_status.stderr return # nothing more to test @@ -57,9 +49,9 @@ def test_initialisation_of_git_repo( ) assert ( "GitHub CLI detected, you can create a repo with the following:\n\n" - f"gh repo create {test_config['github_owner']}/" + f"gh repo create {config['github_owner']}/" f"cookiecutter-test -d " - f'"{test_config["project_short_description"]}" --public -r ' + f'"{config["project_short_description"]}" --public -r ' f"origin --source cookiecutter-test" in result.stdout ) except FileNotFoundError: @@ -69,11 +61,11 @@ def test_initialisation_of_git_repo( assert ( "You now have a local git repository. To sync this to GitHub you " "need to create an empty GitHub repo with the name " - f"{test_config['github_owner']}/" + f"{config['github_owner']}/" f"cookiecutter-test - DO NOT SELECT ANY " "OTHER OPTION.\n\nSee this link for more detail " "https://docs.github.com/en/get-started/quickstart/create-a-repo" ".\n\nThen run:\n\ngit remote add origin git@github.com:" - f"{test_config['github_owner']}/" + f"{config['github_owner']}/" f"cookiecutter-test.git" in result.stdout ) diff --git a/tests/test_package_generation.py b/tests/test_package_generation.py index 6926d9fa..ac6f3b23 100644 --- a/tests/test_package_generation.py +++ b/tests/test_package_generation.py @@ -36,26 +36,19 @@ def get_all_files_folders(root_path: pathlib.Path) -> set[pathlib.Path]: def test_package_generation( - tmp_path: pathlib.Path, + default_config_with: typing.Callable, generate_package: typing.Callable, ) -> None: """Test package generation.""" - test_config = { - "github_owner": "test-user", - "project_short_description": "description", - "project_name": "Cookiecutter Test", - # Not having a git repo makes it easier to check in/out reference - # data files to the main python-tooling git repository - "initialise_git_repository": False, - } - generate_package(config=test_config, path=tmp_path) + # Not having a git repo makes it easier to check in/out reference + # data files to the main python-tooling git repository + config = default_config_with(initialise_git_repository="False") + _, test_project_dir = generate_package(config=config) expected_package_dir = ( pathlib.Path(__file__).parent / "data" / "test_package_generation" ) - # Check project directory exists - test_project_dir = tmp_path / "cookiecutter-test" - assert test_project_dir.exists() + assert test_project_dir.exists(), "Project directory does not exist." actual_files = get_all_files_folders(test_project_dir) expected_files = get_all_files_folders(expected_package_dir) @@ -78,7 +71,7 @@ def test_package_generation( f2.readlines(), fromfile=str(actual_file), tofile=str(expected_file), - ) + ), ) if diff: @@ -94,18 +87,11 @@ def test_package_generation( def test_pip_installable( - tmp_path: pathlib.Path, venv: pytest_venv.VirtualEnvironment, generate_package: typing.Callable, ) -> None: """Test generated package is pip installable.""" - test_config = { - "github_owner": "test-user", - "project_short_description": "description", - "project_name": "Cookiecutter Test", - } - generate_package(config=test_config, path=tmp_path) - test_project_dir = tmp_path / "cookiecutter-test" + _, test_project_dir = generate_package() # Try to install package in virtual environment with pip pipinstall = subprocess.run( # noqa: S603 [ @@ -124,21 +110,16 @@ def test_pip_installable( ) -@pytest.mark.parametrize("funder", ["", "STFC"]) +@pytest.mark.parametrize("funder", ["", "STFC", "UKRI", "Wellcome Trust"]) def test_optional_funder( - tmp_path: pathlib.Path, generate_package: typing.Callable, funder: str + funder: str, + default_config_with: typing.Callable, + generate_package: typing.Callable, ) -> None: """Test specifying funder or not in package generation.""" - config = { - "github_owner": "test-user", - "project_short_description": "description", - "project_name": "Cookiecutter Test", - "funder": funder, - } - - generate_package(config, tmp_path) + config = default_config_with(funder=funder) + _, test_project_dir = generate_package(config) - test_project_dir = tmp_path / "cookiecutter-test" with (test_project_dir / "README.md").open() as f: readme_text = "".join(f.readlines()) @@ -146,24 +127,16 @@ def test_optional_funder( assert "## Acknowledgements" not in readme_text else: assert ( - f"## Acknowledgements\n\nThis work was funded by {config['funder']}." - in readme_text + f"## Acknowledgements\n\nThis work was funded by {funder}." in readme_text ), readme_text def test_docs_build( - tmp_path: pathlib.Path, venv: pytest_venv.VirtualEnvironment, generate_package: typing.Callable, ) -> None: """Test documentation build from package created from template.""" - config = { - "github_owner": "test-user", - "project_short_description": "description", - "project_name": "Cookiecutter Test", - } - generate_package(config, tmp_path) - test_project_dir = tmp_path / "cookiecutter-test" + _, test_project_dir = generate_package() venv.install("tox") tox_docs_process = subprocess.run( # noqa: S603 [