copier/tests/test_answersfile_templating.py
2025-04-22 13:52:51 +02:00

139 lines
4.5 KiB
Python

from __future__ import annotations
from pathlib import Path
import pytest
import copier
from copier._user_data import load_answersfile_data
from .helpers import build_file_tree
@pytest.fixture(scope="module")
def template_path(tmp_path_factory: pytest.TempPathFactory) -> str:
root = tmp_path_factory.mktemp("template")
build_file_tree(
{
root / "{{ _copier_conf.answers_file }}.jinja": """\
# Changes here will be overwritten by Copier
{{ _copier_answers|to_nice_yaml }}
""",
root / "copier.yml": """\
_answers_file: ".copier-answers-{{ module_name }}.yml"
module_name:
type: str
""",
}
)
return str(root)
@pytest.mark.parametrize("answers_file", [None, ".changed-by-user.yml"])
def test_answersfile_templating(
template_path: str, tmp_path: Path, answers_file: str | None
) -> None:
"""
Test copier behaves properly when _answers_file contains a template
Checks that template is resolved successfully and that a subsequent
copy that resolves to a different answers file doesn't clobber the
old answers file.
"""
copier.run_copy(
template_path,
tmp_path,
{"module_name": "mymodule"},
answers_file=answers_file,
defaults=True,
overwrite=True,
unsafe=True,
)
first_answers_file = (
".copier-answers-mymodule.yml"
if answers_file is None
else ".changed-by-user.yml"
)
assert (tmp_path / first_answers_file).exists()
answers = load_answersfile_data(tmp_path, first_answers_file)
assert answers["module_name"] == "mymodule"
copier.run_copy(
template_path,
tmp_path,
{"module_name": "anothermodule"},
defaults=True,
overwrite=True,
unsafe=True,
)
# Assert second one created
second_answers_file = ".copier-answers-anothermodule.yml"
assert (tmp_path / second_answers_file).exists()
answers = load_answersfile_data(tmp_path, second_answers_file)
assert answers["module_name"] == "anothermodule"
# Assert first one still exists
assert (tmp_path / first_answers_file).exists()
answers = load_answersfile_data(tmp_path, first_answers_file)
assert answers["module_name"] == "mymodule"
def test_answersfile_templating_with_message_before_copy(
tmp_path_factory: pytest.TempPathFactory,
) -> None:
"""Test templated `_answers_file` setting with `_message_before_copy`.
Checks that the tempated answers file name is rendered correctly while
having printing a message before the "copy" operation, which uses the render
context before including any answers from the questionnaire.
"""
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
build_file_tree(
{
(src / "copier.yml"): (
"""\
_answers_file: ".copier-answers-{{ module_name }}.yml"
_message_before_copy: "Hello world"
module_name:
type: str
"""
),
(src / "{{ _copier_conf.answers_file }}.jinja"): (
"""\
# Changes here will be overwritten by Copier
{{ _copier_answers|to_nice_yaml }}
"""
),
(src / "result.txt.jinja"): "{{ module_name }}",
}
)
copier.run_copy(
str(src), dst, data={"module_name": "mymodule"}, overwrite=True, unsafe=True
)
assert (dst / ".copier-answers-mymodule.yml").exists()
answers = load_answersfile_data(dst, ".copier-answers-mymodule.yml")
assert answers["module_name"] == "mymodule"
assert (dst / "result.txt").exists()
assert (dst / "result.txt").read_text() == "mymodule"
def test_answersfile_templating_phase(tmp_path_factory: pytest.TempPathFactory) -> None:
"""
Ensure `_copier_phase` is available while render `answers_relpath`.
Not because it is directly useful, but because some extensions might need it.
"""
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
build_file_tree(
{
src / "copier.yml": """\
_answers_file: ".copier-answers-{{ _copier_phase }}.yml"
""",
src / "{{ _copier_conf.answers_file }}.jinja": "",
}
)
copier.run_copy(str(src), dst, overwrite=True, unsafe=True)
assert (dst / ".copier-answers-render.yml").exists()