Coverage for tests/test_validation.py: 95.45%
44 statements
« prev ^ index » next coverage.py v7.6.3, created at 2024-10-17 17:18 +0200
« prev ^ index » next coverage.py v7.6.3, created at 2024-10-17 17:18 +0200
1"""Tests for the `validation` module."""
3from __future__ import annotations
5from inspect import Parameter
6from typing import Any, Callable
8import pytest
10from duty.validation import _get_params_caster, cast_arg, to_bool
11from tests.fixtures import validation as valfix
14@pytest.mark.parametrize(
15 ("value", "expected"),
16 [
17 ("y", True),
18 ("Y", True),
19 ("yes", True),
20 ("YES", True),
21 ("on", True),
22 ("ON", True),
23 ("true", True),
24 ("TRUE", True),
25 ("anything else", True),
26 ("-1", True),
27 ("1", True),
28 ("", False),
29 ("n", False),
30 ("N", False),
31 ("no", False),
32 ("NO", False),
33 ("false", False),
34 ("FALSE", False),
35 ("off", False),
36 ("OFF", False),
37 ],
38)
39def test_bool_casting(value: str, expected: bool) -> None:
40 """Check that we correctly cast string values to booleans.
42 Parameters:
43 value: The value to cast.
44 expected: The expected result.
45 """
46 assert to_bool(value) == expected
49class CustomType1:
50 """Dummy type to test type-casting."""
52 def __init__(self, value: str): # noqa: D107
53 self.value = value
55 def __eq__(self, other: object):
56 return self.value == other.value # type: ignore[attr-defined]
59class CustomType2:
60 """Dummy type to test type-casting."""
62 def __init__(self, value, extra): # noqa: ANN001,D107
63 ... # pragma: no cover
66@pytest.mark.parametrize(
67 ("arg", "annotation", "expected"),
68 [
69 ("hello", Parameter.empty, "hello"),
70 ("off", bool, False),
71 ("on", bool, True),
72 ("1", int, 1),
73 ("1", float, 1.0),
74 ("fie", str, "fie"),
75 ("fih", CustomType1, CustomType1("fih")),
76 ("foh", CustomType2, "foh"),
77 ],
78)
79def test_cast_arg(arg: str, annotation: Any, expected: Any) -> None:
80 """Check that arguments are properly casted given an annotation.
82 Parameters:
83 arg: The argument value to cast.
84 annotation: The annotation to use.
85 expected: The expected result.
86 """
87 assert cast_arg(arg, annotation) == expected
90_parametrization = [
91 (valfix.no_params, (), {}, (), {}),
92 (valfix.pos_or_kw_param, ("1",), {}, (1,), {}),
93 (valfix.pos_or_kw_param, (), {"a": "1"}, (), {"a": 1}),
94 (valfix.pos_or_kw_params, ("1", "2"), {}, (1, 2), {}),
95 (valfix.pos_or_kw_params, ("1",), {"b": "2"}, (1,), {"b": 2}),
96 (valfix.pos_or_kw_params, (), {"a": "1", "b": "2"}, (), {"a": 1, "b": 2}),
97 (valfix.varpos_param, (), {}, (), {}),
98 (valfix.varpos_param, ("1", "2"), {}, (1, 2), {}),
99 (valfix.pos_and_varpos_param, ("1",), {}, (1,), {}),
100 (valfix.pos_and_varpos_param, ("1", "2"), {}, (1, 2), {}),
101 (valfix.pos_and_varpos_param, ("1", "2", "3"), {}, (1, 2, 3), {}),
102 (valfix.kwonly_param, (), {"b": "1"}, (), {"b": 1}),
103 (valfix.kwonly_param, ("2",), {"b": "1"}, (2,), {"b": 1}),
104 (valfix.kwonly_param, ("2", "3"), {"b": "1"}, (2, 3), {"b": 1}),
105 (valfix.varkw_param, ("1",), {}, (1,), {}),
106 (valfix.varkw_param, ("1",), {"b": "2"}, (1,), {"b": 2}),
107 (valfix.varkw_param, ("1",), {"b": "2", "c": "3"}, (1,), {"b": 2, "c": 3}),
108 (valfix.varkw_no_annotation, (), {"a": "1"}, (), {"a": "1"}),
109 (valfix.posonly_marker, ("1", "2"), {}, (1, 2), {}),
110 (valfix.posonly_marker, ("1",), {"b": "2"}, (1,), {"b": 2}),
111 (valfix.kwonly_marker, ("1",), {"b": "2"}, (1,), {"b": 2}),
112 (valfix.kwonly_marker, (), {"a": "1", "b": "2"}, (), {"a": 1, "b": 2}),
113 (valfix.only_markers, ("1",), {"b": "2", "c": "3"}, (1,), {"b": 2, "c": 3}),
114 (valfix.only_markers, ("1", "2"), {"c": "3"}, (1, 2), {"c": 3}),
115 (valfix.full, ("1", "2", "3", "4"), {"d": "5", "e": "6", "f": "7"}, (1, 2, 3, 4), {"d": 5, "e": 6, "f": 7}),
116]
119@pytest.mark.parametrize(
120 ("func", "args", "kwargs", "expected_args", "expected_kwargs"),
121 _parametrization,
122)
123def test_params_caster(func: Callable, args: tuple, kwargs: dict, expected_args: tuple, expected_kwargs: dict) -> None:
124 """Test the whole parameters casting helper class.
126 Parameters:
127 func: The function to work with.
128 args: The positional arguments to cast.
129 kwargs: The keyword arguments to cast.
130 expected_args: The expected positional arguments result.
131 expected_kwargs: The expected keyword arguments result.
132 """
133 caster = _get_params_caster(func, *args, **kwargs)
134 new_args, new_kwargs = caster.cast(*args, **kwargs)
135 assert new_args == expected_args
136 assert new_kwargs == expected_kwargs
139def test_casting_based_on_default_value_type() -> None:
140 """Test that we cast according to the default value type when there is no annotation."""
142 def func(ctx, a=0): # noqa: ANN202, ANN001
143 ...
145 caster = _get_params_caster(func, a="1")
146 _, kwargs = caster.cast(a="1")
147 assert kwargs == {"a": 1}
150def test_validating_modern_annotations() -> None:
151 """Test modern type annotations in function signatures."""
153 def func(ctx, a: int | None = None): # noqa: ANN202, ANN001
154 ...
156 caster = _get_params_caster(func, a=1)
157 _, kwargs = caster.cast(a="1")
158 assert kwargs == {"a": 1}
159 caster = _get_params_caster(func, a=None)
160 _, kwargs = caster.cast(a=None)
161 assert kwargs == {"a": None}
162 caster = _get_params_caster(func)
163 _, kwargs = caster.cast()
164 assert kwargs == {}