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

228 lines
8.2 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.
"""
Unit tests for DbtHttpConfig — verifies that custom HTTP headers and SSL
verification settings are correctly forwarded to requests.get() calls.
"""
from unittest.mock import MagicMock, patch
import pytest
import requests
from metadata.generated.schema.metadataIngestion.dbtconfig.dbtHttpConfig import (
DbtConfigType,
DbtHttpConfig,
)
from metadata.generated.schema.security.ssl.validateSSLClientConfig import (
ValidateSslClientConfig,
)
from metadata.generated.schema.security.ssl.verifySSLConfig import SslConfig, VerifySSL
from metadata.ingestion.models.custom_pydantic import CustomSecretStr
from metadata.ingestion.source.database.dbt.dbt_config import (
DBTConfigException,
get_dbt_details,
)
MANIFEST_URL = "https://example.com/manifest.json"
MANIFEST_JSON = {
"metadata": {},
"nodes": {},
"sources": {},
"exposures": {},
"metrics": {},
}
def _make_json_response(data: dict, status_code: int = 200) -> MagicMock:
"""Build a mock requests.Response whose .json() returns *data*."""
resp = MagicMock()
resp.status_code = status_code
resp.json.return_value = data
resp.raise_for_status.return_value = None
return resp
def _base_config(**kwargs) -> DbtHttpConfig:
"""Minimal valid DbtHttpConfig with only manifest path set."""
return DbtHttpConfig(
dbtConfigType=DbtConfigType.http,
dbtManifestHttpPath=MANIFEST_URL,
**kwargs,
)
class TestDbtHttpConfigNoAuth:
"""requests.get is called with empty headers and verify=True when no auth/SSL is configured."""
def test_no_auth_no_ssl_uses_empty_headers_and_verify_true(self):
config = _base_config()
manifest_resp = _make_json_response(MANIFEST_JSON)
with patch(
"metadata.ingestion.source.database.dbt.dbt_config.requests.get",
return_value=manifest_resp,
) as mock_get:
list(get_dbt_details(config))
mock_get.assert_called_once_with(MANIFEST_URL, headers={}, verify=True, timeout=30)
class TestDbtHttpConfigCustomHeaders:
"""Custom headers dict is forwarded verbatim to every requests.get() call."""
def test_custom_authorization_header_passed_to_manifest_fetch(self):
config = _base_config(dbtHttpHeaders={"Authorization": "Bearer mytoken"})
manifest_resp = _make_json_response(MANIFEST_JSON)
with patch(
"metadata.ingestion.source.database.dbt.dbt_config.requests.get",
return_value=manifest_resp,
) as mock_get:
list(get_dbt_details(config))
_, kwargs = mock_get.call_args
assert kwargs["headers"] == {"Authorization": "Bearer mytoken"}
def test_gitlab_private_token_header_passed(self):
config = _base_config(dbtHttpHeaders={"PRIVATE-TOKEN": "glpat-abc123"})
manifest_resp = _make_json_response(MANIFEST_JSON)
with patch(
"metadata.ingestion.source.database.dbt.dbt_config.requests.get",
return_value=manifest_resp,
) as mock_get:
list(get_dbt_details(config))
_, kwargs = mock_get.call_args
assert kwargs["headers"] == {"PRIVATE-TOKEN": "glpat-abc123"}
def test_headers_forwarded_to_all_optional_fetch_calls(self):
config = _base_config(
dbtHttpHeaders={"Authorization": "Bearer tok"},
dbtRunResultsHttpPath="https://example.com/run_results.json",
dbtCatalogHttpPath="https://example.com/catalog.json",
dbtSourcesHttpPath="https://example.com/sources.json",
)
manifest_resp = _make_json_response(MANIFEST_JSON)
other_resp = _make_json_response({})
with patch(
"requests.get",
side_effect=[manifest_resp, other_resp, other_resp, other_resp],
) as mock_get:
list(get_dbt_details(config))
assert mock_get.call_count == 4
for actual_call in mock_get.call_args_list:
_, kwargs = actual_call
assert kwargs["headers"] == {"Authorization": "Bearer tok"}
class TestDbtHttpConfigSSLVerify:
"""verifySSL enum values produce correct verify= argument to requests.get()."""
def test_verify_ssl_ignore_passes_verify_false(self):
config = _base_config(dbtVerifySSL=VerifySSL.ignore)
manifest_resp = _make_json_response(MANIFEST_JSON)
with patch(
"metadata.ingestion.source.database.dbt.dbt_config.requests.get",
return_value=manifest_resp,
) as mock_get:
list(get_dbt_details(config))
_, kwargs = mock_get.call_args
assert kwargs["verify"] is False
def test_verify_ssl_validate_passes_ca_cert_path(self):
ssl_config = SslConfig(root=ValidateSslClientConfig(caCertificate=CustomSecretStr("/path/to/ca.pem")))
config = _base_config(
dbtVerifySSL=VerifySSL.validate,
dbtSSLConfig=ssl_config,
)
manifest_resp = _make_json_response(MANIFEST_JSON)
with patch(
"metadata.ingestion.source.database.dbt.dbt_config.requests.get",
return_value=manifest_resp,
) as mock_get:
list(get_dbt_details(config))
_, kwargs = mock_get.call_args
assert kwargs["verify"] == "/path/to/ca.pem"
def test_verify_ssl_no_ssl_passes_verify_true(self):
config = _base_config(dbtVerifySSL=VerifySSL.no_ssl)
manifest_resp = _make_json_response(MANIFEST_JSON)
with patch(
"metadata.ingestion.source.database.dbt.dbt_config.requests.get",
return_value=manifest_resp,
) as mock_get:
list(get_dbt_details(config))
_, kwargs = mock_get.call_args
assert kwargs["verify"] is True
class TestDbtHttpConfigErrorHandling:
"""SSL and auth errors raise DBTConfigException with informative messages."""
def test_ssl_error_raises_dbt_config_exception(self):
ssl_config = SslConfig(root=ValidateSslClientConfig(caCertificate=CustomSecretStr("/path/to/ca.pem")))
config = _base_config(
dbtVerifySSL=VerifySSL.validate,
dbtSSLConfig=ssl_config,
)
with patch( # noqa: SIM117
"requests.get",
side_effect=requests.exceptions.SSLError("cert verify failed"),
):
with pytest.raises(DBTConfigException) as exc_info:
list(get_dbt_details(config))
assert "SSL verification failed" in str(exc_info.value)
assert MANIFEST_URL in str(exc_info.value)
def test_401_raises_dbt_config_exception_with_auth_hint(self):
config = _base_config()
mock_resp = MagicMock()
mock_resp.status_code = 401
http_error = requests.exceptions.HTTPError(response=mock_resp)
mock_resp.raise_for_status.side_effect = http_error
with patch( # noqa: SIM117
"metadata.ingestion.source.database.dbt.dbt_config.requests.get",
return_value=mock_resp,
):
with pytest.raises(DBTConfigException) as exc_info:
list(get_dbt_details(config))
assert "dbtHttpHeaders" in str(exc_info.value)
def test_403_raises_dbt_config_exception_with_auth_hint(self):
config = _base_config()
mock_resp = MagicMock()
mock_resp.status_code = 403
http_error = requests.exceptions.HTTPError(response=mock_resp)
mock_resp.raise_for_status.side_effect = http_error
with patch( # noqa: SIM117
"metadata.ingestion.source.database.dbt.dbt_config.requests.get",
return_value=mock_resp,
):
with pytest.raises(DBTConfigException) as exc_info:
list(get_dbt_details(config))
assert "dbtHttpHeaders" in str(exc_info.value)