From 2710192fb7a6cfab39f41e2a681f5300cbb3efc9 Mon Sep 17 00:00:00 2001 From: Adrien Vannson Date: Fri, 20 Dec 2024 18:16:08 +0100 Subject: [PATCH 1/4] Run tests in workflow --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a1858ea..a7906f74 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,6 +56,10 @@ jobs: shell: bash run: poetry install + - name: Run tests + shell: bash + run: poetry run poe test + - name: Generate code from proto files shell: bash run: poetry run python -m tests.generate -v From 928e4f0ee8a0a5c908f8d88f2e63aa5c82f8023e Mon Sep 17 00:00:00 2001 From: Adrien Vannson Date: Fri, 20 Dec 2024 18:16:43 +0100 Subject: [PATCH 2/4] Add back missing tests --- tests/test_module_validation.py | 110 ++++++++++++++++++++++++++++++++ tests/test_typing_compiler.py | 70 ++++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 tests/test_module_validation.py create mode 100644 tests/test_typing_compiler.py diff --git a/tests/test_module_validation.py b/tests/test_module_validation.py new file mode 100644 index 00000000..390b8c75 --- /dev/null +++ b/tests/test_module_validation.py @@ -0,0 +1,110 @@ +from typing import ( + List, + Optional, + Set, +) + +import pytest +from betterproto2.plugin.module_validation import ModuleValidator + + +@pytest.mark.parametrize( + ["text", "expected_collisions"], + [ + pytest.param( + ["import os"], + None, + id="single import", + ), + pytest.param( + ["import os", "import sys"], + None, + id="multiple imports", + ), + pytest.param( + ["import os", "import os"], + {"os"}, + id="duplicate imports", + ), + pytest.param( + ["from os import path", "import os"], + None, + id="duplicate imports with alias", + ), + pytest.param( + ["from os import path", "import os as os_alias"], + None, + id="duplicate imports with alias", + ), + pytest.param( + ["from os import path", "import os as path"], + {"path"}, + id="duplicate imports with alias", + ), + pytest.param( + ["import os", "class os:"], + {"os"}, + id="duplicate import with class", + ), + pytest.param( + ["import os", "class os:", " pass", "import sys"], + {"os"}, + id="duplicate import with class and another", + ), + pytest.param( + ["def test(): pass", "class test:"], + {"test"}, + id="duplicate class and function", + ), + pytest.param( + ["def test(): pass", "def test(): pass"], + {"test"}, + id="duplicate functions", + ), + pytest.param( + ["def test(): pass", "test = 100"], + {"test"}, + id="function and variable", + ), + pytest.param( + ["def test():", " test = 3"], + None, + id="function and variable in function", + ), + pytest.param( + [ + "def test(): pass", + "'''", + "def test(): pass", + "'''", + "def test_2(): pass", + ], + None, + id="duplicate functions with multiline string", + ), + pytest.param( + ["def test(): pass", "# def test(): pass"], + None, + id="duplicate functions with comments", + ), + pytest.param( + ["from test import (", " A", " B", " C", ")"], + None, + id="multiline import", + ), + pytest.param( + ["from test import (", " A", " B", " C", ")", "from test import A"], + {"A"}, + id="multiline import with duplicate", + ), + ], +) +def test_module_validator(text: List[str], expected_collisions: Optional[Set[str]]): + line_iterator = iter(text) + validator = ModuleValidator(line_iterator) + valid = validator.validate() + if expected_collisions is None: + assert valid + else: + assert set(validator.collisions.keys()) == expected_collisions + assert not valid diff --git a/tests/test_typing_compiler.py b/tests/test_typing_compiler.py new file mode 100644 index 00000000..e9157f40 --- /dev/null +++ b/tests/test_typing_compiler.py @@ -0,0 +1,70 @@ +from betterproto2.plugin.typing_compiler import ( + DirectImportTypingCompiler, + NoTyping310TypingCompiler, + TypingImportTypingCompiler, +) + + +def test_direct_import_typing_compiler(): + compiler = DirectImportTypingCompiler() + assert compiler.imports() == {} + assert compiler.optional("str") == "Optional[str]" + assert compiler.imports() == {"typing": {"Optional"}} + assert compiler.list("str") == "List[str]" + assert compiler.imports() == {"typing": {"Optional", "List"}} + assert compiler.dict("str", "int") == "Dict[str, int]" + assert compiler.imports() == {"typing": {"Optional", "List", "Dict"}} + assert compiler.union("str", "int") == "Union[str, int]" + assert compiler.imports() == {"typing": {"Optional", "List", "Dict", "Union"}} + assert compiler.iterable("str") == "Iterable[str]" + assert compiler.imports() == {"typing": {"Optional", "List", "Dict", "Union", "Iterable"}} + assert compiler.async_iterable("str") == "AsyncIterable[str]" + assert compiler.imports() == {"typing": {"Optional", "List", "Dict", "Union", "Iterable", "AsyncIterable"}} + assert compiler.async_iterator("str") == "AsyncIterator[str]" + assert compiler.imports() == { + "typing": { + "Optional", + "List", + "Dict", + "Union", + "Iterable", + "AsyncIterable", + "AsyncIterator", + } + } + + +def test_typing_import_typing_compiler(): + compiler = TypingImportTypingCompiler() + assert compiler.imports() == {} + assert compiler.optional("str") == "typing.Optional[str]" + assert compiler.imports() == {"typing": None} + assert compiler.list("str") == "typing.List[str]" + assert compiler.imports() == {"typing": None} + assert compiler.dict("str", "int") == "typing.Dict[str, int]" + assert compiler.imports() == {"typing": None} + assert compiler.union("str", "int") == "typing.Union[str, int]" + assert compiler.imports() == {"typing": None} + assert compiler.iterable("str") == "typing.Iterable[str]" + assert compiler.imports() == {"typing": None} + assert compiler.async_iterable("str") == "typing.AsyncIterable[str]" + assert compiler.imports() == {"typing": None} + assert compiler.async_iterator("str") == "typing.AsyncIterator[str]" + assert compiler.imports() == {"typing": None} + + +def test_no_typing_311_typing_compiler(): + compiler = NoTyping310TypingCompiler() + assert compiler.imports() == {} + assert compiler.optional("str") == "str | None" + assert compiler.imports() == {} + assert compiler.list("str") == "list[str]" + assert compiler.imports() == {} + assert compiler.dict("str", "int") == "dict[str, int]" + assert compiler.imports() == {} + assert compiler.union("str", "int") == "str | int" + assert compiler.imports() == {} + assert compiler.iterable("str") == "Iterable[str]" + assert compiler.async_iterable("str") == "AsyncIterable[str]" + assert compiler.async_iterator("str") == "AsyncIterator[str]" + assert compiler.imports() == {"collections.abc": {"Iterable", "AsyncIterable", "AsyncIterator"}} From 74aa17011b323928f7843702c3344dde24880259 Mon Sep 17 00:00:00 2001 From: Adrien Vannson Date: Fri, 20 Dec 2024 18:17:07 +0100 Subject: [PATCH 3/4] Delete lib tests --- tests/inputs/bool/bool.json | 3 - tests/inputs/bool/test_bool.py | 24 ---- tests/inputs/bytes/bytes.json | 3 - tests/inputs/casing/casing.json | 4 - tests/inputs/casing/test_casing.py | 17 --- .../test_casing_inner_class.py | 10 -- .../casing_message_field_uppercase.py | 8 -- tests/inputs/config.py | 29 ----- tests/inputs/deprecated/deprecated.json | 6 - tests/inputs/double/double-negative.json | 3 - tests/inputs/double/double.json | 3 - .../inputs/empty_repeated/empty_repeated.json | 3 - tests/inputs/enum/enum.json | 9 -- tests/inputs/enum/test_enum.py | 107 ------------------ .../example_service/test_example_service.py | 81 ------------- .../field_name_identical_to_type.json | 7 -- tests/inputs/fixed/fixed.json | 6 - tests/inputs/float/float.json | 9 -- .../test_google_impl_behavior_equivalence.py | 84 -------------- .../googletypes/googletypes-missing.json | 1 - tests/inputs/googletypes/googletypes.json | 7 -- .../test_googletypes_request.py | 46 -------- .../test_googletypes_response.py | 63 ----------- .../test_googletypes_response_embedded.py | 40 ------- .../googletypes_struct.json | 5 - .../googletypes_value/googletypes_value.json | 11 -- .../test_import_service_input_message.py | 36 ------ tests/inputs/int32/int32.json | 4 - .../invalid_field/test_invalid_field.py | 17 --- tests/inputs/map/map.json | 7 -- tests/inputs/mapmessage/mapmessage.json | 10 -- .../namespace_builtin_types.json | 16 --- .../namespace_keywords.json | 37 ------ tests/inputs/nested/nested.json | 7 -- tests/inputs/nestedtwice/nestedtwice.json | 11 -- tests/inputs/nestedtwice/test_nestedtwice.py | 25 ---- tests/inputs/oneof/oneof-name.json | 3 - tests/inputs/oneof/oneof.json | 3 - tests/inputs/oneof/oneof_name.json | 3 - tests/inputs/oneof/test_oneof.py | 43 ------- .../test_oneof_default_value_serialization.py | 70 ------------ tests/inputs/oneof_empty/oneof_empty.json | 3 - .../oneof_empty/oneof_empty_maybe1.json | 3 - .../oneof_empty/oneof_empty_maybe2.json | 5 - tests/inputs/oneof_empty/test_oneof_empty.py | 0 .../inputs/oneof_enum/oneof_enum-enum-0.json | 3 - .../inputs/oneof_enum/oneof_enum-enum-1.json | 3 - tests/inputs/oneof_enum/oneof_enum.json | 6 - tests/inputs/oneof_enum/test_oneof_enum.py | 40 ------- .../proto3_field_presence.json | 13 --- .../proto3_field_presence_default.json | 1 - .../proto3_field_presence_missing.json | 9 -- .../test_proto3_field_presence.py | 46 -------- .../proto3_field_presence_oneof.json | 3 - .../test_proto3_field_presence_oneof.py | 27 ----- .../recursivemessage/recursivemessage.json | 12 -- tests/inputs/ref/ref.json | 5 - .../regression_387/test_regression_387.py | 12 -- .../regression_414/test_regression_414.py | 15 --- tests/inputs/repeated/repeated.json | 3 - .../repeated_duration_timestamp.json | 4 - .../test_repeated_duration_timestamp.py | 12 -- .../repeatedmessage/repeatedmessage.json | 10 -- .../inputs/repeatedpacked/repeatedpacked.json | 5 - .../test_rpc_empty_input_message.py | 24 ---- .../inputs/service_uppercase/test_service.py | 8 -- tests/inputs/signed/signed.json | 6 - .../test_timestamp_dict_encode.py | 78 ------------- .../timestamp_dict_encode.json | 3 - 69 files changed, 1240 deletions(-) delete mode 100644 tests/inputs/bool/bool.json delete mode 100644 tests/inputs/bool/test_bool.py delete mode 100644 tests/inputs/bytes/bytes.json delete mode 100644 tests/inputs/casing/casing.json delete mode 100644 tests/inputs/casing/test_casing.py delete mode 100644 tests/inputs/casing_inner_class/test_casing_inner_class.py delete mode 100644 tests/inputs/casing_message_field_uppercase/casing_message_field_uppercase.py delete mode 100644 tests/inputs/config.py delete mode 100644 tests/inputs/deprecated/deprecated.json delete mode 100644 tests/inputs/double/double-negative.json delete mode 100644 tests/inputs/double/double.json delete mode 100644 tests/inputs/empty_repeated/empty_repeated.json delete mode 100644 tests/inputs/enum/enum.json delete mode 100644 tests/inputs/enum/test_enum.py delete mode 100644 tests/inputs/example_service/test_example_service.py delete mode 100644 tests/inputs/field_name_identical_to_type/field_name_identical_to_type.json delete mode 100644 tests/inputs/fixed/fixed.json delete mode 100644 tests/inputs/float/float.json delete mode 100644 tests/inputs/google_impl_behavior_equivalence/test_google_impl_behavior_equivalence.py delete mode 100644 tests/inputs/googletypes/googletypes-missing.json delete mode 100644 tests/inputs/googletypes/googletypes.json delete mode 100644 tests/inputs/googletypes_request/test_googletypes_request.py delete mode 100644 tests/inputs/googletypes_response/test_googletypes_response.py delete mode 100644 tests/inputs/googletypes_response_embedded/test_googletypes_response_embedded.py delete mode 100644 tests/inputs/googletypes_struct/googletypes_struct.json delete mode 100644 tests/inputs/googletypes_value/googletypes_value.json delete mode 100644 tests/inputs/import_service_input_message/test_import_service_input_message.py delete mode 100644 tests/inputs/int32/int32.json delete mode 100644 tests/inputs/invalid_field/test_invalid_field.py delete mode 100644 tests/inputs/map/map.json delete mode 100644 tests/inputs/mapmessage/mapmessage.json delete mode 100644 tests/inputs/namespace_builtin_types/namespace_builtin_types.json delete mode 100644 tests/inputs/namespace_keywords/namespace_keywords.json delete mode 100644 tests/inputs/nested/nested.json delete mode 100644 tests/inputs/nestedtwice/nestedtwice.json delete mode 100644 tests/inputs/nestedtwice/test_nestedtwice.py delete mode 100644 tests/inputs/oneof/oneof-name.json delete mode 100644 tests/inputs/oneof/oneof.json delete mode 100644 tests/inputs/oneof/oneof_name.json delete mode 100644 tests/inputs/oneof/test_oneof.py delete mode 100644 tests/inputs/oneof_default_value_serialization/test_oneof_default_value_serialization.py delete mode 100644 tests/inputs/oneof_empty/oneof_empty.json delete mode 100644 tests/inputs/oneof_empty/oneof_empty_maybe1.json delete mode 100644 tests/inputs/oneof_empty/oneof_empty_maybe2.json delete mode 100644 tests/inputs/oneof_empty/test_oneof_empty.py delete mode 100644 tests/inputs/oneof_enum/oneof_enum-enum-0.json delete mode 100644 tests/inputs/oneof_enum/oneof_enum-enum-1.json delete mode 100644 tests/inputs/oneof_enum/oneof_enum.json delete mode 100644 tests/inputs/oneof_enum/test_oneof_enum.py delete mode 100644 tests/inputs/proto3_field_presence/proto3_field_presence.json delete mode 100644 tests/inputs/proto3_field_presence/proto3_field_presence_default.json delete mode 100644 tests/inputs/proto3_field_presence/proto3_field_presence_missing.json delete mode 100644 tests/inputs/proto3_field_presence/test_proto3_field_presence.py delete mode 100644 tests/inputs/proto3_field_presence_oneof/proto3_field_presence_oneof.json delete mode 100644 tests/inputs/proto3_field_presence_oneof/test_proto3_field_presence_oneof.py delete mode 100644 tests/inputs/recursivemessage/recursivemessage.json delete mode 100644 tests/inputs/ref/ref.json delete mode 100644 tests/inputs/regression_387/test_regression_387.py delete mode 100644 tests/inputs/regression_414/test_regression_414.py delete mode 100644 tests/inputs/repeated/repeated.json delete mode 100644 tests/inputs/repeated_duration_timestamp/repeated_duration_timestamp.json delete mode 100644 tests/inputs/repeated_duration_timestamp/test_repeated_duration_timestamp.py delete mode 100644 tests/inputs/repeatedmessage/repeatedmessage.json delete mode 100644 tests/inputs/repeatedpacked/repeatedpacked.json delete mode 100644 tests/inputs/rpc_empty_input_message/test_rpc_empty_input_message.py delete mode 100644 tests/inputs/service_uppercase/test_service.py delete mode 100644 tests/inputs/signed/signed.json delete mode 100644 tests/inputs/timestamp_dict_encode/test_timestamp_dict_encode.py delete mode 100644 tests/inputs/timestamp_dict_encode/timestamp_dict_encode.json diff --git a/tests/inputs/bool/bool.json b/tests/inputs/bool/bool.json deleted file mode 100644 index 348e0319..00000000 --- a/tests/inputs/bool/bool.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "value": true -} diff --git a/tests/inputs/bool/test_bool.py b/tests/inputs/bool/test_bool.py deleted file mode 100644 index 6b0ad0be..00000000 --- a/tests/inputs/bool/test_bool.py +++ /dev/null @@ -1,24 +0,0 @@ -import pytest - -from tests.output_betterproto.bool import Test -from tests.output_betterproto_pydantic.bool import Test as TestPyd - - -def test_value(): - message = Test() - assert not message.value, "Boolean is False by default" - - -def test_pydantic_no_value(): - message = TestPyd() - assert not message.value, "Boolean is False by default" - - -def test_pydantic_value(): - message = TestPyd(value=False) - assert not message.value - - -def test_pydantic_bad_value(): - with pytest.raises(ValueError): - TestPyd(value=123) diff --git a/tests/inputs/bytes/bytes.json b/tests/inputs/bytes/bytes.json deleted file mode 100644 index 34c4554c..00000000 --- a/tests/inputs/bytes/bytes.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "data": "SGVsbG8sIFdvcmxkIQ==" -} diff --git a/tests/inputs/casing/casing.json b/tests/inputs/casing/casing.json deleted file mode 100644 index 559104b1..00000000 --- a/tests/inputs/casing/casing.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "camelCase": 1, - "snakeCase": "ONE" -} diff --git a/tests/inputs/casing/test_casing.py b/tests/inputs/casing/test_casing.py deleted file mode 100644 index feee009a..00000000 --- a/tests/inputs/casing/test_casing.py +++ /dev/null @@ -1,17 +0,0 @@ -import tests.output_betterproto.casing as casing -from tests.output_betterproto.casing import Test - - -def test_message_attributes(): - message = Test() - assert hasattr(message, "snake_case_message"), "snake_case field name is same in python" - assert hasattr(message, "camel_case"), "CamelCase field is snake_case in python" - assert hasattr(message, "uppercase"), "UPPERCASE field is lowercase in python" - - -def test_message_casing(): - assert hasattr(casing, "SnakeCaseMessage"), "snake_case Message name is converted to CamelCase in python" - - -def test_enum_casing(): - assert hasattr(casing, "MyEnum"), "snake_case Enum name is converted to CamelCase in python" diff --git a/tests/inputs/casing_inner_class/test_casing_inner_class.py b/tests/inputs/casing_inner_class/test_casing_inner_class.py deleted file mode 100644 index 2560b6c2..00000000 --- a/tests/inputs/casing_inner_class/test_casing_inner_class.py +++ /dev/null @@ -1,10 +0,0 @@ -import tests.output_betterproto.casing_inner_class as casing_inner_class - - -def test_message_casing_inner_class_name(): - assert hasattr(casing_inner_class, "TestInnerClass"), "Inline defined Message is correctly converted to CamelCase" - - -def test_message_casing_inner_class_attributes(): - message = casing_inner_class.Test(inner=casing_inner_class.TestInnerClass()) - assert hasattr(message.inner, "old_exp"), "Inline defined Message attribute is snake_case" diff --git a/tests/inputs/casing_message_field_uppercase/casing_message_field_uppercase.py b/tests/inputs/casing_message_field_uppercase/casing_message_field_uppercase.py deleted file mode 100644 index 6dc69256..00000000 --- a/tests/inputs/casing_message_field_uppercase/casing_message_field_uppercase.py +++ /dev/null @@ -1,8 +0,0 @@ -from tests.output_betterproto.casing_message_field_uppercase import Test - - -def test_message_casing(): - message = Test() - assert hasattr(message, "uppercase"), "UPPERCASE attribute is converted to 'uppercase' in python" - assert hasattr(message, "uppercase_v2"), "UPPERCASE_V2 attribute is converted to 'uppercase_v2' in python" - assert hasattr(message, "upper_camel_case"), "UPPER_CAMEL_CASE attribute is converted to upper_camel_case in python" diff --git a/tests/inputs/config.py b/tests/inputs/config.py deleted file mode 100644 index 4fb1565f..00000000 --- a/tests/inputs/config.py +++ /dev/null @@ -1,29 +0,0 @@ -# Test cases that are expected to fail, e.g. unimplemented features or bug-fixes. -# Remove from list when fixed. -xfail = { - "namespace_keywords", # 70 - "googletypes_struct", # 9 - "googletypes_value", # 9 - "example", # This is the example in the readme. Not a test. -} - -services = { - "googletypes_request", - "googletypes_response", - "googletypes_response_embedded", - "service", - "service_separate_packages", - "import_service_input_message", - "googletypes_service_returns_empty", - "googletypes_service_returns_googletype", - "example_service", - "empty_service", - "service_uppercase", -} - - -# Indicate json sample messages to skip when testing that json (de)serialization -# is symmetrical becuase some cases legitimately are not symmetrical. -# Each key references the name of the test scenario and the values in the tuple -# Are the names of the json files. -non_symmetrical_json = {"empty_repeated": ("empty_repeated",)} diff --git a/tests/inputs/deprecated/deprecated.json b/tests/inputs/deprecated/deprecated.json deleted file mode 100644 index 43b2b65a..00000000 --- a/tests/inputs/deprecated/deprecated.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "message": { - "value": "hello" - }, - "value": 10 -} diff --git a/tests/inputs/double/double-negative.json b/tests/inputs/double/double-negative.json deleted file mode 100644 index e0776c73..00000000 --- a/tests/inputs/double/double-negative.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "count": -123.45 -} diff --git a/tests/inputs/double/double.json b/tests/inputs/double/double.json deleted file mode 100644 index 321412e5..00000000 --- a/tests/inputs/double/double.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "count": 123.45 -} diff --git a/tests/inputs/empty_repeated/empty_repeated.json b/tests/inputs/empty_repeated/empty_repeated.json deleted file mode 100644 index 12a801c6..00000000 --- a/tests/inputs/empty_repeated/empty_repeated.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "msg": [{"values":[]}] -} diff --git a/tests/inputs/enum/enum.json b/tests/inputs/enum/enum.json deleted file mode 100644 index d68f1c50..00000000 --- a/tests/inputs/enum/enum.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "choice": "FOUR", - "choices": [ - "ZERO", - "ONE", - "THREE", - "FOUR" - ] -} diff --git a/tests/inputs/enum/test_enum.py b/tests/inputs/enum/test_enum.py deleted file mode 100644 index 20c9a4d5..00000000 --- a/tests/inputs/enum/test_enum.py +++ /dev/null @@ -1,107 +0,0 @@ -from tests.output_betterproto.enum import ( - ArithmeticOperator, - Choice, - Test, -) - - -def test_enum_set_and_get(): - assert Test(choice=Choice.ZERO).choice == Choice.ZERO - assert Test(choice=Choice.ONE).choice == Choice.ONE - assert Test(choice=Choice.THREE).choice == Choice.THREE - assert Test(choice=Choice.FOUR).choice == Choice.FOUR - - -def test_enum_set_with_int(): - assert Test(choice=0).choice == Choice.ZERO - assert Test(choice=1).choice == Choice.ONE - assert Test(choice=3).choice == Choice.THREE - assert Test(choice=4).choice == Choice.FOUR - - -def test_enum_is_comparable_with_int(): - assert Test(choice=Choice.ZERO).choice == 0 - assert Test(choice=Choice.ONE).choice == 1 - assert Test(choice=Choice.THREE).choice == 3 - assert Test(choice=Choice.FOUR).choice == 4 - - -def test_enum_to_dict(): - assert "choice" not in Test(choice=Choice.ZERO).to_dict(), "Default enum value is not serialized" - assert Test(choice=Choice.ZERO).to_dict(include_default_values=True)["choice"] == "ZERO" - assert Test(choice=Choice.ONE).to_dict()["choice"] == "ONE" - assert Test(choice=Choice.THREE).to_dict()["choice"] == "THREE" - assert Test(choice=Choice.FOUR).to_dict()["choice"] == "FOUR" - - -def test_repeated_enum_is_comparable_with_int(): - assert Test(choices=[Choice.ZERO]).choices == [0] - assert Test(choices=[Choice.ONE]).choices == [1] - assert Test(choices=[Choice.THREE]).choices == [3] - assert Test(choices=[Choice.FOUR]).choices == [4] - - -def test_repeated_enum_set_and_get(): - assert Test(choices=[Choice.ZERO]).choices == [Choice.ZERO] - assert Test(choices=[Choice.ONE]).choices == [Choice.ONE] - assert Test(choices=[Choice.THREE]).choices == [Choice.THREE] - assert Test(choices=[Choice.FOUR]).choices == [Choice.FOUR] - - -def test_repeated_enum_to_dict(): - assert Test(choices=[Choice.ZERO]).to_dict()["choices"] == ["ZERO"] - assert Test(choices=[Choice.ONE]).to_dict()["choices"] == ["ONE"] - assert Test(choices=[Choice.THREE]).to_dict()["choices"] == ["THREE"] - assert Test(choices=[Choice.FOUR]).to_dict()["choices"] == ["FOUR"] - - all_enums_dict = Test(choices=[Choice.ZERO, Choice.ONE, Choice.THREE, Choice.FOUR]).to_dict() - assert (all_enums_dict["choices"]) == ["ZERO", "ONE", "THREE", "FOUR"] - - -def test_repeated_enum_with_single_value_to_dict(): - assert Test(choices=Choice.ONE).to_dict()["choices"] == ["ONE"] - assert Test(choices=1).to_dict()["choices"] == ["ONE"] - - -def test_repeated_enum_with_non_list_iterables_to_dict(): - assert Test(choices=(1, 3)).to_dict()["choices"] == ["ONE", "THREE"] - assert Test(choices=(1, 3)).to_dict()["choices"] == ["ONE", "THREE"] - assert Test(choices=(Choice.ONE, Choice.THREE)).to_dict()["choices"] == [ - "ONE", - "THREE", - ] - - def enum_generator(): - yield Choice.ONE - yield Choice.THREE - - assert Test(choices=enum_generator()).to_dict()["choices"] == ["ONE", "THREE"] - - -def test_enum_mapped_on_parse(): - # test default value - b = Test().parse(bytes(Test())) - assert b.choice.name == Choice.ZERO.name - assert b.choices == [] - - # test non default value - a = Test().parse(bytes(Test(choice=Choice.ONE))) - assert a.choice.name == Choice.ONE.name - assert b.choices == [] - - # test repeated - c = Test().parse(bytes(Test(choices=[Choice.THREE, Choice.FOUR]))) - assert c.choices[0].name == Choice.THREE.name - assert c.choices[1].name == Choice.FOUR.name - - # bonus: defaults after empty init are also mapped - assert Test().choice.name == Choice.ZERO.name - - -def test_renamed_enum_members(): - assert set(ArithmeticOperator.__members__) == { - "NONE", - "PLUS", - "MINUS", - "_0_PREFIXED", - } diff --git a/tests/inputs/example_service/test_example_service.py b/tests/inputs/example_service/test_example_service.py deleted file mode 100644 index cd2cc40f..00000000 --- a/tests/inputs/example_service/test_example_service.py +++ /dev/null @@ -1,81 +0,0 @@ -from typing import ( - AsyncIterator, -) - -import pytest -from grpclib.testing import ChannelFor - -from tests.output_betterproto.example_service import ( - ExampleRequest, - ExampleResponse, - TestBase, - TestStub, -) - - -class ExampleService(TestBase): - async def example_unary_unary(self, example_request: ExampleRequest) -> "ExampleResponse": - return ExampleResponse( - example_string=example_request.example_string, - example_integer=example_request.example_integer, - ) - - async def example_unary_stream(self, example_request: ExampleRequest) -> AsyncIterator["ExampleResponse"]: - response = ExampleResponse( - example_string=example_request.example_string, - example_integer=example_request.example_integer, - ) - yield response - yield response - yield response - - async def example_stream_unary( - self, example_request_iterator: AsyncIterator["ExampleRequest"] - ) -> "ExampleResponse": - async for example_request in example_request_iterator: - return ExampleResponse( - example_string=example_request.example_string, - example_integer=example_request.example_integer, - ) - - async def example_stream_stream( - self, example_request_iterator: AsyncIterator["ExampleRequest"] - ) -> AsyncIterator["ExampleResponse"]: - async for example_request in example_request_iterator: - yield ExampleResponse( - example_string=example_request.example_string, - example_integer=example_request.example_integer, - ) - - -@pytest.mark.asyncio -async def test_calls_with_different_cardinalities(): - example_request = ExampleRequest("test string", 42) - - async with ChannelFor([ExampleService()]) as channel: - stub = TestStub(channel) - - # unary unary - response = await stub.example_unary_unary(example_request) - assert response.example_string == example_request.example_string - assert response.example_integer == example_request.example_integer - - # unary stream - async for response in stub.example_unary_stream(example_request): - assert response.example_string == example_request.example_string - assert response.example_integer == example_request.example_integer - - # stream unary - async def request_iterator(): - yield example_request - yield example_request - yield example_request - - response = await stub.example_stream_unary(request_iterator()) - assert response.example_string == example_request.example_string - assert response.example_integer == example_request.example_integer - - # stream stream - async for response in stub.example_stream_stream(request_iterator()): - assert response.example_string == example_request.example_string - assert response.example_integer == example_request.example_integer diff --git a/tests/inputs/field_name_identical_to_type/field_name_identical_to_type.json b/tests/inputs/field_name_identical_to_type/field_name_identical_to_type.json deleted file mode 100644 index 7a6e7ae8..00000000 --- a/tests/inputs/field_name_identical_to_type/field_name_identical_to_type.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "int": 26, - "float": 26.0, - "str": "value-for-str", - "bytes": "001a", - "bool": true -} \ No newline at end of file diff --git a/tests/inputs/fixed/fixed.json b/tests/inputs/fixed/fixed.json deleted file mode 100644 index 88587806..00000000 --- a/tests/inputs/fixed/fixed.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "foo": 4294967295, - "bar": -2147483648, - "baz": "18446744073709551615", - "qux": "-9223372036854775808" -} diff --git a/tests/inputs/float/float.json b/tests/inputs/float/float.json deleted file mode 100644 index 3adac974..00000000 --- a/tests/inputs/float/float.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "positive": "Infinity", - "negative": "-Infinity", - "nan": "NaN", - "three": 3.0, - "threePointOneFour": 3.14, - "negThree": -3.0, - "negThreePointOneFour": -3.14 - } diff --git a/tests/inputs/google_impl_behavior_equivalence/test_google_impl_behavior_equivalence.py b/tests/inputs/google_impl_behavior_equivalence/test_google_impl_behavior_equivalence.py deleted file mode 100644 index b6ed5e0f..00000000 --- a/tests/inputs/google_impl_behavior_equivalence/test_google_impl_behavior_equivalence.py +++ /dev/null @@ -1,84 +0,0 @@ -from datetime import ( - datetime, - timezone, -) - -import pytest -from google.protobuf import json_format -from google.protobuf.timestamp_pb2 import Timestamp - -from tests.output_betterproto.google_impl_behavior_equivalence import ( - Empty, - Foo, - Request, - Spam, - Test, -) -from tests.output_reference.google_impl_behavior_equivalence.google_impl_behavior_equivalence_pb2 import ( - Empty as ReferenceEmpty, - Foo as ReferenceFoo, - Request as ReferenceRequest, - Spam as ReferenceSpam, - Test as ReferenceTest, -) - - -def test_oneof_serializes_similar_to_google_oneof(): - tests = [ - (Test(string="abc"), ReferenceTest(string="abc")), - (Test(integer=2), ReferenceTest(integer=2)), - (Test(foo=Foo(bar=1)), ReferenceTest(foo=ReferenceFoo(bar=1))), - # Default values should also behave the same within oneofs - (Test(string=""), ReferenceTest(string="")), - (Test(integer=0), ReferenceTest(integer=0)), - (Test(foo=Foo(bar=0)), ReferenceTest(foo=ReferenceFoo(bar=0))), - ] - for message, message_reference in tests: - # NOTE: As of July 2020, MessageToJson inserts newlines in the output string so, - # just compare dicts - assert message.to_dict() == json_format.MessageToDict(message_reference) - - -def test_bytes_are_the_same_for_oneof(): - message = Test(string="") - message_reference = ReferenceTest(string="") - - message_bytes = bytes(message) - message_reference_bytes = message_reference.SerializeToString() - - assert message_bytes == message_reference_bytes - - message2 = Test().parse(message_reference_bytes) - message_reference2 = ReferenceTest() - message_reference2.ParseFromString(message_reference_bytes) - - assert message == message2 - assert message_reference == message_reference2 - - # None of these fields were explicitly set BUT they should not actually be null - # themselves - assert message.foo is None - assert message2.foo is None - - assert isinstance(message_reference.foo, ReferenceFoo) - assert isinstance(message_reference2.foo, ReferenceFoo) - - -@pytest.mark.parametrize("dt", (datetime.min.replace(tzinfo=timezone.utc),)) -def test_datetime_clamping(dt): # see #407 - ts = Timestamp() - ts.FromDatetime(dt) - assert bytes(Spam(dt)) == ReferenceSpam(ts=ts).SerializeToString() - message_bytes = bytes(Spam(dt)) - - assert Spam().parse(message_bytes).ts.timestamp() == ReferenceSpam.FromString(message_bytes).ts.seconds - - -def test_empty_message_field(): - message = Request() - reference_message = ReferenceRequest() - - message.foo = Empty() - reference_message.foo.CopyFrom(ReferenceEmpty()) - - assert bytes(message) == reference_message.SerializeToString() diff --git a/tests/inputs/googletypes/googletypes-missing.json b/tests/inputs/googletypes/googletypes-missing.json deleted file mode 100644 index 0967ef42..00000000 --- a/tests/inputs/googletypes/googletypes-missing.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/tests/inputs/googletypes/googletypes.json b/tests/inputs/googletypes/googletypes.json deleted file mode 100644 index 0a002e9b..00000000 --- a/tests/inputs/googletypes/googletypes.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "maybe": false, - "ts": "1972-01-01T10:00:20.021Z", - "duration": "1.200s", - "important": 10, - "empty": {} -} diff --git a/tests/inputs/googletypes_request/test_googletypes_request.py b/tests/inputs/googletypes_request/test_googletypes_request.py deleted file mode 100644 index 05573cc3..00000000 --- a/tests/inputs/googletypes_request/test_googletypes_request.py +++ /dev/null @@ -1,46 +0,0 @@ -from datetime import ( - datetime, - timedelta, -) -from typing import ( - Any, - Callable, -) - -import betterproto.lib.google.protobuf as protobuf -import pytest - -from tests.mocks import MockChannel -from tests.output_betterproto.googletypes_request import ( - Input, - TestStub, -) - -test_cases = [ - (TestStub.send_double, protobuf.DoubleValue, 2.5), - (TestStub.send_float, protobuf.FloatValue, 2.5), - (TestStub.send_int64, protobuf.Int64Value, -64), - (TestStub.send_u_int64, protobuf.UInt64Value, 64), - (TestStub.send_int32, protobuf.Int32Value, -32), - (TestStub.send_u_int32, protobuf.UInt32Value, 32), - (TestStub.send_bool, protobuf.BoolValue, True), - (TestStub.send_string, protobuf.StringValue, "string"), - (TestStub.send_bytes, protobuf.BytesValue, bytes(0xFF)[0:4]), - (TestStub.send_datetime, protobuf.Timestamp, datetime(2038, 1, 19, 3, 14, 8)), - (TestStub.send_timedelta, protobuf.Duration, timedelta(seconds=123456)), -] - - -@pytest.mark.asyncio -@pytest.mark.parametrize(["service_method", "wrapper_class", "value"], test_cases) -async def test_channel_receives_wrapped_type( - service_method: Callable[[TestStub, Input], Any], wrapper_class: Callable, value -): - wrapped_value = wrapper_class() - wrapped_value.value = value - channel = MockChannel(responses=[Input()]) - service = TestStub(channel) - - await service_method(service, wrapped_value) - - assert channel.requests[0]["request"] == type(wrapped_value) diff --git a/tests/inputs/googletypes_response/test_googletypes_response.py b/tests/inputs/googletypes_response/test_googletypes_response.py deleted file mode 100644 index 75f6accd..00000000 --- a/tests/inputs/googletypes_response/test_googletypes_response.py +++ /dev/null @@ -1,63 +0,0 @@ -from typing import ( - Any, - Callable, - Optional, -) - -import betterproto.lib.google.protobuf as protobuf -import pytest - -from tests.mocks import MockChannel -from tests.output_betterproto.googletypes_response import ( - Input, - TestStub, -) - -test_cases = [ - (TestStub.get_double, protobuf.DoubleValue, 2.5), - (TestStub.get_float, protobuf.FloatValue, 2.5), - (TestStub.get_int64, protobuf.Int64Value, -64), - (TestStub.get_u_int64, protobuf.UInt64Value, 64), - (TestStub.get_int32, protobuf.Int32Value, -32), - (TestStub.get_u_int32, protobuf.UInt32Value, 32), - (TestStub.get_bool, protobuf.BoolValue, True), - (TestStub.get_string, protobuf.StringValue, "string"), - (TestStub.get_bytes, protobuf.BytesValue, bytes(0xFF)[0:4]), -] - - -@pytest.mark.asyncio -@pytest.mark.parametrize(["service_method", "wrapper_class", "value"], test_cases) -async def test_channel_receives_wrapped_type( - service_method: Callable[[TestStub, Input], Any], wrapper_class: Callable, value -): - wrapped_value = wrapper_class() - wrapped_value.value = value - channel = MockChannel(responses=[wrapped_value]) - service = TestStub(channel) - method_param = Input() - - await service_method(service, method_param) - - assert channel.requests[0]["response_type"] != Optional[type(value)] - assert channel.requests[0]["response_type"] == type(wrapped_value) - - -@pytest.mark.asyncio -@pytest.mark.xfail -@pytest.mark.parametrize(["service_method", "wrapper_class", "value"], test_cases) -async def test_service_unwraps_response( - service_method: Callable[[TestStub, Input], Any], wrapper_class: Callable, value -): - """ - grpclib does not unwrap wrapper values returned by services - """ - wrapped_value = wrapper_class() - wrapped_value.value = value - service = TestStub(MockChannel(responses=[wrapped_value])) - method_param = Input() - - response_value = await service_method(service, method_param) - - assert response_value == value - assert type(response_value) == type(value) diff --git a/tests/inputs/googletypes_response_embedded/test_googletypes_response_embedded.py b/tests/inputs/googletypes_response_embedded/test_googletypes_response_embedded.py deleted file mode 100644 index 57ebce1b..00000000 --- a/tests/inputs/googletypes_response_embedded/test_googletypes_response_embedded.py +++ /dev/null @@ -1,40 +0,0 @@ -import pytest - -from tests.mocks import MockChannel -from tests.output_betterproto.googletypes_response_embedded import ( - Input, - Output, - TestStub, -) - - -@pytest.mark.asyncio -async def test_service_passes_through_unwrapped_values_embedded_in_response(): - """ - We do not not need to implement value unwrapping for embedded well-known types, - as this is already handled by grpclib. This test merely shows that this is the case. - """ - output = Output( - double_value=10.0, - float_value=12.0, - int64_value=-13, - uint64_value=14, - int32_value=-15, - uint32_value=16, - bool_value=True, - string_value="string", - bytes_value=bytes(0xFF)[0:4], - ) - - service = TestStub(MockChannel(responses=[output])) - response = await service.get_output(Input()) - - assert response.double_value == 10.0 - assert response.float_value == 12.0 - assert response.int64_value == -13 - assert response.uint64_value == 14 - assert response.int32_value == -15 - assert response.uint32_value == 16 - assert response.bool_value - assert response.string_value == "string" - assert response.bytes_value == bytes(0xFF)[0:4] diff --git a/tests/inputs/googletypes_struct/googletypes_struct.json b/tests/inputs/googletypes_struct/googletypes_struct.json deleted file mode 100644 index ecc175e0..00000000 --- a/tests/inputs/googletypes_struct/googletypes_struct.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "struct": { - "key": true - } -} diff --git a/tests/inputs/googletypes_value/googletypes_value.json b/tests/inputs/googletypes_value/googletypes_value.json deleted file mode 100644 index db52d5c0..00000000 --- a/tests/inputs/googletypes_value/googletypes_value.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "value1": "hello world", - "value2": true, - "value3": 1, - "value4": null, - "value5": [ - 1, - 2, - 3 - ] -} diff --git a/tests/inputs/import_service_input_message/test_import_service_input_message.py b/tests/inputs/import_service_input_message/test_import_service_input_message.py deleted file mode 100644 index 60c3e202..00000000 --- a/tests/inputs/import_service_input_message/test_import_service_input_message.py +++ /dev/null @@ -1,36 +0,0 @@ -import pytest - -from tests.mocks import MockChannel -from tests.output_betterproto.import_service_input_message import ( - NestedRequestMessage, - RequestMessage, - RequestResponse, - TestStub, -) -from tests.output_betterproto.import_service_input_message.child import ( - ChildRequestMessage, -) - - -@pytest.mark.asyncio -async def test_service_correctly_imports_reference_message(): - mock_response = RequestResponse(value=10) - service = TestStub(MockChannel([mock_response])) - response = await service.do_thing(RequestMessage(1)) - assert mock_response == response - - -@pytest.mark.asyncio -async def test_service_correctly_imports_reference_message_from_child_package(): - mock_response = RequestResponse(value=10) - service = TestStub(MockChannel([mock_response])) - response = await service.do_thing2(ChildRequestMessage(1)) - assert mock_response == response - - -@pytest.mark.asyncio -async def test_service_correctly_imports_nested_reference(): - mock_response = RequestResponse(value=10) - service = TestStub(MockChannel([mock_response])) - response = await service.do_thing3(NestedRequestMessage(1)) - assert mock_response == response diff --git a/tests/inputs/int32/int32.json b/tests/inputs/int32/int32.json deleted file mode 100644 index 34d41119..00000000 --- a/tests/inputs/int32/int32.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "positive": 150, - "negative": -150 -} diff --git a/tests/inputs/invalid_field/test_invalid_field.py b/tests/inputs/invalid_field/test_invalid_field.py deleted file mode 100644 index 947b8e13..00000000 --- a/tests/inputs/invalid_field/test_invalid_field.py +++ /dev/null @@ -1,17 +0,0 @@ -import pytest - - -def test_invalid_field(): - from tests.output_betterproto.invalid_field import Test - - with pytest.raises(TypeError): - Test(unknown_field=12) - - -def test_invalid_field_pydantic(): - from pydantic import ValidationError - - from tests.output_betterproto_pydantic.invalid_field import Test - - with pytest.raises(ValidationError): - Test(unknown_field=12) diff --git a/tests/inputs/map/map.json b/tests/inputs/map/map.json deleted file mode 100644 index 6a1e853b..00000000 --- a/tests/inputs/map/map.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "counts": { - "item1": 1, - "item2": 2, - "item3": 3 - } -} diff --git a/tests/inputs/mapmessage/mapmessage.json b/tests/inputs/mapmessage/mapmessage.json deleted file mode 100644 index a944ddd1..00000000 --- a/tests/inputs/mapmessage/mapmessage.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "items": { - "foo": { - "count": 1 - }, - "bar": { - "count": 2 - } - } -} diff --git a/tests/inputs/namespace_builtin_types/namespace_builtin_types.json b/tests/inputs/namespace_builtin_types/namespace_builtin_types.json deleted file mode 100644 index 82000323..00000000 --- a/tests/inputs/namespace_builtin_types/namespace_builtin_types.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "int": "value-for-int", - "float": "value-for-float", - "complex": "value-for-complex", - "list": "value-for-list", - "tuple": "value-for-tuple", - "range": "value-for-range", - "str": "value-for-str", - "bytearray": "value-for-bytearray", - "bytes": "value-for-bytes", - "memoryview": "value-for-memoryview", - "set": "value-for-set", - "frozenset": "value-for-frozenset", - "map": "value-for-map", - "bool": "value-for-bool" -} \ No newline at end of file diff --git a/tests/inputs/namespace_keywords/namespace_keywords.json b/tests/inputs/namespace_keywords/namespace_keywords.json deleted file mode 100644 index 4f11b602..00000000 --- a/tests/inputs/namespace_keywords/namespace_keywords.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "False": 1, - "None": 2, - "True": 3, - "and": 4, - "as": 5, - "assert": 6, - "async": 7, - "await": 8, - "break": 9, - "class": 10, - "continue": 11, - "def": 12, - "del": 13, - "elif": 14, - "else": 15, - "except": 16, - "finally": 17, - "for": 18, - "from": 19, - "global": 20, - "if": 21, - "import": 22, - "in": 23, - "is": 24, - "lambda": 25, - "nonlocal": 26, - "not": 27, - "or": 28, - "pass": 29, - "raise": 30, - "return": 31, - "try": 32, - "while": 33, - "with": 34, - "yield": 35 -} diff --git a/tests/inputs/nested/nested.json b/tests/inputs/nested/nested.json deleted file mode 100644 index f460cadb..00000000 --- a/tests/inputs/nested/nested.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "nested": { - "count": 150 - }, - "sibling": {}, - "msg": "THIS" -} diff --git a/tests/inputs/nestedtwice/nestedtwice.json b/tests/inputs/nestedtwice/nestedtwice.json deleted file mode 100644 index c9531328..00000000 --- a/tests/inputs/nestedtwice/nestedtwice.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "top": { - "name": "double-nested", - "middle": { - "bottom": [{"foo": "hello"}], - "enumBottom": ["A"], - "topMiddleBottom": [{"a": "hello"}], - "bar": true - } - } -} diff --git a/tests/inputs/nestedtwice/test_nestedtwice.py b/tests/inputs/nestedtwice/test_nestedtwice.py deleted file mode 100644 index ca0557a7..00000000 --- a/tests/inputs/nestedtwice/test_nestedtwice.py +++ /dev/null @@ -1,25 +0,0 @@ -import pytest - -from tests.output_betterproto.nestedtwice import ( - Test, - TestTop, - TestTopMiddle, - TestTopMiddleBottom, - TestTopMiddleEnumBottom, - TestTopMiddleTopMiddleBottom, -) - - -@pytest.mark.parametrize( - ("cls", "expected_comment"), - [ - (Test, "Test doc."), - (TestTopMiddleEnumBottom, "EnumBottom doc."), - (TestTop, "Top doc."), - (TestTopMiddle, "Middle doc."), - (TestTopMiddleTopMiddleBottom, "TopMiddleBottom doc."), - (TestTopMiddleBottom, "Bottom doc."), - ], -) -def test_comment(cls, expected_comment): - assert cls.__doc__.strip() == expected_comment diff --git a/tests/inputs/oneof/oneof-name.json b/tests/inputs/oneof/oneof-name.json deleted file mode 100644 index 605484b6..00000000 --- a/tests/inputs/oneof/oneof-name.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pitier": "Mr. T" -} diff --git a/tests/inputs/oneof/oneof.json b/tests/inputs/oneof/oneof.json deleted file mode 100644 index 65cafc5f..00000000 --- a/tests/inputs/oneof/oneof.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pitied": 100 -} diff --git a/tests/inputs/oneof/oneof_name.json b/tests/inputs/oneof/oneof_name.json deleted file mode 100644 index 605484b6..00000000 --- a/tests/inputs/oneof/oneof_name.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "pitier": "Mr. T" -} diff --git a/tests/inputs/oneof/test_oneof.py b/tests/inputs/oneof/test_oneof.py deleted file mode 100644 index a9d675c9..00000000 --- a/tests/inputs/oneof/test_oneof.py +++ /dev/null @@ -1,43 +0,0 @@ -import betterproto -import pytest - -from tests.output_betterproto.oneof import ( - MixedDrink, - Test, -) -from tests.output_betterproto_pydantic.oneof import Test as TestPyd -from tests.util import get_test_case_json_data - - -def test_which_count(): - message = Test() - message.from_json(get_test_case_json_data("oneof")[0].json) - assert betterproto.which_one_of(message, "foo") == ("pitied", 100) - - -def test_which_name(): - message = Test() - message.from_json(get_test_case_json_data("oneof", "oneof_name.json")[0].json) - assert betterproto.which_one_of(message, "foo") == ("pitier", "Mr. T") - - -def test_which_count_pyd(): - message = TestPyd(pitier="Mr. T", just_a_regular_field=2, bar_name="a_bar") - assert betterproto.which_one_of(message, "foo") == ("pitier", "Mr. T") - - -def test_oneof_constructor_assign(): - message = Test(mixed_drink=MixedDrink(shots=42)) - field, value = betterproto.which_one_of(message, "bar") - assert field == "mixed_drink" - assert value.shots == 42 - - -# Issue #305: -@pytest.mark.xfail -def test_oneof_nested_assign(): - message = Test() - message.mixed_drink.shots = 42 - field, value = betterproto.which_one_of(message, "bar") - assert field == "mixed_drink" - assert value.shots == 42 diff --git a/tests/inputs/oneof_default_value_serialization/test_oneof_default_value_serialization.py b/tests/inputs/oneof_default_value_serialization/test_oneof_default_value_serialization.py deleted file mode 100644 index 4053478e..00000000 --- a/tests/inputs/oneof_default_value_serialization/test_oneof_default_value_serialization.py +++ /dev/null @@ -1,70 +0,0 @@ -import datetime - -import betterproto - -from tests.output_betterproto.oneof_default_value_serialization import ( - Message, - NestedMessage, - Test, -) - - -def assert_round_trip_serialization_works(message: Test) -> None: - assert betterproto.which_one_of(message, "value_type") == betterproto.which_one_of( - Test().from_json(message.to_json()), "value_type" - ) - - -def test_oneof_default_value_serialization_works_for_all_values(): - """ - Serialization from message with oneof set to default -> JSON -> message should keep - default value field intact. - """ - - test_cases = [ - Test(bool_value=False), - Test(int64_value=0), - Test( - timestamp_value=datetime.datetime( - year=1970, - month=1, - day=1, - hour=0, - minute=0, - tzinfo=datetime.timezone.utc, - ) - ), - Test(duration_value=datetime.timedelta(0)), - Test(wrapped_message_value=Message(value=0)), - # NOTE: Do NOT use betterproto.BoolValue here, it will cause JSON serialization - # errors. - # TODO: Do we want to allow use of BoolValue directly within a wrapped field or - # should we simply hard fail here? - Test(wrapped_bool_value=False), - ] - for message in test_cases: - assert_round_trip_serialization_works(message) - - -def test_oneof_no_default_values_passed(): - message = Test() - assert ( - betterproto.which_one_of(message, "value_type") - == betterproto.which_one_of(Test().from_json(message.to_json()), "value_type") - == ("", None) - ) - - -def test_oneof_nested_oneof_messages_are_serialized_with_defaults(): - """ - Nested messages with oneofs should also be handled - """ - message = Test(wrapped_nested_message_value=NestedMessage(id=0, wrapped_message_value=Message(value=0))) - assert ( - betterproto.which_one_of(message, "value_type") - == betterproto.which_one_of(Test().from_json(message.to_json()), "value_type") - == ( - "wrapped_nested_message_value", - NestedMessage(id=0, wrapped_message_value=Message(value=0)), - ) - ) diff --git a/tests/inputs/oneof_empty/oneof_empty.json b/tests/inputs/oneof_empty/oneof_empty.json deleted file mode 100644 index 9d21c897..00000000 --- a/tests/inputs/oneof_empty/oneof_empty.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "nothing": {} -} diff --git a/tests/inputs/oneof_empty/oneof_empty_maybe1.json b/tests/inputs/oneof_empty/oneof_empty_maybe1.json deleted file mode 100644 index f7a2d278..00000000 --- a/tests/inputs/oneof_empty/oneof_empty_maybe1.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "maybe1": {} -} diff --git a/tests/inputs/oneof_empty/oneof_empty_maybe2.json b/tests/inputs/oneof_empty/oneof_empty_maybe2.json deleted file mode 100644 index bc2b385b..00000000 --- a/tests/inputs/oneof_empty/oneof_empty_maybe2.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "maybe2": { - "sometimes": "now" - } -} diff --git a/tests/inputs/oneof_empty/test_oneof_empty.py b/tests/inputs/oneof_empty/test_oneof_empty.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/inputs/oneof_enum/oneof_enum-enum-0.json b/tests/inputs/oneof_enum/oneof_enum-enum-0.json deleted file mode 100644 index be30cf08..00000000 --- a/tests/inputs/oneof_enum/oneof_enum-enum-0.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "signal": "PASS" -} diff --git a/tests/inputs/oneof_enum/oneof_enum-enum-1.json b/tests/inputs/oneof_enum/oneof_enum-enum-1.json deleted file mode 100644 index cb638737..00000000 --- a/tests/inputs/oneof_enum/oneof_enum-enum-1.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "signal": "RESIGN" -} diff --git a/tests/inputs/oneof_enum/oneof_enum.json b/tests/inputs/oneof_enum/oneof_enum.json deleted file mode 100644 index 3220b706..00000000 --- a/tests/inputs/oneof_enum/oneof_enum.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "move": { - "x": 2, - "y": 3 - } -} diff --git a/tests/inputs/oneof_enum/test_oneof_enum.py b/tests/inputs/oneof_enum/test_oneof_enum.py deleted file mode 100644 index 375ea196..00000000 --- a/tests/inputs/oneof_enum/test_oneof_enum.py +++ /dev/null @@ -1,40 +0,0 @@ -import betterproto - -from tests.output_betterproto.oneof_enum import ( - Move, - Signal, - Test, -) -from tests.util import get_test_case_json_data - - -def test_which_one_of_returns_enum_with_default_value(): - """ - returns first field when it is enum and set with default value - """ - message = Test() - message.from_json(get_test_case_json_data("oneof_enum", "oneof_enum-enum-0.json")[0].json) - - assert message.move is None - assert message.signal == Signal.PASS - assert betterproto.which_one_of(message, "action") == ("signal", Signal.PASS) - - -def test_which_one_of_returns_enum_with_non_default_value(): - """ - returns first field when it is enum and set with non default value - """ - message = Test() - message.from_json(get_test_case_json_data("oneof_enum", "oneof_enum-enum-1.json")[0].json) - - assert message.move is None - assert message.signal == Signal.RESIGN - assert betterproto.which_one_of(message, "action") == ("signal", Signal.RESIGN) - - -def test_which_one_of_returns_second_field_when_set(): - message = Test() - message.from_json(get_test_case_json_data("oneof_enum")[0].json) - assert message.move == Move(x=2, y=3) - assert message.signal is None - assert betterproto.which_one_of(message, "action") == ("move", Move(x=2, y=3)) diff --git a/tests/inputs/proto3_field_presence/proto3_field_presence.json b/tests/inputs/proto3_field_presence/proto3_field_presence.json deleted file mode 100644 index 988df8e8..00000000 --- a/tests/inputs/proto3_field_presence/proto3_field_presence.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "test1": 128, - "test2": true, - "test3": "A value", - "test4": "aGVsbG8=", - "test5": { - "test": "Hello" - }, - "test6": "B", - "test7": "8589934592", - "test8": 2.5, - "test9": "2022-01-24T12:12:42Z" -} diff --git a/tests/inputs/proto3_field_presence/proto3_field_presence_default.json b/tests/inputs/proto3_field_presence/proto3_field_presence_default.json deleted file mode 100644 index 0967ef42..00000000 --- a/tests/inputs/proto3_field_presence/proto3_field_presence_default.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/tests/inputs/proto3_field_presence/proto3_field_presence_missing.json b/tests/inputs/proto3_field_presence/proto3_field_presence_missing.json deleted file mode 100644 index b19ae980..00000000 --- a/tests/inputs/proto3_field_presence/proto3_field_presence_missing.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "test1": 0, - "test2": false, - "test3": "", - "test4": "", - "test6": "A", - "test7": "0", - "test8": 0 -} diff --git a/tests/inputs/proto3_field_presence/test_proto3_field_presence.py b/tests/inputs/proto3_field_presence/test_proto3_field_presence.py deleted file mode 100644 index 9c2d6e69..00000000 --- a/tests/inputs/proto3_field_presence/test_proto3_field_presence.py +++ /dev/null @@ -1,46 +0,0 @@ -import json - -from tests.output_betterproto.proto3_field_presence import ( - Test, -) - - -def test_null_fields_json(): - """Ensure that using "null" in JSON is equivalent to not specifying a - field, for fields with explicit presence""" - - def test_json(ref_json: str, obj_json: str) -> None: - """`ref_json` and `obj_json` are JSON strings describing a `Test` object. - Test that deserializing both leads to the same object, and that - `ref_json` is the normalized format.""" - ref_obj = Test().from_json(ref_json) - obj = Test().from_json(obj_json) - - assert obj == ref_obj - assert json.loads(obj.to_json(0)) == json.loads(ref_json) - - test_json("{}", '{ "test1": null, "test2": null, "test3": null }') - test_json("{}", '{ "test4": null, "test5": null, "test6": null }') - test_json("{}", '{ "test7": null, "test8": null }') - test_json('{ "test5": {} }', '{ "test3": null, "test5": {} }') - - # Make sure that if include_default_values is set, None values are - # exported. - obj = Test() - assert obj.to_dict() == {} - assert obj.to_dict(include_default_values=True) == { - "test1": None, - "test2": None, - "test3": None, - "test4": None, - "test5": None, - "test6": None, - "test7": None, - "test8": None, - "test9": None, - } - - -def test_unset_access(): # see #523 - assert Test().test1 is None - assert Test(test1=None).test1 is None diff --git a/tests/inputs/proto3_field_presence_oneof/proto3_field_presence_oneof.json b/tests/inputs/proto3_field_presence_oneof/proto3_field_presence_oneof.json deleted file mode 100644 index da081927..00000000 --- a/tests/inputs/proto3_field_presence_oneof/proto3_field_presence_oneof.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "nested": {} -} diff --git a/tests/inputs/proto3_field_presence_oneof/test_proto3_field_presence_oneof.py b/tests/inputs/proto3_field_presence_oneof/test_proto3_field_presence_oneof.py deleted file mode 100644 index 2320dc64..00000000 --- a/tests/inputs/proto3_field_presence_oneof/test_proto3_field_presence_oneof.py +++ /dev/null @@ -1,27 +0,0 @@ -from tests.output_betterproto.proto3_field_presence_oneof import ( - Nested, - Test, - WithOptional, -) - - -def test_serialization(): - """Ensure that serialization of fields unset but with explicit field - presence do not bloat the serialized payload with length-delimited fields - with length 0""" - - def test_empty_nested(message: Test) -> None: - # '0a' => tag 1, length delimited - # '00' => length: 0 - assert bytes(message) == bytearray.fromhex("0a 00") - - test_empty_nested(Test(nested=Nested())) - test_empty_nested(Test(nested=Nested(inner=None))) - - def test_empty_with_optional(message: Test) -> None: - # '12' => tag 2, length delimited - # '00' => length: 0 - assert bytes(message) == bytearray.fromhex("12 00") - - test_empty_with_optional(Test(with_optional=WithOptional())) - test_empty_with_optional(Test(with_optional=WithOptional(b=None))) diff --git a/tests/inputs/recursivemessage/recursivemessage.json b/tests/inputs/recursivemessage/recursivemessage.json deleted file mode 100644 index e92c3fbf..00000000 --- a/tests/inputs/recursivemessage/recursivemessage.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "Zues", - "child": { - "name": "Hercules" - }, - "intermediate": { - "child": { - "name": "Douglas Adams" - }, - "number": 42 - } -} diff --git a/tests/inputs/ref/ref.json b/tests/inputs/ref/ref.json deleted file mode 100644 index 2c6bdc10..00000000 --- a/tests/inputs/ref/ref.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "greeting": { - "greeting": "hello" - } -} diff --git a/tests/inputs/regression_387/test_regression_387.py b/tests/inputs/regression_387/test_regression_387.py deleted file mode 100644 index 7bb40b2e..00000000 --- a/tests/inputs/regression_387/test_regression_387.py +++ /dev/null @@ -1,12 +0,0 @@ -from tests.output_betterproto.regression_387 import ( - ParentElement, - Test, -) - - -def test_regression_387(): - el = ParentElement(name="test", elems=[Test(id=0), Test(id=42)]) - binary = bytes(el) - decoded = ParentElement().parse(binary) - assert decoded == el - assert decoded.elems == [Test(id=0), Test(id=42)] diff --git a/tests/inputs/regression_414/test_regression_414.py b/tests/inputs/regression_414/test_regression_414.py deleted file mode 100644 index 742c97b4..00000000 --- a/tests/inputs/regression_414/test_regression_414.py +++ /dev/null @@ -1,15 +0,0 @@ -from tests.output_betterproto.regression_414 import Test - - -def test_full_cycle(): - body = bytes([0, 1]) - auth = bytes([2, 3]) - sig = [b""] - - obj = Test(body=body, auth=auth, signatures=sig) - - decoded = Test().parse(bytes(obj)) - assert decoded == obj - assert decoded.body == body - assert decoded.auth == auth - assert decoded.signatures == sig diff --git a/tests/inputs/repeated/repeated.json b/tests/inputs/repeated/repeated.json deleted file mode 100644 index b8a7c4eb..00000000 --- a/tests/inputs/repeated/repeated.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "names": ["one", "two", "three"] -} diff --git a/tests/inputs/repeated_duration_timestamp/repeated_duration_timestamp.json b/tests/inputs/repeated_duration_timestamp/repeated_duration_timestamp.json deleted file mode 100644 index 6ce7b34c..00000000 --- a/tests/inputs/repeated_duration_timestamp/repeated_duration_timestamp.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "times": ["1972-01-01T10:00:20.021Z", "1972-01-01T10:00:20.021Z"], - "durations": ["1.200s", "1.200s"] -} diff --git a/tests/inputs/repeated_duration_timestamp/test_repeated_duration_timestamp.py b/tests/inputs/repeated_duration_timestamp/test_repeated_duration_timestamp.py deleted file mode 100644 index efc34866..00000000 --- a/tests/inputs/repeated_duration_timestamp/test_repeated_duration_timestamp.py +++ /dev/null @@ -1,12 +0,0 @@ -from datetime import ( - datetime, - timedelta, -) - -from tests.output_betterproto.repeated_duration_timestamp import Test - - -def test_roundtrip(): - message = Test() - message.times = [datetime.now(), datetime.now()] - message.durations = [timedelta(), timedelta()] diff --git a/tests/inputs/repeatedmessage/repeatedmessage.json b/tests/inputs/repeatedmessage/repeatedmessage.json deleted file mode 100644 index 90ec5967..00000000 --- a/tests/inputs/repeatedmessage/repeatedmessage.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "greetings": [ - { - "greeting": "hello" - }, - { - "greeting": "hi" - } - ] -} diff --git a/tests/inputs/repeatedpacked/repeatedpacked.json b/tests/inputs/repeatedpacked/repeatedpacked.json deleted file mode 100644 index 106fd908..00000000 --- a/tests/inputs/repeatedpacked/repeatedpacked.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "counts": [1, 2, -1, -2], - "signed": ["1", "2", "-1", "-2"], - "fixed": [1.0, 2.7, 3.4] -} diff --git a/tests/inputs/rpc_empty_input_message/test_rpc_empty_input_message.py b/tests/inputs/rpc_empty_input_message/test_rpc_empty_input_message.py deleted file mode 100644 index f77578f6..00000000 --- a/tests/inputs/rpc_empty_input_message/test_rpc_empty_input_message.py +++ /dev/null @@ -1,24 +0,0 @@ -import pytest -from grpclib.testing import ChannelFor - - -@pytest.mark.asyncio -async def test_rpc_input_message(): - from tests.output_betterproto.rpc_empty_input_message import ( - Response, - ServiceBase, - ServiceStub, - Test, - ) - - class Service(ServiceBase): - async def read(self, test: "Test") -> "Response": - return Response(v=42) - - async with ChannelFor([Service()]) as channel: - client = ServiceStub(channel) - - assert (await client.read(Test())).v == 42 - - # Check that we can call the method without providing the message - assert (await client.read()).v == 42 diff --git a/tests/inputs/service_uppercase/test_service.py b/tests/inputs/service_uppercase/test_service.py deleted file mode 100644 index 35405e13..00000000 --- a/tests/inputs/service_uppercase/test_service.py +++ /dev/null @@ -1,8 +0,0 @@ -import inspect - -from tests.output_betterproto.service_uppercase import TestStub - - -def test_parameters(): - sig = inspect.signature(TestStub.do_thing) - assert len(sig.parameters) == 5, "Expected 5 parameters" diff --git a/tests/inputs/signed/signed.json b/tests/inputs/signed/signed.json deleted file mode 100644 index b171e155..00000000 --- a/tests/inputs/signed/signed.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "signed32": 150, - "negative32": -150, - "string64": "150", - "negative64": "-150" -} diff --git a/tests/inputs/timestamp_dict_encode/test_timestamp_dict_encode.py b/tests/inputs/timestamp_dict_encode/test_timestamp_dict_encode.py deleted file mode 100644 index 35783ea6..00000000 --- a/tests/inputs/timestamp_dict_encode/test_timestamp_dict_encode.py +++ /dev/null @@ -1,78 +0,0 @@ -from datetime import ( - datetime, - timedelta, - timezone, -) - -import pytest - -from tests.output_betterproto.timestamp_dict_encode import Test - -# Current World Timezone range (UTC-12 to UTC+14) -MIN_UTC_OFFSET_MIN = -12 * 60 -MAX_UTC_OFFSET_MIN = 14 * 60 - -# Generate all timezones in range in 15 min increments -timezones = [timezone(timedelta(minutes=x)) for x in range(MIN_UTC_OFFSET_MIN, MAX_UTC_OFFSET_MIN + 1, 15)] - - -@pytest.mark.parametrize("tz", timezones) -def test_timezone_aware_datetime_dict_encode(tz: timezone): - original_time = datetime.now(tz=tz) - original_message = Test() - original_message.ts = original_time - encoded = original_message.to_dict() - decoded_message = Test() - decoded_message.from_dict(encoded) - - # check that the timestamps are equal after decoding from dict - assert original_message.ts.tzinfo is not None - assert decoded_message.ts.tzinfo is not None - assert original_message.ts == decoded_message.ts - - -def test_naive_datetime_dict_encode(): - # make suer naive datetime objects are still treated as utc - original_time = datetime.now() - assert original_time.tzinfo is None - original_message = Test() - original_message.ts = original_time - original_time_utc = original_time.replace(tzinfo=timezone.utc) - encoded = original_message.to_dict() - decoded_message = Test() - decoded_message.from_dict(encoded) - - # check that the timestamps are equal after decoding from dict - assert decoded_message.ts.tzinfo is not None - assert original_time_utc == decoded_message.ts - - -@pytest.mark.parametrize("tz", timezones) -def test_timezone_aware_json_serialize(tz: timezone): - original_time = datetime.now(tz=tz) - original_message = Test() - original_message.ts = original_time - json_serialized = original_message.to_json() - decoded_message = Test() - decoded_message.from_json(json_serialized) - - # check that the timestamps are equal after decoding from dict - assert original_message.ts.tzinfo is not None - assert decoded_message.ts.tzinfo is not None - assert original_message.ts == decoded_message.ts - - -def test_naive_datetime_json_serialize(): - # make suer naive datetime objects are still treated as utc - original_time = datetime.now() - assert original_time.tzinfo is None - original_message = Test() - original_message.ts = original_time - original_time_utc = original_time.replace(tzinfo=timezone.utc) - json_serialized = original_message.to_json() - decoded_message = Test() - decoded_message.from_json(json_serialized) - - # check that the timestamps are equal after decoding from dict - assert decoded_message.ts.tzinfo is not None - assert original_time_utc == decoded_message.ts diff --git a/tests/inputs/timestamp_dict_encode/timestamp_dict_encode.json b/tests/inputs/timestamp_dict_encode/timestamp_dict_encode.json deleted file mode 100644 index 3f455587..00000000 --- a/tests/inputs/timestamp_dict_encode/timestamp_dict_encode.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "ts" : "2023-03-15T22:35:51.253277Z" -} \ No newline at end of file From 83e43b02bf1d05ad3aa184ecfc3b061d2dc4fd61 Mon Sep 17 00:00:00 2001 From: Adrien Vannson Date: Fri, 20 Dec 2024 18:17:15 +0100 Subject: [PATCH 4/4] Add poe test --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 0dd6045a..1570b220 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,6 +61,10 @@ combine-as-imports = true # Dev workflow tasks +[tool.poe.tasks.test] +cmd = "pytest" +help = "Run tests" + [tool.poe.tasks.generate] script = "tests.generate:main" help = "Generate test cases"