From 7c647a5bd83a6f3066155755233838255e272fbd Mon Sep 17 00:00:00 2001 From: Amogh Desai Date: Wed, 20 May 2026 16:34:34 +0530 Subject: [PATCH 1/2] Fix LangChain hook tests failing when langchain is not installed --- .../unit/common/ai/hooks/test_langchain.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/providers/common/ai/tests/unit/common/ai/hooks/test_langchain.py b/providers/common/ai/tests/unit/common/ai/hooks/test_langchain.py index ed2f95819377a..4a5fdf2ad544f 100644 --- a/providers/common/ai/tests/unit/common/ai/hooks/test_langchain.py +++ b/providers/common/ai/tests/unit/common/ai/hooks/test_langchain.py @@ -16,6 +16,7 @@ # under the License. from __future__ import annotations +import sys from unittest.mock import MagicMock, patch import pytest @@ -23,6 +24,23 @@ from airflow.providers.common.ai.hooks.langchain import LangChainHook +@pytest.fixture(autouse=True) +def _stub_langchain_modules(): + # langchain is an optional dep; stub sys.modules so @patch can resolve + # langchain.* targets without it being installed. + mocks = { + "langchain": MagicMock(), + "langchain.chat_models": MagicMock(), + "langchain.embeddings": MagicMock(), + "langchain_core": MagicMock(), + "langchain_core.embeddings": MagicMock(), + "langchain_core.language_models": MagicMock(), + "langchain_core.language_models.chat_models": MagicMock(), + } + with patch.dict(sys.modules, mocks): + yield + + def _conn(password: str = "", host: str = "", extra: dict | None = None) -> MagicMock: mock_conn = MagicMock() mock_conn.password = password From 1f60c4437a456dbdeb5adb534d30fd1463ea6f25 Mon Sep 17 00:00:00 2001 From: Amogh Desai Date: Wed, 20 May 2026 17:45:08 +0530 Subject: [PATCH 2/2] Fix LangChain hook tests failing when langchain is not installed --- .../unit/common/ai/hooks/test_langchain.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/providers/common/ai/tests/unit/common/ai/hooks/test_langchain.py b/providers/common/ai/tests/unit/common/ai/hooks/test_langchain.py index 4a5fdf2ad544f..646f72aa10779 100644 --- a/providers/common/ai/tests/unit/common/ai/hooks/test_langchain.py +++ b/providers/common/ai/tests/unit/common/ai/hooks/test_langchain.py @@ -28,14 +28,19 @@ def _stub_langchain_modules(): # langchain is an optional dep; stub sys.modules so @patch can resolve # langchain.* targets without it being installed. + # Submodule entries are derived from parent mock attributes so @patch + # (which resolves via getattr) and the hook's lazy imports (which read + # sys.modules["langchain.chat_models"]) see the same object. + lc = MagicMock() + lc_core = MagicMock() mocks = { - "langchain": MagicMock(), - "langchain.chat_models": MagicMock(), - "langchain.embeddings": MagicMock(), - "langchain_core": MagicMock(), - "langchain_core.embeddings": MagicMock(), - "langchain_core.language_models": MagicMock(), - "langchain_core.language_models.chat_models": MagicMock(), + "langchain": lc, + "langchain.chat_models": lc.chat_models, + "langchain.embeddings": lc.embeddings, + "langchain_core": lc_core, + "langchain_core.embeddings": lc_core.embeddings, + "langchain_core.language_models": lc_core.language_models, + "langchain_core.language_models.chat_models": lc_core.language_models.chat_models, } with patch.dict(sys.modules, mocks): yield