| 
									
										
										
										
											2024-04-17 12:19:37 +02:00
										 |  |  | import os | 
					
						
							|  |  |  | import shutil | 
					
						
							| 
									
										
										
										
											2024-09-20 08:52:40 +02:00
										 |  |  | import tempfile | 
					
						
							| 
									
										
										
										
											2024-04-17 12:19:37 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | import pytest | 
					
						
							| 
									
										
										
										
											2024-06-17 08:56:28 +02:00
										 |  |  | from sqlalchemy import create_engine, text | 
					
						
							| 
									
										
										
										
											2024-04-17 12:19:37 +02:00
										 |  |  | from testcontainers.mssql import SqlServerContainer | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-26 12:12:04 +02:00
										 |  |  | from _openmetadata_testutils.helpers.docker import copy_dir_to_container, try_bind | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  | from metadata.generated.schema.api.services.createDatabaseService import ( | 
					
						
							|  |  |  |     CreateDatabaseServiceRequest, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2024-06-17 08:56:28 +02:00
										 |  |  | from metadata.generated.schema.entity.services.connections.database.mssqlConnection import ( | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  |     MssqlConnection, | 
					
						
							| 
									
										
										
										
											2024-06-17 08:56:28 +02:00
										 |  |  |     MssqlScheme, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  | from metadata.generated.schema.entity.services.databaseService import ( | 
					
						
							|  |  |  |     DatabaseConnection, | 
					
						
							|  |  |  |     DatabaseService, | 
					
						
							|  |  |  |     DatabaseServiceType, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2024-06-17 08:56:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-19 09:09:35 +02:00
										 |  |  | from ..conftest import ingestion_config as base_ingestion_config | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-17 12:19:37 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-20 08:52:40 +02:00
										 |  |  | @pytest.fixture(scope="session") | 
					
						
							|  |  |  | def db_name(): | 
					
						
							|  |  |  |     return "AdventureWorksLT2022" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class CustomSqlServerContainer(SqlServerContainer): | 
					
						
							|  |  |  |     def start(self) -> "DbContainer": | 
					
						
							|  |  |  |         dockerfile = f"""
 | 
					
						
							|  |  |  |             FROM {self.image} | 
					
						
							|  |  |  |             USER root | 
					
						
							|  |  |  |             RUN mkdir -p /data | 
					
						
							|  |  |  |             RUN chown mssql /data | 
					
						
							|  |  |  |             USER mssql | 
					
						
							|  |  |  |             """
 | 
					
						
							|  |  |  |         temp_dir = os.path.join(tempfile.gettempdir(), "mssql") | 
					
						
							|  |  |  |         os.makedirs(temp_dir, exist_ok=True) | 
					
						
							|  |  |  |         temp_dockerfile_path = os.path.join(temp_dir, "Dockerfile") | 
					
						
							|  |  |  |         with open(temp_dockerfile_path, "w") as temp_dockerfile: | 
					
						
							|  |  |  |             temp_dockerfile.write(dockerfile) | 
					
						
							|  |  |  |         self.get_docker_client().build(temp_dir, tag=self.image) | 
					
						
							|  |  |  |         return super().start() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _configure(self) -> None: | 
					
						
							|  |  |  |         super()._configure() | 
					
						
							|  |  |  |         self.with_env("SQL_SA_PASSWORD", self.password) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @pytest.fixture(scope="session") | 
					
						
							|  |  |  | def mssql_container(tmp_path_factory, db_name): | 
					
						
							|  |  |  |     container = CustomSqlServerContainer( | 
					
						
							|  |  |  |         "mcr.microsoft.com/mssql/server:2022-latest", dbname="master" | 
					
						
							| 
									
										
										
										
											2024-06-17 08:56:28 +02:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2024-04-17 12:19:37 +02:00
										 |  |  |     data_dir = tmp_path_factory.mktemp("data") | 
					
						
							|  |  |  |     shutil.copy( | 
					
						
							| 
									
										
										
										
											2024-09-20 08:52:40 +02:00
										 |  |  |         os.path.join(os.path.dirname(__file__), "data", f"{db_name}.bak"), | 
					
						
							| 
									
										
										
										
											2024-04-17 12:19:37 +02:00
										 |  |  |         str(data_dir), | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     with open(data_dir / "install.sql", "w") as f: | 
					
						
							|  |  |  |         f.write( | 
					
						
							| 
									
										
										
										
											2024-09-20 08:52:40 +02:00
										 |  |  |             f"""
 | 
					
						
							| 
									
										
										
										
											2024-04-17 12:19:37 +02:00
										 |  |  | USE [master] | 
					
						
							| 
									
										
										
										
											2024-09-20 08:52:40 +02:00
										 |  |  | RESTORE FILELISTONLY | 
					
						
							|  |  |  |     FROM DISK = '/data/{db_name}.bak'; | 
					
						
							|  |  |  | GO | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RESTORE DATABASE [{db_name}] | 
					
						
							|  |  |  |     FROM DISK = '/data/{db_name}.bak' | 
					
						
							|  |  |  |     WITH MOVE '{db_name}_Data' TO '/var/opt/mssql/data/{db_name}.mdf', | 
					
						
							|  |  |  |          MOVE '{db_name}_Log' TO '/var/opt/mssql/data/{db_name}.ldf'; | 
					
						
							| 
									
										
										
										
											2024-04-17 12:19:37 +02:00
										 |  |  | GO | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-17 08:56:28 +02:00
										 |  |  |     with try_bind(container, 1433, 1433) as container: | 
					
						
							| 
									
										
										
										
											2024-04-17 12:19:37 +02:00
										 |  |  |         docker_container = container.get_wrapped_container() | 
					
						
							| 
									
										
										
										
											2024-07-26 12:12:04 +02:00
										 |  |  |         copy_dir_to_container(str(data_dir), docker_container, "/data") | 
					
						
							| 
									
										
										
										
											2024-04-17 12:19:37 +02:00
										 |  |  |         res = docker_container.exec_run( | 
					
						
							|  |  |  |             [ | 
					
						
							| 
									
										
										
										
											2024-09-20 08:52:40 +02:00
										 |  |  |                 "bash", | 
					
						
							|  |  |  |                 "-c", | 
					
						
							|  |  |  |                 " ".join( | 
					
						
							|  |  |  |                     [ | 
					
						
							|  |  |  |                         "/opt/mssql-tools*/bin/sqlcmd", | 
					
						
							|  |  |  |                         "-U", | 
					
						
							|  |  |  |                         container.username, | 
					
						
							|  |  |  |                         "-P", | 
					
						
							|  |  |  |                         f"'{container.password}'", | 
					
						
							|  |  |  |                         "-d", | 
					
						
							|  |  |  |                         "master", | 
					
						
							|  |  |  |                         "-i", | 
					
						
							|  |  |  |                         "/data/install.sql", | 
					
						
							|  |  |  |                         "-C", | 
					
						
							|  |  |  |                     ] | 
					
						
							|  |  |  |                 ), | 
					
						
							| 
									
										
										
										
											2024-04-17 12:19:37 +02:00
										 |  |  |             ] | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         if res[0] != 0: | 
					
						
							|  |  |  |             raise Exception("Failed to create mssql database:" + res[1].decode("utf-8")) | 
					
						
							| 
									
										
										
										
											2024-06-17 08:56:28 +02:00
										 |  |  |         engine = create_engine( | 
					
						
							|  |  |  |             "mssql+pytds://" + container.get_connection_url().split("://")[1], | 
					
						
							|  |  |  |             connect_args={"autocommit": True}, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         with engine.connect() as conn: | 
					
						
							|  |  |  |             transaciton = conn.begin() | 
					
						
							|  |  |  |             conn.execute( | 
					
						
							|  |  |  |                 text( | 
					
						
							| 
									
										
										
										
											2024-09-20 08:52:40 +02:00
										 |  |  |                     f"SELECT * INTO {db_name}.SalesLT.CustomerCopy FROM {db_name}.SalesLT.Customer;" | 
					
						
							| 
									
										
										
										
											2024-06-17 08:56:28 +02:00
										 |  |  |                 ) | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |             transaciton.commit() | 
					
						
							| 
									
										
										
										
											2024-04-17 12:19:37 +02:00
										 |  |  |         yield container | 
					
						
							| 
									
										
										
										
											2024-06-17 08:56:28 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @pytest.fixture( | 
					
						
							|  |  |  |     scope="module", | 
					
						
							|  |  |  |     params=[ | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  |         MssqlScheme.mssql_pytds, | 
					
						
							|  |  |  |         MssqlScheme.mssql_pyodbc, | 
					
						
							| 
									
										
										
										
											2024-06-17 08:56:28 +02:00
										 |  |  |     ], | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  | def scheme(request): | 
					
						
							|  |  |  |     return request.param | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @pytest.fixture(scope="module") | 
					
						
							| 
									
										
										
										
											2024-09-20 08:52:40 +02:00
										 |  |  | def create_service_request(mssql_container, scheme, tmp_path_factory, db_name): | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  |     return CreateDatabaseServiceRequest( | 
					
						
							|  |  |  |         name="docker_test_" + tmp_path_factory.mktemp("mssql").name + "_" + scheme.name, | 
					
						
							|  |  |  |         serviceType=DatabaseServiceType.Mssql, | 
					
						
							|  |  |  |         connection=DatabaseConnection( | 
					
						
							|  |  |  |             config=MssqlConnection( | 
					
						
							|  |  |  |                 username=mssql_container.username, | 
					
						
							|  |  |  |                 password=mssql_container.password, | 
					
						
							|  |  |  |                 hostPort="localhost:" | 
					
						
							|  |  |  |                 + mssql_container.get_exposed_port(mssql_container.port), | 
					
						
							| 
									
										
										
										
											2024-09-20 08:52:40 +02:00
										 |  |  |                 database=db_name, | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  |                 scheme=scheme, | 
					
						
							|  |  |  |                 ingestAllDatabases=True, | 
					
						
							|  |  |  |                 connectionOptions={ | 
					
						
							|  |  |  |                     "TrustServerCertificate": "yes", | 
					
						
							|  |  |  |                     "MARS_Connection": "yes", | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |         ), | 
					
						
							| 
									
										
										
										
											2024-06-17 08:56:28 +02:00
										 |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  | @pytest.fixture(scope="module") | 
					
						
							| 
									
										
										
										
											2024-08-19 09:09:35 +02:00
										 |  |  | def ingestion_config( | 
					
						
							| 
									
										
										
										
											2024-09-20 08:52:40 +02:00
										 |  |  |     db_service, | 
					
						
							|  |  |  |     tmp_path_factory, | 
					
						
							|  |  |  |     workflow_config, | 
					
						
							|  |  |  |     sink_config, | 
					
						
							|  |  |  |     base_ingestion_config, | 
					
						
							|  |  |  |     db_name, | 
					
						
							| 
									
										
										
										
											2024-08-19 09:09:35 +02:00
										 |  |  | ): | 
					
						
							|  |  |  |     base_ingestion_config["source"]["sourceConfig"]["config"][ | 
					
						
							|  |  |  |         "databaseFilterPattern" | 
					
						
							|  |  |  |     ] = { | 
					
						
							| 
									
										
										
										
											2024-09-20 08:52:40 +02:00
										 |  |  |         "includes": ["TestDB", db_name], | 
					
						
							| 
									
										
										
										
											2024-06-17 08:56:28 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-08-19 09:09:35 +02:00
										 |  |  |     return base_ingestion_config | 
					
						
							| 
									
										
										
										
											2024-06-17 08:56:28 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @pytest.fixture(scope="module") | 
					
						
							| 
									
										
										
										
											2024-07-17 08:11:34 +02:00
										 |  |  | def unmask_password(create_service_request): | 
					
						
							|  |  |  |     def inner(service: DatabaseService): | 
					
						
							|  |  |  |         service.connection.config.password = ( | 
					
						
							|  |  |  |             create_service_request.connection.config.password | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         return service | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return inner |