async def test_unary_stream_simple(loop): async with ClientServer(loop=loop) as (handler, stub): replies = await stub.UnaryStream(DummyRequest(value='ping')) assert handler.log == [DummyRequest(value='ping')] assert replies == [DummyReply(value='pong1'), DummyReply(value='pong2'), DummyReply(value='pong3')]
async def test_unary_unary_advanced(): async with ClientServer() as (handler, stub): async with stub.UnaryUnary.open() as stream: await stream.send_message(DummyRequest(value='ping'), end=True) reply = await stream.recv_message() assert reply == DummyReply(value='pong') assert handler.log == [DummyRequest(value='ping')]
async def test_stream(): cs = ClientServer(DummyService, DummyServiceStub) async with cs as (_, stub): await stub.UnaryUnary(DummyRequest(value='ping')) handler = next(iter(cs.server._handlers)) handler.__gc_collect__() gc.collect() gc.disable() try: pre = set(collect()) await stub.UnaryUnary(DummyRequest(value='ping')) handler.__gc_collect__() post = collect() diff = set(post).difference(pre) diff.discard(id(pre)) for i in diff: try: print(repr(post[i])[:120]) except Exception: print('...') else: if 'grpclib.' in repr(post[i]): raise AssertionError('Memory leak detected') finally: gc.enable()
async def test_unary_stream_advanced(loop): async with ClientServer(loop=loop) as (handler, stub): async with stub.UnaryStream.open() as stream: await stream.send_message(DummyRequest(value='ping'), end=True) replies = await _to_list(stream) assert handler.log == [DummyRequest(value='ping')] assert replies == [DummyReply(value='pong1'), DummyReply(value='pong2'), DummyReply(value='pong3')]
async def test_stream_stream_simple(): async with ClientServer() as (_, stub): replies = await stub.StreamStream([ DummyRequest(value='foo'), DummyRequest(value='bar'), DummyRequest(value='baz'), ]) assert replies == [ DummyReply(value='foo'), DummyReply(value='bar'), DummyReply(value='baz'), ]
async def test_stream_stream_advanced(): async with ClientServer() as (_, stub): async with stub.StreamStream.open() as stream: await stream.send_message(DummyRequest(value='foo')) assert await stream.recv_message() == DummyReply(value='foo') await stream.send_message(DummyRequest(value='bar')) assert await stream.recv_message() == DummyReply(value='bar') await stream.send_message(DummyRequest(value='baz'), end=True) assert await stream.recv_message() == DummyReply(value='baz') assert await stream.recv_message() is None
async def test_send_trailing_metadata_on_closed_stream(loop): client_h2c, server_h2c = create_connections() to_client_transport = TransportStub(client_h2c) to_server_transport = TransportStub(server_h2c) client_conn = Connection(client_h2c, to_server_transport, loop=loop) server_conn = Connection(server_h2c, to_client_transport, loop=loop) server_proc = EventsProcessor(DummyHandler(), server_conn) client_proc = EventsProcessor(DummyHandler(), client_conn) request = Request(method='POST', scheme='http', path='/', content_type='application/grpc+proto', authority='test.com') client_h2_stream = client_conn.create_stream() await client_h2_stream.send_request(request.to_headers(), _processor=client_proc) request = DummyRequest(value='ping') await send_message(client_h2_stream, ProtoCodec(), request, DummyRequest, end=True) to_server_transport.process(server_proc) server_h2_stream = server_proc.handler.stream request_metadata = decode_metadata(server_proc.handler.headers) send_trailing_metadata_done = False async with Stream(server_h2_stream, Cardinality.UNARY_UNARY, ProtoCodec(), DummyRequest, DummyReply, metadata=request_metadata) as server_stream: await server_stream.send_trailing_metadata(status=Status.UNKNOWN) send_trailing_metadata_done = True assert send_trailing_metadata_done
async def test_exit_and_connection_was_broken(loop): client_h2c, server_h2c = create_connections() to_client_transport = TransportStub(client_h2c) to_server_transport = TransportStub(server_h2c) client_conn = Connection(client_h2c, to_server_transport, loop=loop) server_conn = Connection(server_h2c, to_client_transport, loop=loop) server_proc = EventsProcessor(DummyHandler(), server_conn) client_proc = EventsProcessor(DummyHandler(), client_conn) request = Request(method='POST', scheme='http', path='/', content_type='application/grpc+proto', authority='test.com') client_h2_stream = client_conn.create_stream() await client_h2_stream.send_request(request.to_headers(), _processor=client_proc) request = DummyRequest(value='ping') await send_message(client_h2_stream, ProtoCodec(), request, DummyRequest, end=True) to_server_transport.process(server_proc) server_h2_stream = server_proc.handler.stream request_metadata = decode_metadata(server_proc.handler.headers) with pytest.raises(WriteError): async with Stream(server_h2_stream, Cardinality.UNARY_UNARY, ProtoCodec(), DummyRequest, DummyReply, metadata=request_metadata) as server_stream: await server_stream.recv_message() # simulate broken connection to_client_transport.__raise_on_write__(WriteError)
async def test_exit_and_stream_was_closed(loop): client_h2c, server_h2c = create_connections() to_client_transport = TransportStub(client_h2c) to_server_transport = TransportStub(server_h2c) client_conn = Connection(client_h2c, to_server_transport, loop=loop) server_conn = Connection(server_h2c, to_client_transport, loop=loop) server_proc = EventsProcessor(DummyHandler(), server_conn) client_proc = EventsProcessor(DummyHandler(), client_conn) client_h2_stream = client_conn.create_stream() await client_h2_stream.send_request(create_headers(), _processor=client_proc) request = DummyRequest(value='ping') await send_message(client_h2_stream, ProtoCodec(), request, DummyRequest, end=True) to_server_transport.process(server_proc) server_h2_stream = server_proc.handler.stream request_metadata = decode_metadata(server_proc.handler.headers) async with mk_stream(server_h2_stream, request_metadata) as server_stream: await server_stream.recv_message() # simulating client closing stream await client_h2_stream.reset() to_server_transport.process(server_proc) # we should fail here on this attempt to send something await server_stream.send_message(DummyReply(value='pong'))
async def worker2(): cs = ClientStream(client_conn=client_conn, send_type=DummyRequest, recv_type=DummyReply) async with cs.client_stream as stream: await stream.send_message(DummyRequest(value='ping'), end=True) assert await stream.recv_message() == DummyReply(value='pong')
async def test_no_end(loop): cs = ClientStream(loop=loop, send_type=DummyRequest, recv_type=DummyReply, cardinality=Cardinality.STREAM_UNARY) with pytest.raises(ProtocolError) as exc: async with cs.client_stream as stream: await stream.send_request() await stream.send_message(DummyRequest(value='ping')) # no end events = cs.client_conn.to_server_transport.events() stream_id = events[-1].stream_id cs.client_conn.server_h2c.send_headers( stream_id, [(':status', '200'), ('content-type', 'application/grpc+proto')], ) cs.client_conn.server_flush() await stream.recv_initial_metadata() cs.client_conn.server_h2c.send_data( stream_id, grpc_encode(DummyReply(value='pong'), DummyReply), ) cs.client_conn.server_flush() assert await stream.recv_message() == DummyReply(value='pong') await stream.recv_trailing_metadata() exc.match('Outgoing stream was not ended')
async def test_send_trailing_metadata_on_closed_stream(loop): client_h2c, server_h2c = create_connections() to_client_transport = TransportStub(client_h2c) to_server_transport = TransportStub(server_h2c) client_conn = Connection(client_h2c, to_server_transport, loop=loop) server_conn = Connection(server_h2c, to_client_transport, loop=loop) server_proc = EventsProcessor(DummyHandler(), server_conn) client_proc = EventsProcessor(DummyHandler(), client_conn) client_h2_stream = client_conn.create_stream() await client_h2_stream.send_request(create_headers(), _processor=client_proc) request = DummyRequest(value='ping') await send_message(client_h2_stream, ProtoCodec(), request, DummyRequest, end=True) to_server_transport.process(server_proc) server_h2_stream = server_proc.handler.stream request_metadata = decode_metadata(server_proc.handler.headers) send_trailing_metadata_done = False async with mk_stream(server_h2_stream, request_metadata) as server_stream: await server_stream.send_trailing_metadata(status=Status.UNKNOWN) send_trailing_metadata_done = True assert send_trailing_metadata_done
async def test_server_stream(): cf = ChannelFor([WorkingDummyService()]) async with cf as channel: stub = DummyServiceStub(channel) handler, = cf._server._handlers async with stub.UnaryUnary.open() as stream: await stream.send_request() while not handler._tasks: await asyncio.sleep(0.001) server_stream, = handler._tasks.keys() _is_recent(server_stream.created) assert server_stream.data_sent == 0 assert server_stream.data_received == 0 assert server_stream.connection.messages_sent == 0 assert server_stream.connection.messages_received == 0 assert server_stream.connection.last_message_sent is None assert server_stream.connection.last_message_received is None await stream.send_message(DummyRequest(value='whatever'), end=True) await asyncio.sleep(0.01) assert server_stream.data_sent > 0 assert server_stream.data_received > 0 assert server_stream.connection.messages_sent == 1 assert server_stream.connection.messages_received == 1 _is_recent(server_stream.connection.last_data_sent) _is_recent(server_stream.connection.last_data_received) _is_recent(server_stream.connection.last_message_sent) _is_recent(server_stream.connection.last_message_received) reply = await stream.recv_message() assert reply == DummyReply(value='test')
async def test_exit_and_connection_was_broken(loop): client_h2c, server_h2c = create_connections() to_client_transport = TransportStub(client_h2c) to_server_transport = TransportStub(server_h2c) client_conn = Connection(client_h2c, to_server_transport, loop=loop) server_conn = Connection(server_h2c, to_client_transport, loop=loop) server_proc = EventsProcessor(DummyHandler(), server_conn) client_proc = EventsProcessor(DummyHandler(), client_conn) client_h2_stream = client_conn.create_stream() await client_h2_stream.send_request(create_headers(), _processor=client_proc) request = DummyRequest(value='ping') await send_message(client_h2_stream, ProtoCodec(), request, DummyRequest, end=True) to_server_transport.process(server_proc) server_h2_stream = server_proc.handler.stream request_metadata = decode_metadata(server_proc.handler.headers) with pytest.raises(WriteError): async with mk_stream(server_h2_stream, request_metadata) as server_stream: server_stream.metadata = request_metadata await server_stream.recv_message() # simulate broken connection to_client_transport.__raise_on_write__(WriteError)
async def test_deadline_during_recv_trailing_metadata(loop): cs = ClientStream(loop=loop, timeout=0.01, send_type=DummyRequest, recv_type=DummyReply) with pytest.raises(ErrorDetected): with async_timeout.timeout(5) as safety_timeout: async with cs.client_stream as stream: await stream.send_message(DummyRequest(value='ping'), end=True) events = cs.client_conn.to_server_transport.events() stream_id = events[-1].stream_id cs.client_conn.server_h2c.send_headers( stream_id, [(':status', '200'), ('content-type', 'application/grpc+proto')], ) cs.client_conn.server_flush() await stream.recv_initial_metadata() cs.client_conn.server_h2c.send_data( stream_id, grpc_encode(DummyReply(value='pong'), DummyReply), ) cs.client_conn.server_flush() await stream.recv_message() try: await stream.recv_trailing_metadata() except asyncio.TimeoutError: if safety_timeout.expired: raise else: raise ErrorDetected()
async def test_exit_and_connection_was_closed(loop): client_h2c, server_h2c = create_connections() to_client_transport = TransportStub(client_h2c) to_server_transport = TransportStub(server_h2c) client_conn = Connection(client_h2c, to_server_transport, loop=loop) server_conn = Connection(server_h2c, to_client_transport, loop=loop) server_proc = EventsProcessor(DummyHandler(), server_conn) client_proc = EventsProcessor(DummyHandler(), client_conn) request = Request('POST', 'http', '/', content_type='application/grpc+proto', authority='test.com') client_h2_stream = client_conn.create_stream() await client_h2_stream.send_request(request.to_headers(), _processor=client_proc) request = DummyRequest(value='ping') await send_message(client_h2_stream, ProtoCodec(), request, DummyRequest, end=True) to_server_transport.process(server_proc) server_h2_stream = server_proc.handler.stream request_metadata = Metadata.from_headers(server_proc.handler.headers) async with Stream(server_h2_stream, Cardinality.UNARY_UNARY, ProtoCodec(), DummyRequest, DummyReply, metadata=request_metadata) as server_stream: await server_stream.recv_message() client_h2c.close_connection() to_server_transport.process(server_proc) raise ServerError() # should be suppressed
async def test_unary_unary(cs: ClientStream): async with cs.client_stream as stream: await stream.send_message(DummyRequest(value='ping')) events = cs.client_conn.to_server_transport.events() event = events[-1] assert isinstance(event, StreamEnded) stream_id = event.stream_id cs.client_conn.server_h2c.send_headers( stream_id, [(':status', '200'), ('content-type', 'application/grpc+proto')], ) cs.client_conn.server_h2c.send_data( stream_id, grpc_encode(DummyReply(value='pong'), DummyReply), ) cs.client_conn.server_h2c.send_headers( stream_id, [('grpc-status', str(Status.OK.value))], end_stream=True, ) cs.client_conn.server_flush() assert await stream.recv_message() == DummyReply(value='pong')
async def test_invalid_grpc_status_in_trailers(cs: ClientStream, grpc_status): with pytest.raises(ErrorDetected): async with cs.client_stream as stream: await stream.send_request() await stream.send_message(DummyRequest(value='ping'), end=True) events = cs.client_conn.to_server_transport.events() stream_id = events[-1].stream_id cs.client_conn.server_h2c.send_headers(stream_id, [ (':status', '200'), ('content-type', 'application/grpc+proto'), ]) cs.client_conn.server_h2c.send_data( stream_id, grpc_encode(DummyReply(value='pong'), DummyReply), ) cs.client_conn.server_h2c.send_headers(stream_id, [ ('grpc-status', grpc_status), ], end_stream=True) cs.client_conn.server_flush() await stream.recv_initial_metadata() await stream.recv_message() try: await stream.recv_trailing_metadata() except GRPCError as exc: assert exc assert exc.status == Status.UNKNOWN assert exc.message == ( 'Invalid grpc-status: {!r}'.format(grpc_status)) raise ErrorDetected()
async def test_non_ok_grpc_status_in_headers(cs: ClientStream, grpc_message): with pytest.raises(ErrorDetected): async with cs.client_stream as stream: await stream.send_request() await stream.send_message(DummyRequest(value='ping'), end=True) events = cs.client_conn.to_server_transport.events() stream_id = events[-1].stream_id headers = [ (':status', '200'), ('content-type', 'application/grpc+proto'), ('grpc-status', str(Status.DATA_LOSS.value)), ] if grpc_message is not None: headers.append(('grpc-message', grpc_message)) cs.client_conn.server_h2c.send_headers(stream_id, headers, end_stream=True) cs.client_conn.server_flush() try: await stream.recv_initial_metadata() except GRPCError as exc: assert exc assert exc.status == Status.DATA_LOSS assert exc.message == grpc_message raise ErrorDetected()
async def test_exit_and_connection_was_closed(loop): client_h2c, server_h2c = create_connections() to_client_transport = TransportStub(client_h2c) to_server_transport = TransportStub(server_h2c) client_conn = Connection(client_h2c, to_server_transport, loop=loop) server_conn = Connection(server_h2c, to_client_transport, loop=loop) server_proc = EventsProcessor(DummyHandler(), server_conn) client_proc = EventsProcessor(DummyHandler(), client_conn) client_h2_stream = client_conn.create_stream() await client_h2_stream.send_request(create_headers(), _processor=client_proc) request = DummyRequest(value='ping') await send_message(client_h2_stream, ProtoCodec(), request, DummyRequest, end=True) to_server_transport.process(server_proc) server_h2_stream = server_proc.handler.stream request_metadata = decode_metadata(server_proc.handler.headers) async with mk_stream(server_h2_stream, request_metadata) as server_stream: await server_stream.recv_message() client_h2c.close_connection() to_server_transport.process(server_proc) raise ServerError() # should be suppressed
async def test_stream_cancel_by_ping(): ctx = ClientServer(PingServiceHandler, PingServiceStub, config=Configuration(_keepalive_time=0.01, _keepalive_timeout=0.04, _http2_max_pings_without_data=1, )) # should be successful async with ctx as (handler, stub): await stub.UnaryStream(DummyRequest(value='ping')) assert ctx.channel._protocol.connection.last_ping_sent is not None # disable ping ack logic to cause a timeout and disconnect with patch.object(Connection, 'ping_ack_process') as p: p.return_value = None with pytest.raises(StreamTerminatedError, match='Connection lost'): async with ctx as (handler, stub): await stub.UnaryStream(DummyRequest(value='ping'))
async def test_failure(): class FailingService(DummyService): async def UnaryUnary(self, stream): raise GRPCError(Status.FAILED_PRECONDITION) async with ChannelFor([FailingService()]) as channel: stub = DummyServiceStub(channel) with pytest.raises(GRPCError) as err: await stub.UnaryUnary(DummyRequest(value='ping')) assert err.value.status is Status.FAILED_PRECONDITION
async def test_concurrent_connect(loop): count = 5 reqs = [DummyRequest(value='ping') for _ in range(count)] reps = [DummyReply(value='pong') for _ in range(count)] channel = Channel() stub = DummyServiceStub(channel) async with ChannelFor([DummyService()]) as _channel: with patch.object(loop, 'create_connection') as po: po.side_effect = _create_connection_gen(_channel._protocol) tasks = [loop.create_task(stub.UnaryUnary(req)) for req in reqs] replies = await asyncio.gather(*tasks) assert replies == reps po.assert_called_once_with(ANY, '127.0.0.1', 50051, ssl=None)
async def test_send_trailing_metadata(loop, svc_type): async with ChannelFor( [svc_type()], codec=ProtoCodec(), status_details_codec=ProtoStatusDetailsCodec(), ) as channel: stub = DummyServiceStub(channel) with pytest.raises(GRPCError) as error: await stub.UnaryUnary(DummyRequest(value='ping')) assert error.value.status is Status.DATA_LOSS assert error.value.message == 'Some data loss occurred' assert error.value.details == [ Help(links=[Help.Link(url='https://example.com')]), ]
async def test_connection_close_during_send_message(loop, cs: ClientStream): with pytest.raises(ErrorDetected): async with cs.client_stream as stream: await stream.send_request() cs.client_conn.client_proto.connection.write_ready.clear() task = loop.create_task( stream.send_message(DummyRequest(value='ping'), end=True)) cs.client_conn.server_h2c.close_connection() cs.client_conn.server_flush() try: await asyncio.wait_for(task, timeout=1, loop=loop) except StreamTerminatedError: raise ErrorDetected()
async def test_client_stream(): async with ChannelFor([WorkingDummyService()]) as channel: proto = await channel.__connect__() stub = DummyServiceStub(channel) async with stub.UnaryUnary.open() as stream: assert proto.connection.streams_started == 0 assert proto.connection.streams_succeeded == 0 assert proto.connection.streams_failed == 0 assert proto.connection.last_stream_created is None await stream.send_request() _is_recent(stream._stream.created) assert proto.connection.streams_started == 1 assert proto.connection.streams_succeeded == 0 assert proto.connection.streams_failed == 0 _is_recent(proto.connection.last_stream_created) assert stream._messages_sent == 0 assert stream._stream.data_sent == 0 assert proto.connection.messages_sent == 0 assert proto.connection.data_sent == 0 assert proto.connection.last_message_sent is None await stream.send_message(DummyRequest(value='whatever'), end=True) assert stream._messages_sent == 1 assert stream._stream.data_sent > 0 assert proto.connection.messages_sent == 1 assert proto.connection.data_sent > 0 _is_recent(proto.connection.last_message_sent) _is_recent(proto.connection.last_data_sent) assert stream._messages_received == 0 assert stream._stream.data_received == 0 assert proto.connection.messages_received == 0 assert proto.connection.data_received == 0 assert proto.connection.last_message_received is None reply = await stream.recv_message() assert stream._messages_received == 1 assert stream._stream.data_received > 0 assert proto.connection.messages_received == 1 assert proto.connection.data_received > 0 _is_recent(proto.connection.last_message_received) _is_recent(proto.connection.last_data_received) assert proto.connection.streams_started == 1 assert proto.connection.streams_succeeded == 1 assert proto.connection.streams_failed == 0 assert reply == DummyReply(value='test')
async def test_channel_calls_failed(): async with ChannelFor([FailingDummyService()]) as channel: stub = DummyServiceStub(channel) assert channel._calls_started == 0 assert channel._calls_succeeded == 0 assert channel._calls_failed == 0 assert channel._last_call_started is None with pytest.raises(GRPCError, match='Internal Server Error'): await stub.UnaryUnary(DummyRequest(value='whatever')) assert channel._calls_started == 1 assert channel._calls_succeeded == 0 assert channel._calls_failed == 1 _is_recent(channel._last_call_started)
async def test_deadline_during_recv_initial_metadata(loop): cs = ClientStream(loop=loop, timeout=0.01, send_type=DummyRequest, recv_type=DummyReply) with pytest.raises(ErrorDetected): with async_timeout.timeout(5) as safety_timeout: async with cs.client_stream as stream: await stream.send_message(DummyRequest(value='ping'), end=True) try: await stream.recv_initial_metadata() except asyncio.TimeoutError: if safety_timeout.expired: raise else: raise ErrorDetected()
async def test_channel_calls_succeeded(): async with ChannelFor([WorkingDummyService()]) as channel: stub = DummyServiceStub(channel) assert channel._calls_started == 0 assert channel._calls_succeeded == 0 assert channel._calls_failed == 0 assert channel._last_call_started is None reply = await stub.UnaryUnary(DummyRequest(value='whatever')) assert channel._calls_started == 1 assert channel._calls_succeeded == 1 assert channel._calls_failed == 0 _is_recent(channel._last_call_started) assert reply == DummyReply(value='test')
async def test_deadline_during_send_message(loop): cs = ClientStream(loop=loop, timeout=0.01, send_type=DummyRequest, recv_type=DummyReply) with pytest.raises(ErrorDetected): with async_timeout.timeout(5) as safety_timeout: async with cs.client_stream as stream: await stream.send_request() cs.client_conn.client_proto.connection.write_ready.clear() try: await stream.send_message(DummyRequest(value='ping'), end=True) except asyncio.TimeoutError: if safety_timeout.expired: raise else: raise ErrorDetected()