def test_entity_values() -> None: """Test various entity assigns for value and type correctness.""" ent = EntityTest() # Simple int field. with pytest.raises(TypeError): # noinspection PyTypeHints ent.ival = 'strval' # type: ignore assert static_type_equals(ent.ival, int) assert isinstance(ent.ival, int) assert ent.ival == 345 ent.ival = 346 assert ent.ival == 346 # Simple float field. with pytest.raises(TypeError): # noinspection PyTypeHints ent.fval = 'foo' # type: ignore assert static_type_equals(ent.fval, float) ent.fval = 2 ent.fval = True ent.fval = 1.0 # Simple value list field. assert not ent.slval # bool operator assert len(ent.slval) == 0 with pytest.raises(TypeError): ent.slval.append(1) # type: ignore ent.slval.append('blah') assert ent.slval # bool operator assert len(ent.slval) == 1 assert list(ent.slval) == ['blah'] with pytest.raises(TypeError): # noinspection PyTypeHints ent.slval = ['foo', 'bar', 1] # type: ignore # Simple value dict field. assert not ent.str_int_dict # bool operator assert 'foo' not in ent.str_int_dict # Set with incorrect key type should give TypeError. with pytest.raises(TypeError): ent.str_int_dict[0] = 123 # type: ignore # And set with incorrect value type should do same. with pytest.raises(TypeError): ent.str_int_dict['foo'] = 'bar' # type: ignore ent.str_int_dict['foo'] = 123 assert ent.str_int_dict # bool operator assert static_type_equals(ent.str_int_dict['foo'], int) assert ent.str_int_dict['foo'] == 123 # Waaah; this works at runtime, but it seems that we'd need # to have BoundDictField inherit from Mapping for mypy to accept this. # (which seems to get a bit ugly, but may be worth revisiting) # assert dict(ent.str_int_dict) == {'foo': 123} # Passing key/value pairs as a list works though.. assert dict(ent.str_int_dict.items()) == {'foo': 123}
def test_entity_values_2() -> None: """Test various entity assigns for value and type correctness.""" ent = EntityTest() # Compound value assert static_type_equals(ent.grp, CompoundTest) assert static_type_equals(ent.grp.isubval, int) assert isinstance(ent.grp.isubval, int) with pytest.raises(TypeError): # noinspection PyTypeHints ent.grp.isubval = 'blah' # type: ignore # Compound value inheritance. assert ent.grp2.isubval2 == 3453 assert ent.grp2.isubval == 34532 # Compound list field. with pytest.raises(IndexError): print(ent.compoundlist[0]) with pytest.raises(TypeError): ent.compoundlist[0] = 123 # type: ignore assert len(ent.compoundlist) == 0 assert not ent.compoundlist # bool operator ent.compoundlist.append() assert ent.compoundlist # bool operator assert len(ent.compoundlist) == 1 assert static_type_equals(ent.compoundlist[0], CompoundTest) # Compound dict field. assert not ent.compounddict # bool operator cdval = ent.compounddict.add('foo') assert ent.compounddict # bool operator assert static_type_equals(cdval, CompoundTest) # Set with incorrect key type should give TypeError. with pytest.raises(TypeError): _cdval2 = ent.compounddict.add(1) # type: ignore # Hmm; should this throw a TypeError and not a KeyError?.. with pytest.raises(KeyError): _cdval3 = ent.compounddict[1] # type: ignore assert static_type_equals(ent.compounddict['foo'], CompoundTest) # Enum value with pytest.raises(ValueError): # noinspection PyTypeHints ent.enumval = None # type: ignore assert ent.enumval == EnumTest.FIRST # Optional Enum value ent.enumval2 = None assert ent.enumval2 is None # Nested compound values assert not ent.grp.compoundlist # bool operator val = ent.grp.compoundlist.append() assert ent.grp.compoundlist # bool operator assert static_type_equals(val, SubCompoundTest) assert static_type_equals(ent.grp.compoundlist[0], SubCompoundTest) assert static_type_equals(ent.grp.compoundlist[0].subval, bool)
def test_field_access_from_type() -> None: """Accessing fields through type objects should return the Field objs.""" ent = EntityTest() # Accessing fields through the type should return field objects # instead of values. assert static_type_equals(ent.ival, int) assert isinstance(ent.ival, int) mypytype = 'efro.entity._field.Field[builtins.int*]' assert static_type_equals(type(ent).ival, mypytype) assert isinstance(type(ent).ival, entity.Field) # Accessing subtype on a nested compound field.. assert static_type_equals(type(ent).compoundlist.d_value, CompoundTest) assert isinstance(type(ent).compoundlist.d_value, CompoundTest)
def test_synchronous_messaging() -> None: """Test the full pipeline.""" # Define a class that can send messages and one that can receive them. class TestClassS: """Test class incorporating send functionality.""" msg = _TestMessageSender(TEST_PROTOCOL) def __init__(self, target: TestClassR) -> None: self._target = target @msg.send_raw_handler def _send_raw_message(self, data: bytes) -> bytes: """Test.""" return self._target.receiver.handle_raw_message(data) class TestClassR: """Test class incorporating receive functionality.""" receiver = _TestMessageReceiver(TEST_PROTOCOL) @receiver.handler def handle_test_message_1(self, msg: _TMessage1) -> _TResponse1: """Test.""" if msg.ival == 1: raise CleanError('Testing Clean Error') if msg.ival == 2: raise RuntimeError('Testing Runtime Error') return _TResponse1(bval=True) @receiver.handler def handle_test_message_2( self, msg: _TMessage2) -> Union[_TResponse1, _TResponse2]: """Test.""" del msg # Unused return _TResponse2(fval=1.2) @receiver.handler def handle_test_message_3(self, msg: _TMessage3) -> None: """Test.""" del msg # Unused receiver.validate() obj_r = TestClassR() obj = TestClassS(target=obj_r) response = obj.msg.send(_TMessage1(ival=0)) assert isinstance(response, _TResponse1) response2 = obj.msg.send(_TMessage2(sval='rah')) assert isinstance(response2, (_TResponse1, _TResponse2)) response3 = obj.msg.send(_TMessage3(sval='rah')) assert response3 is None # Make sure static typing lines up too. if os.environ.get('EFRO_TEST_MESSAGE_FAST') != '1': assert static_type_equals(response, _TResponse1) assert static_type_equals(response3, None) # Remote CleanErrors should come across locally as the same. try: _response4 = obj.msg.send(_TMessage1(ival=1)) except Exception as exc: assert isinstance(exc, CleanError) assert str(exc) == 'Testing Clean Error' # Other remote errors should result in RemoteError. with pytest.raises(RemoteError): _response4 = obj.msg.send(_TMessage1(ival=2))
def test_entity_mixin() -> None: """Testing our mixin entity variety.""" ent = EntityTestMixin() assert static_type_equals(ent.isubval2, int) assert ent.isubval2 == 3453
def test_field_copies() -> None: """Test copying various values between fields.""" ent1 = EntityTest() ent2 = EntityTest() # Copying a simple value. ent1.ival = 334 ent2.ival = ent1.ival assert ent2.ival == 334 # Copying a nested compound. ent1.grp.isubval = 543 ent2.grp = ent1.grp assert ent2.grp.isubval == 543 # Type-checker currently allows this because both are Compounds # but should fail at runtime since their subfield arrangement differs. # reveal_type(ent1.grp.blah) with pytest.raises(ValueError): ent2.grp = ent1.grp2 # Copying a value list. ent1.slval = ['foo', 'bar'] assert ent1.slval == ['foo', 'bar'] ent2.slval = ent1.slval assert ent2.slval == ['foo', 'bar'] # Copying a value dict. ent1.str_int_dict['tval'] = 987 ent2.str_int_dict = ent1.str_int_dict assert ent2.str_int_dict['tval'] == 987 # Copying a CompoundList val = ent1.compoundlist.append() val.isubval = 356 assert ent1.compoundlist[0].isubval == 356 assert len(ent1.compoundlist) == 1 ent1.compoundlist.append() assert len(ent1.compoundlist) == 2 assert len(ent2.compoundlist) == 0 # Copying to the same field on different obj should work. ent2.compoundlist = ent1.compoundlist assert ent2.compoundlist[0].isubval == 356 assert len(ent2.compoundlist) == 2 # Cross-field assigns should work too if the field layouts match.. ent1.compoundlist2 = ent1.compoundlist # And not if they don't... # (in this case mypy errors too but that may not always be the case) with pytest.raises(ValueError): ent1.compoundlist3 = ent1.compoundlist # type: ignore # Copying a CompoundDict ent1.compounddict.add('foo') ent1.compounddict.add('bar') assert static_type_equals(ent1.compounddict['foo'].isubval, int) ent1.compounddict['foo'].isubval = 23 # Copying to the same field on different obj should work. ent2.compounddict = ent1.compounddict assert ent2.compounddict.keys() == ['foo', 'bar'] assert ent2.compounddict['foo'].isubval == 23 # Cross field assigns should work too if the field layouts match.. ent1.compounddict2 = ent1.compounddict # ..And should fail otherwise. # (mypy catches this too, but that may not always be the case if # two CompoundValues have the same type but different layouts based # on their __init__ args or whatnot) with pytest.raises(ValueError): ent1.compounddict3 = ent1.compounddict # type: ignore # Make sure invalid key types get caught when setting a full dict: with pytest.raises(TypeError): ent1.compounddict2 = { 'foo': ent1.compounddict['foo'], 2: ent1.compounddict['bar'], # type: ignore }
def test_full_pipeline() -> None: """Test the full pipeline.""" # pylint: disable=too-many-locals # pylint: disable=too-many-statements # Define a class that can send messages and one that can receive them. class TestClassS: """Test class incorporating send functionality.""" msg = _TestMessageSenderBBoth() test_handling_unregistered = False def __init__(self, target: Union[TestClassRSync, TestClassRAsync]) -> None: self.test_sidecar = False self._target = target @msg.send_method def _send_raw_message(self, data: str) -> str: """Handle synchronous sending of raw json message data.""" # Just talk directly to the receiver for this example. # (currently only support synchronous receivers) assert isinstance(self._target, TestClassRSync) try: return self._target.receiver.handle_raw_message( data, raise_unregistered=self.test_handling_unregistered) except UnregisteredMessageIDError: if self.test_handling_unregistered: # Emulate forwarding unregistered messages on to some # other handler... response_dict = self.msg.protocol.response_to_dict( EmptyResponse()) return self.msg.protocol.encode_dict(response_dict) raise @msg.send_async_method async def _send_raw_message_async(self, data: str) -> str: """Handle asynchronous sending of raw json message data.""" # Just talk directly to the receiver for this example. # (we can do sync or async receivers) if isinstance(self._target, TestClassRSync): return self._target.receiver.handle_raw_message(data) return await self._target.receiver.handle_raw_message(data) @msg.encode_filter_method def _encode_filter(self, msg: Message, outdict: dict) -> None: """Filter our outgoing messages.""" if self.test_sidecar: outdict['_sidecar_data'] = getattr(msg, '_sidecar_data') @msg.decode_filter_method def _decode_filter(self, indata: dict, response: Response) -> None: """Filter our incoming responses.""" if self.test_sidecar: setattr(response, '_sidecar_data', indata['_sidecar_data']) class TestClassRSync: """Test class incorporating synchronous receive functionality.""" receiver = _TestSyncMessageReceiver() def __init__(self) -> None: self.test_sidecar = False @receiver.handler def handle_test_message_1(self, msg: _TMsg1) -> _TResp1: """Test.""" if msg.ival == 1: raise CleanError('Testing Clean Error') if msg.ival == 2: raise RuntimeError('Testing Runtime Error') out = _TResp1(bval=True) if self.test_sidecar: setattr(out, '_sidecar_data', getattr(msg, '_sidecar_data')) return out @receiver.handler def handle_test_message_2(self, msg: _TMsg2) -> Union[_TResp1, _TResp2]: """Test.""" del msg # Unused return _TResp2(fval=1.2) @receiver.handler def handle_test_message_3(self, msg: _TMsg3) -> None: """Test.""" del msg # Unused @receiver.decode_filter_method def _decode_filter(self, indata: dict, message: Message) -> None: """Filter our incoming messages.""" if self.test_sidecar: setattr(message, '_sidecar_data', indata['_sidecar_data']) @receiver.encode_filter_method def _encode_filter(self, response: Response, outdict: dict) -> None: """Filter our outgoing responses.""" if self.test_sidecar: outdict['_sidecar_data'] = getattr(response, '_sidecar_data') receiver.validate() class TestClassRAsync: """Test class incorporating asynchronous receive functionality.""" receiver = _TestAsyncMessageReceiver() @receiver.handler async def handle_test_message_1(self, msg: _TMsg1) -> _TResp1: """Test.""" if msg.ival == 1: raise CleanError('Testing Clean Error') if msg.ival == 2: raise RuntimeError('Testing Runtime Error') return _TResp1(bval=True) @receiver.handler async def handle_test_message_2( self, msg: _TMsg2) -> Union[_TResp1, _TResp2]: """Test.""" del msg # Unused return _TResp2(fval=1.2) @receiver.handler async def handle_test_message_3(self, msg: _TMsg3) -> None: """Test.""" del msg # Unused receiver.validate() obj_r_sync = TestClassRSync() obj_r_async = TestClassRAsync() obj = TestClassS(target=obj_r_sync) obj2 = TestClassS(target=obj_r_async) # Test sends (of sync and async varieties). response1 = obj.msg.send(_TMsg1(ival=0)) response2 = obj.msg.send(_TMsg2(sval='rah')) response3 = obj.msg.send(_TMsg3(sval='rah')) response4 = asyncio.run(obj.msg.send_async(_TMsg1(ival=0))) # Make sure static typing lines up with what we expect. if os.environ.get('EFRO_TEST_MESSAGE_FAST') != '1': assert static_type_equals(response1, _TResp1) assert static_type_equals(response3, None) assert isinstance(response1, _TResp1) assert isinstance(response2, (_TResp1, _TResp2)) assert response3 is None assert isinstance(response4, _TResp1) # Remote CleanErrors should come across locally as the same. try: _response5 = obj.msg.send(_TMsg1(ival=1)) except Exception as exc: assert isinstance(exc, CleanError) assert str(exc) == 'Testing Clean Error' # Other remote errors should result in RemoteError. with pytest.raises(RemoteError): _response5 = obj.msg.send(_TMsg1(ival=2)) # Now test sends to async handlers. response6 = asyncio.run(obj2.msg.send_async(_TMsg1(ival=0))) assert isinstance(response6, _TResp1) # Our sender here is using a 'newer' protocol which contains a message # type not in the older protocol used by our receivers. Make sure we # get the expected error when trying to send that message to them. with pytest.raises(RemoteError): _response7 = obj.msg.send(_TMsg4(sval2='blargh')) # Also make sure the receiver can explicitly handle unregistered # messages (by forwarding them along to something that can, etc). obj.test_handling_unregistered = True response7 = obj.msg.send(_TMsg4(sval2='blargh')) assert response7 is None # Make sure static typing lines up with what we expect. if os.environ.get('EFRO_TEST_MESSAGE_FAST') != '1': assert static_type_equals(response6, _TResp1) # Now test adding extra data to messages. This should be transferred # into the encoded message, copied to the response, and again back # through the encoded response using the filter functions we defined. obj.test_sidecar = True obj_r_sync.test_sidecar = True outmsg = _TMsg1(ival=0) setattr(outmsg, '_sidecar_data', 198) # Our test payload. response1 = obj.msg.send(outmsg) assert getattr(response1, '_sidecar_data') == 198 obj.test_sidecar = False obj_r_sync.test_sidecar = False
def test_entity_values() -> None: """Test various entity assigns for value and type correctness.""" # pylint: disable=too-many-statements ent = EntityTest() # Simple int field. with pytest.raises(TypeError): # noinspection PyTypeHints ent.ival = 'strval' # type: ignore assert static_type_equals(ent.ival, int) assert isinstance(ent.ival, int) assert ent.ival == 345 ent.ival = 346 assert ent.ival == 346 # Simple float field. with pytest.raises(TypeError): # noinspection PyTypeHints ent.fval = 'foo' # type: ignore assert static_type_equals(ent.fval, float) ent.fval = 2 ent.fval = True ent.fval = 1.0 # Simple value list field. assert not ent.slval # bool operator assert len(ent.slval) == 0 with pytest.raises(TypeError): ent.slval.append(1) # type: ignore ent.slval.append('blah') assert ent.slval # bool operator assert len(ent.slval) == 1 assert list(ent.slval) == ['blah'] with pytest.raises(TypeError): # noinspection PyTypeHints ent.slval = ['foo', 'bar', 1] # type: ignore # Simple value dict field. assert not ent.str_int_dict # bool operator assert 'foo' not in ent.str_int_dict # Set with incorrect key type should give TypeError. with pytest.raises(TypeError): ent.str_int_dict[0] = 123 # type: ignore # And set with incorrect value type should do same. with pytest.raises(TypeError): ent.str_int_dict['foo'] = 'bar' # type: ignore ent.str_int_dict['foo'] = 123 assert ent.str_int_dict # bool operator assert static_type_equals(ent.str_int_dict['foo'], int) assert ent.str_int_dict['foo'] == 123 # Simple dict with enum key. assert EnumTest.FIRST not in ent.enum_int_dict ent.enum_int_dict[EnumTest.FIRST] = 234 assert EnumTest.FIRST in ent.enum_int_dict assert ent.enum_int_dict[EnumTest.FIRST] == 234 # Set with incorrect key type should give TypeError. with pytest.raises(TypeError): ent.enum_int_dict[0] = 123 # type: ignore with pytest.raises(TypeError): ent.enum_int_dict[EnumTest2.FIRST] = 123 # type: ignore # And set with incorrect value type should do same. with pytest.raises(TypeError): ent.enum_int_dict[EnumTest.FIRST] = 'bar' # type: ignore # Make sure is stored as underlying type. assert ent.d_data['ed'] == {0: 234} # Make sure invalid raw enum values are caught. ent2 = EntityTest() ent2.set_data({}) ent2.set_data({'ed': {0: 111}}) with pytest.raises(ValueError): ent2.set_data({'ed': {5: 111}}) # Waaah; this works at runtime, but it seems that we'd need # to have BoundDictField inherit from Mapping for mypy to accept this. # (which seems to get a bit ugly, but may be worth revisiting) # assert dict(ent.str_int_dict) == {'foo': 123} # Passing key/value pairs as a list works though.. assert dict(ent.str_int_dict.items()) == {'foo': 123}
def test_entity_values_2() -> None: """Test various entity assigns for value and type correctness.""" # pylint: disable=too-many-statements ent = EntityTest() # Compound value assert static_type_equals(ent.grp, CompoundTest) assert static_type_equals(ent.grp.isubval, int) assert isinstance(ent.grp.isubval, int) with pytest.raises(TypeError): # noinspection PyTypeHints ent.grp.isubval = 'blah' # type: ignore # Compound value inheritance. assert ent.grp2.isubval2 == 3453 assert ent.grp2.isubval == 34532 # Compound list field. with pytest.raises(IndexError): print(ent.compoundlist[0]) with pytest.raises(TypeError): ent.compoundlist[0] = 123 # type: ignore assert len(ent.compoundlist) == 0 assert not ent.compoundlist # bool operator ent.compoundlist.append() assert ent.compoundlist # bool operator assert len(ent.compoundlist) == 1 assert static_type_equals(ent.compoundlist[0], CompoundTest) # Compound dict field. assert not ent.compounddict # bool operator cdval = ent.compounddict.add('foo') assert ent.compounddict # bool operator assert static_type_equals(cdval, CompoundTest) # Set with incorrect key type should give TypeError. with pytest.raises(TypeError): _cdval2 = ent.compounddict.add(1) # type: ignore # Hmm; should this throw a TypeError and not a KeyError?.. with pytest.raises(TypeError): _cdval3 = ent.compounddict[1] # type: ignore assert static_type_equals(ent.compounddict['foo'], CompoundTest) # Enum value with pytest.raises(ValueError): # noinspection PyTypeHints ent.enumval = None # type: ignore assert ent.enumval is EnumTest.FIRST # Compound dict with enum key. assert not ent.compounddict4 # bool operator assert EnumTest.FIRST not in ent.compounddict4 _cd4val = ent.compounddict4.add(EnumTest.FIRST) assert ent.compounddict4 # bool operator assert EnumTest.FIRST in ent.compounddict4 ent.compounddict4[EnumTest.FIRST].isubval = 222 assert ent.compounddict4[EnumTest.FIRST].isubval == 222 with pytest.raises(TypeError): ent.compounddict4[0].isubval = 222 # type: ignore assert static_type_equals(ent.compounddict4[EnumTest.FIRST], CompoundTest) # Make sure enum keys are stored as underlying type. assert ent.d_data['td4'] == {0: {'i': 222, 'l': []}} # Optional Enum value ent.enumval2 = None assert ent.enumval2 is None # Nested compound values assert not ent.grp.compoundlist # bool operator val = ent.grp.compoundlist.append() assert ent.grp.compoundlist # bool operator assert static_type_equals(val, SubCompoundTest) assert static_type_equals(ent.grp.compoundlist[0], SubCompoundTest) assert static_type_equals(ent.grp.compoundlist[0].subval, bool) # Make sure we can digest the same data we spit out. ent.set_data(ent.d_data)
def test_full_pipeline() -> None: """Test the full pipeline.""" # Define a class that can send messages and one that can receive them. class TestClassS: """Test class incorporating send functionality.""" msg = _TestMessageSenderBoth() def __init__(self, target: Union[TestClassRSync, TestClassRAsync]) -> None: self._target = target @msg.send_method def _send_raw_message(self, data: str) -> str: """Handle synchronous sending of raw json message data.""" # Just talk directly to the receiver for this example. # (currently only support synchronous receivers) assert isinstance(self._target, TestClassRSync) return self._target.receiver.handle_raw_message(data) @msg.send_async_method async def _send_raw_message_async(self, data: str) -> str: """Handle asynchronous sending of raw json message data.""" # Just talk directly to the receiver for this example. # (we can do sync or async receivers) if isinstance(self._target, TestClassRSync): return self._target.receiver.handle_raw_message(data) return await self._target.receiver.handle_raw_message(data) class TestClassRSync: """Test class incorporating synchronous receive functionality.""" receiver = _TestSyncMessageReceiver() @receiver.handler def handle_test_message_1(self, msg: _TMsg1) -> _TResp1: """Test.""" if msg.ival == 1: raise CleanError('Testing Clean Error') if msg.ival == 2: raise RuntimeError('Testing Runtime Error') return _TResp1(bval=True) @receiver.handler def handle_test_message_2(self, msg: _TMsg2) -> Union[_TResp1, _TResp2]: """Test.""" del msg # Unused return _TResp2(fval=1.2) @receiver.handler def handle_test_message_3(self, msg: _TMsg3) -> None: """Test.""" del msg # Unused receiver.validate() class TestClassRAsync: """Test class incorporating asynchronous receive functionality.""" receiver = _TestAsyncMessageReceiver() @receiver.handler async def handle_test_message_1(self, msg: _TMsg1) -> _TResp1: """Test.""" if msg.ival == 1: raise CleanError('Testing Clean Error') if msg.ival == 2: raise RuntimeError('Testing Runtime Error') return _TResp1(bval=True) @receiver.handler async def handle_test_message_2( self, msg: _TMsg2) -> Union[_TResp1, _TResp2]: """Test.""" del msg # Unused return _TResp2(fval=1.2) @receiver.handler async def handle_test_message_3(self, msg: _TMsg3) -> None: """Test.""" del msg # Unused receiver.validate() obj_r_sync = TestClassRSync() obj_r_async = TestClassRAsync() obj = TestClassS(target=obj_r_sync) obj2 = TestClassS(target=obj_r_async) # Test sends (of sync and async varieties). response1 = obj.msg.send(_TMsg1(ival=0)) response2 = obj.msg.send(_TMsg2(sval='rah')) response3 = obj.msg.send(_TMsg3(sval='rah')) response4 = asyncio.run(obj.msg.send_async(_TMsg1(ival=0))) # Make sure static typing lines up with what we expect. if os.environ.get('EFRO_TEST_MESSAGE_FAST') != '1': assert static_type_equals(response1, _TResp1) assert static_type_equals(response3, None) assert isinstance(response1, _TResp1) assert isinstance(response2, (_TResp1, _TResp2)) assert response3 is None assert isinstance(response4, _TResp1) # Remote CleanErrors should come across locally as the same. try: _response5 = obj.msg.send(_TMsg1(ival=1)) except Exception as exc: assert isinstance(exc, CleanError) assert str(exc) == 'Testing Clean Error' # Other remote errors should result in RemoteError. with pytest.raises(RemoteError): _response5 = obj.msg.send(_TMsg1(ival=2)) # Now test sends to async handlers. response6 = asyncio.run(obj2.msg.send_async(_TMsg1(ival=0))) assert isinstance(response6, _TResp1) # Make sure static typing lines up with what we expect. if os.environ.get('EFRO_TEST_MESSAGE_FAST') != '1': assert static_type_equals(response6, _TResp1)