Coverage for /home/crpier/Projects/snektest/snektest/assertions.py: 47%
105 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-07 00:30 +0300
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-07 00:30 +0300
1from types import TracebackType
2from typing import Any, Self, TypeIs, cast
4from snektest.models import AssertionFailure, BadRequestError
7def assert_raises[T](
8 *expected_exceptions: type[T],
9 msg: str | None = None,
10) -> RaisesContex[T]:
11 return RaisesContex(*expected_exceptions, msg=msg)
14class RaisesContex[T]:
15 def __init__(self, *expected_exceptions: type[T], msg: str | None = None) -> None:
16 self.msg = msg
17 self.expected_exceptions = expected_exceptions
18 self._exception: T | None = None
19 self.has_exited = False
21 def __enter__(self) -> Self:
22 return self
24 def __exit__(
25 self,
26 exc_type: type[BaseException] | None,
27 exc_value: BaseException | None,
28 traceback: TracebackType | None,
29 ) -> bool:
30 self.has_exited = True
31 if exc_type is None:
32 message = (
33 self.msg or "Expected to raise an exception but no exception was raised"
34 )
35 raise AssertionFailure(
36 message,
37 actual=None,
38 expected=self.expected_exceptions,
39 )
40 if not isinstance(exc_value, self.expected_exceptions):
41 expected_exceptions_display_name = " | ".join(
42 e.__name__ for e in self.expected_exceptions
43 )
44 message = (
45 self.msg
46 or f"Expected to raise {expected_exceptions_display_name} but raised {type(exc_value).__name__}"
47 )
48 raise AssertionFailure(
49 message,
50 actual=exc_value,
51 expected=self.expected_exceptions,
52 )
53 self._exception = exc_value
54 return True
56 @property
57 def exception(self) -> T:
58 if not self.has_exited:
59 msg = "Exception under assert_raises was accessed before exiting the context manager"
60 raise BadRequestError(msg)
61 return cast("T", self._exception)
64def assert_eq(actual: Any, expected: Any, *, msg: str | None = None) -> None:
65 """Assert that actual == expected."""
66 if actual != expected:
67 message = msg or f"{actual!r} != {expected!r}"
68 raise AssertionFailure(
69 message,
70 actual=actual,
71 expected=expected,
72 operator="==",
73 )
76def assert_ne(actual: Any, expected: Any, *, msg: str | None = None) -> None:
77 """Assert that actual != expected."""
78 if actual == expected:
79 message = msg or f"{actual!r} == {expected!r}"
80 raise AssertionFailure(
81 message,
82 actual=actual,
83 expected=expected,
84 operator="!=",
85 )
88def assert_true(value: Any, *, msg: str | None = None) -> None:
89 """Assert that value is True (identity check, not truthiness)."""
90 if value is not True:
91 message = msg or f"{value!r} is not True"
92 raise AssertionFailure(
93 message,
94 actual=value,
95 expected=True,
96 operator="is",
97 )
100def assert_false(value: Any, *, msg: str | None = None) -> None:
101 """Assert that value is False (identity check, not falsiness)."""
102 if value is not False:
103 message = msg or f"{value!r} is not False"
104 raise AssertionFailure(
105 message,
106 actual=value,
107 expected=False,
108 operator="is",
109 )
112def assert_is_none(value: Any, *, msg: str | None = None) -> None:
113 """Assert that value is None."""
114 if value is not None:
115 message = msg or f"{value!r} is not None"
116 raise AssertionFailure(
117 message,
118 actual=value,
119 expected=None,
120 operator="is",
121 )
124def assert_is_not_none[T](value: T | None, *, msg: str | None = None) -> TypeIs[T]:
125 """Assert that value is not None."""
126 if value is None:
127 message = msg or "value is None"
128 raise AssertionFailure(
129 message,
130 actual=value,
131 expected="not None",
132 operator="is not",
133 )
134 return True
137def assert_is(actual: Any, expected: Any, *, msg: str | None = None) -> None:
138 """Assert that actual is expected (identity check)."""
139 if actual is not expected:
140 message = msg or f"{actual!r} is not {expected!r}"
141 raise AssertionFailure(
142 message,
143 actual=actual,
144 expected=expected,
145 operator="is",
146 )
149def assert_is_not(actual: Any, expected: Any, *, msg: str | None = None) -> None:
150 """Assert that actual is not expected (identity check)."""
151 if actual is expected:
152 message = msg or f"{actual!r} is {expected!r}"
153 raise AssertionFailure(
154 message,
155 actual=actual,
156 expected=expected,
157 operator="is not",
158 )
161def assert_lt(actual: Any, expected: Any, *, msg: str | None = None) -> None:
162 """Assert that actual < expected."""
163 if not actual < expected:
164 message = msg or f"{actual!r} >= {expected!r}"
165 raise AssertionFailure(
166 message,
167 actual=actual,
168 expected=expected,
169 operator="<",
170 )
173def assert_gt(actual: Any, expected: Any, *, msg: str | None = None) -> None:
174 """Assert that actual > expected."""
175 if not actual > expected:
176 message = msg or f"{actual!r} <= {expected!r}"
177 raise AssertionFailure(
178 message,
179 actual=actual,
180 expected=expected,
181 operator=">",
182 )
185def assert_le(actual: Any, expected: Any, *, msg: str | None = None) -> None:
186 """Assert that actual <= expected."""
187 if not actual <= expected:
188 message = msg or f"{actual!r} > {expected!r}"
189 raise AssertionFailure(
190 message,
191 actual=actual,
192 expected=expected,
193 operator="<=",
194 )
197def assert_ge(actual: Any, expected: Any, *, msg: str | None = None) -> None:
198 """Assert that actual >= expected."""
199 if not actual >= expected:
200 message = msg or f"{actual!r} < {expected!r}"
201 raise AssertionFailure(
202 message,
203 actual=actual,
204 expected=expected,
205 operator=">=",
206 )
209def assert_in(member: Any, container: Any, *, msg: str | None = None) -> None:
210 """Assert that member in container."""
211 if member not in container:
212 message = msg or f"{member!r} not found in {container!r}"
213 raise AssertionFailure(
214 message,
215 actual=member,
216 expected=container,
217 operator="in",
218 )
221def assert_not_in(member: Any, container: Any, *, msg: str | None = None) -> None:
222 """Assert that member not in container."""
223 if member in container:
224 message = msg or f"{member!r} found in {container!r}"
225 raise AssertionFailure(
226 message,
227 actual=member,
228 expected=container,
229 operator="not in",
230 )
233def assert_isinstance(
234 obj: Any, classinfo: type | tuple[type, ...], *, msg: str | None = None
235) -> None:
236 """Assert that isinstance(obj, classinfo) is True."""
237 if not isinstance(obj, classinfo):
238 type_name = (
239 classinfo.__name__ if isinstance(classinfo, type) else str(classinfo)
240 )
241 message = msg or f"{obj!r} is not an instance of {type_name}"
242 raise AssertionFailure(
243 message,
244 actual=type(obj).__name__,
245 expected=type_name,
246 operator="isinstance",
247 )
250def assert_not_isinstance(
251 obj: Any, classinfo: type | tuple[type, ...], *, msg: str | None = None
252) -> None:
253 """Assert that isinstance(obj, classinfo) is False."""
254 if isinstance(obj, classinfo):
255 type_name = (
256 classinfo.__name__ if isinstance(classinfo, type) else str(classinfo)
257 )
258 message = msg or f"{obj!r} is an instance of {type_name}"
259 raise AssertionFailure(
260 message,
261 actual=type(obj).__name__,
262 expected=f"not {type_name}",
263 operator="not isinstance",
264 )
267def assert_len(obj: Any, expected_length: int, *, msg: str | None = None) -> None:
268 """Assert that len(obj) == expected_length."""
269 actual_length = len(obj)
270 if actual_length != expected_length:
271 message = msg or f"Length {actual_length} != {expected_length}"
272 raise AssertionFailure(
273 message,
274 actual=actual_length,
275 expected=expected_length,
276 operator="len ==",
277 )
280def fail(msg: str | None = None) -> None:
281 """Unconditionally raise an AssertionFailure.
283 Args:
284 msg: Optional custom message
285 """
286 message = msg or "Assertion failed"
287 raise AssertionFailure(message)