async def test_mock_timer__return_pastdue(self): async with testutils.start_mockhost( script_root=self.timer_funcs_dir) as host: func_id, r = await host.load_function('return_pastdue') self.assertEqual(r.response.function_id, func_id) self.assertEqual(r.response.result.status, protos.StatusResult.Success) async def call_and_check(due: bool): _, r = await host.invoke_function('return_pastdue', [ protos.ParameterBinding( name='timer', data=protos.TypedData( json=json.dumps({'IsPastDue': due}))) ]) self.assertEqual(r.response.result.status, protos.StatusResult.Success) self.assertEqual(list(r.response.output_data), [ protos.ParameterBinding( name='pastdue', data=protos.TypedData(string=str(due))) ]) await call_and_check(True) await call_and_check(False)
async def test_mock_generic_as_str(self): async with testutils.start_mockhost( script_root=self.generic_funcs_dir) as host: func_id, r = await host.load_function('foobar_as_str') self.assertEqual(r.response.function_id, func_id) self.assertEqual(r.response.result.status, protos.StatusResult.Success) _, r = await host.invoke_function( 'foobar_as_str', [ protos.ParameterBinding( name='input', data=protos.TypedData( string='test' ) ) ] ) self.assertEqual(r.response.result.status, protos.StatusResult.Success) self.assertEqual( r.response.return_value, protos.TypedData(string='test') )
async def test_mock_eventhub_trigger_iot(self): async with testutils.start_mockhost( script_root=self.mock_funcs_dir) as host: func_id, r = await host.load_function('eventhub_trigger_iot') self.assertEqual(r.response.function_id, func_id) self.assertEqual(r.response.result.status, protos.StatusResult.Success) async def call_and_check(): _, r = await host.invoke_function( 'eventhub_trigger_iot', [ protos.ParameterBinding( name='event', data=protos.TypedData( json=json.dumps({'id': 'foo'})), ), ], metadata={ 'iothub-device-id': protos.TypedData(string='mock-iothub-device-id'), 'iothub-auth-data': protos.TypedData(string='mock-iothub-auth-data') }) self.assertEqual(r.response.result.status, protos.StatusResult.Success) self.assertIn('mock-iothub-device-id', r.response.return_value.string) await call_and_check()
async def test_load_broken__syntax_error(self): async with testutils.start_mockhost( script_root='broken_functions') as host: func_id, r = await host.load_function('syntax_error') self.assertEqual(r.response.function_id, func_id) self.assertEqual(r.response.result.status, protos.StatusResult.Failure) self.assertIn('SyntaxError', r.response.result.exception.message)
async def test_load_broken__module_not_found_error(self): async with testutils.start_mockhost( script_root=self.broken_funcs_dir) as host: func_id, r = await host.load_function('module_not_found_error') self.assertEqual(r.response.function_id, func_id) self.assertEqual(r.response.result.status, protos.StatusResult.Failure) self.assertIn('ModuleNotFoundError', r.response.result.exception.message)
async def test_load_broken__inout_param(self): async with testutils.start_mockhost( script_root=self.broken_funcs_dir) as host: func_id, r = await host.load_function('inout_param') self.assertEqual(r.response.function_id, func_id) self.assertEqual(r.response.result.status, protos.StatusResult.Failure) self.assertRegex( r.response.result.exception.message, r'.*cannot load the inout_param function' r'.*"inout" bindings.*')
async def test_handles_unsupported_messages_gracefully(self): async with testutils.start_mockhost() as host: # Intentionally send a message to worker that isn't # going to be ever supported by it. The idea is that # workers should survive such messages and continue # their operation. If anything, the host can always # terminate the worker. await host.send( protos.StreamingMessage( worker_heartbeat=protos.WorkerHeartbeat())) _, r = await host.load_function('return_out') self.assertEqual(r.response.result.status, protos.StatusResult.Success)
async def test_load_broken__invalid_context_param(self): async with testutils.start_mockhost( script_root='broken_functions') as host: func_id, r = await host.load_function('invalid_context_param') self.assertEqual(r.response.function_id, func_id) self.assertEqual(r.response.result.status, protos.StatusResult.Failure) self.assertRegex( r.response.result.exception.message, r'.*cannot load the invalid_context_param function' r'.*the "context" parameter.*')
async def test_load_broken__invalid_in_anno_non_type(self): async with testutils.start_mockhost( script_root='broken_functions') as host: func_id, r = await host.load_function('invalid_in_anno_non_type') self.assertEqual(r.response.function_id, func_id) self.assertEqual(r.response.result.status, protos.StatusResult.Failure) self.assertRegex( r.response.result.exception.message, r'.*cannot load the invalid_in_anno_non_type function: ' r'binding req has invalid non-type annotation 123')
async def test_load_broken__bad_out_annotation(self): async with testutils.start_mockhost( script_root='broken_functions') as host: func_id, r = await host.load_function('bad_out_annotation') self.assertEqual(r.response.function_id, func_id) self.assertEqual(r.response.result.status, protos.StatusResult.Failure) self.assertRegex( r.response.result.exception.message, r'.*cannot load the bad_out_annotation function' r'.*binding foo has invalid Out annotation.*')
async def test_load_broken__wrong_param_dir(self): async with testutils.start_mockhost( script_root='broken_functions') as host: func_id, r = await host.load_function('wrong_param_dir') self.assertEqual(r.response.function_id, func_id) self.assertEqual(r.response.result.status, protos.StatusResult.Failure) self.assertRegex( r.response.result.exception.message, r'.*cannot load the wrong_param_dir function' r'.*binding foo is declared to have the "out".*')
async def test_load_broken__return_param_in(self): async with testutils.start_mockhost( script_root='broken_functions') as host: func_id, r = await host.load_function('return_param_in') self.assertEqual(r.response.function_id, func_id) self.assertEqual(r.response.result.status, protos.StatusResult.Failure) self.assertRegex( r.response.result.exception.message, r'.*cannot load the return_param_in function' r'.*"\$return" .* set to "out"')
async def test_load_broken__invalid_return_anno(self): async with testutils.start_mockhost( script_root='broken_functions') as host: func_id, r = await host.load_function('invalid_return_anno') self.assertEqual(r.response.function_id, func_id) self.assertEqual(r.response.result.status, protos.StatusResult.Failure) self.assertRegex( r.response.result.exception.message, r'.*cannot load the invalid_return_anno function' r'.*Python return annotation "int" does not match ' r'binding type "http"')
async def test_load_broken__import_error(self): async with testutils.start_mockhost( script_root=self.broken_funcs_dir) as host: func_id, r = await host.load_function('import_error') self.assertEqual(r.response.function_id, func_id) self.assertEqual(r.response.result.status, protos.StatusResult.Failure) self.assertIn('ImportError', r.response.result.exception.message) self.assertNotIn('<frozen importlib._bootstrap>', r.response.result.exception.message) self.assertNotIn('<frozen importlib._bootstrap_external>', r.response.result.exception.message)
async def test_load_broken__missing_py_param(self): async with testutils.start_mockhost( script_root='broken_functions') as host: func_id, r = await host.load_function('missing_py_param') self.assertEqual(r.response.function_id, func_id) self.assertEqual(r.response.result.status, protos.StatusResult.Failure) self.assertRegex( r.response.result.exception.message, r".*cannot load the missing_py_param function" r".*parameters are declared in function.json" r".*'req'.*")
async def test_load_broken__unsupported_ret_type(self): # Test that we won't load a function with a bind type we don't support. async with testutils.start_mockhost( script_root='broken_functions') as host: func_id, r = await host.load_function('unsupported_ret_type') self.assertEqual(r.response.function_id, func_id) self.assertEqual(r.response.result.status, protos.StatusResult.Failure) self.assertRegex( r.response.result.exception.message, r'.*cannot load the unsupported_ret_type function' r'.*unknown type .*\$return.* "yolo".*')
async def test_load_broken__invalid_in_anno(self): async with testutils.start_mockhost( script_root='broken_functions') as host: func_id, r = await host.load_function('invalid_in_anno') self.assertEqual(r.response.function_id, func_id) self.assertEqual(r.response.result.status, protos.StatusResult.Failure) self.assertRegex( r.response.result.exception.message, r'.*cannot load the invalid_in_anno function' r'.*type of req binding .* "httpTrigger" ' r'does not match its Python annotation "HttpResponse"')
async def test_call_sync_function_check_logs(self): async with testutils.start_mockhost() as host: await host.load_function('sync_logging') invoke_id, r = await host.invoke_function('sync_logging', [ protos.ParameterBinding( name='req', data=protos.TypedData(http=protos.RpcHttp(method='GET'))) ]) self.assertEqual(r.response.result.status, protos.StatusResult.Success) self.assertEqual(len(r.logs), 1) log = r.logs[0] self.assertEqual(log.invocation_id, invoke_id) self.assertTrue( log.message.startswith('a gracefully handled error')) self.assertEqual(r.response.return_value.string, 'OK-sync')
async def test_call_async_function_check_logs(self): async with testutils.start_mockhost() as host: await host.load_function('async_logging') invoke_id, r = await host.invoke_function('async_logging', [ protos.ParameterBinding( name='req', data=protos.TypedData(http=protos.RpcHttp(method='GET'))) ]) self.assertEqual(r.response.result.status, protos.StatusResult.Success) self.assertEqual(len(r.logs), 2) self.assertEqual(r.logs[0].invocation_id, invoke_id) self.assertEqual(r.logs[0].message, 'hello info') self.assertEqual(r.logs[0].level, protos.RpcLog.Information) self.assertEqual(r.logs[1].invocation_id, invoke_id) self.assertTrue(r.logs[1].message.startswith('and another error')) self.assertEqual(r.logs[1].level, protos.RpcLog.Error) self.assertEqual(r.response.return_value.string, 'OK-async')