Skip to content

Base extension code coverage improvements #16

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
InConverter,
OutConverter,
get_binding_registry,
check_deferred_bindings_enabled
)
from .sdkType import SdkType
from .web import (
Expand All @@ -29,7 +28,6 @@
'OutConverter',
'SdkType',
'get_binding_registry',
'check_deferred_bindings_enabled',
'ModuleTrackerMeta',
'RequestTrackerMeta',
'ResponseTrackerMeta',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,3 @@ def encode(cls, obj: Any, *,

def get_binding_registry():
return _ConverterMeta


def check_deferred_bindings_enabled(cls, sdk_binding_registry: _ConverterMeta, pytype: type) -> bool:
return (sdk_binding_registry is not None
and _ConverterMeta.check_supported_type(pytype))
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from abc import abstractmethod


class SdkType:
def __init__(self, *, data: dict = None):
self._data = data or {}
Expand Down
183 changes: 180 additions & 3 deletions azure-functions-extension-base/tests/test_meta.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

from typing import Mapping, List
import unittest
from unittest.mock import patch

from azure.functions.extension.base import meta
from azure.functions.extension.base import meta, sdkType


class TestMeta(unittest.TestCase):
Expand Down Expand Up @@ -34,11 +34,15 @@ def test_datum_single_level_python_value(self):
self.assertEqual(datum.python_value, 43.2103)
self.assertEqual(datum.python_type, float)

datum = meta.Datum(value="other", type="other")
self.assertEqual(datum.python_value, "other")

def test_datum_collections_python_value(self):
class DatumCollectionString:
def __init__(self, *args: List[str]):
self.string = args
datum = meta.Datum(value=DatumCollectionString("string 1", "string 2"),
datum = meta.Datum(value=DatumCollectionString("string 1",
"string 2"),
type="collection_string")
self.assertListEqual(datum.python_value, ["string 1", "string 2"])
self.assertEqual(datum.python_type, list)
Expand All @@ -51,6 +55,14 @@ def __init__(self, *args: List[bytes]):
self.assertListEqual(datum.python_value, [b"bytes 1", b"bytes 2"])
self.assertEqual(datum.python_type, list)

class DatumCollectionDouble:
def __init__(self, *args: List[bytes]):
self.double = args
datum = meta.Datum(value=DatumCollectionDouble(43.2103, 45.601),
type="collection_double")
self.assertListEqual(datum.python_value, [43.2103, 45.601])
self.assertEqual(datum.python_type, list)

class DatumCollectionSint64:
def __init__(self, *args: List[int]):
self.sint64 = args
Expand Down Expand Up @@ -105,3 +117,168 @@ def test_datum_json_python_value(self):
"name": "awesome",
"value": "cool"})
self.assertEqual(datum.python_type, dict)

def test_equals(self):
str_datum = meta.Datum(value="awesome string", type="string")
str_datum_copy = meta.Datum(value="awesome string", type="string")
str_datum_wrong_copy = meta.Datum(value="not awesome string",
type="string")
self.assertFalse(str_datum.__eq__(dict))
self.assertTrue(str_datum.__eq__(str_datum_copy))
self.assertFalse(str_datum.__eq__(str_datum_wrong_copy))

def test_hash(self):
str_datum = meta.Datum(value="awesome string", type="string")
datum_hash = str_datum.__hash__()
self.assertIsInstance(datum_hash, int)

def test_repr(self):
str_datum = meta.Datum(value="awesome", type="string")
self.assertEqual(str_datum.__repr__(), "<Datum string 'awesome'>")

long_str_datum = meta.Datum(value="awesome string", type="string")
self.assertEqual(long_str_datum.__repr__(),
"<Datum string 'awesome s...>")

def test_registry(self):
registry = meta.get_binding_registry()
self.assertIsInstance(registry, type(meta._ConverterMeta))
self.assertIsNone(registry.get('test'))

class MockIndexedFunction:
_bindings = {}
_trigger = None

self.assertEqual(registry.get_raw_bindings(MockIndexedFunction, []),
[])

self.assertFalse(registry.check_supported_type(None))
self.assertFalse(registry.check_supported_type("hello"))
self.assertTrue(registry.check_supported_type(sdkType.SdkType))

self.assertFalse(registry.has_trigger_support(MockIndexedFunction))

def test_decode_typed_data(self):
# Case 1: data is None
self.assertIsNone(meta._BaseConverter._decode_typed_data(
data=None, python_type=str))

# Case 2: data.type is model_binding_data
datum_mbd = meta.Datum(value='{}', type='model_binding_data')
self.assertEqual(meta._BaseConverter._decode_typed_data(
datum_mbd, python_type=str), '{}')

# Case 3: data.type is None
datum_none = meta.Datum(value='{}', type=None)
self.assertIsNone(meta._BaseConverter._decode_typed_data(
datum_none, python_type=str))

# Case 4: data.type is unsupported
datum_unsupp = meta.Datum(value='{}', type=dict)
with self.assertRaises(ValueError):
meta._BaseConverter._decode_typed_data(
datum_unsupp, python_type=str)

# Case 5: can't coerce
datum_coerce_fail = meta.Datum(value='{}', type='model_binding_data')
with self.assertRaises(ValueError):
meta._BaseConverter._decode_typed_data(
datum_coerce_fail, python_type=(tuple, list, dict))

# Case 6: attempt coerce & fail
datum_attempt_coerce = meta.Datum(value=1, type='model_binding_data')
with self.assertRaises(ValueError):
meta._BaseConverter._decode_typed_data(
datum_attempt_coerce, python_type=dict)

# Case 7: attempt to coerce and pass
datum_coerce_pass = meta.Datum(value=1, type='model_binding_data')
self.assertEquals(meta._BaseConverter._decode_typed_data(
datum_coerce_pass, python_type=str), '1')

def test_decode_trigger_metadata_field(self):
datum_mbd = meta.Datum(value='{}', type='model_binding_data')
mock_trigger_metadata = {'key': datum_mbd}

self.assertIsNone(meta._BaseConverter._decode_trigger_metadata_field(
trigger_metadata=mock_trigger_metadata,
field="fakeKey",
python_type=str))

self.assertEqual(
meta._BaseConverter._decode_trigger_metadata_field(
trigger_metadata=mock_trigger_metadata,
field="key",
python_type=str),
'{}')

@patch("azure.functions.extension.base.meta."
"InConverter.__abstractmethods__", set())
def test_in_converter(self):
class MockInConverter(meta.InConverter, binding='test1'):
_sample = ''

mock_converter = MockInConverter()
self.assertIsNone(mock_converter.check_input_type_annotation(
pytype=str))

with self.assertRaises(NotImplementedError):
mock_converter.decode(data=None, trigger_metadata={})

self.assertFalse(mock_converter.has_implicit_output())

@patch("azure.functions.extension.base.meta."
"OutConverter.__abstractmethods__", set())
def test_out_converter(self):
class MockOutConverter(meta.OutConverter, binding='test2'):
_sample = ''

mock_converter = MockOutConverter()
mock_converter.check_output_type_annotation(pytype=str)

with self.assertRaises(NotImplementedError):
mock_converter.encode(obj=None, expected_type=None)

def test_get_registry(self):
registry = meta.get_binding_registry()
self.assertEqual(registry, meta._ConverterMeta)

@patch("azure.functions.extension.base.meta."
"OutConverter.__abstractmethods__", set())
def test_converter_meta(self):
class BindingNoneConverter(meta.OutConverter, binding=None):
_sample = ''

registry = meta.get_binding_registry()
self.assertEqual(len(registry._bindings), 0)

class BindingBlobConverter(meta.OutConverter, binding='blob'):
_sample = ''

registry = meta.get_binding_registry()
self.assertEqual(len(registry._bindings), 1)
self.assertIsNotNone(registry._bindings.get('blob'))
self.assertEqual(registry._bindings.get('blob'), BindingBlobConverter)

with self.assertRaises(RuntimeError):
class BindingBlob2Converter(meta.OutConverter, binding='blob'):
_sample = ''

registry = meta.get_binding_registry()
self.assertEqual(len(registry._bindings), 1)
self.assertIsNotNone(registry._bindings.get('blob'))
self.assertEqual(registry._bindings.get('blob'), BindingBlobConverter)

class BindingServiceBusConverter(meta.OutConverter,
binding='serviceBus',
trigger='serviceBusTrigger'):
_sample = ''

registry = meta.get_binding_registry()
self.assertEqual(len(registry._bindings), 3)
self.assertIsNotNone(registry._bindings.get('serviceBus'))
self.assertEqual(registry._bindings.get('serviceBus'),
BindingServiceBusConverter)
self.assertIsNotNone(registry._bindings.get('serviceBusTrigger'))
self.assertEqual(registry._bindings.get('serviceBusTrigger'),
BindingServiceBusConverter)
22 changes: 22 additions & 0 deletions azure-functions-extension-base/tests/test_sdk_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
import unittest

from azure.functions.extension.base import sdkType


class TestSdkType(unittest.TestCase):

def test_init(self):
data_populated = sdkType.SdkType(data={'key': 'value'})
self.assertEqual(data_populated._data, {'key': 'value'})

data_empty = sdkType.SdkType()
self.assertEqual(data_empty._data, {})

def test_get_sdk_type(self):
class MockSdkType(sdkType.SdkType):
_sample = ''

mock_type = MockSdkType()
self.assertIsNone(mock_type.get_sdk_type())
Loading