|
3 | 3 | from typing import NoReturn |
4 | 4 |
|
5 | 5 | import pytest |
| 6 | +from google.protobuf.any_pb2 import Any as AnyPb |
| 7 | +from google.protobuf.duration_pb2 import Duration |
6 | 8 | from google.protobuf.struct_pb2 import Struct, Value |
7 | 9 | from pyqwest import Client, SyncClient |
8 | 10 | from pyqwest.testing import ASGITransport, WSGITransport |
9 | 11 |
|
| 12 | +from connectrpc._protocol import ConnectWireError |
10 | 13 | from connectrpc.code import Code |
11 | 14 | from connectrpc.errors import ConnectError, pack_any |
12 | 15 |
|
@@ -81,3 +84,49 @@ async def make_hat(self, request, ctx) -> NoReturn: |
81 | 84 | s1 = Struct() |
82 | 85 | assert exc_info.value.details[1].Unpack(s1) |
83 | 86 | assert s1.fields["color"].string_value == "red" |
| 87 | + |
| 88 | + |
| 89 | +def test_error_detail_debug_field() -> None: |
| 90 | + """Debug field is populated when proto descriptors are available.""" |
| 91 | + wire_error = ConnectWireError.from_exception( |
| 92 | + ConnectError( |
| 93 | + Code.RESOURCE_EXHAUSTED, |
| 94 | + "Resource exhausted", |
| 95 | + details=[Struct(fields={"animal": Value(string_value="bear")})], |
| 96 | + ) |
| 97 | + ) |
| 98 | + data = wire_error.to_dict() |
| 99 | + assert len(data["details"]) == 1 |
| 100 | + detail = data["details"][0] |
| 101 | + assert "debug" in detail |
| 102 | + # Struct uses proto-JSON well-known type mapping: becomes a plain JSON object |
| 103 | + assert detail["debug"] == {"animal": "bear"} |
| 104 | + |
| 105 | + |
| 106 | +def test_error_detail_debug_field_well_known_type() -> None: |
| 107 | + """Debug field uses proto-JSON well-known type representation (e.g. Duration as string).""" |
| 108 | + wire_error = ConnectWireError.from_exception( |
| 109 | + ConnectError( |
| 110 | + Code.RESOURCE_EXHAUSTED, "Resource exhausted", details=[Duration(seconds=1)] |
| 111 | + ) |
| 112 | + ) |
| 113 | + data = wire_error.to_dict() |
| 114 | + assert len(data["details"]) == 1 |
| 115 | + detail = data["details"][0] |
| 116 | + assert "debug" in detail |
| 117 | + # Duration uses proto-JSON well-known type mapping: serializes as "1s" |
| 118 | + assert detail["debug"] == "1s" |
| 119 | + |
| 120 | + |
| 121 | +def test_error_detail_debug_field_absent_for_unknown_type() -> None: |
| 122 | + """Debug field is omitted when no descriptor is available for the type.""" |
| 123 | + unknown_detail = AnyPb( |
| 124 | + type_url="type.googleapis.com/completely.Unknown.Message", value=b"\x08\x01" |
| 125 | + ) |
| 126 | + wire_error = ConnectWireError( |
| 127 | + code=Code.INTERNAL, message="test", details=[unknown_detail] |
| 128 | + ) |
| 129 | + data = wire_error.to_dict() |
| 130 | + assert len(data["details"]) == 1 |
| 131 | + detail = data["details"][0] |
| 132 | + assert "debug" not in detail |
0 commit comments