diff --git a/airbyte_cdk/sources/declarative/parsers/custom_code_compiler.py b/airbyte_cdk/sources/declarative/parsers/custom_code_compiler.py index d759cd90df..69626a3ee1 100644 --- a/airbyte_cdk/sources/declarative/parsers/custom_code_compiler.py +++ b/airbyte_cdk/sources/declarative/parsers/custom_code_compiler.py @@ -89,7 +89,6 @@ def validate_python_code( { "expected_checksum": checksum, "actual_checksum": calculated_checksum, - "code_text": code_text, } ), ) diff --git a/airbyte_cdk/test/standard_tests/declarative_sources.py b/airbyte_cdk/test/standard_tests/declarative_sources.py index 18a2a5910e..37ef8d0c23 100644 --- a/airbyte_cdk/test/standard_tests/declarative_sources.py +++ b/airbyte_cdk/test/standard_tests/declarative_sources.py @@ -16,12 +16,11 @@ def md5_checksum(file_path: Path) -> str: - """Helper function to calculate the MD5 checksum of a file. + """Calculate the MD5 checksum of the file text, as injected by `create_connector`. This is used to calculate the checksum of the `components.py` file, if it exists. """ - with open(file_path, "rb") as file: - return md5(file.read()).hexdigest() + return md5(file_path.read_text().encode()).hexdigest() class DeclarativeSourceTestSuite(SourceTestSuiteBase): diff --git a/unit_tests/source_declarative_manifest/test_source_declarative_w_custom_components.py b/unit_tests/source_declarative_manifest/test_source_declarative_w_custom_components.py index a8b086a6d9..8d866e0e96 100644 --- a/unit_tests/source_declarative_manifest/test_source_declarative_w_custom_components.py +++ b/unit_tests/source_declarative_manifest/test_source_declarative_w_custom_components.py @@ -158,17 +158,23 @@ def test_invalid_checksum_fails_to_run( datetime.datetime.now() - datetime.timedelta(days=2) ).strftime("%Y-%m-%d") + actual_checksum = py_components_config_dict["__injected_components_py_checksums"][hash_type] py_components_config_dict["__injected_components_py_checksums"][hash_type] = "invalid_checksum" with NamedTemporaryFile(delete=False, suffix=".json") as temp_config_file: json_str = json.dumps(py_components_config_dict) Path(temp_config_file.name).write_text(json_str) temp_config_file.flush() - with pytest.raises(AirbyteCodeTamperedError): + with pytest.raises(AirbyteCodeTamperedError) as exc_info: source = create_declarative_source( ["check", "--config", temp_config_file.name], ) + # No components code in the message: it lands in failure reasons and logs + assert str(exc_info.value) == f"{hash_type} checksum does not match." + str( + {"expected_checksum": "invalid_checksum", "actual_checksum": actual_checksum} + ) + @pytest.mark.parametrize( "env_value, should_raise", diff --git a/unit_tests/test/test_standard_tests.py b/unit_tests/test/test_standard_tests.py index 6250d04ac7..8f11ade6e1 100644 --- a/unit_tests/test/test_standard_tests.py +++ b/unit_tests/test/test_standard_tests.py @@ -9,9 +9,11 @@ from airbyte_cdk.sources.declarative.concurrent_declarative_source import ( ConcurrentDeclarativeSource, ) +from airbyte_cdk.sources.declarative.parsers.custom_code_compiler import validate_python_code from airbyte_cdk.sources.source import Source from airbyte_cdk.test.models.scenario import ConnectorTestScenario from airbyte_cdk.test.standard_tests._job_runner import IConnector +from airbyte_cdk.test.standard_tests.declarative_sources import md5_checksum from airbyte_cdk.test.standard_tests.docker_base import DockerConnectorTestSuite from airbyte_cdk.test.standard_tests.pytest_hooks import _scenario_test_ids @@ -108,3 +110,14 @@ def test_dedup_scenarios_conflicting_statuses_raise() -> None: def test_scenario_test_ids(config_paths: list[Path], expected_ids: list[str]) -> None: scenarios = [ConnectorTestScenario(config_path=path) for path in config_paths] assert _scenario_test_ids(scenarios) == expected_ids + + +def test_md5_checksum_matches_injected_text_with_crlf(tmp_path: Path) -> None: + """Assert the checksum covers `read_text()`, which `create_connector` injects, not raw bytes.""" + components_py_path = tmp_path / "components.py" + components_py_path.write_bytes(b"def sample_function():\r\n return 1\r\n") + + validate_python_code( + code_text=components_py_path.read_text(), + checksums={"md5": md5_checksum(components_py_path)}, + )