diff --git a/tests/unittests/durable_functions/activity_trigger/function.json b/tests/unittests/durable_functions/activity_trigger/function.json new file mode 100644 index 000000000..ebf8bfa62 --- /dev/null +++ b/tests/unittests/durable_functions/activity_trigger/function.json @@ -0,0 +1,10 @@ +{ + "scriptFile": "main.py", + "bindings": [ + { + "type": "activityTrigger", + "name": "input", + "direction": "in" + } + ] + } diff --git a/tests/unittests/durable_functions/activity_trigger/main.py b/tests/unittests/durable_functions/activity_trigger/main.py new file mode 100644 index 000000000..dbe730061 --- /dev/null +++ b/tests/unittests/durable_functions/activity_trigger/main.py @@ -0,0 +1,2 @@ +def main(input: str) -> str: + return input diff --git a/tests/unittests/durable_functions/activity_trigger_no_anno/function.json b/tests/unittests/durable_functions/activity_trigger_no_anno/function.json new file mode 100644 index 000000000..ebf8bfa62 --- /dev/null +++ b/tests/unittests/durable_functions/activity_trigger_no_anno/function.json @@ -0,0 +1,10 @@ +{ + "scriptFile": "main.py", + "bindings": [ + { + "type": "activityTrigger", + "name": "input", + "direction": "in" + } + ] + } diff --git a/tests/unittests/durable_functions/activity_trigger_no_anno/main.py b/tests/unittests/durable_functions/activity_trigger_no_anno/main.py new file mode 100644 index 000000000..96f42af54 --- /dev/null +++ b/tests/unittests/durable_functions/activity_trigger_no_anno/main.py @@ -0,0 +1,2 @@ +def main(input): + return input diff --git a/tests/unittests/durable_functions/orchestration_trigger/function.json b/tests/unittests/durable_functions/orchestration_trigger/function.json new file mode 100644 index 000000000..c8ef14a94 --- /dev/null +++ b/tests/unittests/durable_functions/orchestration_trigger/function.json @@ -0,0 +1,10 @@ +{ + "scriptFile": "main.py", + "bindings": [ + { + "type": "orchestrationTrigger", + "name": "context", + "direction": "in" + } + ] + } diff --git a/tests/unittests/durable_functions/orchestration_trigger/main.py b/tests/unittests/durable_functions/orchestration_trigger/main.py new file mode 100644 index 000000000..7ac02dfd7 --- /dev/null +++ b/tests/unittests/durable_functions/orchestration_trigger/main.py @@ -0,0 +1,13 @@ +# import azure.durable_functions as df + + +def generator_function(context): + final_result = yield context.call_activity('activity_trigger', 'foobar') + return final_result + + +def main(context): + # orchestrate = df.Orchestrator.create(generator_function) + # result = orchestrate(context) + # return result + return f'{context} :)' diff --git a/tests/unittests/generic_functions/foobar_as_bytes_no_anno/function.json b/tests/unittests/generic_functions/foobar_as_bytes_no_anno/function.json new file mode 100644 index 000000000..f0117f606 --- /dev/null +++ b/tests/unittests/generic_functions/foobar_as_bytes_no_anno/function.json @@ -0,0 +1,17 @@ +{ + "scriptFile": "main.py", + "bindings": [ + { + "type": "foobar", + "name": "input", + "direction": "in", + "dataType": "binary" + }, + { + "direction": "out", + "name": "$return", + "type": "foobar", + "dataType": "binary" + } + ] +} diff --git a/tests/unittests/generic_functions/foobar_as_bytes_no_anno/main.py b/tests/unittests/generic_functions/foobar_as_bytes_no_anno/main.py new file mode 100644 index 000000000..30548a4d3 --- /dev/null +++ b/tests/unittests/generic_functions/foobar_as_bytes_no_anno/main.py @@ -0,0 +1,3 @@ +# Input as bytes, without annotation +def main(input): + return input diff --git a/tests/unittests/generic_functions/foobar_as_str_no_anno/function.json b/tests/unittests/generic_functions/foobar_as_str_no_anno/function.json new file mode 100644 index 000000000..144593c6a --- /dev/null +++ b/tests/unittests/generic_functions/foobar_as_str_no_anno/function.json @@ -0,0 +1,17 @@ +{ + "scriptFile": "main.py", + "bindings": [ + { + "type": "foobar", + "name": "input", + "direction": "in", + "dataType": "string" + }, + { + "direction": "out", + "name": "$return", + "type": "foobar", + "dataType": "string" + } + ] +} diff --git a/tests/unittests/generic_functions/foobar_as_str_no_anno/main.py b/tests/unittests/generic_functions/foobar_as_str_no_anno/main.py new file mode 100644 index 000000000..b130a1761 --- /dev/null +++ b/tests/unittests/generic_functions/foobar_as_str_no_anno/main.py @@ -0,0 +1,3 @@ +# Input as string, without annotation +def main(input): + return input diff --git a/tests/unittests/generic_functions/foobar_implicit_output/function.json b/tests/unittests/generic_functions/foobar_implicit_output/function.json new file mode 100644 index 000000000..6f8a83ec0 --- /dev/null +++ b/tests/unittests/generic_functions/foobar_implicit_output/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "main.py", + "bindings": [ + { + "type": "foobar", + "name": "input", + "direction": "in", + "dataType": "string" + } + ] +} diff --git a/tests/unittests/generic_functions/foobar_implicit_output/main.py b/tests/unittests/generic_functions/foobar_implicit_output/main.py new file mode 100644 index 000000000..78959586d --- /dev/null +++ b/tests/unittests/generic_functions/foobar_implicit_output/main.py @@ -0,0 +1,3 @@ +# Input as string, without annotation +def main(input: str): + return input diff --git a/tests/unittests/generic_functions/foobar_with_no_datatype/function.json b/tests/unittests/generic_functions/foobar_with_no_datatype/function.json new file mode 100644 index 000000000..4e49f1942 --- /dev/null +++ b/tests/unittests/generic_functions/foobar_with_no_datatype/function.json @@ -0,0 +1,10 @@ +{ + "scriptFile": "main.py", + "bindings": [ + { + "type": "foobar", + "name": "input", + "direction": "in" + } + ] +} diff --git a/tests/unittests/generic_functions/foobar_with_no_datatype/main.py b/tests/unittests/generic_functions/foobar_with_no_datatype/main.py new file mode 100644 index 000000000..dbe730061 --- /dev/null +++ b/tests/unittests/generic_functions/foobar_with_no_datatype/main.py @@ -0,0 +1,2 @@ +def main(input: str) -> str: + return input diff --git a/tests/unittests/test_mock_durable_functions.py b/tests/unittests/test_mock_durable_functions.py new file mode 100644 index 000000000..e2881b35d --- /dev/null +++ b/tests/unittests/test_mock_durable_functions.py @@ -0,0 +1,87 @@ +from azure_functions_worker import protos +from azure_functions_worker import testutils + + +class TestDurableFunctions(testutils.AsyncTestCase): + durable_functions_dir = testutils.UNIT_TESTS_FOLDER / 'durable_functions' + + async def test_mock_activity_trigger(self): + async with testutils.start_mockhost( + script_root=self.durable_functions_dir) as host: + + func_id, r = await host.load_function('activity_trigger') + + self.assertEqual(r.response.function_id, func_id) + self.assertEqual(r.response.result.status, + protos.StatusResult.Success) + + _, r = await host.invoke_function( + 'activity_trigger', [ + 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_activity_trigger_no_anno(self): + async with testutils.start_mockhost( + script_root=self.durable_functions_dir) as host: + + func_id, r = await host.load_function('activity_trigger_no_anno') + + self.assertEqual(r.response.function_id, func_id) + self.assertEqual(r.response.result.status, + protos.StatusResult.Success) + + _, r = await host.invoke_function( + 'activity_trigger_no_anno', [ + protos.ParameterBinding( + name='input', + data=protos.TypedData( + bytes=b'\x34\x93\x04\x70' + ) + ) + ] + ) + self.assertEqual(r.response.result.status, + protos.StatusResult.Success) + self.assertEqual( + r.response.return_value, + protos.TypedData(bytes=b'\x34\x93\x04\x70') + ) + + async def test_mock_orchestration_trigger(self): + async with testutils.start_mockhost( + script_root=self.durable_functions_dir) as host: + + func_id, r = await host.load_function('orchestration_trigger') + + self.assertEqual(r.response.function_id, func_id) + self.assertEqual(r.response.result.status, + protos.StatusResult.Success) + + _, r = await host.invoke_function( + 'orchestration_trigger', [ + protos.ParameterBinding( + name='context', + data=protos.TypedData( + string='Durable functions coming soon' + ) + ) + ] + ) + self.assertEqual(r.response.result.status, + protos.StatusResult.Success) + self.assertEqual( + r.response.return_value, + protos.TypedData(string='Durable functions coming soon :)') + ) diff --git a/tests/unittests/test_mock_generic_functions.py b/tests/unittests/test_mock_generic_functions.py index 2cb895953..a8a69cd4f 100644 --- a/tests/unittests/test_mock_generic_functions.py +++ b/tests/unittests/test_mock_generic_functions.py @@ -58,3 +58,107 @@ async def test_mock_generic_as_bytes(self): r.response.return_value, protos.TypedData(bytes=b'\x00\x01') ) + + async def test_mock_generic_as_str_no_anno(self): + async with testutils.start_mockhost( + script_root=self.generic_funcs_dir) as host: + + func_id, r = await host.load_function('foobar_as_str_no_anno') + + 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_no_anno', [ + 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_generic_as_bytes_no_anno(self): + async with testutils.start_mockhost( + script_root=self.generic_funcs_dir) as host: + + func_id, r = await host.load_function('foobar_as_bytes_no_anno') + + self.assertEqual(r.response.function_id, func_id) + self.assertEqual(r.response.result.status, + protos.StatusResult.Success) + + _, r = await host.invoke_function( + 'foobar_as_bytes_no_anno', [ + protos.ParameterBinding( + name='input', + data=protos.TypedData( + bytes=b'\x00\x01' + ) + ) + ] + ) + self.assertEqual(r.response.result.status, + protos.StatusResult.Success) + self.assertEqual( + r.response.return_value, + protos.TypedData(bytes=b'\x00\x01') + ) + + async def test_mock_generic_should_not_support_implicit_output(self): + async with testutils.start_mockhost( + script_root=self.generic_funcs_dir) as host: + + func_id, r = await host.load_function('foobar_implicit_output') + + self.assertEqual(r.response.function_id, func_id) + self.assertEqual(r.response.result.status, + protos.StatusResult.Success) + + _, r = await host.invoke_function( + 'foobar_as_bytes_no_anno', [ + protos.ParameterBinding( + name='input', + data=protos.TypedData( + bytes=b'\x00\x01' + ) + ) + ] + ) + # It should fail here, since generic binding requires + # $return statement in function.json to pass output + self.assertEqual(r.response.result.status, + protos.StatusResult.Failure) + + async def test_mock_generic_should_support_without_datatype(self): + async with testutils.start_mockhost( + script_root=self.generic_funcs_dir) as host: + + func_id, r = await host.load_function('foobar_with_no_datatype') + + self.assertEqual(r.response.function_id, func_id) + self.assertEqual(r.response.result.status, + protos.StatusResult.Success) + + _, r = await host.invoke_function( + 'foobar_with_no_datatype', [ + protos.ParameterBinding( + name='input', + data=protos.TypedData( + bytes=b'\x00\x01' + ) + ) + ] + ) + # It should fail here, since the generic binding requires datatype + # to be defined in function.json + self.assertEqual(r.response.result.status, + protos.StatusResult.Failure)