| 
									
										
										
										
											2024-06-20 16:54:12 +02:00
										 |  |  | import logging | 
					
						
							| 
									
										
										
										
											2024-05-22 17:12:00 +02:00
										 |  |  | import sys | 
					
						
							| 
									
										
										
										
											2024-07-19 12:12:34 +02:00
										 |  |  | from typing import List, Tuple, Type | 
					
						
							| 
									
										
										
										
											2024-05-22 17:12:00 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | import pytest | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-25 07:51:22 +02:00
										 |  |  | from _openmetadata_testutils.ometa import int_admin_ometa | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  | from metadata.generated.schema.entity.services.databaseService import DatabaseService | 
					
						
							| 
									
										
										
										
											2025-04-02 08:38:17 +02:00
										 |  |  | from metadata.generated.schema.metadataIngestion.databaseServiceAutoClassificationPipeline import ( | 
					
						
							|  |  |  |     AutoClassificationConfigType, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2024-08-21 09:47:30 +02:00
										 |  |  | from metadata.generated.schema.metadataIngestion.databaseServiceMetadataPipeline import ( | 
					
						
							|  |  |  |     DatabaseMetadataConfigType, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  | from metadata.generated.schema.metadataIngestion.workflow import LogLevels | 
					
						
							| 
									
										
										
										
											2024-08-20 16:33:55 +05:30
										 |  |  | from metadata.ingestion.api.common import Entity | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  | from metadata.ingestion.ometa.ometa_api import OpenMetadata | 
					
						
							|  |  |  | from metadata.workflow.ingestion import IngestionWorkflow | 
					
						
							| 
									
										
										
										
											2024-05-22 17:12:00 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | if not sys.version_info >= (3, 9): | 
					
						
							| 
									
										
										
										
											2024-09-12 07:13:01 +02:00
										 |  |  |     # these tests use test-containers which are not supported in python 3.8 | 
					
						
							|  |  |  |     collect_ignore = ["trino", "kafka", "datalake"] | 
					
						
							| 
									
										
										
										
											2024-05-22 17:12:00 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-20 16:54:12 +02:00
										 |  |  | @pytest.fixture(scope="session", autouse=True) | 
					
						
							|  |  |  | def configure_logging(): | 
					
						
							|  |  |  |     logging.getLogger("sqlfluff").setLevel(logging.CRITICAL) | 
					
						
							|  |  |  |     logging.getLogger("pytds").setLevel(logging.CRITICAL) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  | @pytest.fixture(scope="session") | 
					
						
							| 
									
										
										
										
											2024-05-22 17:12:00 +02:00
										 |  |  | def metadata(): | 
					
						
							|  |  |  |     return int_admin_ometa() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def pytest_pycollect_makeitem(collector, name, obj): | 
					
						
							|  |  |  |     try: | 
					
						
							| 
									
										
										
										
											2024-07-02 09:56:35 +02:00
										 |  |  |         bases = [base.__name__ for base in obj.mro()] | 
					
						
							|  |  |  |         for cls in ("BaseModel", "Enum"): | 
					
						
							|  |  |  |             if cls in bases: | 
					
						
							|  |  |  |                 return [] | 
					
						
							|  |  |  |     except (AttributeError, TypeError): | 
					
						
							| 
									
										
										
										
											2024-05-22 17:12:00 +02:00
										 |  |  |         pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @pytest.fixture(scope="session", autouse=sys.version_info >= (3, 9)) | 
					
						
							|  |  |  | def config_testcontatiners(): | 
					
						
							|  |  |  |     from testcontainers.core.config import testcontainers_config | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     testcontainers_config.max_tries = 10 | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @pytest.fixture(scope="session") | 
					
						
							|  |  |  | def sink_config(metadata): | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |         "type": "metadata-rest", | 
					
						
							|  |  |  |         "config": {}, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @pytest.fixture(scope="session") | 
					
						
							|  |  |  | def workflow_config(metadata): | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |         "loggerLevel": LogLevels.DEBUG.value, | 
					
						
							|  |  |  |         "openMetadataServerConfig": metadata.config.model_dump(), | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-20 10:42:10 +02:00
										 |  |  | @pytest.fixture(scope="module") | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  | def profiler_config(db_service, workflow_config, sink_config): | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |         "source": { | 
					
						
							|  |  |  |             "type": db_service.connection.config.type.value.lower(), | 
					
						
							|  |  |  |             "serviceName": db_service.fullyQualifiedName.root, | 
					
						
							|  |  |  |             "sourceConfig": { | 
					
						
							|  |  |  |                 "config": { | 
					
						
							|  |  |  |                     "type": "Profiler", | 
					
						
							| 
									
										
										
										
											2024-11-07 18:37:31 +01:00
										 |  |  |                     "timeoutSeconds": 600, | 
					
						
							|  |  |  |                     "threadCount": 1,  # easier for debugging | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  |                 } | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         "processor": { | 
					
						
							|  |  |  |             "type": "orm-profiler", | 
					
						
							|  |  |  |             "config": {}, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         "sink": sink_config, | 
					
						
							|  |  |  |         "workflowConfig": workflow_config, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-02 08:38:17 +02:00
										 |  |  | @pytest.fixture(scope="module") | 
					
						
							|  |  |  | def classifier_config(db_service, workflow_config, sink_config): | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |         "source": { | 
					
						
							|  |  |  |             "type": db_service.connection.config.type.value.lower(), | 
					
						
							|  |  |  |             "serviceName": db_service.fullyQualifiedName.root, | 
					
						
							|  |  |  |             "sourceConfig": { | 
					
						
							|  |  |  |                 "config": { | 
					
						
							|  |  |  |                     "type": AutoClassificationConfigType.AutoClassification.value, | 
					
						
							|  |  |  |                     "storeSampleData": True, | 
					
						
							|  |  |  |                     "enableAutoClassification": True, | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         "processor": { | 
					
						
							|  |  |  |             "type": "orm-profiler", | 
					
						
							|  |  |  |             "config": {}, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         "sink": sink_config, | 
					
						
							|  |  |  |         "workflowConfig": workflow_config, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-20 10:42:10 +02:00
										 |  |  | @pytest.fixture(scope="module") | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  | def run_workflow(): | 
					
						
							| 
									
										
										
										
											2024-08-08 16:31:31 +02:00
										 |  |  |     def _run(workflow_type: Type[IngestionWorkflow], config, raise_from_status=True): | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  |         workflow: IngestionWorkflow = workflow_type.create(config) | 
					
						
							|  |  |  |         workflow.execute() | 
					
						
							|  |  |  |         if raise_from_status: | 
					
						
							| 
									
										
										
										
											2024-11-27 08:50:54 +01:00
										 |  |  |             workflow.print_status() | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  |             workflow.raise_from_status() | 
					
						
							|  |  |  |         return workflow | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return _run | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @pytest.fixture(scope="module") | 
					
						
							|  |  |  | def db_service(metadata, create_service_request, unmask_password): | 
					
						
							|  |  |  |     service_entity = metadata.create_or_update(data=create_service_request) | 
					
						
							|  |  |  |     fqn = service_entity.fullyQualifiedName.root | 
					
						
							|  |  |  |     yield unmask_password(service_entity) | 
					
						
							|  |  |  |     service_entity = metadata.get_by_name(DatabaseService, fqn) | 
					
						
							|  |  |  |     if service_entity: | 
					
						
							|  |  |  |         metadata.delete( | 
					
						
							|  |  |  |             DatabaseService, service_entity.id, recursive=True, hard_delete=True | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @pytest.fixture(scope="module") | 
					
						
							|  |  |  | def unmask_password(create_service_request): | 
					
						
							|  |  |  |     """Unmask the db passwrod returned by the metadata service.
 | 
					
						
							|  |  |  |     You can override this at the test_module level to implement custom password handling. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Example: | 
					
						
							|  |  |  |     @pytest.fixture(scope="module") | 
					
						
							|  |  |  |     def unmask_password(my_container1, my_container2): | 
					
						
							|  |  |  |         def patch_password(service: DatabaseService): | 
					
						
							|  |  |  |             if service.connection.config.authType.password == "my_password": | 
					
						
							|  |  |  |               ... # do something else | 
					
						
							|  |  |  |             return service | 
					
						
							|  |  |  |         return patch_password | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def patch_password(service: DatabaseService): | 
					
						
							| 
									
										
										
										
											2025-01-10 10:10:11 +05:30
										 |  |  |         if hasattr(service.connection.config, "authType"): | 
					
						
							|  |  |  |             service.connection.config.authType.password = ( | 
					
						
							|  |  |  |                 create_service_request.connection.config.authType.password | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |             return service | 
					
						
							|  |  |  |         service.connection.config.password = ( | 
					
						
							|  |  |  |             create_service_request.connection.config.password | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  |         ) | 
					
						
							|  |  |  |         return service | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return patch_password | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @pytest.fixture(scope="module") | 
					
						
							|  |  |  | def create_service_request(): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Implement in the test module to create a service request | 
					
						
							|  |  |  |     Example: | 
					
						
							|  |  |  |     def create_service_request(scope="module"): | 
					
						
							|  |  |  |         return CreateDatabaseServiceRequest( | 
					
						
							|  |  |  |             name="my_service", | 
					
						
							|  |  |  |             serviceType=DatabaseServiceType.MyService, | 
					
						
							|  |  |  |             connection=DatabaseConnection( | 
					
						
							|  |  |  |                 config=MyServiceConnection( | 
					
						
							|  |  |  |                     username="my_user", | 
					
						
							|  |  |  |                     password="my_password", | 
					
						
							|  |  |  |                     host="localhost", | 
					
						
							|  |  |  |                     port="5432", | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     raise NotImplementedError("Implement in the test module") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-20 10:42:10 +02:00
										 |  |  | @pytest.fixture(scope="module") | 
					
						
							|  |  |  | def monkeymodule(): | 
					
						
							|  |  |  |     with pytest.MonkeyPatch.context() as mp: | 
					
						
							|  |  |  |         yield mp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @pytest.fixture(scope="module") | 
					
						
							|  |  |  | def patch_passwords_for_db_services(db_service, unmask_password, monkeymodule): | 
					
						
							| 
									
										
										
										
											2024-08-19 09:09:35 +02:00
										 |  |  |     """Patch the password for all db services returned by the metadata service.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Usage: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_my_test(db_service, patch_passwords_for_db_services): | 
					
						
							|  |  |  |         ... | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     OR | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @pytest.usefixtures("patch_passwords_for_db_services") | 
					
						
							|  |  |  |     def test_my_test(db_service): | 
					
						
							|  |  |  |         ... | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  |     def override_password(getter): | 
					
						
							|  |  |  |         def inner(*args, **kwargs): | 
					
						
							|  |  |  |             result = getter(*args, **kwargs) | 
					
						
							|  |  |  |             if isinstance(result, DatabaseService): | 
					
						
							|  |  |  |                 if result.fullyQualifiedName.root == db_service.fullyQualifiedName.root: | 
					
						
							|  |  |  |                     return unmask_password(result) | 
					
						
							|  |  |  |             return result | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return inner | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-20 10:42:10 +02:00
										 |  |  |     monkeymodule.setattr( | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  |         "metadata.ingestion.ometa.ometa_api.OpenMetadata.get_by_name", | 
					
						
							|  |  |  |         override_password(OpenMetadata.get_by_name), | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-20 10:42:10 +02:00
										 |  |  |     monkeymodule.setattr( | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  |         "metadata.ingestion.ometa.ometa_api.OpenMetadata.get_by_id", | 
					
						
							|  |  |  |         override_password(OpenMetadata.get_by_id), | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2024-07-19 12:12:34 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @pytest.fixture | 
					
						
							|  |  |  | def cleanup_fqns(metadata): | 
					
						
							|  |  |  |     fqns: List[Tuple[Type[Entity], str]] = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def inner(entity_type: Type[Entity], fqn: str): | 
					
						
							|  |  |  |         fqns.append((entity_type, fqn)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     yield inner | 
					
						
							|  |  |  |     for etype, fqn in fqns: | 
					
						
							|  |  |  |         entity = metadata.get_by_name(etype, fqn, fields=["*"]) | 
					
						
							|  |  |  |         if entity: | 
					
						
							|  |  |  |             metadata.delete(etype, entity.id, recursive=True, hard_delete=True) | 
					
						
							| 
									
										
										
										
											2024-08-19 09:09:35 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @pytest.fixture(scope="module") | 
					
						
							|  |  |  | def ingestion_config(db_service, metadata, workflow_config, sink_config): | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |         "source": { | 
					
						
							|  |  |  |             "type": db_service.connection.config.type.value.lower(), | 
					
						
							|  |  |  |             "serviceName": db_service.fullyQualifiedName.root, | 
					
						
							| 
									
										
										
										
											2024-08-21 09:47:30 +02:00
										 |  |  |             "sourceConfig": { | 
					
						
							|  |  |  |                 "config": {"type": DatabaseMetadataConfigType.DatabaseMetadata.value} | 
					
						
							|  |  |  |             }, | 
					
						
							| 
									
										
										
										
											2024-08-19 09:09:35 +02:00
										 |  |  |             "serviceConnection": db_service.connection.model_dump(), | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         "sink": sink_config, | 
					
						
							|  |  |  |         "workflowConfig": workflow_config, | 
					
						
							|  |  |  |     } |