From 0df7710ffc4825bac4f7c70b86dc5c88bf02471b Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 09:10:18 +0000 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=A7=AA=20[testing=20improvement]=20Ad?= =?UTF-8?q?d=20missing=20test=20coverage=20for=20extract=5Fresponse=5Fmapp?= =?UTF-8?q?ing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: SatoryKono <13055362+SatoryKono@users.noreply.github.com> --- .../adapters/common/test_response_shapes.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/unit/infrastructure/adapters/common/test_response_shapes.py b/tests/unit/infrastructure/adapters/common/test_response_shapes.py index 999874b855..2285a8691d 100644 --- a/tests/unit/infrastructure/adapters/common/test_response_shapes.py +++ b/tests/unit/infrastructure/adapters/common/test_response_shapes.py @@ -2,6 +2,8 @@ from __future__ import annotations +from collections import OrderedDict + from bioetl.infrastructure.adapters.common.response_shapes import ( extract_response_items, extract_response_mapping, @@ -40,3 +42,11 @@ def test_extract_response_text_returns_only_string_values() -> None: assert extract_response_text({"nextCursor": "cursor-1"}, "nextCursor") == "cursor-1" assert extract_response_text({"nextCursor": 10}, "nextCursor") is None assert extract_response_text({}, "nextCursor") is None + + +def test_extract_response_mapping_handles_various_edge_cases() -> None: + assert extract_response_mapping({"meta": None}, "meta") is None + assert extract_response_mapping({"meta": "string_not_mapping"}, "meta") is None + + ordered_map = OrderedDict([("a", 1), ("b", 2)]) + assert extract_response_mapping({"meta": ordered_map}, "meta") == ordered_map From a08d93241b4830918b851c2f84f0a3d89903aba8 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 09:35:15 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=A7=AA=20[testing=20improvement]=20Ad?= =?UTF-8?q?d=20missing=20test=20coverage=20for=20extract=5Fresponse=5Fmapp?= =?UTF-8?q?ing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: SatoryKono <13055362+SatoryKono@users.noreply.github.com> --- .../build/__pycache__/__init__.cpython-313.pyc | Bin 257 -> 0 bytes .../__pycache__/mkdocs_build.cpython-313.pyc | Bin 3620 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 scripts/docs/build/__pycache__/__init__.cpython-313.pyc delete mode 100644 scripts/docs/build/__pycache__/mkdocs_build.cpython-313.pyc diff --git a/scripts/docs/build/__pycache__/__init__.cpython-313.pyc b/scripts/docs/build/__pycache__/__init__.cpython-313.pyc deleted file mode 100644 index 56574c1e4537fe35b63f3a97bd6361cfb89cf252..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257 zcmXv}!Ab)$5KUCH2>pj#dvFn<7m-S}MGESnUIVjfl5U6A&6>;>*8lJ${H4A61LA&w zO-lPN^WMO`H?QmWFImLD{==s@f08(YA4u1W`X_f3=l diff --git a/scripts/docs/build/__pycache__/mkdocs_build.cpython-313.pyc b/scripts/docs/build/__pycache__/mkdocs_build.cpython-313.pyc deleted file mode 100644 index 4b9d96cc7d38018c3cac407d52cc2c3f80056bf1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3620 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 Date: Wed, 27 May 2026 10:19:01 +0000 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=A7=AA=20[testing=20improvement]=20Ad?= =?UTF-8?q?d=20missing=20test=20coverage=20for=20extract=5Fresponse=5Fmapp?= =?UTF-8?q?ing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: SatoryKono <13055362+SatoryKono@users.noreply.github.com> --- .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 257 bytes .../__pycache__/mkdocs_build.cpython-313.pyc | Bin 0 -> 3620 bytes .../workflow_foreign_key_reconciliation.py | 18 +++++++++--------- .../health/observability_backend_process.py | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 scripts/docs/build/__pycache__/__init__.cpython-313.pyc create mode 100644 scripts/docs/build/__pycache__/mkdocs_build.cpython-313.pyc diff --git a/scripts/docs/build/__pycache__/__init__.cpython-313.pyc b/scripts/docs/build/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..56574c1e4537fe35b63f3a97bd6361cfb89cf252 GIT binary patch literal 257 zcmXv}!Ab)$5KUCH2>pj#dvFn<7m-S}MGESnUIVjfl5U6A&6>;>*8lJ${H4A61LA&w zO-lPN^WMO`H?QmWFImLD{==s@f08(YA4u1W`X_f3=l literal 0 HcmV?d00001 diff --git a/scripts/docs/build/__pycache__/mkdocs_build.cpython-313.pyc b/scripts/docs/build/__pycache__/mkdocs_build.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4b9d96cc7d38018c3cac407d52cc2c3f80056bf1 GIT binary patch literal 3620 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 None: + self._validate_required_fields() + self._validate_composite_keys() + + def _validate_required_fields(self) -> None: if not self.source_table.strip(): raise ValueError("source_table cannot be empty") if not self.reference_table.strip(): @@ -37,24 +41,20 @@ def __post_init__(self) -> None: raise ValueError("reference_key cannot be empty") if not self.primary_keys: raise ValueError("primary_keys cannot be empty") + + def _validate_composite_keys(self) -> None: if self.source_keys is None and self.reference_keys is None: return if self.source_keys is None or self.reference_keys is None: - raise ValueError( - "source_keys and reference_keys must be provided together" - ) + raise ValueError("source_keys and reference_keys must be provided together") if not self.source_keys or not self.reference_keys: raise ValueError("source_keys and reference_keys cannot be empty") if len(self.source_keys) != len(self.reference_keys): - raise ValueError( - "source_keys and reference_keys must have the same length" - ) + raise ValueError("source_keys and reference_keys must have the same length") if self.source_keys[0].strip() != self.source_key.strip(): raise ValueError("source_key must match the first source_keys entry") if self.reference_keys[0].strip() != self.reference_key.strip(): - raise ValueError( - "reference_key must match the first reference_keys entry" - ) + raise ValueError("reference_key must match the first reference_keys entry") @property def effective_source_keys(self) -> tuple[str, ...]: diff --git a/src/bioetl/interfaces/cli/commands/domains/health/observability_backend_process.py b/src/bioetl/interfaces/cli/commands/domains/health/observability_backend_process.py index 5a6c38f08e..1208f4d1b0 100644 --- a/src/bioetl/interfaces/cli/commands/domains/health/observability_backend_process.py +++ b/src/bioetl/interfaces/cli/commands/domains/health/observability_backend_process.py @@ -136,7 +136,7 @@ def _build_detached_backend_env( current_env: dict[str, str] | None = None, ) -> dict[str, str]: """Ensure detached backend subprocess can import the src-layout package.""" - env = dict(current_env if current_env is not None else getattr(os, "environ")) + env = dict(current_env if current_env is not None else os.environ) src_root = Path(__file__).resolve().parents[6] existing_pythonpath = env.get("PYTHONPATH", "").strip() pythonpath_parts = [str(src_root)]