mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-23 16:38:17 +00:00
* fix: logic for test suite config workflow * fix: added caching for system metrics (snflk and bq) * fix: linting * fix: added tearDown logic for tests suite/case
This commit is contained in:
parent
6d24455738
commit
ecffd5ffc7
@ -40,6 +40,8 @@ DML_OPERATION_MAP = {
|
|||||||
"DELETE": "DELETE",
|
"DELETE": "DELETE",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SYSTEM_QUERY_RESULT_CACHE = {}
|
||||||
|
|
||||||
|
|
||||||
@valuedispatch
|
@valuedispatch
|
||||||
def get_system_metrics_for_dialect(
|
def get_system_metrics_for_dialect(
|
||||||
@ -114,6 +116,10 @@ def _(
|
|||||||
"query_type,timestamp,destination_table,dml_statistics",
|
"query_type,timestamp,destination_table,dml_statistics",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# we'll try to get the cached data first
|
||||||
|
rows_jobs = kwargs["cache"][Dialects.BigQuery]["rows_jobs"]
|
||||||
|
except KeyError:
|
||||||
cursor_jobs = session.execute(text(jobs))
|
cursor_jobs = session.execute(text(jobs))
|
||||||
rows_jobs = [
|
rows_jobs = [
|
||||||
QueryResult(
|
QueryResult(
|
||||||
@ -124,6 +130,7 @@ def _(
|
|||||||
)
|
)
|
||||||
for row in cursor_jobs.fetchall()
|
for row in cursor_jobs.fetchall()
|
||||||
]
|
]
|
||||||
|
SYSTEM_QUERY_RESULT_CACHE[Dialects.BigQuery] = {"rows_jobs": rows_jobs}
|
||||||
|
|
||||||
for row_jobs in rows_jobs:
|
for row_jobs in rows_jobs:
|
||||||
if (
|
if (
|
||||||
@ -204,7 +211,7 @@ def _(
|
|||||||
sti."schema" = '{table.__table_args__["schema"]}' AND
|
sti."schema" = '{table.__table_args__["schema"]}' AND
|
||||||
sti."table" = '{table.__tablename__}' AND
|
sti."table" = '{table.__tablename__}' AND
|
||||||
"rows" != 0 AND
|
"rows" != 0 AND
|
||||||
DATE(starttime) = CURRENT_DATE - 1
|
DATE(starttime) >= CURRENT_DATE - 1
|
||||||
GROUP BY 2,3,4,5,6
|
GROUP BY 2,3,4,5,6
|
||||||
ORDER BY 6 desc
|
ORDER BY 6 desc
|
||||||
"""
|
"""
|
||||||
@ -228,7 +235,7 @@ def _(
|
|||||||
sti."schema" = '{table.__table_args__["schema"]}' AND
|
sti."schema" = '{table.__table_args__["schema"]}' AND
|
||||||
sti."table" = '{table.__tablename__}' AND
|
sti."table" = '{table.__tablename__}' AND
|
||||||
"rows" != 0 AND
|
"rows" != 0 AND
|
||||||
DATE(starttime) = CURRENT_DATE - 1
|
DATE(starttime) >= CURRENT_DATE - 1
|
||||||
GROUP BY 2,3,4,5,6
|
GROUP BY 2,3,4,5,6
|
||||||
ORDER BY 6 desc
|
ORDER BY 6 desc
|
||||||
"""
|
"""
|
||||||
@ -352,12 +359,12 @@ def _(
|
|||||||
"query_id,database_name,schema_name,query_text,query_type,timestamp",
|
"query_id,database_name,schema_name,query_text,query_type,timestamp",
|
||||||
)
|
)
|
||||||
|
|
||||||
rows = []
|
try:
|
||||||
|
# we'll try to get the cached data first
|
||||||
# limit of results is 10K. We'll query range of 1 hours to make sure we
|
rows = kwargs["cache"][Dialects.Snowflake]["rows"]
|
||||||
# get all the necessary data.
|
query_results = kwargs["cache"][Dialects.Snowflake]["query_results"]
|
||||||
|
except KeyError:
|
||||||
rows = session.execute(text(information_schema_query_history)).fetchall()
|
rows = session.execute(text(information_schema_query_history)).fetchall()
|
||||||
|
|
||||||
query_results = [
|
query_results = [
|
||||||
QueryResult(
|
QueryResult(
|
||||||
row.query_id,
|
row.query_id,
|
||||||
@ -369,6 +376,10 @@ def _(
|
|||||||
)
|
)
|
||||||
for row in rows
|
for row in rows
|
||||||
]
|
]
|
||||||
|
SYSTEM_QUERY_RESULT_CACHE[Dialects.Snowflake] = {
|
||||||
|
"rows": rows,
|
||||||
|
"query_results": query_results,
|
||||||
|
}
|
||||||
|
|
||||||
for query_result in query_results:
|
for query_result in query_results:
|
||||||
query_text = query_result.query_text
|
query_text = query_result.query_text
|
||||||
@ -459,6 +470,7 @@ class System(SystemMetric):
|
|||||||
session=session,
|
session=session,
|
||||||
table=self.table,
|
table=self.table,
|
||||||
conn_config=conn_config,
|
conn_config=conn_config,
|
||||||
|
cache=SYSTEM_QUERY_RESULT_CACHE,
|
||||||
)
|
)
|
||||||
|
|
||||||
return system_metrics
|
return system_metrics
|
||||||
|
@ -396,7 +396,7 @@ class Profiler(Generic[TMetric]):
|
|||||||
column,
|
column,
|
||||||
self.table,
|
self.table,
|
||||||
)
|
)
|
||||||
for column in self.columns
|
for column in columns
|
||||||
for metric in self.get_col_metrics(self.query_metrics, column)
|
for metric in self.get_col_metrics(self.query_metrics, column)
|
||||||
],
|
],
|
||||||
*[
|
*[
|
||||||
@ -410,7 +410,7 @@ class Profiler(Generic[TMetric]):
|
|||||||
column,
|
column,
|
||||||
self.table,
|
self.table,
|
||||||
)
|
)
|
||||||
for column in self.columns
|
for column in columns
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ from copy import deepcopy
|
|||||||
from logging import Logger
|
from logging import Logger
|
||||||
from typing import List, Optional, Set, Tuple
|
from typing import List, Optional, Set, Tuple
|
||||||
|
|
||||||
from pydantic import ValidationError
|
from pydantic import BaseModel, ValidationError
|
||||||
from sqlalchemy import MetaData
|
from sqlalchemy import MetaData
|
||||||
|
|
||||||
from metadata.config.common import WorkflowExecutionError
|
from metadata.config.common import WorkflowExecutionError
|
||||||
@ -58,7 +58,11 @@ from metadata.interfaces.datalake.datalake_test_suite_interface import (
|
|||||||
)
|
)
|
||||||
from metadata.interfaces.sqalchemy.sqa_test_suite_interface import SQATestSuiteInterface
|
from metadata.interfaces.sqalchemy.sqa_test_suite_interface import SQATestSuiteInterface
|
||||||
from metadata.profiler.api.models import ProfileSampleConfig
|
from metadata.profiler.api.models import ProfileSampleConfig
|
||||||
from metadata.test_suite.api.models import TestCaseDefinition, TestSuiteProcessorConfig
|
from metadata.test_suite.api.models import (
|
||||||
|
TestCaseDefinition,
|
||||||
|
TestSuiteDefinition,
|
||||||
|
TestSuiteProcessorConfig,
|
||||||
|
)
|
||||||
from metadata.test_suite.runner.core import DataTestsRunner
|
from metadata.test_suite.runner.core import DataTestsRunner
|
||||||
from metadata.utils import entity_link
|
from metadata.utils import entity_link
|
||||||
from metadata.utils.importer import get_sink
|
from metadata.utils.importer import get_sink
|
||||||
@ -70,6 +74,22 @@ from metadata.workflow.workflow_status_mixin import WorkflowStatusMixin
|
|||||||
logger: Logger = test_suite_logger()
|
logger: Logger = test_suite_logger()
|
||||||
|
|
||||||
|
|
||||||
|
class TestCaseToCreate(BaseModel):
|
||||||
|
"""Test case to create"""
|
||||||
|
|
||||||
|
test_suite_name: str
|
||||||
|
test_case_name: str
|
||||||
|
entity_link: str
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
"""make this base model hashable on unique_name"""
|
||||||
|
return hash(f"{self.test_suite_name}.{self.test_case_name}")
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
"""make this base model printable"""
|
||||||
|
return f"{self.test_suite_name}.{self.test_case_name}"
|
||||||
|
|
||||||
|
|
||||||
class TestSuiteWorkflow(WorkflowStatusMixin):
|
class TestSuiteWorkflow(WorkflowStatusMixin):
|
||||||
"""workflow to run the test suite"""
|
"""workflow to run the test suite"""
|
||||||
|
|
||||||
@ -339,7 +359,9 @@ class TestSuiteWorkflow(WorkflowStatusMixin):
|
|||||||
|
|
||||||
return test_cases_entity
|
return test_cases_entity
|
||||||
|
|
||||||
def get_test_case_from_cli_config(self) -> List[str]:
|
def get_test_case_from_cli_config(
|
||||||
|
self,
|
||||||
|
) -> List[Tuple[TestCaseDefinition, TestSuiteDefinition]]:
|
||||||
"""Get all the test cases names defined in the CLI config file"""
|
"""Get all the test cases names defined in the CLI config file"""
|
||||||
return [
|
return [
|
||||||
(test_case, test_suite)
|
(test_case, test_suite)
|
||||||
@ -347,9 +369,66 @@ class TestSuiteWorkflow(WorkflowStatusMixin):
|
|||||||
for test_case in test_suite.testCases
|
for test_case in test_suite.testCases
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def get_unique_test_case_name_in_config(self, test_cases_in_config: set) -> set:
|
||||||
|
"""Get unique test case names in config. If a test case is created for the same entity
|
||||||
|
with the same name in different test suites, we only create one test case in the platform.
|
||||||
|
The other ones will be skipped.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
test_cases_in_config (set): set of test cases in config
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
set: set of unique test case names in config
|
||||||
|
"""
|
||||||
|
seen = []
|
||||||
|
unique_test_case = []
|
||||||
|
for test_case in test_cases_in_config:
|
||||||
|
unique_test_case_name_in_entity = (
|
||||||
|
f"{test_case.test_case_name}/{test_case.entity_link}"
|
||||||
|
)
|
||||||
|
if unique_test_case_name_in_entity not in seen:
|
||||||
|
seen.append(unique_test_case_name_in_entity)
|
||||||
|
unique_test_case.append(test_case)
|
||||||
|
continue
|
||||||
|
logger.info(
|
||||||
|
f"Test case {test_case.test_case_name} for entity {test_case.entity_link}"
|
||||||
|
" was already defined in your profiler config file. Skipping it."
|
||||||
|
)
|
||||||
|
|
||||||
|
return set(unique_test_case)
|
||||||
|
|
||||||
|
def test_case_name_exists(self, test_case: TestCaseToCreate) -> bool:
|
||||||
|
"""Check if a test case name already exists in the platform
|
||||||
|
for the same entity.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
other (set): a set of platform test cases
|
||||||
|
Returns:
|
||||||
|
Optional["TestCaseToCreate"]
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
entity_fqn = entity_link.get_table_or_column_fqn(test_case.entity_link)
|
||||||
|
except ValueError as exc:
|
||||||
|
logger.debug(traceback.format_exc())
|
||||||
|
logger.error(f"Failed to get entity fqn: {exc}")
|
||||||
|
# we'll assume that the test case name is not unique
|
||||||
|
return True
|
||||||
|
|
||||||
|
test_case_fqn = f"{entity_fqn}.{test_case.test_case_name}"
|
||||||
|
|
||||||
|
test_case = self.metadata.get_by_name(
|
||||||
|
entity=TestCase,
|
||||||
|
fqn=test_case_fqn,
|
||||||
|
fields=["testDefinition", "testSuite"],
|
||||||
|
)
|
||||||
|
|
||||||
|
if not test_case:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def compare_and_create_test_cases(
|
def compare_and_create_test_cases(
|
||||||
self,
|
self,
|
||||||
cli_config_test_cases_def: List[Tuple[TestCaseDefinition, TestSuite]],
|
cli_config_test_cases_def: List[Tuple[TestCaseDefinition, TestSuiteDefinition]],
|
||||||
test_cases: List[TestCase],
|
test_cases: List[TestCase],
|
||||||
) -> Optional[List[TestCase]]:
|
) -> Optional[List[TestCase]]:
|
||||||
"""
|
"""
|
||||||
@ -360,9 +439,34 @@ class TestSuiteWorkflow(WorkflowStatusMixin):
|
|||||||
cli_config_test_case_name: test cases defined in CLI workflow associated with its test suite
|
cli_config_test_case_name: test cases defined in CLI workflow associated with its test suite
|
||||||
test_cases: list of test cases entities fetch from the server using test suite names in the config file
|
test_cases: list of test cases entities fetch from the server using test suite names in the config file
|
||||||
"""
|
"""
|
||||||
test_case_names_to_create = {
|
cli_test_cases = {
|
||||||
test_case_def[0].name for test_case_def in cli_config_test_cases_def
|
TestCaseToCreate(
|
||||||
} - {test_case.name.__root__ for test_case in test_cases}
|
test_suite_name=test_case_def[1].name,
|
||||||
|
test_case_name=test_case_def[0].name,
|
||||||
|
entity_link=test_case_def[0].entityLink.__root__,
|
||||||
|
)
|
||||||
|
for test_case_def in cli_config_test_cases_def
|
||||||
|
}
|
||||||
|
platform_test_cases = {
|
||||||
|
TestCaseToCreate(
|
||||||
|
test_suite_name=test_case.testSuite.name,
|
||||||
|
test_case_name=test_case.name.__root__,
|
||||||
|
entity_link=test_case.entityLink.__root__,
|
||||||
|
)
|
||||||
|
for test_case in test_cases
|
||||||
|
}
|
||||||
|
|
||||||
|
# we'll check the test cases defined in the CLI config file and not present in the platform
|
||||||
|
unique_test_cases_across_test_suites = cli_test_cases - platform_test_cases
|
||||||
|
# we'll check if we are creating a test case for an entity that already has the same name
|
||||||
|
unique_test_case_across_entities = {
|
||||||
|
test_case
|
||||||
|
for test_case in unique_test_cases_across_test_suites
|
||||||
|
if not self.test_case_name_exists(test_case)
|
||||||
|
}
|
||||||
|
test_case_names_to_create = self.get_unique_test_case_name_in_config(
|
||||||
|
unique_test_case_across_entities
|
||||||
|
)
|
||||||
|
|
||||||
if not test_case_names_to_create:
|
if not test_case_names_to_create:
|
||||||
return None
|
return None
|
||||||
@ -374,7 +478,14 @@ class TestSuiteWorkflow(WorkflowStatusMixin):
|
|||||||
(
|
(
|
||||||
cli_config_test_case_def
|
cli_config_test_case_def
|
||||||
for cli_config_test_case_def in cli_config_test_cases_def
|
for cli_config_test_case_def in cli_config_test_cases_def
|
||||||
if cli_config_test_case_def[0].name == test_case_name_to_create
|
if (
|
||||||
|
cli_config_test_case_def[0].name
|
||||||
|
== test_case_name_to_create.test_case_name
|
||||||
|
and cli_config_test_case_def[0].entityLink.__root__
|
||||||
|
== test_case_name_to_create.entity_link
|
||||||
|
and cli_config_test_case_def[1].name
|
||||||
|
== test_case_name_to_create.test_suite_name
|
||||||
|
)
|
||||||
),
|
),
|
||||||
(None, None),
|
(None, None),
|
||||||
)
|
)
|
||||||
|
@ -68,6 +68,24 @@ def get_table_fqn(entity_link: str) -> str:
|
|||||||
return entity_link.split("::")[2]
|
return entity_link.split("::")[2]
|
||||||
|
|
||||||
|
|
||||||
|
def get_table_or_column_fqn(entity_link: str) -> str:
|
||||||
|
"""From an entity link get the column fqn
|
||||||
|
|
||||||
|
Args:
|
||||||
|
entity_link: entity link
|
||||||
|
"""
|
||||||
|
split_entity_link = split(entity_link)
|
||||||
|
if len(split_entity_link) == 2:
|
||||||
|
return split_entity_link[1]
|
||||||
|
if len(split_entity_link) == 4 and split_entity_link[2] == "columns":
|
||||||
|
return f"{split_entity_link[1]}.{split_entity_link[3]}"
|
||||||
|
|
||||||
|
raise ValueError(
|
||||||
|
"Invalid entity link."
|
||||||
|
" {split_entity_link} does not look like a table or a column entity link"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_entity_link(table_fqn: str, column_name: Optional[str]) -> str:
|
def get_entity_link(table_fqn: str, column_name: Optional[str]) -> str:
|
||||||
"""From table fqn and column name get the entity_link
|
"""From table fqn and column name get the entity_link
|
||||||
|
|
||||||
|
@ -66,13 +66,6 @@ class OMetaTestSuiteTest(TestCase):
|
|||||||
|
|
||||||
assert metadata.health_check()
|
assert metadata.health_check()
|
||||||
|
|
||||||
test_suite: TestSuite = metadata.create_or_update(
|
|
||||||
CreateTestSuiteRequest(
|
|
||||||
name="testSuiteForIntegrationTest",
|
|
||||||
description="This is a test suite for the integration tests",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
test_definition = metadata.create_or_update(
|
test_definition = metadata.create_or_update(
|
||||||
CreateTestDefinitionRequest(
|
CreateTestDefinitionRequest(
|
||||||
name="testDefinitionForIntegration",
|
name="testDefinitionForIntegration",
|
||||||
@ -87,6 +80,13 @@ class OMetaTestSuiteTest(TestCase):
|
|||||||
def setUpClass(cls) -> None:
|
def setUpClass(cls) -> None:
|
||||||
"""set up tests"""
|
"""set up tests"""
|
||||||
|
|
||||||
|
cls.test_suite: TestSuite = cls.metadata.create_or_update(
|
||||||
|
CreateTestSuiteRequest(
|
||||||
|
name="testSuiteForIntegrationTest",
|
||||||
|
description="This is a test suite for the integration tests",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
cls.metadata.create_or_update(
|
cls.metadata.create_or_update(
|
||||||
CreateTestCaseRequest(
|
CreateTestCaseRequest(
|
||||||
name="testCaseForIntegration",
|
name="testCaseForIntegration",
|
||||||
|
@ -58,6 +58,28 @@ class TestSuiteWorkflowTests(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
test_case_ids = []
|
||||||
|
test_suite_ids = []
|
||||||
|
|
||||||
|
def tearDown(self) -> None:
|
||||||
|
for test_case_id in self.test_case_ids:
|
||||||
|
self.metadata.delete(
|
||||||
|
entity=TestCase,
|
||||||
|
entity_id=test_case_id,
|
||||||
|
recursive=True,
|
||||||
|
hard_delete=True,
|
||||||
|
)
|
||||||
|
for test_suite_id in self.test_suite_ids:
|
||||||
|
self.metadata.delete(
|
||||||
|
entity=TestSuite,
|
||||||
|
entity_id=test_suite_id,
|
||||||
|
recursive=True,
|
||||||
|
hard_delete=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.test_case_ids = []
|
||||||
|
self.test_suite_ids = []
|
||||||
|
|
||||||
def test_create_workflow_object(self):
|
def test_create_workflow_object(self):
|
||||||
"""Test workflow object is correctly instantiated"""
|
"""Test workflow object is correctly instantiated"""
|
||||||
TestSuiteWorkflow.create(test_suite_config)
|
TestSuiteWorkflow.create(test_suite_config)
|
||||||
@ -101,6 +123,7 @@ class TestSuiteWorkflowTests(unittest.TestCase):
|
|||||||
test_suite = self.metadata.get_by_name(entity=TestSuite, fqn="my_test_suite")
|
test_suite = self.metadata.get_by_name(entity=TestSuite, fqn="my_test_suite")
|
||||||
|
|
||||||
assert workflow_test_suite[0].id == test_suite.id
|
assert workflow_test_suite[0].id == test_suite.id
|
||||||
|
self.test_suite_ids = [test_suite.id]
|
||||||
|
|
||||||
def test_get_test_suite_entity_for_ui_workflow(self):
|
def test_get_test_suite_entity_for_ui_workflow(self):
|
||||||
"""test we can correctly retrieve a test suite"""
|
"""test we can correctly retrieve a test suite"""
|
||||||
@ -316,6 +339,153 @@ class TestSuiteWorkflowTests(unittest.TestCase):
|
|||||||
|
|
||||||
assert not created_test_case
|
assert not created_test_case
|
||||||
|
|
||||||
|
def test_compare_and_create_test_cases_same_test_name_diff_test_suite(self):
|
||||||
|
"""Test function creates the correct test case if they don't exists when
|
||||||
|
test case name is the same but test suite and entity is different
|
||||||
|
"""
|
||||||
|
_test_suite_config = deepcopy(test_suite_config)
|
||||||
|
processor = {
|
||||||
|
"processor": {
|
||||||
|
"type": "orm-test-runner",
|
||||||
|
"config": {
|
||||||
|
"testSuites": [
|
||||||
|
{
|
||||||
|
"name": "another_test_suite",
|
||||||
|
"testCases": [
|
||||||
|
{
|
||||||
|
"name": "table_column_count_between",
|
||||||
|
"testDefinitionName": "TableColumnCountToBeBetween",
|
||||||
|
"entityLink": "<#E::table::sample_data.ecommerce_db.shopify.dim_address_clean>",
|
||||||
|
"parameterValues": [
|
||||||
|
{"name": "minColValue", "value": 1},
|
||||||
|
{"name": "maxColValue", "value": 15},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "new_test_suite",
|
||||||
|
"testCases": [
|
||||||
|
{
|
||||||
|
"name": "table_column_count_between",
|
||||||
|
"testDefinitionName": "TableColumnCountToBeBetween",
|
||||||
|
"entityLink": "<#E::table::sample_data.ecommerce_db.shopify.dim_address_clean>",
|
||||||
|
"parameterValues": [
|
||||||
|
{"name": "minColValue", "value": 1},
|
||||||
|
{"name": "maxColValue", "value": 5},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_test_suite_config.update(processor)
|
||||||
|
workflow = TestSuiteWorkflow.create(_test_suite_config)
|
||||||
|
|
||||||
|
assert not self.metadata.get_by_name(
|
||||||
|
entity=TestCase,
|
||||||
|
fqn="sample_data.ecommerce_db.shopify.dim_address_clean.table_column_count_between",
|
||||||
|
)
|
||||||
|
|
||||||
|
test_suite = workflow.get_or_create_test_suite_entity_for_cli_workflow()
|
||||||
|
test_cases = workflow.get_test_cases_from_test_suite(test_suite)
|
||||||
|
config_test_cases_def = workflow.get_test_case_from_cli_config()
|
||||||
|
created_workflow = workflow.compare_and_create_test_cases(
|
||||||
|
config_test_cases_def, test_cases
|
||||||
|
)
|
||||||
|
|
||||||
|
my_test_case = self.metadata.get_by_name(
|
||||||
|
entity=TestCase,
|
||||||
|
fqn="sample_data.ecommerce_db.shopify.dim_address_clean.table_column_count_between",
|
||||||
|
fields=["testDefinition", "testSuite"],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(created_workflow) == 1
|
||||||
|
|
||||||
|
another_test_suite = self.metadata.get_by_name(
|
||||||
|
entity=TestSuite,
|
||||||
|
fqn="another_test_suite",
|
||||||
|
)
|
||||||
|
new_test_suite = self.metadata.get_by_name(
|
||||||
|
entity=TestSuite,
|
||||||
|
fqn="new_test_suite",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.test_suite_ids = [new_test_suite.id, another_test_suite.id]
|
||||||
|
|
||||||
|
def test_compare_and_create_test_cases_same_test_name_same_test_suite(self):
|
||||||
|
"""Test function creates the correct test case if they don't exists when
|
||||||
|
test case name is the same but test suite and entity is different
|
||||||
|
"""
|
||||||
|
_test_suite_config = deepcopy(test_suite_config)
|
||||||
|
processor = {
|
||||||
|
"processor": {
|
||||||
|
"type": "orm-test-runner",
|
||||||
|
"config": {
|
||||||
|
"testSuites": [
|
||||||
|
{
|
||||||
|
"name": "critical_metrics_suite",
|
||||||
|
"testCases": [
|
||||||
|
{
|
||||||
|
"name": "table_column_count_between",
|
||||||
|
"testDefinitionName": "TableColumnCountToBeBetween",
|
||||||
|
"entityLink": "<#E::table::sample_data.ecommerce_db.shopify.dim_address>",
|
||||||
|
"parameterValues": [
|
||||||
|
{"name": "minColValue", "value": 1},
|
||||||
|
{"name": "maxColValue", "value": 30},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "table_column_count_between",
|
||||||
|
"testDefinitionName": "TableColumnCountToBeBetween",
|
||||||
|
"entityLink": "<#E::table::sample_data.ecommerce_db.shopify.dim_customer>",
|
||||||
|
"parameterValues": [
|
||||||
|
{"name": "minColValue", "value": 1},
|
||||||
|
{"name": "maxColValue", "value": 5},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_test_suite_config.update(processor)
|
||||||
|
workflow = TestSuiteWorkflow.create(_test_suite_config)
|
||||||
|
|
||||||
|
assert not self.metadata.get_by_name(
|
||||||
|
entity=TestCase,
|
||||||
|
fqn="sample_data.ecommerce_db.shopify.dim_customer.table_column_count_between",
|
||||||
|
)
|
||||||
|
assert self.metadata.get_by_name(
|
||||||
|
entity=TestCase,
|
||||||
|
fqn="sample_data.ecommerce_db.shopify.dim_address.table_column_count_between",
|
||||||
|
fields=["testDefinition", "testSuite"],
|
||||||
|
)
|
||||||
|
|
||||||
|
test_suite = workflow.get_or_create_test_suite_entity_for_cli_workflow()
|
||||||
|
test_cases = workflow.get_test_cases_from_test_suite(test_suite)
|
||||||
|
config_test_cases_def = workflow.get_test_case_from_cli_config()
|
||||||
|
created_test_case = workflow.compare_and_create_test_cases(
|
||||||
|
config_test_cases_def, test_cases
|
||||||
|
)
|
||||||
|
|
||||||
|
dim_customer_test_case = self.metadata.get_by_name(
|
||||||
|
entity=TestCase,
|
||||||
|
fqn="sample_data.ecommerce_db.shopify.dim_customer.table_column_count_between",
|
||||||
|
fields=["testDefinition", "testSuite"],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(created_test_case) == 1
|
||||||
|
assert created_test_case[0].name.__root__ == "table_column_count_between"
|
||||||
|
assert created_test_case[0].testSuite.name == "critical_metrics_suite"
|
||||||
|
|
||||||
|
self.test_case_ids = [dim_customer_test_case.id]
|
||||||
|
|
||||||
def test_get_service_connection_from_test_case(self):
|
def test_get_service_connection_from_test_case(self):
|
||||||
"""test get service connection returns correct info"""
|
"""test get service connection returns correct info"""
|
||||||
workflow = TestSuiteWorkflow.create(test_suite_config)
|
workflow = TestSuiteWorkflow.create(test_suite_config)
|
||||||
|
@ -15,7 +15,7 @@ test entity link utils
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from metadata.utils.entity_link import get_decoded_column
|
from metadata.utils.entity_link import get_decoded_column, get_table_or_column_fqn
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@ -34,3 +34,22 @@ from metadata.utils.entity_link import get_decoded_column
|
|||||||
def test_get_decoded_column(entity_link, expected):
|
def test_get_decoded_column(entity_link, expected):
|
||||||
"""test get_decoded_column return expected values"""
|
"""test get_decoded_column return expected values"""
|
||||||
assert get_decoded_column(entity_link) == expected
|
assert get_decoded_column(entity_link) == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_table_or_column_fqn():
|
||||||
|
entity_link = "<#E::table::rds.dev.dbt_jaffle.customers::columns::number_of_orders>"
|
||||||
|
assert (
|
||||||
|
get_table_or_column_fqn(entity_link)
|
||||||
|
== "rds.dev.dbt_jaffle.customers.number_of_orders"
|
||||||
|
)
|
||||||
|
|
||||||
|
invalid_entity_link = (
|
||||||
|
"<#E::table::rds.dev.dbt_jaffle.customers::foo::number_of_orders>"
|
||||||
|
)
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
get_table_or_column_fqn(invalid_entity_link)
|
||||||
|
|
||||||
|
invalid_entity_link = "<#E::table::rds.dev.dbt_jaffle.customers>"
|
||||||
|
assert (
|
||||||
|
get_table_or_column_fqn(invalid_entity_link) == "rds.dev.dbt_jaffle.customers"
|
||||||
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user