From 7cb33b9f695408ad6c4b24ec2c44377c53a9cb35 Mon Sep 17 00:00:00 2001 From: robcxyz Date: Wed, 14 Jun 2023 22:52:27 +0700 Subject: [PATCH] chore: add function types tests #161 --- tests/functions/test_functions_types.py | 119 ++++++++++++++++++ .../functions/types-fixtures/base-embed.yaml | 27 ++++ tests/functions/types-fixtures/base.yaml | 21 ++++ tests/functions/types-fixtures/dict-base.yaml | 19 +++ .../functions/types-fixtures/enum-basic.yaml | 27 ++++ .../functions/types-fixtures/enum-types.yaml | 27 ++++ tests/functions/types-fixtures/list-base.yaml | 18 +++ 7 files changed, 258 insertions(+) create mode 100644 tests/functions/test_functions_types.py create mode 100644 tests/functions/types-fixtures/base-embed.yaml create mode 100644 tests/functions/types-fixtures/base.yaml create mode 100644 tests/functions/types-fixtures/dict-base.yaml create mode 100644 tests/functions/types-fixtures/enum-basic.yaml create mode 100644 tests/functions/types-fixtures/enum-types.yaml create mode 100644 tests/functions/types-fixtures/list-base.yaml diff --git a/tests/functions/test_functions_types.py b/tests/functions/test_functions_types.py new file mode 100644 index 000000000..84f7224d3 --- /dev/null +++ b/tests/functions/test_functions_types.py @@ -0,0 +1,119 @@ +import pytest + +from tackle import tackle +from tackle import exceptions +from tackle.parser import parse_function_type +from tackle.models import Context + + +@pytest.fixture() +def context_fixture(change_dir): + return Context( + hook_dirs=['.hooks'], + calling_file='', + private_hooks={}, + ) + + +BASE_ID = "pydantic.main.Base" +TYPE_FIXTURES = [ + ('Base', f""), + ('list[str]', 'list[str]'), + ('list[str, int]', 'list[str, int]'), + ('list[Base]', f'list[{BASE_ID}]'), + ('List[Base]', f'typing.List[{BASE_ID}]'), + ('dict[str,Base]', f'dict[str, {BASE_ID}]'), + ('Dict[str, Base]', f'typing.Dict[str, {BASE_ID}]'), + ('optional[Base]', f'typing.Optional[{BASE_ID}]'), + ('Optional[Base]', f'typing.Optional[{BASE_ID}]'), + ('union[Base, str]', f'typing.Union[{BASE_ID}, str]'), + ('Union[Base, str]', f'typing.Union[{BASE_ID}, str]'), + ('Optional[Union[Base, str]]', f'typing.Union[{BASE_ID}, str, NoneType]'), + ('list[Base, dict[str, list]]', f'list[{BASE_ID}, dict[str, list]]'), + ('list[dict[str, Base], Base]', f'list[dict[str, {BASE_ID}], {BASE_ID}]'), + ('list[Base, dict[str, Base]]', f'list[{BASE_ID}, dict[str, {BASE_ID}]]'), +] + + +@pytest.mark.parametrize("type_str,expected_repr", TYPE_FIXTURES) +def test_functions_types_parse_function_type(type_str, expected_repr, context_fixture): + """ + Check complex type parsing where a `Base` hook is imported from the current + directory's '.hooks' dir with various typing around it. + """ + output = parse_function_type( + context=context_fixture, + type_str=type_str, + func_name="foo", + ) + assert repr(output) == expected_repr + + +BAD_TYPE_FIXTURES = [ + 'NotBase', + 'Str', + 'list[Str]', + 'list[NotBase]', + 'Optional[Base, str]', +] + + +@pytest.mark.parametrize("type_str", BAD_TYPE_FIXTURES) +def test_functions_types_malformed_field_exceptions(type_str, context_fixture): + """Check that an error is thrown for bad types.""" + with pytest.raises(exceptions.MalformedFunctionFieldException): + parse_function_type( + context=context_fixture, + type_str=type_str, + func_name="foo", + ) + + +@pytest.fixture() +def types_fixtures(chdir): + chdir('types-fixtures') + + +def test_functions_types_base(types_fixtures): + """Check that we can compose a single hook type.""" + output = tackle('base.yaml') + assert output['check_ok']['bar']['foo'] == 'baz' + assert output['check_false'] == 1 + + +def test_functions_types_base_embed(types_fixtures): + """Check that we can compose embedded hook types.""" + output = tackle('base-embed.yaml') + assert output['check_ok']['bar']['foo2']['foo1'] == 'baz' + assert output['check_false'] == 1 + + +def test_functions_types_list_base(types_fixtures): + """Validate a list with hooks.""" + output = tackle('list-base.yaml') + assert output['check_ok']['bar'][0]['foo'] == 'baz' + assert output['check_ok']['bar'][1]['foo'] == 'baz2' + assert output['check_false'] == 1 + + +def test_functions_types_dict_base(types_fixtures): + """Validate a dict with hooks.""" + output = tackle('dict-base.yaml') + assert output['check_ok']['bar']['bar']['foo'] == 'baz' + assert output['check_false'] == 1 + + +def test_functions_composition_enum_basic(types_fixtures): + """ + Show that we can create enum types, that they fail when we give them some value + out of its members, and that it is properly deserialized. + """ + output = tackle('enum-basic.yaml') + + assert output['failure'] + assert output['failure_default'] + + assert output['success']['color'] == 'blue' + assert output['success']['color_default'] == 'red' + assert output['success_default']['color'] == 'blue' + assert output['success_default']['color_default'] == 'green' diff --git a/tests/functions/types-fixtures/base-embed.yaml b/tests/functions/types-fixtures/base-embed.yaml new file mode 100644 index 000000000..2eecbb746 --- /dev/null +++ b/tests/functions/types-fixtures/base-embed.yaml @@ -0,0 +1,27 @@ +base1<-: + foo1: bar + +base2<-: + foo2: + type: base1 + +obj<-: + stuff: things + bar: + type: base2 + +check_ok: + ->: obj + bar: + foo2: + foo1: baz + +thing: + foo2: + foo1: baz +check_ok_render->: obj --bar {{thing}} + +check_false: + ->: obj --try --except 1 + bar: + not foo: baz diff --git a/tests/functions/types-fixtures/base.yaml b/tests/functions/types-fixtures/base.yaml new file mode 100644 index 000000000..ac8f74945 --- /dev/null +++ b/tests/functions/types-fixtures/base.yaml @@ -0,0 +1,21 @@ +base<-: + foo: bar + +obj<-: + stuff: things + bar: + type: base + +check_ok: + ->: obj + bar: + foo: baz + +thing: + foo: baz +check_ok_render->: obj --bar {{thing}} + +check_false: + ->: obj --try --except 1 + bar: + not foo: baz diff --git a/tests/functions/types-fixtures/dict-base.yaml b/tests/functions/types-fixtures/dict-base.yaml new file mode 100644 index 000000000..817810640 --- /dev/null +++ b/tests/functions/types-fixtures/dict-base.yaml @@ -0,0 +1,19 @@ +base<-: + foo: bar + +obj<-: + stuff: things + bar: + type: dict[str, base] + +check_ok: + ->: obj + bar: + bar: + foo: baz + +check_false: + ->: obj --try --except 1 + bar: + bar: + not foo: baz diff --git a/tests/functions/types-fixtures/enum-basic.yaml b/tests/functions/types-fixtures/enum-basic.yaml new file mode 100644 index 000000000..661e35a61 --- /dev/null +++ b/tests/functions/types-fixtures/enum-basic.yaml @@ -0,0 +1,27 @@ + + +Schema<-: + foo: + type: str + default: bar + color: + type: string + enum: + - red + - green + - blue + + color_default: + type: string + default: red + enum: + - red + - green + - blue + + +success->: Schema --color blue +success_default->: Schema --color blue --color_default green + +failure->: Schema --color grey --try --except true +failure_default->: Schema --color blue --color_default grey --try --except true diff --git a/tests/functions/types-fixtures/enum-types.yaml b/tests/functions/types-fixtures/enum-types.yaml new file mode 100644 index 000000000..661e35a61 --- /dev/null +++ b/tests/functions/types-fixtures/enum-types.yaml @@ -0,0 +1,27 @@ + + +Schema<-: + foo: + type: str + default: bar + color: + type: string + enum: + - red + - green + - blue + + color_default: + type: string + default: red + enum: + - red + - green + - blue + + +success->: Schema --color blue +success_default->: Schema --color blue --color_default green + +failure->: Schema --color grey --try --except true +failure_default->: Schema --color blue --color_default grey --try --except true diff --git a/tests/functions/types-fixtures/list-base.yaml b/tests/functions/types-fixtures/list-base.yaml new file mode 100644 index 000000000..3a77a8284 --- /dev/null +++ b/tests/functions/types-fixtures/list-base.yaml @@ -0,0 +1,18 @@ +base<-: + foo: bar + +obj<-: + stuff: things + bar: + type: list[base] + +check_ok: + ->: obj + bar: + - foo: baz + - foo: baz2 + +check_false: + ->: obj --try --except 1 + bar: + - not foo: baz