From 35548cb43e7d71ecc2c2ccc4a076f1bf7ea7c032 Mon Sep 17 00:00:00 2001 From: boukeversteegh Date: Sun, 24 May 2020 12:33:36 +0200 Subject: [PATCH] Test all supported wrapper types. Add xfail test for unwrapping the value --- betterproto/tests/googletypes_service.proto | 18 ------ .../googletypes_response.proto | 17 +++--- .../test_googletypes_response.py | 55 +++++++++++++++---- betterproto/tests/mocks.py | 39 +++++++++++++ 4 files changed, 93 insertions(+), 36 deletions(-) delete mode 100644 betterproto/tests/googletypes_service.proto create mode 100644 betterproto/tests/mocks.py diff --git a/betterproto/tests/googletypes_service.proto b/betterproto/tests/googletypes_service.proto deleted file mode 100644 index 4bdca68..0000000 --- a/betterproto/tests/googletypes_service.proto +++ /dev/null @@ -1,18 +0,0 @@ -syntax = "proto3"; - -import "google/protobuf/wrappers.proto"; - -service Test { - rpc GetInt32 (Input) returns (google.protobuf.Int32Value); - rpc GetAnotherInt32 (Input) returns (google.protobuf.Int32Value); - rpc GetInt64 (Input) returns (google.protobuf.Int64Value); - rpc GetOutput (Input) returns (Output); -} - -message Input { - -} - -message Output { - google.protobuf.Int64Value int64 = 1; -} \ No newline at end of file diff --git a/betterproto/tests/inputs/googletypes_response/googletypes_response.proto b/betterproto/tests/inputs/googletypes_response/googletypes_response.proto index 4bdca68..ee8dbbd 100644 --- a/betterproto/tests/inputs/googletypes_response/googletypes_response.proto +++ b/betterproto/tests/inputs/googletypes_response/googletypes_response.proto @@ -2,17 +2,20 @@ syntax = "proto3"; import "google/protobuf/wrappers.proto"; +// Tests that wrapped return values can be used + service Test { - rpc GetInt32 (Input) returns (google.protobuf.Int32Value); - rpc GetAnotherInt32 (Input) returns (google.protobuf.Int32Value); + rpc GetDouble (Input) returns (google.protobuf.DoubleValue); + rpc GetFloat (Input) returns (google.protobuf.FloatValue); rpc GetInt64 (Input) returns (google.protobuf.Int64Value); - rpc GetOutput (Input) returns (Output); + rpc GetUInt64 (Input) returns (google.protobuf.UInt64Value); + rpc GetInt32 (Input) returns (google.protobuf.Int32Value); + rpc GetUInt32 (Input) returns (google.protobuf.UInt32Value); + rpc GetBool (Input) returns (google.protobuf.BoolValue); + rpc GetString (Input) returns (google.protobuf.StringValue); + rpc GetBytes (Input) returns (google.protobuf.BytesValue); } message Input { } - -message Output { - google.protobuf.Int64Value int64 = 1; -} \ No newline at end of file diff --git a/betterproto/tests/inputs/googletypes_response/test_googletypes_response.py b/betterproto/tests/inputs/googletypes_response/test_googletypes_response.py index fba2070..76c012b 100644 --- a/betterproto/tests/inputs/googletypes_response/test_googletypes_response.py +++ b/betterproto/tests/inputs/googletypes_response/test_googletypes_response.py @@ -1,20 +1,53 @@ -from typing import Optional +from typing import Any, Callable, Optional +import google.protobuf.wrappers_pb2 as wrappers import pytest +from betterproto.tests.mocks import MockChannel from betterproto.tests.output_betterproto.googletypes_response.googletypes_response import ( - TestStub + TestStub, ) - -class TestStubChild(TestStub): - async def _unary_unary(self, route, request, response_type, **kwargs): - self.response_type = response_type +test_cases = [ + (TestStub.get_double, wrappers.DoubleValue, 2.5), + (TestStub.get_float, wrappers.FloatValue, 2.5), + (TestStub.get_int64, wrappers.Int64Value, -64), + (TestStub.get_u_int64, wrappers.UInt64Value, 64), + (TestStub.get_int32, wrappers.Int32Value, -32), + (TestStub.get_u_int32, wrappers.UInt32Value, 32), + (TestStub.get_bool, wrappers.BoolValue, True), + (TestStub.get_string, wrappers.StringValue, "string"), + (TestStub.get_bytes, wrappers.BytesValue, bytes(0xFF)[0:4]), +] @pytest.mark.asyncio -async def test(): - pytest.skip("todo") - stub = TestStubChild(None) - await stub.get_int64() - assert stub.response_type != Optional[int] +@pytest.mark.parametrize(["service_method", "wrapper_class", "value"], test_cases) +async def test_channel_receives_wrapped_type( + service_method: Callable[[TestStub], Any], wrapper_class: Callable, value +): + wrapped_value = wrapper_class() + wrapped_value.value = value + channel = MockChannel(responses=[wrapped_value]) + service = TestStub(channel) + + await service_method(service) + + assert channel.requests[0]["response_type"] != Optional[type(value)] + assert channel.requests[0]["response_type"] == type(wrapped_value) + + +@pytest.mark.asyncio +@pytest.mark.xfail +@pytest.mark.parametrize(["service_method", "wrapper_class", "value"], test_cases) +async def test_service_unwraps_response( + service_method: Callable[[TestStub], Any], wrapper_class: Callable, value +): + wrapped_value = wrapper_class() + wrapped_value.value = value + service = TestStub(MockChannel(responses=[wrapped_value])) + + response_value = await service_method(service) + + assert type(response_value) == value + assert type(response_value) == type(value) diff --git a/betterproto/tests/mocks.py b/betterproto/tests/mocks.py new file mode 100644 index 0000000..287a58f --- /dev/null +++ b/betterproto/tests/mocks.py @@ -0,0 +1,39 @@ +from typing import List + +from grpclib.client import Channel + + +class MockChannel(Channel): + # noinspection PyMissingConstructor + def __init__(self, responses: List) -> None: + self.responses = responses + self.requests = [] + + def request(self, route, cardinality, request, response_type, **kwargs): + self.requests.append( + { + "route": route, + "cardinality": cardinality, + "request": request, + "response_type": response_type, + } + ) + return MockStream(self.responses) + + +class MockStream: + def __init__(self, responses: List) -> None: + super().__init__() + self.responses = responses + + async def recv_message(self): + return next(self.responses) + + async def send_message(self, *args, **kwargs): + pass + + async def __aexit__(self, exc_type, exc_val, exc_tb): + return True + + async def __aenter__(self): + return True