diff --git a/tests/test_rpc.py b/tests/test_rpc.py new file mode 100644 index 0000000..ec286f5 --- /dev/null +++ b/tests/test_rpc.py @@ -0,0 +1,80 @@ +""" +RPC related +""" +import pytest +import tractor +import trio + + +async def sleep_back_actor( + actor_name, + func_name, + func_defined, + exposed_mods, +): + async with tractor.find_actor(actor_name) as portal: + try: + await portal.run(__name__, func_name) + except tractor.RemoteActorError as err: + if not func_defined: + expect = AttributeError + if not exposed_mods: + expect = tractor.ModuleNotExposed + + assert err.type is expect + raise + + +async def short_sleep(): + await trio.sleep(0) + + +@pytest.mark.parametrize( + 'to_call', [ + ([], 'short_sleep'), + ([__name__], 'short_sleep'), + ([__name__], 'fake_func'), + ], + ids=['no_mods', 'this_mod', 'this_mod_bad_func'], +) +def test_rpc_errors(arb_addr, to_call): + """Test errors when making various RPC requests to an actor + that either doesn't have the requested module exposed or doesn't define + the named function. + """ + exposed_mods, funcname = to_call + func_defined = globals().get(funcname, False) + + async def main(): + actor = tractor.current_actor() + assert actor.is_arbiter + + # spawn a subactor which calls us back + async with tractor.open_nursery() as n: + await n.run_in_actor( + 'subactor', + sleep_back_actor, + actor_name=actor.name, + # function from this module the subactor will invoke + # when it RPCs back to this actor + func_name=funcname, + exposed_mods=exposed_mods, + func_defined=True if func_defined else False, + ) + + def run(): + tractor.run( + main, + arbiter_addr=arb_addr, + rpc_module_paths=exposed_mods, + ) + + # handle both parameterized cases + if exposed_mods and func_defined: + run() + else: + # underlying errors are propogated upwards (yet) + with pytest.raises(tractor.RemoteActorError) as err: + run() + + assert err.value.type is tractor.RemoteActorError