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

1from collections.abc import Callable, Generator 

2from contextlib import contextmanager 

3 

4import grpc 

5import pytest 

6 

7 

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. 

15 

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. 

18 

19 Yields the pytest.ExceptionInfo, whose `value` is populated once the block exits, for any further checks. 

20 

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 

27 

28 error = exc_info.value 

29 assert error.code() == code, f"Expected status {code}, got {error.code()} with details {error.details()!r}" 

30 

31 if details is None: 

32 return 

33 

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"