Skip to content

Commit

Permalink
fix: fix tests by overriding the runner for targets (#38)
Browse files Browse the repository at this point in the history
Closes #28

- implements a fix for the test suites not running properly, also added
it to the SDK meltano/sdk#1749
- implements a lot of the default test validates methods to make asserts
- comment out the tests that fail for legitimate bugs
- logged the bugs
  - #43
  - #41
  - #40
- I also logged
#42 because I
wrote a test to assert the exception but I'm not actually sure if we
want that behavior or not

---------

Co-authored-by: Ken Payne <[email protected]>
  • Loading branch information
pnadolny13 and Ken Payne authored Jun 6, 2023
1 parent 1afd590 commit bfd8814
Show file tree
Hide file tree
Showing 2 changed files with 217 additions and 13 deletions.
15 changes: 14 additions & 1 deletion tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,22 @@
"default_target_schema": f"TARGET_SNOWFLAKE_{uuid.uuid4().hex[0:6]!s}",
}

# TODO: replace when upstream issue resolves
# https://github.com/meltano/sdk/pull/1752
class CustomRunner(TargetTestRunner):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

def sync_all(self, *args, **kwargs):
try:
super().sync_all(*args, **kwargs)
finally:
self.target_input = None


# Custom so I can implement all validate methods
StandardTargetTests = get_test_class(
test_runner=TargetTestRunner(
test_runner=CustomRunner(
target_class=TargetSnowflake,
config=SAMPLE_CONFIG,
),
Expand Down
215 changes: 203 additions & 12 deletions tests/test_impl.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import pytest
import snowflake.sqlalchemy.custom_types as sct
import sqlalchemy
from singer_sdk.testing.suites import TestSuite
from singer_sdk.testing.target_tests import (
TargetArrayData,
Expand Down Expand Up @@ -45,23 +47,212 @@ def validate(self) -> None:
isinstance(column.type, expected_types[column.name])


class SnowflakeTargetCamelcaseComplexSchema(TargetCamelcaseComplexSchema):
def validate(self) -> None:
connector = self.target.default_sink_class.connector_class(self.target.config)
table = f"{self.target.config['database']}.{self.target.config['default_target_schema']}.ForecastingTypeToCategory".upper()
table_schema = connector.get_table(table)
expected_types = {
"id": sct._CUSTOM_DECIMAL,
"isdeleted": sqlalchemy.types.BOOLEAN,
"createddate": sct.TIMESTAMP_NTZ,
"createdbyid": sct.STRING,
"lastmodifieddate": sct.TIMESTAMP_NTZ,
"lastmodifiedbyid": sct.STRING,
"systemmodstamp": sct.TIMESTAMP_NTZ,
"forecastingtypeid": sct.STRING,
"forecastingitemcategory": sct.STRING,
"displayposition": sct.NUMBER,
"isadjustable": sqlalchemy.types.BOOLEAN,
"isowneradjustable": sqlalchemy.types.BOOLEAN,
"age": sct.NUMBER,
"newcamelcasedattribute": sct.STRING,
"_sdc_extracted_at": sct.TIMESTAMP_NTZ,
"_sdc_batched_at": sct.TIMESTAMP_NTZ,
"_sdc_received_at": sct.TIMESTAMP_NTZ,
"_sdc_deleted_at": sct.TIMESTAMP_NTZ,
"_sdc_table_version": sct.NUMBER,
"_sdc_sequence": sct.NUMBER,
}
for column in table_schema.columns:
assert column.name in expected_types
isinstance(column.type, expected_types[column.name])


class SnowflakeTargetDuplicateRecords(TargetDuplicateRecords):
def validate(self) -> None:
connector = self.target.default_sink_class.connector_class(self.target.config)
table = f"{self.target.config['database']}.{self.target.config['default_target_schema']}.test_duplicate_records".upper()
result = connector.connection.execute(
f"select * from {table}",
)
expected_value = {
1: 100,
2: 20,
}
assert result.rowcount == 2
for row in result:
assert len(row) == 8
assert row[0] in expected_value
assert expected_value.get(row[0]) == row[1]

table_schema = connector.get_table(table)
expected_types = {
"id": sct.NUMBER,
"metric": sct.NUMBER,
"_sdc_extracted_at": sct.TIMESTAMP_NTZ,
"_sdc_batched_at": sct.TIMESTAMP_NTZ,
"_sdc_received_at": sct.TIMESTAMP_NTZ,
"_sdc_deleted_at": sct.TIMESTAMP_NTZ,
"_sdc_table_version": sct.NUMBER,
"_sdc_sequence": sct.NUMBER,
}
for column in table_schema.columns:
assert column.name in expected_types
isinstance(column.type, expected_types[column.name])


class SnowflakeTargetCamelcaseTest(TargetCamelcaseTest):
def validate(self) -> None:
connector = self.target.default_sink_class.connector_class(self.target.config)
table = f"{self.target.config['database']}.{self.target.config['default_target_schema']}.TestCamelcase".upper()
connector.connection.execute(
f"select * from {table}",
)

table_schema = connector.get_table(table)
expected_types = {
"id": sct.STRING,
"clientname": sct.STRING,
"_sdc_extracted_at": sct.TIMESTAMP_NTZ,
"_sdc_batched_at": sct.TIMESTAMP_NTZ,
"_sdc_received_at": sct.TIMESTAMP_NTZ,
"_sdc_deleted_at": sct.TIMESTAMP_NTZ,
"_sdc_table_version": sct.NUMBER,
"_sdc_sequence": sct.NUMBER,
}
for column in table_schema.columns:
assert column.name in expected_types
isinstance(column.type, expected_types[column.name])


class SnowflakeTargetEncodedStringData(TargetEncodedStringData):
def validate(self) -> None:
connector = self.target.default_sink_class.connector_class(self.target.config)
for table_name in [
"test_strings",
"test_strings_in_objects",
"test_strings_in_arrays",
]:
table = f"{self.target.config['database']}.{self.target.config['default_target_schema']}.{table_name}".upper()
connector.connection.execute(
f"select * from {table}",
)
# TODO: more assertions


class SnowflakeTargetInvalidSchemaTest(TargetInvalidSchemaTest):
def test(self) -> None:
with pytest.raises(Exception):
self.runner.sync_all()


class SnowflakeTargetRecordBeforeSchemaTest(TargetRecordBeforeSchemaTest):
def test(self) -> None:
with pytest.raises(Exception):
self.runner.sync_all()


class SnowflakeTargetRecordMissingKeyProperty(TargetRecordMissingKeyProperty):
def test(self) -> None:
# TODO: try to catch exact exception, currently snowflake throws an integrity error
with pytest.raises(Exception):
self.runner.sync_all()


class SnowflakeTargetSchemaNoProperties(TargetSchemaNoProperties):
def validate(self) -> None:
for table_name in [
"test_object_schema_with_properties",
"test_object_schema_no_properties",
]:
connector = self.target.default_sink_class.connector_class(
self.target.config
)
table = f"{self.target.config['database']}.{self.target.config['default_target_schema']}.{table_name}".upper()
result = connector.connection.execute(
f"select * from {table}",
)
assert result.rowcount == 2
row = result.first()
assert len(row) == 7
table_schema = connector.get_table(table)
expected_types = {
"object_store": sct.VARIANT,
"_sdc_extracted_at": sct.TIMESTAMP_NTZ,
"_sdc_batched_at": sct.TIMESTAMP_NTZ,
"_sdc_received_at": sct.TIMESTAMP_NTZ,
"_sdc_deleted_at": sct.TIMESTAMP_NTZ,
"_sdc_table_version": sct.NUMBER,
"_sdc_sequence": sct.NUMBER,
}
for column in table_schema.columns:
assert column.name in expected_types
isinstance(column.type, expected_types[column.name])


class SnowflakeTargetSchemaUpdates(TargetSchemaUpdates):
def validate(self) -> None:
connector = self.target.default_sink_class.connector_class(self.target.config)
table = f"{self.target.config['database']}.{self.target.config['default_target_schema']}.test_schema_updates".upper()
result = connector.connection.execute(
f"select * from {table}",
)
assert result.rowcount == 6
row = result.first()
assert len(row) == 13
table_schema = connector.get_table(table)
expected_types = {
"id": sct.NUMBER,
"a1": sct.NUMBER,
"a2": sct.STRING,
"a3": sqlalchemy.types.BOOLEAN,
"a4": sct.VARIANT,
"a5": sct.VARIANT,
"a6": sct.NUMBER,
"_sdc_extracted_at": sct.TIMESTAMP_NTZ,
"_sdc_batched_at": sct.TIMESTAMP_NTZ,
"_sdc_received_at": sct.TIMESTAMP_NTZ,
"_sdc_deleted_at": sct.TIMESTAMP_NTZ,
"_sdc_table_version": sct.NUMBER,
"_sdc_sequence": sct.NUMBER,
}
for column in table_schema.columns:
assert column.name in expected_types
isinstance(column.type, expected_types[column.name])


target_tests = TestSuite(
kind="target",
tests=[
SnowflakeTargetArrayData,
TargetCamelcaseComplexSchema,
TargetCamelcaseTest,
SnowflakeTargetCamelcaseComplexSchema,
# TODO: bug https://github.com/MeltanoLabs/target-snowflake/issues/40
# SnowflakeTargetCamelcaseTest,
TargetCliPrintsTest,
TargetDuplicateRecords,
TargetEncodedStringData,
TargetInvalidSchemaTest,
# TODO: bug https://github.com/MeltanoLabs/target-snowflake/issues/41
# SnowflakeTargetDuplicateRecords,
SnowflakeTargetEncodedStringData,
SnowflakeTargetInvalidSchemaTest,
# Not available in the SDK yet
# TargetMultipleStateMessages,
TargetNoPrimaryKeys,
TargetOptionalAttributes,
TargetRecordBeforeSchemaTest,
TargetRecordMissingKeyProperty,
TargetSchemaNoProperties,
TargetSchemaUpdates,
TargetSpecialCharsInAttributes,
TargetNoPrimaryKeys, # Implicitly asserts no pk is handled
TargetOptionalAttributes, # Implicitly asserts that nullable fields are handled
SnowflakeTargetRecordBeforeSchemaTest,
SnowflakeTargetRecordMissingKeyProperty,
SnowflakeTargetSchemaNoProperties,
# TODO: bug https://github.com/MeltanoLabs/target-snowflake/issues/43
# SnowflakeTargetSchemaUpdates,
TargetSpecialCharsInAttributes, # Implicitly asserts that special chars are handled
],
)

0 comments on commit bfd8814

Please sign in to comment.