Files
wehub-resource-sync bf2343b7e4
Integration Tests - MySQL + Elasticsearch / Detect Changes (push) Has been cancelled
Integration Tests - MySQL + Elasticsearch / integration-tests-mysql-elasticsearch (push) Has been cancelled
Integration Tests - PostgreSQL + Elasticsearch + Redis / Detect Changes (push) Has been cancelled
Integration Tests - PostgreSQL + Elasticsearch + Redis / integration-tests-postgres-elasticsearch-redis (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / Detect Changes (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / integration-tests-postgres-opensearch (push) Has been cancelled
Java Checkstyle / java-checkstyle (push) Has been cancelled
Maven Collate Tests / maven-collate-ci (push) Has been cancelled
OpenMetadata Service Unit Tests / openmetadata-service-unit-tests-status (push) Has been cancelled
Publish Package to Maven Central Repository / publish-maven-packages (push) Has been cancelled
OpenMetadata Service Unit Tests / Detect Changes (push) Has been cancelled
OpenMetadata Service Unit Tests / openmetadata-service-unit-tests (push) Has been cancelled
OpenMetadata Service Unit Tests / k8s_operator-unit-tests (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:35:45 +08:00

208 lines
7.4 KiB
Python

# Copyright 2025 Collate
# Licensed under the Collate Community License, Version 1.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# https://github.com/open-metadata/OpenMetadata/blob/main/ingestion/LICENSE
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Integration tests for Rule Library SQL Expression validator
"""
from dataclasses import dataclass
from typing import List # noqa: UP035
import pytest
from metadata.data_quality.api.models import TestCaseDefinition
from metadata.generated.schema.api.tests.createTestDefinition import (
CreateTestDefinitionRequest,
)
from metadata.generated.schema.entity.data.table import DataType
from metadata.generated.schema.entity.services.databaseService import DatabaseService
from metadata.generated.schema.metadataIngestion.testSuitePipeline import (
TestSuiteConfigType,
TestSuitePipeline,
)
from metadata.generated.schema.tests.basic import TestCaseStatus
from metadata.generated.schema.tests.testCase import TestCase
from metadata.generated.schema.tests.testDefinition import (
EntityType,
TestCaseParameterDefinition,
TestDefinition,
TestPlatform,
)
from metadata.generated.schema.type.basic import Markdown, SqlQuery, TestCaseEntityName
from metadata.ingestion.ometa.ometa_api import OpenMetadata
from metadata.workflow.data_quality import TestSuiteWorkflow
from metadata.workflow.metadata import MetadataWorkflow
from ..integration_base import generate_name # noqa: TID252
NUMERIC_DATA_TYPES = [
DataType.INT,
DataType.BIGINT,
DataType.SMALLINT,
DataType.TINYINT,
DataType.NUMBER,
DataType.FLOAT,
DataType.DOUBLE,
DataType.DECIMAL,
DataType.NUMERIC,
]
@pytest.fixture(scope="module")
def rule_library_test_definition(
metadata: OpenMetadata,
) -> TestDefinition:
"""Create a rule library test definition for SQL expression validation.
The sqlExpression uses Jinja2 templating for parameter substitution.
"""
test_def_name = TestCaseEntityName(generate_name().root)
test_def = metadata.create_or_update(
CreateTestDefinitionRequest(
name=test_def_name,
description=Markdown(root="Rule library test definition for custom SQL expression validation"),
entityType=EntityType.COLUMN,
testPlatforms=[TestPlatform.OpenMetadata],
supportedDataTypes=NUMERIC_DATA_TYPES,
parameterDefinition=[
TestCaseParameterDefinition(
name="minValue",
displayName="Minimum Value",
dataType="INT",
description="Minimum value for comparison",
required=False,
),
TestCaseParameterDefinition(
name="maxValue",
displayName="Maximum Value",
dataType="INT",
description="Maximum value for comparison",
required=False,
),
],
sqlExpression=SqlQuery(
root="SELECT {{ column_name }} FROM {{ table_name }} WHERE {{ column_name }} > {{ minValue }}"
),
validatorClass="ColumnRuleLibrarySqlExpressionValidator",
)
)
yield test_def
metadata.delete(TestDefinition, test_def.id, hard_delete=True)
@pytest.fixture()
def get_rule_library_test_suite_config(workflow_config, sink_config):
def inner(entity_fqn: str, test_case_definitions: List[TestCaseDefinition]): # noqa: UP006
return {
"source": {
"type": "postgres",
"serviceName": "RuleLibraryTestSuite",
"sourceConfig": {
"config": TestSuitePipeline(
type=TestSuiteConfigType.TestSuite,
entityFullyQualifiedName=entity_fqn,
)
},
},
"processor": {
"type": "orm-test-runner",
"config": {"testCases": [obj.model_dump() for obj in test_case_definitions]},
},
"sink": sink_config,
"workflowConfig": workflow_config,
}
return inner
@dataclass
class RuleLibraryTestParameter:
entity_fqn: str
test_case_definition: TestCaseDefinition
expected_status: TestCaseStatus
@pytest.fixture(
params=[
RuleLibraryTestParameter(
entity_fqn="{database_service_fqn}.dvdrental.public.customer",
test_case_definition=TestCaseDefinition(
name="rule_library_customer_ids_greater_than_zero",
testDefinitionName="{test_def_name}",
columnName="customer_id",
parameterValues=[{"name": "minValue", "value": "0"}],
),
expected_status=TestCaseStatus.Failed,
),
RuleLibraryTestParameter(
entity_fqn="{database_service_fqn}.dvdrental.public.customer",
test_case_definition=TestCaseDefinition(
name="rule_library_customer_ids_greater_than_max",
testDefinitionName="{test_def_name}",
columnName="customer_id",
parameterValues=[{"name": "minValue", "value": "99999"}],
),
expected_status=TestCaseStatus.Success,
),
],
ids=lambda x: x.test_case_definition.name,
)
def rule_library_parameters(request, db_service, rule_library_test_definition):
request.param.entity_fqn = request.param.entity_fqn.format(database_service_fqn=db_service.fullyQualifiedName.root)
request.param.test_case_definition.testDefinitionName = rule_library_test_definition.name.root
return request.param
def test_rule_library_sql_expression_validator(
patch_passwords_for_db_services,
run_workflow,
ingestion_config,
db_service: DatabaseService,
metadata: OpenMetadata,
rule_library_parameters: RuleLibraryTestParameter,
get_rule_library_test_suite_config,
cleanup_fqns,
rule_library_test_definition,
):
"""Test the Rule Library SQL Expression validator with various scenarios.
Tests validate that:
1. The Jinja2 template compilation works correctly
2. SQLAlchemy bind parameters are properly substituted
3. The COUNT(*) wrapping returns correct row counts
4. Test case status is correctly determined based on row count (0 = success)
"""
run_workflow(MetadataWorkflow, ingestion_config)
test_suite_config = get_rule_library_test_suite_config(
rule_library_parameters.entity_fqn,
[rule_library_parameters.test_case_definition],
)
run_workflow(TestSuiteWorkflow, test_suite_config)
test_case_fqn = (
f"{rule_library_parameters.entity_fqn}."
f"{rule_library_parameters.test_case_definition.columnName}."
f"{rule_library_parameters.test_case_definition.name}"
)
test_case: TestCase = metadata.get_by_name(
TestCase,
test_case_fqn,
fields=["*"],
nullable=False,
)
cleanup_fqns(TestCase, test_case.fullyQualifiedName.root)
assert test_case.testCaseResult is not None
assert test_case.testCaseResult.testCaseStatus == rule_library_parameters.expected_status