Coverage for app/backend/src/tests/fixtures/rpc_errors.py: 100%
16 statements
« prev ^ index » next coverage.py v7.17.3, created at 2026-11-14 17:59 +0000
« prev ^ index » next coverage.py v7.17.3, created at 2026-11-14 17:59 +0000
1from collections.abc import Callable, Generator
2from contextlib import contextmanager
4import grpc
5import pytest
8@contextmanager
9def expect_rpc_error(
10 code: grpc.StatusCode,
11 details: str | Callable[[str], bool] | None = None,
12) -> Generator[pytest.ExceptionInfo[grpc.RpcError]]:
13 """
14 Asserts that the body raises a grpc.RpcError with the given status code.
16 If `details` is a string, the error details must match it exactly.
17 If it is a callable, it is called with the error details and must return True.
19 Yields the pytest.ExceptionInfo, whose `value` is populated once the block exits, for any further checks.
21 Example:
22 with expect_rpc_error(grpc.StatusCode.NOT_FOUND, "Couldn't find that user."):
23 api.GetUser(api_pb2.GetUserReq(user="nonexistent"))
24 """
25 with pytest.raises(grpc.RpcError) as exc_info:
26 yield exc_info
28 error = exc_info.value
29 assert error.code() == code, f"Expected status {code}, got {error.code()} with details {error.details()!r}"
31 if details is None:
32 return
34 actual_details = error.details() or ""
35 if isinstance(details, str):
36 assert actual_details == details, f"Expected error details {details!r}, got {actual_details!r}"
37 else:
38 assert details(actual_details), f"Error details {actual_details!r} did not match the predicate"