Skip to content
This repository was archived by the owner on Mar 26, 2026. It is now read-only.

Commit 792e400

Browse files
authored
test: fix async showcase test (#2441)
1 parent c084bd4 commit 792e400

File tree

3 files changed

+42
-8
lines changed

3 files changed

+42
-8
lines changed

tests/system/conftest.py

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from unittest import mock
1818
import os
1919
import pytest
20+
import pytest_asyncio
2021

2122
from typing import Sequence, Tuple
2223

@@ -60,6 +61,8 @@
6061
except:
6162
HAS_ASYNC_REST_IDENTITY_TRANSPORT = False
6263

64+
_GRPC_VERSION = grpc.__version__
65+
6366
# TODO: use async auth anon credentials by default once the minimum version of google-auth is upgraded.
6467
# See related issue: https://github.com/googleapis/gapic-generator-python/issues/2107.
6568
def async_anonymous_credentials():
@@ -79,7 +82,7 @@ def async_anonymous_credentials():
7982
def event_loop():
8083
return asyncio.get_event_loop()
8184

82-
@pytest.fixture(params=["grpc_asyncio", "rest_asyncio"])
85+
@pytest_asyncio.fixture(params=["grpc_asyncio", "rest_asyncio"])
8386
def async_echo(use_mtls, request, event_loop):
8487
transport = request.param
8588
if transport == "rest_asyncio" and not HAS_ASYNC_REST_ECHO_TRANSPORT:
@@ -94,7 +97,7 @@ def async_echo(use_mtls, request, event_loop):
9497
credentials=async_anonymous_credentials(),
9598
)
9699

97-
@pytest.fixture(params=["grpc_asyncio", "rest_asyncio"])
100+
@pytest_asyncio.fixture(params=["grpc_asyncio", "rest_asyncio"])
98101
def async_identity(use_mtls, request, event_loop):
99102
transport = request.param
100103
if transport == "rest_asyncio" and not HAS_ASYNC_REST_IDENTITY_TRANSPORT:
@@ -310,9 +313,18 @@ def __init__(self, key, value):
310313

311314
def _add_request_metadata(self, client_call_details):
312315
if client_call_details.metadata is not None:
316+
# https://grpc.github.io/grpc/python/glossary.html#term-metadata.
317+
# For sync, `ClientCallDetails.metadata` is a list.
318+
# Whereas for async, `ClientCallDetails.metadata` is a mapping.
319+
# https://grpc.github.io/grpc/python/grpc_asyncio.html#grpc.aio.Metadata
313320
client_call_details.metadata.append((self._key, self._value))
314321
self.request_metadata = client_call_details.metadata
315322

323+
def _read_response_metadata_stream(self):
324+
# Access the metadata via the original stream object
325+
if hasattr(self, "_original_stream"):
326+
self.response_metadata = self._original_stream.trailing_metadata()
327+
316328
def intercept_unary_unary(self, continuation, client_call_details, request):
317329
self._add_request_metadata(client_call_details)
318330
response = continuation(client_call_details, request)
@@ -323,6 +335,7 @@ def intercept_unary_unary(self, continuation, client_call_details, request):
323335
def intercept_unary_stream(self, continuation, client_call_details, request):
324336
self._add_request_metadata(client_call_details)
325337
response_it = continuation(client_call_details, request)
338+
self._original_stream = response_it
326339
return response_it
327340

328341
def intercept_stream_unary(
@@ -337,6 +350,7 @@ def intercept_stream_stream(
337350
):
338351
self._add_request_metadata(client_call_details)
339352
response_it = continuation(client_call_details, request_iterator)
353+
self._original_stream = response_it
340354
return response_it
341355

342356

@@ -354,8 +368,21 @@ def __init__(self, key, value):
354368

355369
async def _add_request_metadata(self, client_call_details):
356370
if client_call_details.metadata is not None:
357-
client_call_details.metadata.append((self._key, self._value))
358-
self.request_metadata = client_call_details.metadata
371+
# As of gRPC 1.75.0 and newer,
372+
# https://grpc.github.io/grpc/python/grpc_asyncio.html#grpc.aio.Metadata
373+
# Note that for async, `ClientCallDetails.metadata` is a mapping.
374+
# Whereas for sync, `ClientCallDetails.metadata` is a list.
375+
# https://grpc.github.io/grpc/python/glossary.html#term-metadata.
376+
# Prior to gRPC 1.75.0, `ClientCallDetails.metadata` is a list
377+
# for both sync and async.
378+
grpc_major, grpc_minor = [
379+
int(part) for part in _GRPC_VERSION.split(".")[0:2]
380+
]
381+
if grpc_major == 1 and grpc_minor < 75:
382+
client_call_details.metadata.append((self._key, self._value))
383+
else:
384+
client_call_details.metadata[self._key] = self._value
385+
self.request_metadata = list(client_call_details.metadata)
359386

360387
async def intercept_unary_unary(self, continuation, client_call_details, request):
361388
await self._add_request_metadata(client_call_details)
@@ -407,8 +434,8 @@ def intercepted_echo_grpc(use_mtls):
407434
return EchoClient(transport=transport), interceptor
408435

409436

410-
@pytest.fixture
411-
def intercepted_echo_grpc_async():
437+
@pytest_asyncio.fixture
438+
async def intercepted_echo_grpc_async():
412439
# The interceptor adds 'showcase-trailer' client metadata. Showcase server
413440
# echoes any metadata with key 'showcase-trailer', so the same metadata
414441
# should appear as trailing metadata in the response.

tests/system/test_grpc_interceptor_streams.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@ def test_unary_stream(intercepted_echo_grpc):
3737
response_metadata = [
3838
(metadata.key, metadata.value) for metadata in responses.trailing_metadata()
3939
]
40+
assert intercepted_metadata[0] in interceptor.request_metadata
4041
assert intercepted_metadata[0] in response_metadata
41-
interceptor.response_metadata = response_metadata
42+
interceptor._read_response_metadata_stream()
43+
assert intercepted_metadata[0] in interceptor.response_metadata
4244

4345

4446
def test_stream_stream(intercepted_echo_grpc):
@@ -54,5 +56,7 @@ def test_stream_stream(intercepted_echo_grpc):
5456
response_metadata = [
5557
(metadata.key, metadata.value) for metadata in responses.trailing_metadata()
5658
]
59+
assert intercepted_metadata[0] in interceptor.request_metadata
5760
assert intercepted_metadata[0] in response_metadata
58-
interceptor.response_metadata = response_metadata
61+
interceptor._read_response_metadata_stream()
62+
assert intercepted_metadata[0] in interceptor.response_metadata

tests/system/test_streams.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
from google import showcase
2020

2121

22+
# `_METADATA` will be sent as part of the request, and the
23+
# showcase server will echo it (since it has key 'showcase-trailer') as trailing
24+
# metadata.
2225
_METADATA = (("showcase-trailer", "hello world"),)
2326

2427

0 commit comments

Comments
 (0)