forked from home-assistant/core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtest_exceptions.py
269 lines (233 loc) · 9.4 KB
/
test_exceptions.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
"""Test to verify that Home Assistant exceptions work."""
from __future__ import annotations
from typing import Any
from unittest.mock import patch
import pytest
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import (
ConditionErrorContainer,
ConditionErrorIndex,
ConditionErrorMessage,
HomeAssistantError,
TemplateError,
)
def test_conditionerror_format() -> None:
"""Test ConditionError stringifiers."""
error1 = ConditionErrorMessage("test", "A test error")
assert str(error1) == "In 'test' condition: A test error"
error2 = ConditionErrorMessage("test", "Another error")
assert str(error2) == "In 'test' condition: Another error"
error_pos1 = ConditionErrorIndex("box", index=0, total=2, error=error1)
assert (
str(error_pos1)
== """In 'box' (item 1 of 2):
In 'test' condition: A test error"""
)
error_pos2 = ConditionErrorIndex("box", index=1, total=2, error=error2)
assert (
str(error_pos2)
== """In 'box' (item 2 of 2):
In 'test' condition: Another error"""
)
error_container1 = ConditionErrorContainer("box", errors=[error_pos1, error_pos2])
assert (
str(error_container1)
== """In 'box' (item 1 of 2):
In 'test' condition: A test error
In 'box' (item 2 of 2):
In 'test' condition: Another error"""
)
error_pos3 = ConditionErrorIndex("box", index=0, total=1, error=error1)
assert (
str(error_pos3)
== """In 'box':
In 'test' condition: A test error"""
)
@pytest.mark.parametrize(
("arg", "expected"),
[
("message", "message"),
(Exception("message"), "Exception: message"),
],
)
def test_template_message(arg: str | Exception, expected: str) -> None:
"""Ensure we can create TemplateError."""
template_error = TemplateError(arg)
assert str(template_error) == expected
@pytest.mark.parametrize(
("exception_args", "exception_kwargs", "args_base_class", "message"),
[
((), {}, (), ""),
(("bla",), {}, ("bla",), "bla"),
((None,), {}, (None,), "None"),
((type_error_bla := TypeError("bla"),), {}, (type_error_bla,), "bla"),
(
(),
{"translation_domain": "test", "translation_key": "test"},
("test",),
"test",
),
(
(),
{"translation_domain": "test", "translation_key": "bla"},
("bla",),
"{bla} from cache",
),
(
(),
{
"translation_domain": "test",
"translation_key": "bla",
"translation_placeholders": {"bla": "Bla"},
},
("bla",),
"Bla from cache",
),
],
)
async def test_home_assistant_error(
hass: HomeAssistant,
exception_args: tuple[Any, ...],
exception_kwargs: dict[str, Any],
args_base_class: tuple[Any],
message: str,
) -> None:
"""Test edge cases with HomeAssistantError."""
with patch(
"homeassistant.helpers.translation.async_get_cached_translations",
return_value={"component.test.exceptions.bla.message": "{bla} from cache"},
):
with pytest.raises(HomeAssistantError) as exc:
raise HomeAssistantError(*exception_args, **exception_kwargs)
assert exc.value.args == args_base_class
assert str(exc.value) == message
# Get string of exception again from the cache
assert str(exc.value) == message
async def test_home_assistant_error_subclass(hass: HomeAssistant) -> None:
"""Test __str__ method on an HomeAssistantError subclass."""
class _SubExceptionDefault(HomeAssistantError):
"""Sub class, default with generated message."""
class _SubExceptionConstructor(HomeAssistantError):
"""Sub class with constructor, no generated message."""
def __init__(
self,
custom_arg: str,
translation_domain: str | None = None,
translation_key: str | None = None,
translation_placeholders: dict[str, str] | None = None,
) -> None:
super().__init__(
translation_domain=translation_domain,
translation_key=translation_key,
translation_placeholders=translation_placeholders,
)
self.custom_arg = custom_arg
class _SubExceptionConstructorGenerate(HomeAssistantError):
"""Sub class with constructor, with generated message."""
generate_message: bool = True
def __init__(
self,
custom_arg: str,
translation_domain: str | None = None,
translation_key: str | None = None,
translation_placeholders: dict[str, str] | None = None,
) -> None:
super().__init__(
translation_domain=translation_domain,
translation_key=translation_key,
translation_placeholders=translation_placeholders,
)
self.custom_arg = custom_arg
class _SubExceptionGenerate(HomeAssistantError):
"""Sub class, no generated message."""
generate_message: bool = True
class _SubClassWithExceptionGroup(HomeAssistantError, BaseExceptionGroup):
"""Sub class with exception group, no generated message."""
class _SubClassWithExceptionGroupGenerate(HomeAssistantError, BaseExceptionGroup):
"""Sub class with exception group and generated message."""
generate_message: bool = True
with patch(
"homeassistant.helpers.translation.async_get_cached_translations",
return_value={"component.test.exceptions.bla.message": "{bla} from cache"},
):
# A subclass without a constructor generates a message by default
with pytest.raises(HomeAssistantError) as exc:
raise _SubExceptionDefault(
translation_domain="test",
translation_key="bla",
translation_placeholders={"bla": "Bla"},
)
assert str(exc.value) == "Bla from cache"
# A subclass with a constructor that does not parse `args` to the super class
with pytest.raises(HomeAssistantError) as exc:
raise _SubExceptionConstructor(
"custom arg",
translation_domain="test",
translation_key="bla",
translation_placeholders={"bla": "Bla"},
)
assert str(exc.value) == "Bla from cache"
with pytest.raises(HomeAssistantError) as exc:
raise _SubExceptionConstructor(
"custom arg",
)
assert str(exc.value) == ""
# A subclass with a constructor that generates the message
with pytest.raises(HomeAssistantError) as exc:
raise _SubExceptionConstructorGenerate(
"custom arg",
translation_domain="test",
translation_key="bla",
translation_placeholders={"bla": "Bla"},
)
assert str(exc.value) == "Bla from cache"
# A subclass without overridden constructors and passed args
# defaults to the passed args
with pytest.raises(HomeAssistantError) as exc:
raise _SubExceptionDefault(
ValueError("wrong value"),
translation_domain="test",
translation_key="bla",
translation_placeholders={"bla": "Bla"},
)
assert str(exc.value) == "wrong value"
# A subclass without overridden constructors and passed args
# and generate_message = True, generates a message
with pytest.raises(HomeAssistantError) as exc:
raise _SubExceptionGenerate(
ValueError("wrong value"),
translation_domain="test",
translation_key="bla",
translation_placeholders={"bla": "Bla"},
)
assert str(exc.value) == "Bla from cache"
# A subclass with an ExceptionGroup subclass requires a message to be passed.
# As we pass args, we will not generate the message.
# The __str__ constructor defaults to that of the super class.
with pytest.raises(HomeAssistantError) as exc:
raise _SubClassWithExceptionGroup(
"group message",
[ValueError("wrong value"), TypeError("wrong type")],
translation_domain="test",
translation_key="bla",
translation_placeholders={"bla": "Bla"},
)
assert str(exc.value) == "group message (2 sub-exceptions)"
with pytest.raises(HomeAssistantError) as exc:
raise _SubClassWithExceptionGroup(
"group message",
[ValueError("wrong value"), TypeError("wrong type")],
)
assert str(exc.value) == "group message (2 sub-exceptions)"
# A subclass with an ExceptionGroup subclass requires a message to be passed.
# The `generate_message` flag is set.`
# The __str__ constructor will return the generated message.
with pytest.raises(HomeAssistantError) as exc:
raise _SubClassWithExceptionGroupGenerate(
"group message",
[ValueError("wrong value"), TypeError("wrong type")],
translation_domain="test",
translation_key="bla",
translation_placeholders={"bla": "Bla"},
)
assert str(exc.value) == "Bla from cache"