From 2b5f8bdcb6fb550c573ff24f8c76743130656691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez=20Mondrag=C3=B3n?= Date: Fri, 17 Nov 2023 18:10:10 -0600 Subject: [PATCH] refactor: Break up `TapTestClassFactory._annotate_test_class` into simpler methods --- singer_sdk/testing/factory.py | 168 ++++++++++++++++++++-------------- singer_sdk/testing/suites.py | 22 +++-- 2 files changed, 113 insertions(+), 77 deletions(-) diff --git a/singer_sdk/testing/factory.py b/singer_sdk/testing/factory.py index f319300174..5680dac86d 100644 --- a/singer_sdk/testing/factory.py +++ b/singer_sdk/testing/factory.py @@ -8,6 +8,7 @@ from .config import SuiteConfig from .runners import TapTestRunner, TargetTestRunner from .suites import ( + TestSuite, tap_stream_attribute_tests, tap_stream_tests, tap_tests, @@ -15,7 +16,14 @@ ) if t.TYPE_CHECKING: - from singer_sdk import Tap, Target + from singer_sdk import Stream, Tap, Target + + from .templates import ( + AttributeTestTemplate, + StreamTestTemplate, + TapTestTemplate, + TargetTestTemplate, + ) class BaseTestClass: @@ -144,11 +152,93 @@ def runner(self) -> TapTestRunner | TargetTestRunner: return TapTestClass - # TODO: Refactor this. It's too long and nested. - def _annotate_test_class( # noqa: C901 + def _annotate_tap_tests( + self, + empty_test_class: type[BaseTestClass], + suite: TestSuite[TapTestTemplate], + ) -> None: + for test_class in suite.tests: + test = test_class() + test_name = f"test_{suite.kind}_{test.name}" + setattr(empty_test_class, test_name, test.run) + + def _annotate_stream_tests( self, empty_test_class: type[BaseTestClass], - test_suites: list, + suite: TestSuite[StreamTestTemplate], + streams: list[Stream], + ) -> None: + params = [ + { + "stream": stream, + } + for stream in streams + ] + param_ids = [stream.name for stream in streams] + + for test_class in suite.tests: + test = test_class() + test_name = f"test_{suite.kind}_{test.name}" + setattr( + test_class, + test_name, + test.run, + ) + empty_test_class.params[test_name] = params + empty_test_class.param_ids[test_name] = param_ids + + def _annotate_attribute_tests( + self, + empty_test_class: type[BaseTestClass], + suite: TestSuite[AttributeTestTemplate], + streams: list[Stream], + ) -> None: + for test_class in suite.tests: + test = test_class() + test_name = f"test_{suite.kind}_{test.name}" + test_params = [] + test_ids: list[str] = [] + for stream in streams: + final_schema = stream.stream_maps[-1].transformed_schema["properties"] + test_params.extend( + [ + { + "stream": stream, + "attribute_name": prop_name, + } + for prop_name, prop_schema in final_schema.items() + if test_class.evaluate( + stream=stream, + property_name=prop_name, + property_schema=prop_schema, + ) + ], + ) + test_ids.extend( + [ + f"{stream.name}.{prop_name}" + for prop_name, prop_schema in final_schema.items() + if test_class.evaluate( + stream=stream, + property_name=prop_name, + property_schema=prop_schema, + ) + ], + ) + + if test_params: + setattr( + empty_test_class, + test_name, + test.run, + ) + empty_test_class.params[test_name] = test_params + empty_test_class.param_ids[test_name] = test_ids + + def _annotate_test_class( + self, + empty_test_class: type[BaseTestClass], + test_suites: list[TestSuite], test_runner: TapTestRunner, ) -> type[BaseTestClass]: """Annotate test class with test methods. @@ -163,78 +253,16 @@ def _annotate_test_class( # noqa: C901 """ for suite in test_suites: if suite.kind == "tap": - for test_class in suite.tests: - test = test_class() - test_name = f"test_{suite.kind}_{test.name}" - setattr(empty_test_class, test_name, test.run) + self._annotate_tap_tests(empty_test_class, suite) if suite.kind in {"tap_stream", "tap_stream_attribute"}: streams = list(test_runner.new_tap().streams.values()) if suite.kind == "tap_stream": - params = [ - { - "stream": stream, - } - for stream in streams - ] - param_ids = [stream.name for stream in streams] - - for test_class in suite.tests: - test = test_class() - test_name = f"test_{suite.kind}_{test.name}" - setattr( - empty_test_class, - test_name, - test.run, - ) - empty_test_class.params[test_name] = params - empty_test_class.param_ids[test_name] = param_ids + self._annotate_stream_tests(empty_test_class, suite, streams) if suite.kind == "tap_stream_attribute": - for test_class in suite.tests: - test = test_class() - test_name = f"test_{suite.kind}_{test.name}" - test_params = [] - test_ids: list[str] = [] - for stream in streams: - final_schema = stream.stream_maps[-1].transformed_schema[ - "properties" - ] - test_params.extend( - [ - { - "stream": stream, - "attribute_name": prop_name, - } - for prop_name, prop_schema in final_schema.items() - if test_class.evaluate( - stream=stream, - property_name=prop_name, - property_schema=prop_schema, - ) - ], - ) - test_ids.extend( - [ - f"{stream.name}.{prop_name}" - for prop_name, prop_schema in final_schema.items() - if test_class.evaluate( - stream=stream, - property_name=prop_name, - property_schema=prop_schema, - ) - ], - ) - - if test_params: - setattr( - empty_test_class, - test_name, - test.run, - ) - empty_test_class.params[test_name] = test_params - empty_test_class.param_ids[test_name] = test_ids + self._annotate_attribute_tests(empty_test_class, suite, streams) return empty_test_class @@ -336,7 +364,7 @@ def runner(self) -> TargetTestRunner: def _annotate_test_class( self, empty_test_class: type[BaseTestClass], - test_suites: list, + test_suites: list[TestSuite[TargetTestTemplate]], ) -> type[BaseTestClass]: """Annotate test class with test methods. diff --git a/singer_sdk/testing/suites.py b/singer_sdk/testing/suites.py index d795cf1537..c4525f14a2 100644 --- a/singer_sdk/testing/suites.py +++ b/singer_sdk/testing/suites.py @@ -44,19 +44,27 @@ ) if t.TYPE_CHECKING: - from .templates import TapTestTemplate, TargetTestTemplate, TestTemplate + from .templates import ( + AttributeTestTemplate, + StreamTestTemplate, + TapTestTemplate, + TargetTestTemplate, + TestTemplate, + ) + + T = t.TypeVar("T", bound=TestTemplate) @dataclass -class TestSuite: +class TestSuite(t.Generic[T]): """Test Suite container class.""" kind: str - tests: list[type[TestTemplate] | type[TapTestTemplate] | type[TargetTestTemplate]] + tests: list[type[T]] # Tap Test Suites -tap_tests = TestSuite( +tap_tests: TestSuite[TapTestTemplate] = TestSuite( kind="tap", tests=[ TapCLIPrintsTest, @@ -65,7 +73,7 @@ class TestSuite: TapValidFinalStateTest, ], ) -tap_stream_tests = TestSuite( +tap_stream_tests: TestSuite[StreamTestTemplate] = TestSuite( kind="tap_stream", tests=[ StreamCatalogSchemaMatchesRecordTest, @@ -75,7 +83,7 @@ class TestSuite: StreamPrimaryKeysTest, ], ) -tap_stream_attribute_tests = TestSuite( +tap_stream_attribute_tests: TestSuite[AttributeTestTemplate] = TestSuite( kind="tap_stream_attribute", tests=[ AttributeIsBooleanTest, @@ -89,7 +97,7 @@ class TestSuite: # Target Test Suites -target_tests = TestSuite( +target_tests: TestSuite[TargetTestTemplate] = TestSuite( kind="target", tests=[ TargetArrayData,