This is an automated email from the ASF dual-hosted git repository.

jscheffl pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 2c9e759c7a4 Fix SFTPHookAsync test fixture to return async context 
managers (#66678)
2c9e759c7a4 is described below

commit 2c9e759c7a4620f2d7de90393fb225cc4c024ca5
Author: Jarek Potiuk <[email protected]>
AuthorDate: Mon May 11 07:09:27 2026 +0200

    Fix SFTPHookAsync test fixture to return async context managers (#66678)
    
    After #64465 changed the production code from
    ``sftp = await ssh_conn.start_sftp_client()`` to
    ``async with ssh_conn.start_sftp_client() as sftp:`` (and likewise for
    ``sftp.open()``), the ``sftp_hook_mocked`` fixture in
    ``providers/sftp/tests/conftest.py`` started returning coroutines from
    those calls — ``AsyncMock(spec=SSHClientConnection)`` auto-makes
    ``start_sftp_client`` an ``AsyncMock`` whose call yields a coroutine,
    not an async context manager — and every ``TestSFTPHookAsync`` test
    that uses the fixture failed with ``TypeError: 'coroutine' object does
    not support the asynchronous context manager protocol``.
    
    In real asyncssh, both ``start_sftp_client()`` and ``SFTPClient.open()``
    are decorated so a call returns an object that is both awaitable and
    async-context-manageable; the production code only uses the
    context-manager half. Override the auto-spec'd AsyncMocks with sync
    MagicMocks that return a context-manager mock with ``__aenter__`` /
    ``__aexit__`` configured.
    
    Restores 28 ``TestSFTPHookAsync`` tests in
    ``providers/sftp/tests/unit/sftp/hooks/test_sftp.py`` (blocking CI on
    unrelated PRs since #64465 merged).
---
 providers/sftp/tests/conftest.py | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/providers/sftp/tests/conftest.py b/providers/sftp/tests/conftest.py
index 352f17c22a3..89b41e6625f 100644
--- a/providers/sftp/tests/conftest.py
+++ b/providers/sftp/tests/conftest.py
@@ -18,7 +18,7 @@ from __future__ import annotations
 
 from collections.abc import Generator
 from typing import TYPE_CHECKING, Any
-from unittest.mock import AsyncMock, patch
+from unittest.mock import AsyncMock, MagicMock, patch
 
 import pytest
 from asyncssh import SFTPClient, SSHClientConnection
@@ -39,12 +39,21 @@ def sftp_hook_mocked() -> Generator[tuple[SFTPHookAsync, 
SFTPClient], Any, None]
 
     sftp_client_mock = AsyncMock(spec=SFTPClient)
     sftp_client_mock.readdir.return_value = []
-
-    client_connection_mock = AsyncMock(spec=SSHClientConnection)
-    sftp_cm_mock = client_connection_mock.start_sftp_client.return_value
+    # asyncssh's SFTPClient.open() is decorated to be both awaitable and 
async-context-
+    # manageable; the production code uses ``async with sftp.open(...) as 
remote_file``,
+    # so the mock must return a context manager from a *synchronous* call. The 
spec
+    # auto-makes it an AsyncMock (call → coroutine); override it with a sync 
MagicMock.
+    sftp_client_mock.open = MagicMock()
+
+    # asyncssh's start_sftp_client() has the same shape — production uses it as
+    # ``async with ssh_conn.start_sftp_client() as sftp:``. Same override 
pattern.
+    sftp_cm_mock = MagicMock()
     sftp_cm_mock.__aenter__ = AsyncMock(return_value=sftp_client_mock)
     sftp_cm_mock.__aexit__ = AsyncMock(return_value=None)
 
+    client_connection_mock = AsyncMock(spec=SSHClientConnection)
+    client_connection_mock.start_sftp_client = 
MagicMock(return_value=sftp_cm_mock)
+
     with patch("airflow.providers.sftp.hooks.sftp.SFTPHookAsync._get_conn") as 
mock_get_conn:
         mock_get_conn.return_value.__aenter__.return_value = 
client_connection_mock
 

Reply via email to