copier/tests/test_copy.py
ChaCha e38caa20cc
fix: version guess from tags that don't start with v, but are still PEP440 compliant (#1048)
Without this change, Dunamai by default needs tags to be prefixed with a 'v'. ie: v0.1.2 v1.1.2 ...

Fixes https://github.com/copier-org/copier/issues/1042.

Co-authored-by: cclecle <clement.chastanier@gmail.com>
Co-authored-by: Jairo Llopis <yajo.sk8@gmail.com>
2023-10-01 07:32:31 +00:00

888 lines
27 KiB
Python

import platform
import stat
import sys
from contextlib import nullcontext as does_not_raise
from pathlib import Path
from typing import Any, ContextManager, List
import pytest
import yaml
from plumbum import local
from plumbum.cmd import git
from prompt_toolkit.validation import ValidationError
import copier
from copier import run_copy
from copier.types import AnyByStrDict
from .helpers import (
BRACKET_ENVOPS,
BRACKET_ENVOPS_JSON,
PROJECT_TEMPLATE,
assert_file,
build_file_tree,
filecmp,
git_save,
render,
)
def test_project_not_found(tmp_path: Path) -> None:
with pytest.raises(ValueError):
copier.run_copy("foobar", tmp_path)
with pytest.raises(ValueError):
copier.run_copy(__file__, tmp_path)
def test_copy_with_non_version_tags(tmp_path_factory: pytest.TempPathFactory) -> None:
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
build_file_tree(
{
(src / ".copier-answers.yml.jinja"): (
"""\
# Changes here will be overwritten by Copier
{{ _copier_answers|to_nice_yaml }}
"""
),
(src / "copier.yaml"): (
"""\
_tasks:
- cat v1.txt
"""
),
(src / "v1.txt"): "file only in v1",
}
)
with local.cwd(src):
git("init")
git("add", ".")
git("commit", "-m1")
git("tag", "test_tag.post23+deadbeef")
run_copy(
str(src),
dst,
defaults=True,
overwrite=True,
unsafe=True,
vcs_ref="HEAD",
)
@pytest.mark.parametrize(
"tag2, use_prereleases, expected_version",
[
("1.2.3.post1", True, "2"),
("1.2.4.dev1", True, "2"),
("1.2.3.post1", False, "2"),
("1.2.4.dev1", False, "1"),
],
)
@pytest.mark.parametrize("prefix", ["", "v"])
def test_tag_autodetect_v_prefix_optional(
tmp_path_factory: pytest.TempPathFactory,
tag2: str,
use_prereleases: bool,
expected_version,
prefix: str,
) -> None:
"""Tags with and without `v` prefix should work the same."""
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
build_file_tree(
{
(src / "version.txt"): "1",
(
src / "{{_copier_conf.answers_file}}.jinja"
): "{{ _copier_answers|to_nice_yaml -}}",
}
)
# One stable tag
git_save(src, tag=f"{prefix}1.2.3")
# One pre or post release tag
(src / "version.txt").write_text("2")
git_save(src, tag=f"{prefix}{tag2}")
# One untagged change
(src / "version.txt").write_text("3")
git_save(src)
# Copy, leaving latest tag auto-detection
copier.run_copy(
str(src),
dst,
use_prereleases=use_prereleases,
defaults=True,
unsafe=True,
)
# Check it works as expected
assert (dst / "version.txt").read_text() == expected_version
def test_copy_with_non_version_tags_and_vcs_ref(
tmp_path_factory: pytest.TempPathFactory,
) -> None:
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
build_file_tree(
{
(src / ".copier-answers.yml.jinja"): (
"""\
# Changes here will be overwritten by Copier
{{ _copier_answers|to_nice_yaml }}
"""
),
(src / "copier.yaml"): (
"""\
_tasks:
- cat v1.txt
"""
),
(src / "v1.txt"): "file only in v1",
}
)
with local.cwd(src):
git("init")
git("add", ".")
git("commit", "-m1")
git("tag", "test_tag.post23+deadbeef")
run_copy(
str(src),
dst,
defaults=True,
overwrite=True,
unsafe=True,
vcs_ref="test_tag.post23+deadbeef",
)
def test_copy_with_vcs_ref_branch(tmp_path_factory: pytest.TempPathFactory) -> None:
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
with local.cwd(src):
git("init")
main_branch = git("branch", "--show-current").strip()
git("add", ".")
git("commit", "-m1", "--allow-empty")
git("checkout", "-b", "branch")
git("commit", "-m2", "--allow-empty")
git("checkout", main_branch)
run_copy(str(src), dst, vcs_ref="branch")
def test_copy(tmp_path: Path) -> None:
render(tmp_path)
generated = (tmp_path / "pyproject.toml").read_text()
control = (Path(__file__).parent / "reference_files" / "pyproject.toml").read_text()
assert generated == control
assert_file(tmp_path, "doc", "mañana.txt")
assert_file(tmp_path, "doc", "images", "nslogo.gif")
p1 = tmp_path / "awesome" / "hello.txt"
p2 = PROJECT_TEMPLATE / "[[ myvar ]]" / "hello.txt"
assert filecmp.cmp(p1, p2)
assert (tmp_path / "README.txt").read_text() == "This is the README for Copier.\n"
p1 = tmp_path / "awesome.txt"
p2 = PROJECT_TEMPLATE / "[[ myvar ]].txt"
assert filecmp.cmp(p1, p2)
assert not (tmp_path / "[% if not py3 %]py2_only.py[% endif %]").exists()
assert not (tmp_path / "[% if py3 %]py3_only.py[% endif %]").exists()
assert not (tmp_path / "py2_only.py").exists()
assert (tmp_path / "py3_only.py").exists()
assert not (
tmp_path / "[% if not py3 %]py2_folder[% endif %]" / "thing.py"
).exists()
assert not (tmp_path / "[% if py3 %]py3_folder[% endif %]" / "thing.py").exists()
assert not (tmp_path / "py2_folder" / "thing.py").exists()
assert (tmp_path / "py3_folder" / "thing.py").exists()
assert (tmp_path / "aaaa.txt").exists()
@pytest.mark.impure
def test_copy_repo(tmp_path: Path) -> None:
copier.run_copy(
"gh:copier-org/copier.git",
tmp_path,
vcs_ref="HEAD",
quiet=True,
exclude=["*", "!README.*"],
)
assert (tmp_path / "README.md").exists()
def test_default_exclude(tmp_path: Path) -> None:
render(tmp_path)
assert not (tmp_path / ".svn").exists()
def test_include_file(tmp_path: Path) -> None:
render(tmp_path, exclude=["!.svn"])
assert_file(tmp_path, ".svn")
def test_include_pattern(tmp_path: Path) -> None:
render(tmp_path, exclude=["!.*"])
assert (tmp_path / ".svn").exists()
@pytest.mark.xfail(
condition=platform.system() == "Darwin",
reason="Mac claims to use UTF-8 filesystem, but behaves differently.",
strict=True,
)
def test_exclude_file(tmp_path: Path) -> None:
print(f"Filesystem encoding is {sys.getfilesystemencoding()}")
# This file name is b"man\xcc\x83ana.txt".decode()
render(tmp_path, exclude=["mañana.txt"])
assert not (tmp_path / "doc" / "mañana.txt").exists()
# This file name is b"ma\xc3\xb1ana.txt".decode()
assert (tmp_path / "doc" / "mañana.txt").exists()
assert (tmp_path / "doc" / "manana.txt").exists()
def test_exclude_extends(tmp_path_factory: pytest.TempPathFactory) -> None:
"""Exclude argument extends the original exclusions instead of replacing them."""
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
build_file_tree({src / "test.txt": "Test text", src / "test.json": '"test json"'})
# Convert to git repo
with local.cwd(src):
git("init")
git("add", ".")
git("commit", "-m", "hello world")
copier.run_copy(str(src), dst, exclude=["*.txt"])
assert (dst / "test.json").is_file()
assert not (dst / "test.txt").exists()
# .git exists in src, but not in dst because it is excluded by default
assert not (dst / ".git").exists()
def test_exclude_replaces(tmp_path_factory: pytest.TempPathFactory) -> None:
"""Exclude in copier.yml replaces default values."""
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
build_file_tree(
{
src / "test.txt": "Test text",
src / "test.json": '"test json"',
src / "test.yaml": '"test yaml"',
src / "copier.yaml.jinja": "purpose: template inception",
src / "copier.yml": "_exclude: ['*.json']",
}
)
copier.run_copy(str(src), dst, exclude=["*.txt"])
assert (dst / "test.yaml").is_file()
assert not (dst / "test.txt").exists()
assert not (dst / "test.json").exists()
assert (dst / "copier.yml").exists()
assert (dst / "copier.yaml").is_file()
def test_skip_if_exists(tmp_path: Path) -> None:
copier.run_copy(str(Path("tests", "demo_skip_dst")), tmp_path)
copier.run_copy(
"tests/demo_skip_src",
tmp_path,
skip_if_exists=["b.noeof.txt", "meh/c.noeof.txt"],
defaults=True,
overwrite=True,
)
assert (tmp_path / "a.noeof.txt").read_text() == "SKIPPED"
assert (tmp_path / "b.noeof.txt").read_text() == "SKIPPED"
assert (tmp_path / "meh" / "c.noeof.txt").read_text() == "SKIPPED"
def test_skip_if_exists_rendered_patterns(tmp_path: Path) -> None:
copier.run_copy("tests/demo_skip_dst", tmp_path)
copier.run_copy(
"tests/demo_skip_src",
tmp_path,
data={"name": "meh"},
skip_if_exists=["{{ name }}/c.noeof.txt"],
defaults=True,
overwrite=True,
)
assert (tmp_path / "a.noeof.txt").read_text() == "SKIPPED"
assert (tmp_path / "b.noeof.txt").read_text() == "OVERWRITTEN"
assert (tmp_path / "meh" / "c.noeof.txt").read_text() == "SKIPPED"
def test_skip_option(tmp_path: Path) -> None:
render(tmp_path)
path = tmp_path / "pyproject.toml"
content = "lorem ipsum"
path.write_text(content)
render(tmp_path, skip_if_exists=["**"])
assert path.read_text() == content
def test_force_option(tmp_path: Path) -> None:
render(tmp_path)
path = tmp_path / "pyproject.toml"
content = "lorem ipsum"
path.write_text(content)
render(tmp_path, defaults=True, overwrite=True)
assert path.read_text() != content
def test_pretend_option(tmp_path: Path) -> None:
render(tmp_path, pretend=True)
assert not (tmp_path / "doc").exists()
assert not (tmp_path / "config.py").exists()
assert not (tmp_path / "pyproject.toml").exists()
@pytest.mark.parametrize("generate", [True, False])
def test_empty_dir(tmp_path_factory: pytest.TempPathFactory, generate: bool) -> None:
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
build_file_tree(
{
(src / "copier.yaml"): (
f"""\
_subdirectory: tpl
_templates_suffix: .jinja
_envops: {BRACKET_ENVOPS_JSON}
do_it:
type: bool
"""
),
(src / "tpl" / "[% if do_it %]one_dir[% endif %]" / "one.txt.jinja"): (
"[[ do_it ]]"
),
(src / "tpl" / "two.txt"): "[[ do_it ]]",
(src / "tpl" / "[% if do_it %]three.txt[% endif %].jinja"): "[[ do_it ]]",
(src / "tpl" / "[% if do_it %]four.txt[% endif %].jinja"): Path(
"[% if do_it %]three.txt[% endif %].jinja"
),
(src / "tpl" / "five" / "[% if do_it %]six.txt[% endif %].jinja"): (
"[[ do_it ]]"
),
},
)
copier.run_copy(str(src), dst, {"do_it": generate}, defaults=True, overwrite=True)
assert (dst / "five").is_dir()
assert (dst / "two.txt").read_text() == "[[ do_it ]]"
assert (dst / "one_dir").exists() == generate
assert (dst / "three.txt").exists() == generate
assert (dst / "four.txt").exists() == generate
assert (dst / "one_dir").is_dir() == generate
assert (dst / "one_dir" / "one.txt").is_file() == generate
if generate:
assert (dst / "one_dir" / "one.txt").read_text() == repr(generate)
assert (dst / "three.txt").read_text() == repr(generate)
assert not (dst / "four.txt").is_symlink()
assert (dst / "four.txt").read_text() == repr(generate)
assert (dst / "five" / "six.txt").read_text() == repr(generate)
@pytest.mark.skipif(
condition=platform.system() == "Windows",
reason="Windows does not have UNIX-like permissions",
)
@pytest.mark.parametrize(
"permissions",
(
stat.S_IRUSR,
stat.S_IRWXU,
stat.S_IRWXU | stat.S_IRWXG,
stat.S_IRWXU | stat.S_IRWXO,
stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO,
),
)
def test_preserved_permissions(
tmp_path_factory: pytest.TempPathFactory, permissions: int
) -> None:
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
src_file = src / "the_file"
src_file.write_text("content")
src_file.chmod(permissions)
copier.run_copy(str(src), dst, defaults=True, overwrite=True)
assert ((dst / "the_file").stat().st_mode & permissions) == permissions
def test_commit_hash(tmp_path_factory: pytest.TempPathFactory) -> None:
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
build_file_tree(
{
src / "commit.jinja": "{{_copier_conf.vcs_ref_hash}}",
src / "tag.jinja": "{{_copier_answers._commit}}",
}
)
with local.cwd(src):
git("init")
git("add", "-A")
git("commit", "-m1")
git("tag", "1.0")
commit = git("rev-parse", "HEAD").strip()
copier.run_copy(str(src), dst)
assert (dst / "tag").read_text() == "1.0"
assert (dst / "commit").read_text() == commit
def test_value_with_forward_slash(tmp_path_factory: pytest.TempPathFactory) -> None:
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
build_file_tree(
{
(src / "{{ filename.replace('.', _copier_conf.sep) }}.txt"): (
"This is template."
),
}
)
copier.run_copy(str(src), dst, data={"filename": "a.b.c"})
assert (dst / "a" / "b" / "c.txt").read_text() == "This is template."
@pytest.mark.parametrize(
"spec, value, expected",
[
({"type": "int"}, "no-int", pytest.raises(ValueError)),
({"type": "int"}, "0", does_not_raise()),
(
{
"type": "int",
"validator": "[% if q <= 0 %]must be positive[% endif %]",
},
"0",
pytest.raises(ValidationError),
),
(
{
"type": "int",
"validator": "[% if q <= 0 %]must be positive[% endif %]",
},
"1",
does_not_raise(),
),
(
{
"type": "str",
"validator": "[% if q|length < 3 %]too short[% endif %]",
},
"ab",
pytest.raises(ValidationError),
),
(
{
"type": "str",
"validator": "[% if q|length < 3 %]too short[% endif %]",
},
"abc",
does_not_raise(),
),
({"type": "bool"}, "true", does_not_raise()),
({"type": "bool"}, "false", does_not_raise()),
(
{"type": "int", "choices": [1, 2, 3]},
"1",
does_not_raise(),
),
(
{"type": "int", "choices": [1, 2, 3]},
"4",
pytest.raises(ValueError),
),
(
{
"type": "int",
"choices": [1, ["2", {"value": None}], 3],
},
"2",
does_not_raise(),
),
(
{
"type": "int",
"choices": [1, ["2", {"value": None, "disabled": ""}], 3],
},
"2",
does_not_raise(),
),
(
{
"type": "int",
"choices": [1, ["2", {"value": None, "validator": "disabled"}], 3],
},
"2",
pytest.raises(ValueError, match="Invalid choice: disabled"),
),
(
{"type": "int", "choices": {"one": 1, "two": 2, "three": 3}},
"1",
does_not_raise(),
),
(
{"type": "int", "choices": {"one": 1, "two": 2, "three": 3}},
"4",
pytest.raises(ValueError),
),
(
{"type": "int", "choices": {"one": 1, "two": {"value": 2}, "three": 3}},
"2",
does_not_raise(),
),
(
{
"type": "int",
"choices": {
"one": 1,
"two": {"value": 2, "validator": ""},
"three": 3,
},
},
"2",
does_not_raise(),
),
(
{
"type": "int",
"choices": {
"one": 1,
"two": {"value": 2, "validator": "disabled"},
"three": 3,
},
},
"2",
pytest.raises(ValueError, match="Invalid choice: disabled"),
),
(
{"type": "str", "choices": {"one": None, "two": None, "three": None}},
"one",
does_not_raise(),
),
(
{"type": "str", "choices": {"one": None, "two": None, "three": None}},
"four",
pytest.raises(ValueError),
),
(
{
"type": "str",
"choices": {
"banana": {"value": "yellow", "validator": "invalid"},
"tshirt": {"value": "yellow", "validator": ""},
},
},
"yellow",
does_not_raise(),
),
(
{"type": "yaml", "choices": {"one": None, "two": 2, "three": "null"}},
"one",
does_not_raise(),
),
(
{"type": "yaml", "choices": {"one": None, "two": 2, "three": "null"}},
"2",
does_not_raise(),
),
(
{"type": "yaml", "choices": {"one": None, "two": 2, "three": "null"}},
"null",
does_not_raise(),
),
(
{"type": "yaml", "choices": {"one": None, "two": 2, "three": "null"}},
"two",
pytest.raises(ValueError),
),
(
{"type": "yaml", "choices": {"one": None, "two": 2, "three": "null"}},
"three",
pytest.raises(ValueError),
),
(
{"type": "yaml", "choices": {"array": "[a, b]"}},
"[a, b]",
does_not_raise(),
),
(
{"type": "yaml", "choices": {"object": "k: v"}},
"k: v",
does_not_raise(),
),
(
{"type": "yaml", "choices": [{"one": 1, "two": 2}]},
"one: 1\ntwo: 2",
does_not_raise(),
),
(
{"type": "yaml", "choices": {"complex": {"value": {"key": "value"}}}},
"key: value",
does_not_raise(),
),
(
{
"type": "yaml",
"choices": {"complex": {"value": {"key": "value"}, "validator": ""}},
},
"key: value",
does_not_raise(),
),
(
{
"type": "yaml",
"choices": {
"complex": {"value": {"key": "value"}, "validator": "disabled"}
},
},
"key: value",
pytest.raises(ValueError, match="Invalid choice: disabled"),
),
(
{"type": "yaml", "choices": {"complex": {"key": "value"}}},
"key: value",
pytest.raises(KeyError, match="Property 'value' is required"),
),
(
{"type": "yaml", "choices": {"complex": {"value": 1, "validator": {}}}},
"1",
pytest.raises(ValueError, match="Property 'validator' must be a string"),
),
(
{"type": "yaml", "choices": [{"key": "value"}]},
"key: value",
does_not_raise(),
),
(
{"type": "yaml", "choices": [{"value": 1, "validator": {}}]},
"value: 1\nvalidator: {}",
does_not_raise(),
),
(
{"type": "yaml", "choices": [["complex", {"value": {"key": "value"}}]]},
"key: value",
does_not_raise(),
),
(
{
"type": "yaml",
"choices": [["complex", {"value": {"key": "value"}, "validator": ""}]],
},
"key: value",
does_not_raise(),
),
(
{
"type": "yaml",
"choices": [
["complex", {"value": {"key": "value"}, "validator": "disabled"}]
],
},
"key: value",
pytest.raises(ValueError, match="Invalid choice: disabled"),
),
(
{"type": "yaml", "choices": [["complex", {"key": "value"}]]},
"key: value",
pytest.raises(KeyError, match="Property 'value' is required"),
),
(
{"type": "yaml", "choices": [["complex", {"value": 1, "validator": {}}]]},
"1",
pytest.raises(ValueError, match="Property 'validator' must be a string"),
),
],
)
def test_validate_init_data(
tmp_path_factory: pytest.TempPathFactory,
spec: AnyByStrDict,
value: str,
expected: ContextManager[None],
) -> None:
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
build_file_tree(
{
(src / "copier.yml"): yaml.dump(
{
"_envops": BRACKET_ENVOPS,
"_templates_suffix": ".jinja",
"q": spec,
}
),
}
)
with expected:
copier.run_copy(str(src), dst, data={"q": value})
@pytest.mark.parametrize("defaults", [False, True])
def test_validate_init_data_with_skipped_question(
tmp_path_factory: pytest.TempPathFactory, defaults: bool
) -> None:
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
build_file_tree(
{
(src / "copier.yml"): (
f"""\
_envops: {BRACKET_ENVOPS_JSON}
kind:
type: str
help: What kind do you need?
choices:
foo: foo
bar: bar
testbar:
when: "[[ kind == 'bar' ]]"
type: str
help: any string bar?
testfoo:
when: "[[ kind == 'foo' ]]"
type: str
help: any string foo?
"""
),
(src / "result.jinja"): (
"""\
[[ kind ]]
[[ testbar ]]
[[ testfoo ]]
"""
),
}
)
copier.run_copy(
str(src), dst, defaults=defaults, data={"kind": "foo", "testfoo": "helloworld"}
)
assert (dst / "result").read_text() == "foo\n\nhelloworld\n"
@pytest.mark.parametrize(
"type_name",
["str", "int", "float", "bool", "json", "yaml"],
)
def test_required_question_without_data(
tmp_path_factory: pytest.TempPathFactory, type_name: str
) -> None:
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
build_file_tree(
{
(src / "copier.yml"): yaml.safe_dump(
{
"question": {
"type": type_name,
}
}
)
}
)
with pytest.raises(ValueError, match='Question "question" is required'):
copier.run_copy(str(src), dst, defaults=True)
@pytest.mark.parametrize(
"type_name, choices",
[
("str", ["one", "two", "three"]),
("int", [1, 2, 3]),
("float", [1.0, 2.0, 3.0]),
("json", ["[1]", "[2]", "[3]"]),
("yaml", ["- 1", "- 2", "- 3"]),
],
)
def test_required_choice_question_without_data(
tmp_path_factory: pytest.TempPathFactory, type_name: str, choices: List[Any]
) -> None:
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
build_file_tree(
{
(src / "copier.yml"): yaml.safe_dump(
{
"question": {
"type": type_name,
"choices": choices,
}
}
)
}
)
with pytest.raises(ValueError, match='Question "question" is required'):
copier.run_copy(str(src), dst, defaults=True)
@pytest.mark.parametrize(
"type_name, default, expected",
[
("str", "string", does_not_raise()),
("str", "1.0", does_not_raise()),
("str", 1.0, does_not_raise()),
("str", None, pytest.raises(TypeError)),
("int", 1, does_not_raise()),
("int", 1.0, does_not_raise()),
("int", "1", does_not_raise()),
("int", "1.0", pytest.raises(ValueError)),
("int", "no-int", pytest.raises(ValueError)),
("int", None, pytest.raises(TypeError)),
("int", {}, pytest.raises(TypeError)),
("int", [], pytest.raises(TypeError)),
("float", 1.1, does_not_raise()),
("float", 1, does_not_raise()),
("float", "1.1", does_not_raise()),
("float", "no-float", pytest.raises(ValueError)),
("float", None, pytest.raises(TypeError)),
("float", {}, pytest.raises(TypeError)),
("float", [], pytest.raises(TypeError)),
("bool", True, does_not_raise()),
("bool", False, does_not_raise()),
("bool", "y", does_not_raise()),
("bool", "n", does_not_raise()),
("bool", None, pytest.raises(TypeError)),
("json", '"string"', does_not_raise()),
("json", "1", does_not_raise()),
("json", 1, does_not_raise()),
("json", "1.1", does_not_raise()),
("json", 1.1, does_not_raise()),
("json", "true", does_not_raise()),
("json", True, does_not_raise()),
("json", "false", does_not_raise()),
("json", False, does_not_raise()),
("json", "{}", does_not_raise()),
("json", {}, does_not_raise()),
("json", "[]", does_not_raise()),
("json", [], does_not_raise()),
("json", "null", does_not_raise()),
("json", None, does_not_raise()),
("yaml", '"string"', does_not_raise()),
("yaml", "string", does_not_raise()),
("yaml", "1", does_not_raise()),
("yaml", 1, does_not_raise()),
("yaml", "1.1", does_not_raise()),
("yaml", 1.1, does_not_raise()),
("yaml", "true", does_not_raise()),
("yaml", True, does_not_raise()),
("yaml", "false", does_not_raise()),
("yaml", False, does_not_raise()),
("yaml", "{}", does_not_raise()),
("yaml", {}, does_not_raise()),
("yaml", "[]", does_not_raise()),
("yaml", [], does_not_raise()),
("yaml", "null", does_not_raise()),
("yaml", None, does_not_raise()),
],
)
def test_validate_default_value(
tmp_path_factory: pytest.TempPathFactory,
type_name: str,
default: Any,
expected: ContextManager[None],
) -> None:
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
build_file_tree(
{
(src / "copier.yml"): yaml.dump(
{
"q": {
"type": type_name,
"default": default,
}
}
)
}
)
with expected:
copier.run_copy(str(src), dst, defaults=True)