|
| 1 | +# Copyright (c) Microsoft Corporation. All rights reserved. |
| 2 | +# Licensed under the MIT License. |
| 3 | +import unittest |
| 4 | +import json |
| 5 | +import azure.functions as func |
| 6 | +from azure.functions.meta import Datum |
| 7 | +from azure.functions.connectors import ConnectorTriggerConverter |
| 8 | + |
| 9 | + |
| 10 | +class TestConnectorTriggerConverter(unittest.TestCase): |
| 11 | + """Unit tests for ConnectorTriggerConverter""" |
| 12 | + |
| 13 | + def test_check_input_type_annotation_valid_types(self): |
| 14 | + self.assertTrue(ConnectorTriggerConverter.check_input_type_annotation(str)) |
| 15 | + self.assertTrue(ConnectorTriggerConverter.check_input_type_annotation(dict)) |
| 16 | + self.assertTrue(ConnectorTriggerConverter.check_input_type_annotation(bytes)) |
| 17 | + |
| 18 | + def test_check_input_type_annotation_invalid_type(self): |
| 19 | + with self.assertRaises(TypeError): |
| 20 | + ConnectorTriggerConverter.check_input_type_annotation(123) # not a type |
| 21 | + |
| 22 | + class Dummy: |
| 23 | + pass |
| 24 | + self.assertFalse(ConnectorTriggerConverter.check_input_type_annotation(Dummy)) |
| 25 | + |
| 26 | + def test_has_implicit_output(self): |
| 27 | + self.assertTrue(ConnectorTriggerConverter.has_implicit_output()) |
| 28 | + |
| 29 | + def test_decode_json(self): |
| 30 | + data = Datum(type='json', value={'foo': 'bar', 'count': 42}) |
| 31 | + result = ConnectorTriggerConverter.decode(data, trigger_metadata={}) |
| 32 | + self.assertEqual(result, {'foo': 'bar', 'count': 42}) |
| 33 | + |
| 34 | + def test_decode_string(self): |
| 35 | + data = Datum(type='string', value='hello connector') |
| 36 | + result = ConnectorTriggerConverter.decode(data, trigger_metadata={}) |
| 37 | + self.assertEqual(result, 'hello connector') |
| 38 | + |
| 39 | + def test_decode_bytes(self): |
| 40 | + data = Datum(type='bytes', value=b'binary data') |
| 41 | + result = ConnectorTriggerConverter.decode(data, trigger_metadata={}) |
| 42 | + self.assertEqual(result, b'binary data') |
| 43 | + |
| 44 | + def test_decode_other_without_python_value(self): |
| 45 | + data = Datum(type='other', value='fallback value') |
| 46 | + result = ConnectorTriggerConverter.decode(data, trigger_metadata={}) |
| 47 | + self.assertEqual(result, 'fallback value') |
| 48 | + |
| 49 | + def test_decode_other_with_python_value(self): |
| 50 | + class MockDatum: |
| 51 | + type = 'custom' |
| 52 | + value = 'original' |
| 53 | + python_value = 'python version' |
| 54 | + |
| 55 | + data = MockDatum() |
| 56 | + result = ConnectorTriggerConverter.decode(data, trigger_metadata={}) |
| 57 | + self.assertEqual(result, 'python version') |
| 58 | + |
| 59 | + def test_encode_none(self): |
| 60 | + result = ConnectorTriggerConverter.encode(None) |
| 61 | + self.assertEqual(result.type, 'string') |
| 62 | + self.assertEqual(result.value, '') |
| 63 | + |
| 64 | + def test_encode_string(self): |
| 65 | + result = ConnectorTriggerConverter.encode('hello connector') |
| 66 | + self.assertEqual(result.type, 'string') |
| 67 | + self.assertEqual(result.value, 'hello connector') |
| 68 | + |
| 69 | + def test_encode_bytes(self): |
| 70 | + result = ConnectorTriggerConverter.encode(b'\x00\x01\x02') |
| 71 | + self.assertEqual(result.type, 'bytes') |
| 72 | + self.assertEqual(result.value, b'\x00\x01\x02') |
| 73 | + |
| 74 | + def test_encode_bytearray(self): |
| 75 | + result = ConnectorTriggerConverter.encode(bytearray(b'\x01\x02\x03')) |
| 76 | + self.assertEqual(result.type, 'bytes') |
| 77 | + self.assertEqual(result.value, b'\x01\x02\x03') |
| 78 | + |
| 79 | + def test_encode_dict(self): |
| 80 | + input_dict = {'status': 'success', 'data': [1, 2, 3]} |
| 81 | + result = ConnectorTriggerConverter.encode(input_dict) |
| 82 | + self.assertEqual(result.type, 'string') |
| 83 | + # Parse the JSON to verify it's correct |
| 84 | + parsed = json.loads(result.value) |
| 85 | + self.assertEqual(parsed, input_dict) |
| 86 | + |
| 87 | + def test_encode_dict_with_nested_data(self): |
| 88 | + input_dict = { |
| 89 | + 'name': 'test', |
| 90 | + 'nested': {'key': 'value'}, |
| 91 | + 'list': [1, 2, 3] |
| 92 | + } |
| 93 | + result = ConnectorTriggerConverter.encode(input_dict) |
| 94 | + self.assertEqual(result.type, 'string') |
| 95 | + parsed = json.loads(result.value) |
| 96 | + self.assertEqual(parsed, input_dict) |
| 97 | + |
| 98 | + def test_encode_other_type(self): |
| 99 | + result = ConnectorTriggerConverter.encode(42) |
| 100 | + self.assertEqual(result.type, 'string') |
| 101 | + self.assertEqual(result.value, '42') |
| 102 | + |
| 103 | + result = ConnectorTriggerConverter.encode(True) |
| 104 | + self.assertEqual(result.type, 'string') |
| 105 | + self.assertEqual(result.value, 'True') |
| 106 | + |
| 107 | + |
| 108 | +class TestConnectorDecoratorIntegration(unittest.TestCase): |
| 109 | + """Integration tests for the connector trigger decorator""" |
| 110 | + |
| 111 | + def test_decorator_creates_function_with_trigger(self): |
| 112 | + app = func.FunctionApp() |
| 113 | + |
| 114 | + @app.connector_trigger(arg_name="payload") |
| 115 | + def connector_function(payload): |
| 116 | + return f"Received: {payload}" |
| 117 | + |
| 118 | + # Get the built function |
| 119 | + funcs = app.get_functions() |
| 120 | + self.assertEqual(len(funcs), 1) |
| 121 | + |
| 122 | + built_func = funcs[0] |
| 123 | + self.assertIsNotNone(built_func.get_trigger()) |
| 124 | + self.assertEqual(built_func.get_trigger().type, 'connectorTrigger') |
| 125 | + |
| 126 | + def test_decorator_with_data_type(self): |
| 127 | + app = func.FunctionApp() |
| 128 | + |
| 129 | + @app.connector_trigger( |
| 130 | + arg_name="context", |
| 131 | + data_type=func.DataType.STRING |
| 132 | + ) |
| 133 | + def connector_with_datatype(context): |
| 134 | + return context |
| 135 | + |
| 136 | + funcs = app.get_functions() |
| 137 | + self.assertEqual(len(funcs), 1) |
| 138 | + |
| 139 | + built_func = funcs[0] |
| 140 | + trigger = built_func.get_trigger() |
| 141 | + self.assertIsNotNone(trigger) |
| 142 | + self.assertEqual(trigger.get_dict_repr()['dataType'], func.DataType.STRING) |
| 143 | + |
| 144 | + def test_decorator_with_kwargs(self): |
| 145 | + app = func.FunctionApp() |
| 146 | + |
| 147 | + @app.connector_trigger( |
| 148 | + arg_name="data", |
| 149 | + custom_field="custom_value", |
| 150 | + another_property=123 |
| 151 | + ) |
| 152 | + def connector_with_kwargs(data): |
| 153 | + return data |
| 154 | + |
| 155 | + funcs = app.get_functions() |
| 156 | + self.assertEqual(len(funcs), 1) |
| 157 | + |
| 158 | + built_func = funcs[0] |
| 159 | + bindings = built_func.get_bindings() |
| 160 | + self.assertEqual(len(bindings), 1) |
| 161 | + |
| 162 | + trigger_dict = bindings[0].get_dict_repr() |
| 163 | + self.assertEqual(trigger_dict['type'], 'connectorTrigger') |
| 164 | + self.assertEqual(trigger_dict['customField'], 'custom_value') |
| 165 | + self.assertEqual(trigger_dict['anotherProperty'], 123) |
0 commit comments