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

262 lines
10 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.
"""
OpenMetadata high-level API DataContract test
"""
import time
import uuid
from datetime import datetime
import pytest
from metadata.generated.schema.api.data.createDataContract import (
CreateDataContractRequest,
)
from metadata.generated.schema.api.data.createTable import CreateTableRequest
from metadata.generated.schema.entity.data.dataContract import DataContract
from metadata.generated.schema.entity.data.table import Column, DataType, Table
from metadata.generated.schema.entity.datacontract.dataContractResult import (
DataContractResult,
)
from metadata.generated.schema.entity.datacontract.qualityValidation import (
QualityValidation,
)
from metadata.generated.schema.entity.datacontract.schemaValidation import (
SchemaValidation,
)
from metadata.generated.schema.type.basic import EntityName, Uuid
from metadata.generated.schema.type.contractExecutionStatus import (
ContractExecutionStatus,
)
from metadata.generated.schema.type.entityReference import EntityReference
from metadata.generated.schema.type.status import EntityStatus
@pytest.fixture(scope="module")
def test_database(metadata, database_service):
"""Module-scoped database for data contract tests."""
from metadata.generated.schema.api.data.createDatabase import CreateDatabaseRequest
from metadata.generated.schema.entity.data.database import Database
database_request = CreateDatabaseRequest(
name="test-db-datacontract",
service=database_service.fullyQualifiedName,
)
database = metadata.create_or_update(data=database_request)
yield database
metadata.delete(entity=Database, entity_id=database.id, hard_delete=True)
@pytest.fixture(scope="module")
def test_schema(metadata, test_database):
"""Module-scoped database schema for data contract tests."""
from metadata.generated.schema.api.data.createDatabaseSchema import (
CreateDatabaseSchemaRequest,
)
from metadata.generated.schema.entity.data.databaseSchema import DatabaseSchema
schema_request = CreateDatabaseSchemaRequest(
name="test-schema-datacontract",
database=test_database.fullyQualifiedName,
)
schema = metadata.create_or_update(data=schema_request)
yield schema
metadata.delete(entity=DatabaseSchema, entity_id=schema.id, recursive=True, hard_delete=True)
@pytest.fixture(scope="module")
def test_table(metadata, test_schema):
"""Module-scoped table for data contract tests."""
table_request = CreateTableRequest(
name="test-table-datacontract",
databaseSchema=test_schema.fullyQualifiedName,
columns=[
Column(name="id", dataType=DataType.BIGINT),
Column(name="name", dataType=DataType.STRING),
],
)
table = metadata.create_or_update(data=table_request)
yield table
metadata.delete(entity=Table, entity_id=table.id, hard_delete=True)
@pytest.fixture
def data_contract_request(test_table):
"""Create data contract request."""
return CreateDataContractRequest(
name=EntityName(root="TestDataContract"),
description="Test data contract for validation",
entity=EntityReference(id=test_table.id, type="table"),
entityStatus=EntityStatus.Draft,
schema=test_table.columns[:2],
)
class TestOMetaDataContractAPI:
"""
Data Contract API integration tests.
Tests CRUD operations, execution results, and status updates.
Uses fixtures from conftest:
- metadata: OpenMetadata client (session scope)
- database_service: DatabaseService (module scope)
"""
def test_create_data_contract(self, metadata, test_table, data_contract_request):
"""
We can create a DataContract and we receive it back as Entity
"""
res = metadata.create_or_update(data=data_contract_request)
assert res.name == data_contract_request.name
assert res.description == data_contract_request.description
assert res.entityStatus == data_contract_request.entityStatus
assert res.entity.id == test_table.id
assert res.entity.type == "table"
assert len(res.schema_) == 2
metadata.delete(entity=DataContract, entity_id=res.id, hard_delete=True)
def test_get_data_contract_by_name(self, metadata, data_contract_request):
"""
We can fetch DataContract by name
"""
contract = metadata.create_or_update(data=data_contract_request)
res = metadata.get_by_name(entity=DataContract, fqn=contract.fullyQualifiedName.root)
assert res.name == data_contract_request.name
assert res.description == data_contract_request.description
assert res.entityStatus == data_contract_request.entityStatus
metadata.delete(entity=DataContract, entity_id=res.id, hard_delete=True)
def test_get_data_contract_by_id(self, metadata, data_contract_request):
"""
We can fetch DataContract by ID
"""
created_contract = metadata.create_or_update(data=data_contract_request)
res = metadata.get_by_id(entity=DataContract, entity_id=created_contract.id)
assert res.name == data_contract_request.name
assert res.id == created_contract.id
assert res.entityStatus == data_contract_request.entityStatus
metadata.delete(entity=DataContract, entity_id=res.id, hard_delete=True)
def test_put_data_contract_result(self, metadata, data_contract_request):
"""
We can create and store DataContract execution results
"""
created_contract = metadata.create_or_update(data=data_contract_request)
contract_result = DataContractResult(
id=Uuid(root=uuid.uuid4()),
dataContractFQN=created_contract.fullyQualifiedName,
timestamp=int(datetime.now().timestamp() * 1000),
contractExecutionStatus=ContractExecutionStatus.Success,
schemaValidation=SchemaValidation(
passed=2,
failed=0,
total=2,
),
qualityValidation=QualityValidation(
passed=0,
failed=0,
total=0,
),
)
result = metadata.put_data_contract_result(created_contract.id, contract_result)
assert result is not None
assert result.contractExecutionStatus == ContractExecutionStatus.Success
latest_result = metadata.get_latest_data_contract_result(created_contract.id)
assert latest_result is not None
assert latest_result.contractExecutionStatus == ContractExecutionStatus.Success
# Retry logic for eventual consistency - backend may take time to index results
all_results = None
for attempt in range(5): # noqa: B007
all_results = metadata.get_data_contract_results(created_contract.id)
if all_results and len(all_results) >= 1:
break
time.sleep(0.5) # Wait 500ms before retry
assert all_results is not None
assert len(all_results) >= 1
metadata.delete(entity=DataContract, entity_id=created_contract.id, hard_delete=True)
def test_update_data_contract_status(self, metadata, test_table, data_contract_request):
"""
We can update DataContract status
"""
created_contract = metadata.create_or_update(data=data_contract_request)
assert created_contract.entityStatus == EntityStatus.Draft
updated_request = CreateDataContractRequest(
name=data_contract_request.name,
description=data_contract_request.description,
entity=data_contract_request.entity,
entityStatus=EntityStatus.Approved,
schema=data_contract_request.schema_,
)
updated_contract = metadata.create_or_update(data=updated_request)
assert updated_contract.entityStatus == EntityStatus.Approved
assert updated_contract.id == created_contract.id
metadata.delete(entity=DataContract, entity_id=updated_contract.id, hard_delete=True)
def test_get_data_contract_by_entity_id(self, metadata, test_table, data_contract_request):
"""
We can fetch DataContract by entity ID
"""
created_contract = metadata.create_or_update(data=data_contract_request)
res = metadata.get_data_contract_by_entity_id(entity_id=test_table.id, entity_type="table")
# The contract was created with entity=EntityReference(id=test_table.id),
# so the backend must return it when queried by that entity ID.
assert res is not None
assert isinstance(res, DataContract)
assert res.id == created_contract.id
metadata.delete(entity=DataContract, entity_id=created_contract.id, hard_delete=True)
def test_validate_data_contract_by_entity_id(self, metadata, test_table, data_contract_request):
created_contract = metadata.create_or_update(data=data_contract_request)
res = metadata.validate_data_contract_by_entity_id(entity_id=test_table.id, entity_type="table")
assert res is None or isinstance(res, DataContractResult)
metadata.delete(entity=DataContract, entity_id=created_contract.id, hard_delete=True)
def test_delete_data_contract_results_before(self, metadata, data_contract_request):
created_contract = metadata.create_or_update(data=data_contract_request)
# Call delete endpoint
timestamp = int(datetime.now().timestamp() * 1000)
res = metadata.delete_data_contract_results_before(created_contract.id, timestamp)
assert type(res) is bool
metadata.delete(entity=DataContract, entity_id=created_contract.id, hard_delete=True)