import os
import re
import asyncio
import tempfile
from unittest.mock import (
    Mock,
    AsyncMock,
    patch,
    mock_open,
    MagicMock,
)
from pytest import mark, fixture, raises
from aiohttp import ClientResponseError, ClientSession
from aiohttp.client_exceptions import (
    ClientConnectorError,
    ClientPayloadError,
    ServerTimeoutError,
    ServerDisconnectedError,
    ClientOSError,
    InvalidURL,
)

import proximl.utils.transfer as specimen
from proximl.exceptions import ConnectionError, ProxiMLException

pytestmark = [mark.sdk, mark.unit]


class NormalizeEndpointTests:
    def test_normalize_endpoint_with_https(self):
        result = specimen.normalize_endpoint("https://example.com")
        assert result == "https://example.com"

    def test_normalize_endpoint_with_http(self):
        result = specimen.normalize_endpoint("http://example.com")
        assert result == "http://example.com"

    def test_normalize_endpoint_without_protocol(self):
        result = specimen.normalize_endpoint("example.com")
        assert result == "https://example.com"

    def test_normalize_endpoint_with_trailing_slash(self):
        result = specimen.normalize_endpoint("https://example.com/")
        assert result == "https://example.com"

    def test_normalize_endpoint_empty_string(self):
        with raises(ValueError, match="Endpoint URL cannot be empty"):
            specimen.normalize_endpoint("")

    def test_normalize_endpoint_multiple_trailing_slashes(self):
        result = specimen.normalize_endpoint("https://example.com///")
        assert result == "https://example.com"


class RetryRequestTests:
    @mark.asyncio
    async def test_retry_request_success_first_attempt(self):
        func = AsyncMock(return_value="success")
        result = await specimen.retry_request(func)
        assert result == "success"
        assert func.call_count == 1

    @mark.asyncio
    async def test_retry_request_retry_on_502(self):
        func = AsyncMock(
            side_effect=[
                ClientResponseError(
                    request_info=Mock(),
                    history=(),
                    status=502,
                    message="Bad Gateway",
                ),
                "success",
            ]
        )
        with patch("asyncio.sleep", new_callable=AsyncMock):
            result = await specimen.retry_request(func, max_retries=3)
        assert result == "success"
        assert func.call_count == 2

    @mark.asyncio
    async def test_retry_request_retry_on_503(self):
        func = AsyncMock(
            side_effect=[
                ClientResponseError(
                    request_info=Mock(),
                    history=(),
                    status=503,
                    message="Service Unavailable",
                ),
                "success",
            ]
        )
        with patch("asyncio.sleep", new_callable=AsyncMock):
            result = await specimen.retry_request(func, max_retries=3)
        assert result == "success"

    @mark.asyncio
    async def test_retry_request_retry_on_504(self):
        func = AsyncMock(
            side_effect=[
                ClientResponseError(
                    request_info=Mock(),
                    history=(),
                    status=504,
                    message="Gateway Timeout",
                ),
                "success",
            ]
        )
        with patch("asyncio.sleep", new_callable=AsyncMock):
            result = await specimen.retry_request(func, max_retries=3)
        assert result == "success"

    @mark.asyncio
    async def test_retry_request_max_retries_exceeded(self):
        func = AsyncMock(
            side_effect=ClientResponseError(
                request_info=Mock(),
                history=(),
                status=502,
                message="Bad Gateway",
            )
        )
        with patch("asyncio.sleep", new_callable=AsyncMock):
            with raises(ClientResponseError):
                await specimen.retry_request(func, max_retries=3)
        assert func.call_count == 3

    @mark.asyncio
    async def test_retry_request_non_retry_status(self):
        func = AsyncMock(
            side_effect=ClientResponseError(
                request_info=Mock(),
                history=(),
                status=400,
                message="Bad Request",
            )
        )
        with patch("asyncio.sleep", new_callable=AsyncMock):
            with raises(ClientResponseError):
                await specimen.retry_request(func, max_retries=3)
        assert func.call_count == 1

    @mark.asyncio
    async def test_retry_request_connection_error(self):
        func = AsyncMock(
            side_effect=ClientConnectorError(
                connection_key=Mock(), os_error=OSError("Connection failed")
            )
        )
        with patch("asyncio.sleep", new_callable=AsyncMock):
            with raises(ClientConnectorError):
                await specimen.retry_request(func, max_retries=2)
        # DNS errors now use DNS_MAX_RETRIES (7) instead of max_retries when ClientConnectorError is encountered
        assert func.call_count == 7

    @mark.asyncio
    async def test_retry_request_server_disconnected_error(self):
        func = AsyncMock(side_effect=ServerDisconnectedError())
        with patch("asyncio.sleep", new_callable=AsyncMock):
            with raises(ServerDisconnectedError):
                await specimen.retry_request(func, max_retries=2)
        assert func.call_count == 2

    @mark.asyncio
    async def test_retry_request_client_os_error(self):
        func = AsyncMock(
            side_effect=ClientOSError(Mock(), OSError("OS error"))
        )
        with patch("asyncio.sleep", new_callable=AsyncMock):
            with raises(ClientOSError):
                await specimen.retry_request(func, max_retries=2)
        assert func.call_count == 2

    @mark.asyncio
    async def test_retry_request_timeout_error(self):
        func = AsyncMock(side_effect=asyncio.TimeoutError())
        with patch("asyncio.sleep", new_callable=AsyncMock):
            with raises(asyncio.TimeoutError):
                await specimen.retry_request(func, max_retries=2)
        assert func.call_count == 2

    @mark.asyncio
    async def test_retry_request_exponential_backoff(self):
        func = AsyncMock(side_effect=[ServerTimeoutError(), "success"])
        sleep_mock = AsyncMock()
        with patch("asyncio.sleep", sleep_mock):
            await specimen.retry_request(func, max_retries=3, retry_backoff=2)
        # Should sleep with exponential backoff: 2^1 = 2 seconds
        assert sleep_mock.call_count == 1
        sleep_mock.assert_called_with(2)


class PingEndpointTests:
    """Tests for ping_endpoint (lines 72-140 in transfer.py)."""

    def _make_ping_session_mock(self, response_status, response_text=""):
        """Build mocked ClientSession + session.get returning response_status."""
        mock_resp = AsyncMock()
        mock_resp.status = response_status
        mock_resp.request_info = Mock()
        mock_resp.history = ()
        mock_resp.text = AsyncMock(return_value=response_text)
        mock_resp_ctx = AsyncMock()
        mock_resp_ctx.__aenter__ = AsyncMock(return_value=mock_resp)
        mock_resp_ctx.__aexit__ = AsyncMock(return_value=None)

        mock_session_instance = AsyncMock()
        mock_session_instance.get = Mock(return_value=mock_resp_ctx)

        mock_session_ctx = AsyncMock()
        mock_session_ctx.__aenter__ = AsyncMock(
            return_value=mock_session_instance
        )
        mock_session_ctx.__aexit__ = AsyncMock(return_value=None)
        return mock_session_ctx

    @mark.asyncio
    async def test_ping_endpoint_success(self):
        with patch(
            "proximl.utils.transfer.aiohttp.ClientSession"
        ) as mock_session_class:
            mock_session_ctx = self._make_ping_session_mock(200)
            mock_session_class.return_value = mock_session_ctx
            with patch("asyncio.sleep", new_callable=AsyncMock):
                await specimen.ping_endpoint("https://host", "token")
        mock_session_class.assert_called_once()

    @mark.asyncio
    async def test_ping_endpoint_http_error_retry_then_success(self):
        call_count = [0]

        def get_ctx(*args, **kwargs):
            call_count[0] += 1
            status = 200 if call_count[0] > 1 else 404
            mock_resp = AsyncMock()
            mock_resp.status = status
            mock_resp.request_info = Mock()
            mock_resp.history = ()
            mock_resp.text = AsyncMock(return_value="Not Found")
            mock_resp_ctx = AsyncMock()
            mock_resp_ctx.__aenter__ = AsyncMock(return_value=mock_resp)
            mock_resp_ctx.__aexit__ = AsyncMock(return_value=None)
            return mock_resp_ctx

        with patch(
            "proximl.utils.transfer.aiohttp.ClientSession"
        ) as mock_session_class:
            mock_session_instance = AsyncMock()
            mock_session_instance.get = Mock(side_effect=get_ctx)
            mock_session_ctx = AsyncMock()
            mock_session_ctx.__aenter__ = AsyncMock(
                return_value=mock_session_instance
            )
            mock_session_ctx.__aexit__ = AsyncMock(return_value=None)
            mock_session_class.return_value = mock_session_ctx
            with patch("asyncio.sleep", new_callable=AsyncMock):
                await specimen.ping_endpoint(
                    "https://host", "token", max_retries=3
                )
        assert call_count[0] == 2

    @mark.asyncio
    async def test_ping_endpoint_http_error_max_retries(self):
        mock_session_ctx = self._make_ping_session_mock(404, "Not Found")
        with patch(
            "proximl.utils.transfer.aiohttp.ClientSession"
        ) as mock_session_class:
            mock_session_class.return_value = mock_session_ctx
            with patch("asyncio.sleep", new_callable=AsyncMock):
                with raises(
                    ConnectionError, match="ping failed after 2 attempts"
                ):
                    await specimen.ping_endpoint(
                        "https://host", "token", max_retries=2
                    )

    @mark.asyncio
    async def test_ping_endpoint_connector_error_dns_retries_then_success(
        self,
    ):
        call_count = [0]

        def session_get(*args, **kwargs):
            call_count[0] += 1
            if call_count[0] == 1:
                raise ClientConnectorError(
                    connection_key=Mock(), os_error=OSError("dns")
                )
            mock_resp = AsyncMock()
            mock_resp.status = 200
            mock_resp.request_info = Mock()
            mock_resp.history = ()
            mock_resp.text = AsyncMock(return_value="")
            mock_resp_ctx = AsyncMock()
            mock_resp_ctx.__aenter__ = AsyncMock(return_value=mock_resp)
            mock_resp_ctx.__aexit__ = AsyncMock(return_value=None)
            return mock_resp_ctx

        with patch(
            "proximl.utils.transfer.aiohttp.ClientSession"
        ) as mock_session_class:
            mock_session_instance = AsyncMock()
            mock_session_instance.get = Mock(side_effect=session_get)
            mock_session_ctx = AsyncMock()
            mock_session_ctx.__aenter__ = AsyncMock(
                return_value=mock_session_instance
            )
            mock_session_ctx.__aexit__ = AsyncMock(return_value=None)
            mock_session_class.return_value = mock_session_ctx
            sleep_mock = AsyncMock()
            with patch("asyncio.sleep", sleep_mock):
                await specimen.ping_endpoint(
                    "https://host", "token", max_retries=2
                )
            assert call_count[0] == 2
            sleep_mock.assert_called_once_with(specimen.DNS_INITIAL_DELAY)

    @mark.asyncio
    async def test_ping_endpoint_connector_error_max_retries(self):
        with patch(
            "proximl.utils.transfer.aiohttp.ClientSession"
        ) as mock_session_class:
            mock_session_instance = AsyncMock()
            mock_session_instance.get = Mock(
                side_effect=ClientConnectorError(
                    connection_key=Mock(), os_error=OSError("dns")
                )
            )
            mock_session_ctx = AsyncMock()
            mock_session_ctx.__aenter__ = AsyncMock(
                return_value=mock_session_instance
            )
            mock_session_ctx.__aexit__ = AsyncMock(return_value=None)
            mock_session_class.return_value = mock_session_ctx
            sleep_mock = AsyncMock()
            with patch("asyncio.sleep", sleep_mock):
                with raises(
                    ConnectionError,
                    match="ping failed after .* attempts due to DNS/connection",
                ):
                    await specimen.ping_endpoint(
                        "https://host", "token", max_retries=2
                    )
            assert sleep_mock.call_count >= 1

    @mark.asyncio
    async def test_ping_endpoint_connector_error_backoff_after_first_retry(
        self,
    ):
        call_count = [0]

        def session_get(*args, **kwargs):
            call_count[0] += 1
            if call_count[0] < 3:
                raise ClientConnectorError(
                    connection_key=Mock(), os_error=OSError("dns")
                )
            mock_resp = AsyncMock()
            mock_resp.status = 200
            mock_resp.request_info = Mock()
            mock_resp.history = ()
            mock_resp.text = AsyncMock(return_value="")
            mock_resp_ctx = AsyncMock()
            mock_resp_ctx.__aenter__ = AsyncMock(return_value=mock_resp)
            mock_resp_ctx.__aexit__ = AsyncMock(return_value=None)
            return mock_resp_ctx

        with patch(
            "proximl.utils.transfer.aiohttp.ClientSession"
        ) as mock_session_class:
            mock_session_instance = AsyncMock()
            mock_session_instance.get = Mock(side_effect=session_get)
            mock_session_ctx = AsyncMock()
            mock_session_ctx.__aenter__ = AsyncMock(
                return_value=mock_session_instance
            )
            mock_session_ctx.__aexit__ = AsyncMock(return_value=None)
            mock_session_class.return_value = mock_session_ctx
            sleep_mock = AsyncMock()
            with patch("asyncio.sleep", sleep_mock):
                await specimen.ping_endpoint(
                    "https://host", "token", max_retries=5, retry_backoff=2
                )
            assert call_count[0] == 3
            assert sleep_mock.call_count == 2
            sleep_mock.assert_any_call(specimen.DNS_INITIAL_DELAY)
            sleep_mock.assert_any_call(2)

    @mark.asyncio
    async def test_ping_endpoint_other_error_retry_then_success(self):
        call_count = [0]

        def session_get(*args, **kwargs):
            call_count[0] += 1
            if call_count[0] == 1:
                raise ServerDisconnectedError("disconnected")
            mock_resp = AsyncMock()
            mock_resp.status = 200
            mock_resp.request_info = Mock()
            mock_resp.history = ()
            mock_resp.text = AsyncMock(return_value="")
            mock_resp_ctx = AsyncMock()
            mock_resp_ctx.__aenter__ = AsyncMock(return_value=mock_resp)
            mock_resp_ctx.__aexit__ = AsyncMock(return_value=None)
            return mock_resp_ctx

        with patch(
            "proximl.utils.transfer.aiohttp.ClientSession"
        ) as mock_session_class:
            mock_session_instance = AsyncMock()
            mock_session_instance.get = Mock(side_effect=session_get)
            mock_session_ctx = AsyncMock()
            mock_session_ctx.__aenter__ = AsyncMock(
                return_value=mock_session_instance
            )
            mock_session_ctx.__aexit__ = AsyncMock(return_value=None)
            mock_session_class.return_value = mock_session_ctx
            with patch("asyncio.sleep", new_callable=AsyncMock):
                await specimen.ping_endpoint(
                    "https://host", "token", max_retries=3
                )
        assert call_count[0] == 2

    @mark.asyncio
    async def test_ping_endpoint_other_error_max_retries(self):
        with patch(
            "proximl.utils.transfer.aiohttp.ClientSession"
        ) as mock_session_class:
            mock_session_instance = AsyncMock()
            mock_session_instance.get = Mock(
                side_effect=ServerDisconnectedError("disconnected")
            )
            mock_session_ctx = AsyncMock()
            mock_session_ctx.__aenter__ = AsyncMock(
                return_value=mock_session_instance
            )
            mock_session_ctx.__aexit__ = AsyncMock(return_value=None)
            mock_session_class.return_value = mock_session_ctx
            with patch("asyncio.sleep", new_callable=AsyncMock):
                with raises(
                    ConnectionError,
                    match="ping failed after 2 attempts",
                ):
                    await specimen.ping_endpoint(
                        "https://host", "token", max_retries=2
                    )

    @mark.asyncio
    async def test_ping_endpoint_client_payload_error_retry_then_success(self):
        call_count = [0]

        def session_get(*args, **kwargs):
            call_count[0] += 1
            if call_count[0] == 1:
                raise ClientPayloadError("payload error")
            mock_resp = AsyncMock()
            mock_resp.status = 200
            mock_resp.request_info = Mock()
            mock_resp.history = ()
            mock_resp.text = AsyncMock(return_value="")
            mock_resp_ctx = AsyncMock()
            mock_resp_ctx.__aenter__ = AsyncMock(return_value=mock_resp)
            mock_resp_ctx.__aexit__ = AsyncMock(return_value=None)
            return mock_resp_ctx

        with patch(
            "proximl.utils.transfer.aiohttp.ClientSession"
        ) as mock_session_class:
            mock_session_instance = AsyncMock()
            mock_session_instance.get = Mock(side_effect=session_get)
            mock_session_ctx = AsyncMock()
            mock_session_ctx.__aenter__ = AsyncMock(
                return_value=mock_session_instance
            )
            mock_session_ctx.__aexit__ = AsyncMock(return_value=None)
            mock_session_class.return_value = mock_session_ctx
            with patch("asyncio.sleep", new_callable=AsyncMock):
                await specimen.ping_endpoint(
                    "https://host", "token", max_retries=3
                )
        assert call_count[0] == 2

    @mark.asyncio
    async def test_ping_endpoint_timeout_error_retry_then_success(self):
        call_count = [0]

        def session_get(*args, **kwargs):
            call_count[0] += 1
            if call_count[0] == 1:
                raise asyncio.TimeoutError()
            mock_resp = AsyncMock()
            mock_resp.status = 200
            mock_resp.request_info = Mock()
            mock_resp.history = ()
            mock_resp.text = AsyncMock(return_value="")
            mock_resp_ctx = AsyncMock()
            mock_resp_ctx.__aenter__ = AsyncMock(return_value=mock_resp)
            mock_resp_ctx.__aexit__ = AsyncMock(return_value=None)
            return mock_resp_ctx

        with patch(
            "proximl.utils.transfer.aiohttp.ClientSession"
        ) as mock_session_class:
            mock_session_instance = AsyncMock()
            mock_session_instance.get = Mock(side_effect=session_get)
            mock_session_ctx = AsyncMock()
            mock_session_ctx.__aenter__ = AsyncMock(
                return_value=mock_session_instance
            )
            mock_session_ctx.__aexit__ = AsyncMock(return_value=None)
            mock_session_class.return_value = mock_session_ctx
            with patch("asyncio.sleep", new_callable=AsyncMock):
                await specimen.ping_endpoint(
                    "https://host", "token", max_retries=3
                )
        assert call_count[0] == 2

    @mark.asyncio
    async def test_ping_endpoint_normalizes_endpoint(self):
        with patch(
            "proximl.utils.transfer.aiohttp.ClientSession"
        ) as mock_session_class:
            mock_session_ctx = self._make_ping_session_mock(200)
            mock_session_class.return_value = mock_session_ctx
            with patch("asyncio.sleep", new_callable=AsyncMock):
                await specimen.ping_endpoint("host.no.scheme", "token")
            call_kw = mock_session_instance = (
                mock_session_class.return_value.__aenter__.return_value
            )
            get_call = call_kw.get.call_args
            assert get_call is not None
            (url,) = get_call[0]
            assert url == "https://host.no.scheme/ping"

    @mark.asyncio
    async def test_ping_endpoint_sends_bearer_auth(self):
        with patch(
            "proximl.utils.transfer.aiohttp.ClientSession"
        ) as mock_session_class:
            mock_session_ctx = self._make_ping_session_mock(200)
            mock_session_class.return_value = mock_session_ctx
            with patch("asyncio.sleep", new_callable=AsyncMock):
                await specimen.ping_endpoint("https://host", "my_token")
            sess = mock_session_class.return_value.__aenter__.return_value
            sess.get.assert_called_once()
            call_kw = sess.get.call_args[1]
            assert call_kw["headers"]["Authorization"] == "Bearer my_token"


class UploadChunkTests:
    @mark.asyncio
    async def test_upload_chunk_success(self):
        # Mock session.put to return an async context manager
        mock_response = AsyncMock()
        mock_response.status = 200
        mock_response.release = AsyncMock()
        mock_response.request_info = Mock()
        mock_response.history = ()
        mock_response.__aenter__ = AsyncMock(return_value=mock_response)
        mock_response.__aexit__ = AsyncMock(return_value=None)

        session = AsyncMock()
        # session.put() should return the response directly (not a coroutine)
        session.put = Mock(return_value=mock_response)

        # Mock retry_request to actually call and await the function passed to it
        # The function (_upload) does: async with session.put() as response: ...
        async def mock_retry(func, *args, **kwargs):
            # func is _upload, which does async with session.put() as response:
            return await func(*args, **kwargs)

        with patch(
            "proximl.utils.transfer.retry_request", new_callable=AsyncMock
        ) as mock_retry_patch:
            mock_retry_patch.side_effect = mock_retry
            await specimen.upload_chunk(
                session,
                "https://example.com",
                "token",
                100,
                b"data",
                0,
            )
            mock_response.release.assert_called_once()

    @mark.asyncio
    async def test_upload_chunk_content_range_header(self):
        session = AsyncMock()
        response = AsyncMock()
        response.status = 200
        response.release = AsyncMock()
        response.request_info = Mock()
        response.history = ()
        session.put = AsyncMock(return_value=response.__aenter__())
        response.__aenter__ = AsyncMock(return_value=response)
        response.__aexit__ = AsyncMock(return_value=None)

        # Mock the response that _upload returns
        mock_response_context = AsyncMock()
        mock_response_context.status = 200
        mock_response_context.release = AsyncMock()
        mock_response_context.request_info = Mock()
        mock_response_context.history = ()
        mock_response_context.__aenter__ = AsyncMock(
            return_value=mock_response_context
        )
        mock_response_context.__aexit__ = AsyncMock(return_value=None)

        async def _upload(*args, **kwargs):
            return mock_response_context

        with patch("proximl.utils.transfer.retry_request") as mock_retry:
            mock_retry.side_effect = _upload
            await specimen.upload_chunk(
                session,
                "https://example.com",
                "token",
                100,
                b"data",
                10,
            )
            # Verify headers were set correctly by checking session.put was called
            # Note: session.put is called inside _upload, but we're mocking retry_request
            # So we verify the function completed successfully
            assert True  # Test passes if no exception

    @mark.asyncio
    async def test_upload_chunk_retry_status(self):
        session = AsyncMock()
        response = AsyncMock()
        response.status = 502
        response.text = AsyncMock(return_value="Bad Gateway")
        response.request_info = Mock()
        response.history = ()
        session.put = AsyncMock(return_value=response.__aenter__())
        response.__aenter__ = AsyncMock(return_value=response)
        response.__aexit__ = AsyncMock(return_value=None)

        with patch("proximl.utils.transfer.retry_request") as mock_retry:

            async def _upload():
                async with session.put(
                    f"https://example.com/upload",
                    headers={},
                    data=b"data",
                    timeout=30,
                ) as resp:
                    if resp.status == 502:
                        text = await resp.text()
                        raise ClientResponseError(
                            request_info=resp.request_info,
                            history=resp.history,
                            status=resp.status,
                            message=text,
                        )

            mock_retry.side_effect = ClientResponseError(
                request_info=Mock(),
                history=(),
                status=502,
                message="Bad Gateway",
            )
            with raises(ClientResponseError):
                await specimen.upload_chunk(
                    session,
                    "https://example.com",
                    "token",
                    100,
                    b"data",
                    0,
                )

    @mark.asyncio
    async def test_upload_chunk_error_status(self):
        session = AsyncMock()
        response = AsyncMock()
        response.status = 400
        response.text = AsyncMock(return_value="Bad Request")
        response.request_info = Mock()
        response.history = ()
        session.put = AsyncMock(return_value=response.__aenter__())
        response.__aenter__ = AsyncMock(return_value=response)
        response.__aexit__ = AsyncMock(return_value=None)

        with patch("proximl.utils.transfer.retry_request") as mock_retry:
            mock_retry.side_effect = ConnectionError(
                "Chunk 0-3 failed with status 400: Bad Request"
            )
            with raises(ConnectionError, match="Chunk.*failed"):
                await specimen.upload_chunk(
                    session,
                    "https://example.com",
                    "token",
                    100,
                    b"data",
                    0,
                )

    @mark.asyncio
    async def test_upload_chunk_retry_status_direct(self):
        """Test upload_chunk retry status (lines 106-113) - direct execution"""
        session = AsyncMock()

        # Create a response with retry status (502)
        mock_response = AsyncMock()
        mock_response.status = 502
        mock_response.text = AsyncMock(return_value="Bad Gateway")
        mock_response.request_info = Mock()
        mock_response.history = ()
        mock_response.release = AsyncMock()

        # Make session.put return an async context manager
        class AwaitableContextManager:
            def __init__(self, return_value):
                self.return_value = return_value

            def __await__(self):
                yield
                return self

            async def __aenter__(self):
                return self.return_value

            async def __aexit__(self, *args):
                return None

        mock_put_context = AwaitableContextManager(mock_response)
        session.put = Mock(return_value=mock_put_context)

        # Mock retry_request to actually call the function passed to it
        async def mock_retry(func, *args, **kwargs):
            return await func(*args, **kwargs)

        with patch(
            "proximl.utils.transfer.retry_request", side_effect=mock_retry
        ):
            with raises(ClientResponseError):
                await specimen.upload_chunk(
                    session,
                    "https://example.com",
                    "token",
                    100,
                    b"data",
                    0,
                )

    @mark.asyncio
    async def test_upload_chunk_error_status_direct(self):
        """Test upload_chunk error status (lines 114-118) - direct execution"""
        session = AsyncMock()

        # Create a response with non-retry error status (400)
        mock_response = AsyncMock()
        mock_response.status = 400
        mock_response.text = AsyncMock(return_value="Bad Request")
        mock_response.request_info = Mock()
        mock_response.history = ()
        mock_response.release = AsyncMock()

        # Make session.put return an async context manager
        class AwaitableContextManager:
            def __init__(self, return_value):
                self.return_value = return_value

            def __await__(self):
                yield
                return self

            async def __aenter__(self):
                return self.return_value

            async def __aexit__(self, *args):
                return None

        mock_put_context = AwaitableContextManager(mock_response)
        session.put = Mock(return_value=mock_put_context)

        # Mock retry_request to actually call the function passed to it
        async def mock_retry(func, *args, **kwargs):
            return await func(*args, **kwargs)

        with patch(
            "proximl.utils.transfer.retry_request", side_effect=mock_retry
        ):
            with raises(
                ConnectionError, match="Chunk.*failed with status 400"
            ):
                await specimen.upload_chunk(
                    session,
                    "https://example.com",
                    "token",
                    100,
                    b"data",
                    0,
                )


class UploadTests:
    @mark.asyncio
    async def test_upload_file_not_found(self):
        with patch(
            "proximl.utils.transfer.ping_endpoint", new_callable=AsyncMock
        ):
            with raises(ValueError, match="Path not found"):
                await specimen.upload(
                    "https://example.com", "token", "/nonexistent/path"
                )

    @mark.asyncio
    async def test_upload_invalid_path_type(self):
        # Test path that is neither file nor directory
        # This is hard to create in practice, but we can mock it
        with tempfile.NamedTemporaryFile() as tmp:
            with patch(
                "proximl.utils.transfer.ping_endpoint", new_callable=AsyncMock
            ):
                with patch("os.path.isfile", return_value=False):
                    with patch("os.path.isdir", return_value=False):
                        with patch("os.path.exists", return_value=True):
                            with raises(
                                ValueError,
                                match="Path is neither a file nor directory",
                            ):
                                await specimen.upload(
                                    "example.com", "token", tmp.name
                                )

    @mark.asyncio
    async def test_upload_file(self):
        with tempfile.NamedTemporaryFile() as tmp:
            tmp.write(b"test content")
            tmp.flush()

            with patch(
                "proximl.utils.transfer.ping_endpoint", new_callable=AsyncMock
            ):
                with patch(
                    "asyncio.create_subprocess_exec"
                ) as mock_subprocess:
                    mock_process = AsyncMock()
                    mock_process.stdout.read = AsyncMock(
                        side_effect=[b"data", b""]
                    )
                    mock_process.returncode = 0
                    mock_process.wait = AsyncMock(return_value=0)
                    mock_process.stderr.read = AsyncMock(return_value=b"")
                    mock_subprocess.return_value = mock_process

                    with patch("aiohttp.ClientSession") as mock_session:
                        mock_session_instance = AsyncMock()
                        mock_session.return_value.__aenter__ = AsyncMock(
                            return_value=mock_session_instance
                        )
                        mock_session.return_value.__aexit__ = AsyncMock(
                            return_value=None
                        )

                        with patch(
                            "proximl.utils.transfer.upload_chunk",
                            new_callable=AsyncMock,
                        ) as mock_upload_chunk:
                            mock_finalize_response = AsyncMock()
                            mock_finalize_response.status = 200
                            mock_finalize_response.json = AsyncMock(
                                return_value={"status": "ok"}
                            )
                            mock_finalize_response.__aenter__ = AsyncMock(
                                return_value=mock_finalize_response
                            )
                            mock_finalize_response.__aexit__ = AsyncMock(
                                return_value=None
                            )

                            # session.post() should return something that is both awaitable and an async context manager
                            class AwaitableContextManager:
                                def __init__(self, return_value):
                                    self.return_value = return_value

                                def __await__(self):
                                    yield
                                    return self

                                async def __aenter__(self):
                                    return self.return_value

                                async def __aexit__(self, *args):
                                    return None

                            mock_post_context = AwaitableContextManager(
                                mock_finalize_response
                            )
                            mock_session_instance.post = Mock(
                                return_value=mock_post_context
                            )

                            await specimen.upload(
                                "example.com", "token", tmp.name
                            )
                            # Verify upload_chunk was called
                            assert mock_upload_chunk.called

    @mark.asyncio
    async def test_upload_directory(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            test_file = os.path.join(tmpdir, "test.txt")
            with open(test_file, "w") as f:
                f.write("test content")

            with patch(
                "proximl.utils.transfer.ping_endpoint", new_callable=AsyncMock
            ):
                with patch(
                    "asyncio.create_subprocess_exec"
                ) as mock_subprocess:
                    mock_process = AsyncMock()
                    mock_process.stdout.read = AsyncMock(
                        side_effect=[b"data", b""]
                    )
                    mock_process.returncode = 0
                    mock_process.wait = AsyncMock(return_value=0)
                    mock_process.stderr.read = AsyncMock(return_value=b"")
                    mock_subprocess.return_value = mock_process

                    with patch("aiohttp.ClientSession") as mock_session:
                        mock_session_instance = AsyncMock()
                        mock_session.return_value.__aenter__ = AsyncMock(
                            return_value=mock_session_instance
                        )
                        mock_session.return_value.__aexit__ = AsyncMock(
                            return_value=None
                        )

                        with patch(
                            "proximl.utils.transfer.upload_chunk",
                            new_callable=AsyncMock,
                        ) as mock_upload_chunk:
                            mock_finalize_response = AsyncMock()
                            mock_finalize_response.status = 200
                            mock_finalize_response.json = AsyncMock(
                                return_value={"status": "ok"}
                            )
                            mock_finalize_response.__aenter__ = AsyncMock(
                                return_value=mock_finalize_response
                            )
                            mock_finalize_response.__aexit__ = AsyncMock(
                                return_value=None
                            )

                            # session.post() should return something that is both awaitable and an async context manager
                            class AwaitableContextManager:
                                def __init__(self, return_value):
                                    self.return_value = return_value

                                def __await__(self):
                                    yield
                                    return self

                                async def __aenter__(self):
                                    return self.return_value

                                async def __aexit__(self, *args):
                                    return None

                            mock_post_context = AwaitableContextManager(
                                mock_finalize_response
                            )
                            mock_session_instance.post = Mock(
                                return_value=mock_post_context
                            )

                            await specimen.upload(
                                "example.com", "token", tmpdir
                            )
                            # Verify upload_chunk was called
                            assert mock_upload_chunk.called

    @mark.asyncio
    async def test_upload_tar_command_failure(self):
        with tempfile.NamedTemporaryFile() as tmp:
            with patch("asyncio.create_subprocess_exec") as mock_subprocess:
                mock_process = AsyncMock()
                mock_process.stdout.read = AsyncMock(
                    side_effect=[b"data", b""]
                )
                mock_process.wait = AsyncMock(return_value=1)
                mock_process.stderr.read = AsyncMock(return_value=b"tar error")
                mock_subprocess.return_value = mock_process

                with patch("aiohttp.ClientSession") as mock_session:
                    mock_session_instance = AsyncMock()
                    mock_session.return_value.__aenter__ = AsyncMock(
                        return_value=mock_session_instance
                    )
                    mock_session.return_value.__aexit__ = AsyncMock(
                        return_value=None
                    )
                    with patch(
                        "proximl.utils.transfer.ping_endpoint",
                        new_callable=AsyncMock,
                    ):
                        with patch(
                            "proximl.utils.transfer.upload_chunk",
                            new_callable=AsyncMock,
                        ):
                            with raises(
                                ProxiMLException, match="tar command failed"
                            ):
                                await specimen.upload(
                                    "example.com", "token", tmp.name
                                )

    @mark.asyncio
    async def test_upload_tar_command_failure_no_stderr(self):
        with tempfile.NamedTemporaryFile() as tmp:
            with patch("asyncio.create_subprocess_exec") as mock_subprocess:
                mock_process = AsyncMock()
                mock_process.stdout.read = AsyncMock(
                    side_effect=[b"data", b""]
                )
                mock_process.wait = AsyncMock(return_value=1)
                mock_process.stderr.read = AsyncMock(return_value=None)
                mock_subprocess.return_value = mock_process

                with patch("aiohttp.ClientSession") as mock_session:
                    mock_session_instance = AsyncMock()
                    mock_session.return_value.__aenter__ = AsyncMock(
                        return_value=mock_session_instance
                    )
                    mock_session.return_value.__aexit__ = AsyncMock(
                        return_value=None
                    )
                    with patch(
                        "proximl.utils.transfer.ping_endpoint",
                        new_callable=AsyncMock,
                    ):
                        with patch(
                            "proximl.utils.transfer.upload_chunk",
                            new_callable=AsyncMock,
                        ):
                            with raises(
                                ProxiMLException, match="tar command failed"
                            ):
                                await specimen.upload(
                                    "example.com", "token", tmp.name
                                )

    @mark.asyncio
    async def test_upload_finalize_failure(self):
        with tempfile.NamedTemporaryFile() as tmp:
            with patch(
                "proximl.utils.transfer.ping_endpoint", new_callable=AsyncMock
            ):
                with patch(
                    "asyncio.create_subprocess_exec"
                ) as mock_subprocess:
                    mock_process = AsyncMock()
                    mock_process.stdout.read = AsyncMock(
                        side_effect=[b"data", b""]
                    )
                    mock_process.returncode = 0
                    mock_process.wait = AsyncMock(return_value=0)
                    mock_process.stderr.read = AsyncMock(return_value=b"")
                    mock_subprocess.return_value = mock_process

                    with patch("aiohttp.ClientSession") as mock_session:
                        mock_session_instance = AsyncMock()
                        mock_session.return_value.__aenter__ = AsyncMock(
                            return_value=mock_session_instance
                        )
                        mock_session.return_value.__aexit__ = AsyncMock(
                            return_value=None
                        )

                        with patch(
                            "proximl.utils.transfer.upload_chunk",
                            new_callable=AsyncMock,
                        ) as mock_upload_chunk:
                            mock_finalize_response = AsyncMock()
                            mock_finalize_response.status = 500
                            mock_finalize_response.text = AsyncMock(
                                return_value="Finalize error"
                            )
                            mock_finalize_response.__aenter__ = AsyncMock(
                                return_value=mock_finalize_response
                            )
                            mock_finalize_response.__aexit__ = AsyncMock(
                                return_value=None
                            )

                            # session.post() should return something that is both awaitable and an async context manager
                            class AwaitableContextManager:
                                def __init__(self, return_value):
                                    self.return_value = return_value

                                def __await__(self):
                                    yield
                                    return self

                                async def __aenter__(self):
                                    return self.return_value

                                async def __aexit__(self, *args):
                                    return None

                            mock_post_context = AwaitableContextManager(
                                mock_finalize_response
                            )
                            mock_session_instance.post = Mock(
                                return_value=mock_post_context
                            )

                            with patch(
                                "proximl.utils.transfer.ping_endpoint",
                                new_callable=AsyncMock,
                            ):
                                with raises(
                                    ConnectionError, match="Finalize failed"
                                ):
                                    await specimen.upload(
                                        "example.com", "token", tmp.name
                                    )
                            # Verify upload_chunk was called before finalize
                            assert mock_upload_chunk.called

    @mark.asyncio
    async def test_upload_multiple_chunks(self):
        with tempfile.NamedTemporaryFile() as tmp:
            tmp.write(b"x" * (10 * 1024 * 1024))  # 10MB file
            tmp.flush()

            with patch("asyncio.create_subprocess_exec") as mock_subprocess:
                mock_process = AsyncMock()
                # Simulate multiple chunks
                mock_process.stdout.read = AsyncMock(
                    side_effect=[
                        b"x" * (5 * 1024 * 1024),
                        b"x" * (5 * 1024 * 1024),
                        b"",
                    ]
                )
                mock_process.returncode = 0
                mock_process.wait = AsyncMock(return_value=0)
                mock_process.stderr.read = AsyncMock(return_value=b"")
                mock_subprocess.return_value = mock_process

                with patch("aiohttp.ClientSession") as mock_session:
                    mock_session_instance = AsyncMock()
                    mock_session.return_value.__aenter__ = AsyncMock(
                        return_value=mock_session_instance
                    )
                    mock_session.return_value.__aexit__ = AsyncMock(
                        return_value=None
                    )

                    upload_chunk_mock = AsyncMock()
                    with patch(
                        "proximl.utils.transfer.upload_chunk",
                        upload_chunk_mock,
                    ):
                        mock_finalize_response = AsyncMock()
                        mock_finalize_response.status = 200
                        mock_finalize_response.json = AsyncMock(
                            return_value={"status": "ok"}
                        )
                        mock_finalize_response.__aenter__ = AsyncMock(
                            return_value=mock_finalize_response
                        )
                        mock_finalize_response.__aexit__ = AsyncMock(
                            return_value=None
                        )

                        # session.post() should return something that is both awaitable and an async context manager
                        class AwaitableContextManager:
                            def __init__(self, return_value):
                                self.return_value = return_value

                            def __await__(self):
                                yield
                                return self

                            async def __aenter__(self):
                                return self.return_value

                            async def __aexit__(self, *args):
                                return None

                        mock_post_context = AwaitableContextManager(
                            mock_finalize_response
                        )
                        mock_session_instance.post = Mock(
                            return_value=mock_post_context
                        )

                        with patch(
                            "proximl.utils.transfer.ping_endpoint",
                            new_callable=AsyncMock,
                        ):
                            await specimen.upload(
                                "example.com", "token", tmp.name
                            )
                        # Should have called upload_chunk twice (one per chunk)
                        assert upload_chunk_mock.call_count == 2


class DownloadTests:
    @mark.asyncio
    async def test_download_creates_directory(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            target_dir = os.path.join(tmpdir, "new_dir")

            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                # Mock /info endpoint returning TAR mode
                def mock_get(*args, **kwargs):
                    url = args[0] if args else kwargs.get("url", "")
                    # Handle /ping endpoint for ping_endpoint
                    if "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    # Handle /info endpoint
                    elif "/info" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.json = AsyncMock(
                            return_value={"archive": False}
                        )
                        mock_resp.text = AsyncMock(return_value="")
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    else:
                        # For /download endpoint, return awaitable that resolves to response
                        async def get_download_response():
                            mock_resp = AsyncMock()
                            mock_resp.status = 200
                            mock_resp.headers = {
                                "Content-Type": "application/x-tar"
                            }

                            async def chunk_iter():
                                yield b"tar data"
                                yield b""

                            mock_resp.content.iter_chunked = (
                                lambda size: chunk_iter()
                            )
                            mock_resp.close = Mock()
                            return mock_resp

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # retry_request is called 3 times: _get_info, _download, _finalize
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    with patch(
                        "asyncio.create_subprocess_exec"
                    ) as mock_subprocess:
                        mock_process = AsyncMock()
                        mock_process.stdin = Mock()
                        mock_process.stdin.write = Mock()
                        mock_process.stdin.drain = AsyncMock()
                        mock_process.stdin.close = Mock()
                        mock_process.returncode = 0
                        mock_process.wait = AsyncMock(return_value=0)
                        mock_process.stderr.read = AsyncMock(return_value=b"")
                        mock_subprocess.return_value = mock_process

                        mock_finalize_response = AsyncMock()
                        mock_finalize_response.status = 200
                        mock_finalize_response.json = AsyncMock(
                            return_value={"status": "ok"}
                        )
                        mock_finalize_response.__aenter__ = AsyncMock(
                            return_value=mock_finalize_response
                        )
                        mock_finalize_response.__aexit__ = AsyncMock(
                            return_value=None
                        )

                        # session.post() should return something that is both awaitable and an async context manager
                        class AwaitableContextManager:
                            def __init__(self, return_value):
                                self.return_value = return_value

                            def __await__(self):
                                yield
                                return self

                            async def __aenter__(self):
                                return self.return_value

                            async def __aexit__(self, *args):
                                return None

                        mock_post_context = AwaitableContextManager(
                            mock_finalize_response
                        )
                        mock_session_instance.post = Mock(
                            return_value=mock_post_context
                        )

                        with patch(
                            "proximl.utils.transfer.ping_endpoint",
                            new_callable=AsyncMock,
                        ):
                            await specimen.download(
                                "example.com", "token", target_dir
                            )

            assert os.path.isdir(target_dir)

    @mark.asyncio
    async def test_download_tar_mode(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                # Mock /info endpoint - needs to return async context manager for async with
                def mock_get(*args, **kwargs):
                    url = args[0] if args else kwargs.get("url", "")
                    if "/info" in url or "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.json = AsyncMock(
                            return_value={"archive": False}
                        )
                        mock_resp.text = AsyncMock(return_value="")
                        # Configure as async context manager
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    else:
                        # For /download endpoint, return awaitable that resolves to response
                        async def get_download_response():
                            mock_resp = AsyncMock()
                            mock_resp.status = 200
                            mock_resp.headers = {
                                "Content-Type": "application/x-tar"
                            }

                            async def chunk_iter():
                                yield b"tar data"
                                yield b""

                            mock_resp.content.iter_chunked = (
                                lambda size: chunk_iter()
                            )
                            mock_resp.close = Mock()
                            return mock_resp

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # retry_request is called 3 times: _get_info, _download, _finalize
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    with patch(
                        "asyncio.create_subprocess_exec"
                    ) as mock_subprocess:
                        mock_process = AsyncMock()
                        mock_process.stdin = Mock()
                        mock_process.stdin.write = Mock()
                        mock_process.stdin.drain = AsyncMock()
                        mock_process.stdin.close = Mock()
                        mock_process.returncode = 0
                        mock_process.wait = AsyncMock(return_value=0)
                        mock_process.stderr.read = AsyncMock(return_value=b"")
                        mock_subprocess.return_value = mock_process

                        mock_finalize_response = AsyncMock()
                        mock_finalize_response.status = 200
                        mock_finalize_response.json = AsyncMock(
                            return_value={"status": "ok"}
                        )
                        mock_finalize_response.__aenter__ = AsyncMock(
                            return_value=mock_finalize_response
                        )
                        mock_finalize_response.__aexit__ = AsyncMock(
                            return_value=None
                        )

                        # session.post() should return something that is both awaitable and an async context manager
                        class AwaitableContextManager:
                            def __init__(self, return_value):
                                self.return_value = return_value

                            def __await__(self):
                                yield
                                return self

                            async def __aenter__(self):
                                return self.return_value

                            async def __aexit__(self, *args):
                                return None

                        mock_post_context = AwaitableContextManager(
                            mock_finalize_response
                        )
                        mock_session_instance.post = Mock(
                            return_value=mock_post_context
                        )

                        with patch(
                            "proximl.utils.transfer.ping_endpoint",
                            new_callable=AsyncMock,
                        ):
                            await specimen.download(
                                "example.com", "token", tmpdir
                            )

    @mark.asyncio
    async def test_download_zip_mode(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                # Mock /info endpoint returning ZIP mode - needs to return async context manager for async with
                def mock_get(*args, **kwargs):
                    url = args[0] if args else kwargs.get("url", "")
                    if "/info" in url or "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.json = AsyncMock(
                            return_value={"archive": True}
                        )
                        mock_resp.text = AsyncMock(return_value="")
                        # Configure as async context manager
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    else:
                        # For /download endpoint, return awaitable that resolves to response
                        async def get_download_response():
                            mock_resp = AsyncMock()
                            mock_resp.status = 200
                            mock_resp.headers = {
                                "Content-Type": "application/zip",
                                "Content-Disposition": 'attachment; filename="archive.zip"',
                            }

                            async def chunk_iter():
                                yield b"zip data"
                                yield b""

                            mock_resp.content.iter_chunked = (
                                lambda size: chunk_iter()
                            )
                            mock_resp.close = Mock()
                            return mock_resp

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # Mock retry_request to actually call the function passed to it
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    mock_file_context = AsyncMock()
                    mock_file_context.__aenter__ = AsyncMock(
                        return_value=AsyncMock()
                    )
                    mock_file_context.__aexit__ = AsyncMock(return_value=None)
                    mock_file_context.__aenter__.return_value.write = (
                        AsyncMock()
                    )
                    with patch(
                        "aiofiles.open", return_value=mock_file_context
                    ) as mock_file:
                        mock_finalize_response = AsyncMock()
                        mock_finalize_response.status = 200
                        mock_finalize_response.json = AsyncMock(
                            return_value={"status": "ok"}
                        )
                        mock_finalize_response.__aenter__ = AsyncMock(
                            return_value=mock_finalize_response
                        )
                        mock_finalize_response.__aexit__ = AsyncMock(
                            return_value=None
                        )

                        # session.post() should return something that is both awaitable and an async context manager
                        class AwaitableContextManager:
                            def __init__(self, return_value):
                                self.return_value = return_value

                            def __await__(self):
                                yield
                                return self

                            async def __aenter__(self):
                                return self.return_value

                            async def __aexit__(self, *args):
                                return None

                        mock_post_context = AwaitableContextManager(
                            mock_finalize_response
                        )
                        mock_session_instance.post = Mock(
                            return_value=mock_post_context
                        )

                        with patch(
                            "proximl.utils.transfer.ping_endpoint",
                            new_callable=AsyncMock,
                        ):
                            await specimen.download(
                                "example.com", "token", tmpdir, "test.zip"
                            )

    @mark.asyncio
    async def test_download_info_endpoint_404_fallback(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                # Mock /info endpoint returning 404
                def mock_get(*args, **kwargs):
                    url = args[0] if args else kwargs.get("url", "")
                    # Handle /ping endpoint for ping_endpoint
                    if "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    # Handle /info endpoint - returns 404
                    elif "/info" in url:
                        raise ClientResponseError(
                            request_info=Mock(),
                            history=(),
                            status=404,
                            message="Not found",
                        )
                    else:
                        # For /download endpoint, return awaitable that resolves to response
                        async def get_download_response():
                            mock_resp = AsyncMock()
                            mock_resp.status = 200
                            mock_resp.headers = {
                                "Content-Type": "application/x-tar"
                            }

                            async def chunk_iter():
                                yield b"tar data"
                                yield b""

                            mock_resp.content.iter_chunked = (
                                lambda size: chunk_iter()
                            )
                            mock_resp.close = Mock()
                            return mock_resp

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # retry_request is called multiple times: _get_info (raises 404), _download, _finalize
                call_count = [0]

                async def mock_retry(func, *args, **kwargs):
                    call_count[0] += 1
                    if call_count[0] == 1:
                        # First call is _get_info, which should raise 404
                        raise ClientResponseError(
                            request_info=Mock(),
                            history=(),
                            status=404,
                            message="Not found",
                        )
                    else:
                        # Subsequent calls should proceed normally
                        return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    with patch(
                        "asyncio.create_subprocess_exec"
                    ) as mock_subprocess:
                        mock_process = AsyncMock()
                        mock_process.stdin = Mock()
                        mock_process.stdin.write = Mock()
                        mock_process.stdin.drain = AsyncMock()
                        mock_process.stdin.close = Mock()
                        mock_process.returncode = 0
                        mock_process.wait = AsyncMock(return_value=0)
                        mock_process.stderr.read = AsyncMock(return_value=b"")
                        mock_subprocess.return_value = mock_process

                        mock_finalize_response = AsyncMock()
                        mock_finalize_response.status = 200
                        mock_finalize_response.json = AsyncMock(
                            return_value={"status": "ok"}
                        )
                        mock_finalize_response.__aenter__ = AsyncMock(
                            return_value=mock_finalize_response
                        )
                        mock_finalize_response.__aexit__ = AsyncMock(
                            return_value=None
                        )

                        # session.post() should return something that is both awaitable and an async context manager
                        class AwaitableContextManager:
                            def __init__(self, return_value):
                                self.return_value = return_value

                            def __await__(self):
                                yield
                                return self

                            async def __aenter__(self):
                                return self.return_value

                            async def __aexit__(self, *args):
                                return None

                        mock_post_context = AwaitableContextManager(
                            mock_finalize_response
                        )
                        mock_session_instance.post = Mock(
                            return_value=mock_post_context
                        )

                        with patch(
                            "proximl.utils.transfer.ping_endpoint",
                            new_callable=AsyncMock,
                        ):
                            await specimen.download(
                                "example.com", "token", tmpdir
                            )

    @mark.asyncio
    async def test_download_info_endpoint_connection_error_404(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                # Mock /info endpoint returning ConnectionError with 404
                def mock_get(*args, **kwargs):
                    url = args[0] if args else kwargs.get("url", "")
                    # Handle /ping endpoint for ping_endpoint
                    if "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    # Handle /info endpoint - raises ConnectionError
                    elif "/info" in url:
                        raise ConnectionError(
                            "Failed to get server info (status 404): Not found"
                        )
                    else:
                        # For /download endpoint, return awaitable that resolves to response
                        async def get_download_response():
                            mock_resp = AsyncMock()
                            mock_resp.status = 200
                            mock_resp.headers = {
                                "Content-Type": "application/x-tar"
                            }

                            async def chunk_iter():
                                yield b"tar data"
                                yield b""

                            mock_resp.content.iter_chunked = (
                                lambda size: chunk_iter()
                            )
                            mock_resp.close = Mock()
                            return mock_resp

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # retry_request is called multiple times: _get_info (raises ConnectionError), _download, _finalize
                call_count = [0]

                async def mock_retry(func, *args, **kwargs):
                    call_count[0] += 1
                    if call_count[0] == 1:
                        # First call is _get_info, which should raise ConnectionError
                        raise ConnectionError(
                            "Failed to get server info (status 404): Not found"
                        )
                    else:
                        # Subsequent calls should proceed normally
                        return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    with patch(
                        "asyncio.create_subprocess_exec"
                    ) as mock_subprocess:
                        mock_process = AsyncMock()
                        mock_process.stdin = Mock()
                        mock_process.stdin.write = Mock()
                        mock_process.stdin.drain = AsyncMock()
                        mock_process.stdin.close = Mock()
                        mock_process.returncode = 0
                        mock_process.wait = AsyncMock(return_value=0)
                        # Return empty stderr to avoid tar error message
                        mock_process.stderr.read = AsyncMock(return_value=b"")
                        mock_subprocess.return_value = mock_process
                        # Ensure stdin is properly set up
                        if (
                            not hasattr(mock_process, "stdin")
                            or mock_process.stdin is None
                        ):
                            mock_process.stdin = Mock()
                            mock_process.stdin.write = Mock()
                            mock_process.stdin.drain = AsyncMock()
                            mock_process.stdin.close = Mock()

                        mock_finalize_response = AsyncMock()
                        mock_finalize_response.status = 200
                        mock_finalize_response.json = AsyncMock(
                            return_value={"status": "ok"}
                        )

                        mock_finalize_response.__aenter__ = AsyncMock(
                            return_value=mock_finalize_response
                        )

                        mock_finalize_response.__aexit__ = AsyncMock(
                            return_value=None
                        )

                        # session.post() should return something that is both awaitable and an async context manager
                        class AwaitableContextManager:
                            def __init__(self, return_value):
                                self.return_value = return_value

                            def __await__(self):
                                yield
                                return self

                            async def __aenter__(self):
                                return self.return_value

                            async def __aexit__(self, *args):
                                return None

                        mock_post_context = AwaitableContextManager(
                            mock_finalize_response
                        )
                        mock_session_instance.post = Mock(
                            return_value=mock_post_context
                        )

                        with patch(
                            "proximl.utils.transfer.ping_endpoint",
                            new_callable=AsyncMock,
                        ):
                            await specimen.download(
                                "example.com", "token", tmpdir
                            )

    @mark.asyncio
    async def test_download_info_endpoint_non_404_error(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                # Mock /info endpoint returning non-404 error
                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=ConnectionError(
                        "Failed to get server info (status 500): Server error"
                    ),
                ):
                    with patch(
                        "proximl.utils.transfer.ping_endpoint",
                        new_callable=AsyncMock,
                    ):
                        with raises(ConnectionError):
                            await specimen.download(
                                "example.com", "token", tmpdir
                            )

    @mark.asyncio
    async def test_download_info_endpoint_invalid_url(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                # Mock /info endpoint returning InvalidURL
                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=InvalidURL("Invalid URL"),
                ):
                    with patch(
                        "proximl.utils.transfer.ping_endpoint",
                        new_callable=AsyncMock,
                    ):
                        with raises(
                            ConnectionError, match="Invalid endpoint URL"
                        ):
                            await specimen.download(
                                "example.com", "token", tmpdir
                            )

    @mark.asyncio
    async def test_download_info_endpoint_error_reading_body(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                # Mock /info endpoint with error reading response body
                # This tests the exception handling in _get_info when response.text() raises
                async def _get_info_with_error(*args, **kwargs):
                    mock_resp = AsyncMock()
                    mock_resp.status = 500
                    mock_resp.text = AsyncMock(
                        side_effect=Exception("Read error")
                    )
                    mock_resp.request_info = Mock()
                    mock_resp.history = ()
                    mock_resp.__aenter__ = AsyncMock(return_value=mock_resp)
                    mock_resp.__aexit__ = AsyncMock(return_value=None)
                    async with mock_resp:
                        if mock_resp.status != 200:
                            try:
                                error_text = await mock_resp.text()
                            except Exception:
                                error_text = f"Unable to read response body (status: {mock_resp.status})"
                            raise ConnectionError(
                                f"Failed to get server info (status {mock_resp.status}): {error_text}"
                            )

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=_get_info_with_error,
                ):
                    with patch(
                        "proximl.utils.transfer.ping_endpoint",
                        new_callable=AsyncMock,
                    ):
                        with raises(
                            ConnectionError, match="Failed to get server info"
                        ):
                            await specimen.download(
                                "example.com", "token", tmpdir
                            )

    @mark.asyncio
    async def test_download_zip_content_type_fallback(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                # Mock /info endpoint returning TAR mode but Content-Type says zip
                def mock_get(*args, **kwargs):
                    url = args[0] if args else kwargs.get("url", "")
                    if "/info" in url or "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.json = AsyncMock(
                            return_value={"archive": False}
                        )
                        mock_resp.text = AsyncMock(return_value="")
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    else:
                        # For /download endpoint, return awaitable that resolves to response
                        async def get_download_response():
                            mock_resp = AsyncMock()
                            mock_resp.status = 200
                            mock_resp.headers = {
                                "Content-Type": "application/zip",
                                "Content-Disposition": 'attachment; filename="archive.zip"',
                            }

                            async def chunk_iter():
                                yield b"zip data"
                                yield b""

                            mock_resp.content.iter_chunked = (
                                lambda size: chunk_iter()
                            )
                            mock_resp.close = Mock()
                            return mock_resp

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # retry_request is called 3 times: _get_info, _download, _finalize
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    mock_file_context = AsyncMock()
                    mock_file_context.__aenter__ = AsyncMock(
                        return_value=AsyncMock()
                    )
                    mock_file_context.__aexit__ = AsyncMock(return_value=None)
                    mock_file_context.__aenter__.return_value.write = (
                        AsyncMock()
                    )
                    with patch(
                        "aiofiles.open", return_value=mock_file_context
                    ) as mock_file:
                        mock_finalize_response = AsyncMock()
                        mock_finalize_response.status = 200
                        mock_finalize_response.json = AsyncMock(
                            return_value={"status": "ok"}
                        )

                        mock_finalize_response.__aenter__ = AsyncMock(
                            return_value=mock_finalize_response
                        )

                        mock_finalize_response.__aexit__ = AsyncMock(
                            return_value=None
                        )

                        # session.post() should return something that is both awaitable and an async context manager
                        class AwaitableContextManager:
                            def __init__(self, return_value):
                                self.return_value = return_value

                            def __await__(self):
                                yield
                                return self

                            async def __aenter__(self):
                                return self.return_value

                            async def __aexit__(self, *args):
                                return None

                        mock_post_context = AwaitableContextManager(
                            mock_finalize_response
                        )
                        mock_session_instance.post = Mock(
                            return_value=mock_post_context
                        )

                        with patch(
                            "proximl.utils.transfer.ping_endpoint",
                            new_callable=AsyncMock,
                        ):
                            await specimen.download(
                                "example.com", "token", tmpdir
                            )

    @mark.asyncio
    async def test_download_content_length_logging(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                def mock_get(*args, **kwargs):
                    url = args[0] if args else kwargs.get("url", "")
                    if "/info" in url or "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.json = AsyncMock(
                            return_value={"archive": False}
                        )
                        mock_resp.text = AsyncMock(return_value="")
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    else:
                        # For /download endpoint, return awaitable that resolves to response
                        async def get_download_response():
                            mock_resp = AsyncMock()
                            mock_resp.status = 200
                            mock_resp.headers = {
                                "Content-Type": "application/x-tar",
                                "Content-Length": "1024",
                            }

                            async def chunk_iter():
                                yield b"tar data"
                                yield b""

                            mock_resp.content.iter_chunked = (
                                lambda size: chunk_iter()
                            )
                            mock_resp.close = Mock()
                            return mock_resp

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # retry_request is called 3 times: _get_info, _download, _finalize
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    with patch(
                        "asyncio.create_subprocess_exec"
                    ) as mock_subprocess:
                        mock_process = AsyncMock()
                        mock_process.stdin = Mock()
                        mock_process.stdin.write = Mock()
                        mock_process.stdin.drain = AsyncMock()
                        mock_process.stdin.close = Mock()
                        mock_process.returncode = 0
                        mock_process.wait = AsyncMock(return_value=0)
                        mock_process.stderr.read = AsyncMock(return_value=b"")
                        mock_subprocess.return_value = mock_process

                        mock_finalize_response = AsyncMock()
                        mock_finalize_response.status = 200
                        mock_finalize_response.json = AsyncMock(
                            return_value={"status": "ok"}
                        )
                        mock_finalize_response.__aenter__ = AsyncMock(
                            return_value=mock_finalize_response
                        )
                        mock_finalize_response.__aexit__ = AsyncMock(
                            return_value=None
                        )

                        # session.post() should return something that is both awaitable and an async context manager
                        class AwaitableContextManager:
                            def __init__(self, return_value):
                                self.return_value = return_value

                            def __await__(self):
                                yield
                                return self

                            async def __aenter__(self):
                                return self.return_value

                            async def __aexit__(self, *args):
                                return None

                        mock_post_context = AwaitableContextManager(
                            mock_finalize_response
                        )
                        mock_session_instance.post = Mock(
                            return_value=mock_post_context
                        )

                        with patch(
                            "proximl.utils.transfer.ping_endpoint",
                            new_callable=AsyncMock,
                        ):
                            await specimen.download(
                                "example.com", "token", tmpdir
                            )

    @mark.asyncio
    async def test_download_empty_file_error(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                def mock_get(*args, **kwargs):
                    url = args[0] if args else kwargs.get("url", "")
                    if "/info" in url or "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.json = AsyncMock(
                            return_value={"archive": True}
                        )
                        mock_resp.text = AsyncMock(return_value="")
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    else:
                        # For /download endpoint, return awaitable that resolves to response
                        async def get_download_response():
                            mock_resp = AsyncMock()
                            mock_resp.status = 200
                            mock_resp.headers = {
                                "Content-Type": "application/zip"
                            }

                            async def chunk_iter():
                                return
                                yield  # Make it an async generator

                            mock_resp.content.iter_chunked = (
                                lambda size: chunk_iter()
                            )
                            mock_resp.close = Mock()
                            return mock_resp

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # Mock retry_request to actually call the function passed to it
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    mock_file_context = AsyncMock()
                    mock_file_context.__aenter__ = AsyncMock(
                        return_value=AsyncMock()
                    )
                    mock_file_context.__aexit__ = AsyncMock(return_value=None)
                    mock_file_context.__aenter__.return_value.write = (
                        AsyncMock()
                    )
                    with patch(
                        "aiofiles.open", return_value=mock_file_context
                    ) as mock_file:
                        with patch(
                            "proximl.utils.transfer.ping_endpoint",
                            new_callable=AsyncMock,
                        ):
                            with raises(
                                ConnectionError,
                                match="Downloaded file is empty",
                            ):
                                await specimen.download(
                                    "example.com", "token", tmpdir
                                )

    @mark.asyncio
    async def test_download_tar_extraction_failure(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                def mock_get(*args, **kwargs):
                    url = args[0] if args else kwargs.get("url", "")
                    if "/info" in url or "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.json = AsyncMock(
                            return_value={"archive": False}
                        )
                        mock_resp.text = AsyncMock(return_value="")
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    else:
                        # For /download endpoint, return awaitable that resolves to response
                        async def get_download_response():
                            mock_resp = AsyncMock()
                            mock_resp.status = 200
                            mock_resp.headers = {
                                "Content-Type": "application/x-tar"
                            }

                            async def chunk_iter():
                                yield b"tar data"
                                yield b""

                            mock_resp.content.iter_chunked = (
                                lambda size: chunk_iter()
                            )
                            mock_resp.close = Mock()
                            return mock_resp

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # retry_request is called 3 times: _get_info, _download, _finalize
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    with patch(
                        "asyncio.create_subprocess_exec"
                    ) as mock_subprocess:
                        mock_process = AsyncMock()
                        mock_process.stdin = Mock()
                        mock_process.stdin.write = Mock()
                        mock_process.stdin.drain = AsyncMock()
                        mock_process.stdin.close = Mock()
                        mock_process.wait = AsyncMock(return_value=1)
                        mock_process.stderr.read = AsyncMock(
                            return_value=b"tar error"
                        )
                        mock_subprocess.return_value = mock_process

                        with patch(
                            "proximl.utils.transfer.ping_endpoint",
                            new_callable=AsyncMock,
                        ):
                            with raises(
                                ProxiMLException, match="tar extraction failed"
                            ):
                                await specimen.download(
                                    "example.com", "token", tmpdir
                                )

    @mark.asyncio
    async def test_download_tar_extraction_failure_no_stderr(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                def mock_get(*args, **kwargs):
                    url = args[0] if args else kwargs.get("url", "")
                    if "/info" in url or "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.json = AsyncMock(
                            return_value={"archive": False}
                        )
                        mock_resp.text = AsyncMock(return_value="")
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    else:
                        # For /download endpoint, return awaitable that resolves to response
                        async def get_download_response():
                            mock_resp = AsyncMock()
                            mock_resp.status = 200
                            mock_resp.headers = {
                                "Content-Type": "application/x-tar"
                            }

                            async def chunk_iter():
                                yield b"tar data"
                                yield b""

                            mock_resp.content.iter_chunked = (
                                lambda size: chunk_iter()
                            )
                            mock_resp.close = Mock()
                            return mock_resp

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # retry_request is called 3 times: _get_info, _download, _finalize
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    with patch(
                        "asyncio.create_subprocess_exec"
                    ) as mock_subprocess:
                        mock_process = AsyncMock()
                        mock_process.stdin = Mock()
                        mock_process.stdin.write = Mock()
                        mock_process.stdin.drain = AsyncMock()
                        mock_process.stdin.close = Mock()
                        mock_process.returncode = 1
                        mock_process.wait = AsyncMock(return_value=1)
                        mock_process.stderr.read = AsyncMock(return_value=None)
                        mock_subprocess.return_value = mock_process

                        with patch(
                            "proximl.utils.transfer.ping_endpoint",
                            new_callable=AsyncMock,
                        ):
                            with raises(
                                ProxiMLException, match="tar extraction failed"
                            ):
                                await specimen.download(
                                    "example.com", "token", tmpdir
                                )

    @mark.asyncio
    async def test_download_404_error(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                def mock_get(*args, **kwargs):
                    url = args[0] if args else kwargs.get("url", "")
                    if "/info" in url or "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.json = AsyncMock(
                            return_value={"archive": False}
                        )
                        mock_resp.text = AsyncMock(return_value="")
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    else:
                        # For /download endpoint, return awaitable that raises 404
                        async def get_download_response():
                            raise ClientResponseError(
                                request_info=Mock(),
                                history=(),
                                status=404,
                                message="Not found",
                            )

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # Mock retry_request to call through, which will raise the 404 error
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    with patch(
                        "proximl.utils.transfer.ping_endpoint",
                        new_callable=AsyncMock,
                    ):
                        with raises(ClientResponseError):
                            await specimen.download(
                                "example.com", "token", tmpdir
                            )

    @mark.asyncio
    async def test_download_non_404_error_status(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                def mock_get(*args, **kwargs):
                    url = args[0] if args else kwargs.get("url", "")
                    if "/info" in url or "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.json = AsyncMock(
                            return_value={"archive": False}
                        )
                        mock_resp.text = AsyncMock(return_value="")
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    else:
                        # For /download endpoint, return awaitable that raises 500
                        async def get_download_response():
                            raise ClientResponseError(
                                request_info=Mock(),
                                history=(),
                                status=500,
                                message="Server error",
                            )

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # Mock retry_request to call through, which will raise the 500 error
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    with patch(
                        "proximl.utils.transfer.ping_endpoint",
                        new_callable=AsyncMock,
                    ):
                        with raises(ClientResponseError):
                            await specimen.download(
                                "example.com", "token", tmpdir
                            )

    @mark.asyncio
    async def test_download_finalize_failure(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                def mock_get(*args, **kwargs):
                    url = args[0] if args else kwargs.get("url", "")
                    if "/info" in url or "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.json = AsyncMock(
                            return_value={"archive": False}
                        )
                        mock_resp.text = AsyncMock(return_value="")
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    else:
                        # For /download endpoint, return awaitable that resolves to response
                        async def get_download_response():
                            mock_resp = AsyncMock()
                            mock_resp.status = 200
                            mock_resp.headers = {
                                "Content-Type": "application/x-tar"
                            }

                            async def chunk_iter():
                                yield b"tar data"
                                yield b""

                            mock_resp.content.iter_chunked = (
                                lambda size: chunk_iter()
                            )
                            mock_resp.close = Mock()
                            return mock_resp

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # retry_request is called 3 times: _get_info, _download, _finalize
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    with patch(
                        "asyncio.create_subprocess_exec"
                    ) as mock_subprocess:
                        mock_process = AsyncMock()
                        mock_process.stdin = Mock()
                        mock_process.stdin.write = Mock()
                        mock_process.stdin.drain = AsyncMock()
                        mock_process.stdin.close = Mock()
                        mock_process.returncode = 0
                        mock_process.wait = AsyncMock(return_value=0)
                        mock_process.stderr.read = AsyncMock(return_value=b"")
                        mock_subprocess.return_value = mock_process

                        mock_finalize_response = AsyncMock()
                        mock_finalize_response.status = 500

                        mock_finalize_response.text = AsyncMock(
                            return_value="Finalize error"
                        )

                        mock_finalize_response.__aenter__ = AsyncMock(
                            return_value=mock_finalize_response
                        )

                        mock_finalize_response.__aexit__ = AsyncMock(
                            return_value=None
                        )

                        # session.post() should return something that is both awaitable and an async context manager
                        class AwaitableContextManager:
                            def __init__(self, return_value):
                                self.return_value = return_value

                            def __await__(self):
                                yield
                                return self

                            async def __aenter__(self):
                                return self.return_value

                            async def __aexit__(self, *args):
                                return None

                        mock_post_context = AwaitableContextManager(
                            mock_finalize_response
                        )
                        mock_session_instance.post = Mock(
                            return_value=mock_post_context
                        )

                        with patch(
                            "proximl.utils.transfer.ping_endpoint",
                            new_callable=AsyncMock,
                        ):
                            with raises(
                                ConnectionError, match="Finalize failed"
                            ):
                                await specimen.download(
                                    "example.com", "token", tmpdir
                                )

    @mark.asyncio
    async def test_download_content_disposition_filename(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                def mock_get(*args, **kwargs):
                    url = args[0] if args else kwargs.get("url", "")
                    if "/info" in url or "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.json = AsyncMock(
                            return_value={"archive": True}
                        )
                        mock_resp.text = AsyncMock(return_value="")
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    else:
                        # For /download endpoint, return awaitable that resolves to response
                        async def get_download_response():
                            mock_resp = AsyncMock()
                            mock_resp.status = 200
                            mock_resp.headers = {
                                "Content-Type": "application/zip",
                                "Content-Disposition": 'attachment; filename="custom-name.zip"',
                            }

                            async def chunk_iter():
                                yield b"zip data"
                                yield b""

                            mock_resp.content.iter_chunked = (
                                lambda size: chunk_iter()
                            )
                            mock_resp.close = Mock()
                            return mock_resp

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # Mock retry_request to actually call the function passed to it
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    mock_file_context = AsyncMock()
                    mock_file_context.__aenter__ = AsyncMock(
                        return_value=AsyncMock()
                    )
                    mock_file_context.__aexit__ = AsyncMock(return_value=None)
                    mock_file_context.__aenter__.return_value.write = (
                        AsyncMock()
                    )
                    with patch(
                        "aiofiles.open", return_value=mock_file_context
                    ) as mock_file:
                        mock_finalize_response = AsyncMock()
                        mock_finalize_response.status = 200
                        mock_finalize_response.json = AsyncMock(
                            return_value={"status": "ok"}
                        )

                        mock_finalize_response.__aenter__ = AsyncMock(
                            return_value=mock_finalize_response
                        )

                        mock_finalize_response.__aexit__ = AsyncMock(
                            return_value=None
                        )

                        # session.post() should return something that is both awaitable and an async context manager
                        class AwaitableContextManager:
                            def __init__(self, return_value):
                                self.return_value = return_value

                            def __await__(self):
                                yield
                                return self

                            async def __aenter__(self):
                                return self.return_value

                            async def __aexit__(self, *args):
                                return None

                        mock_post_context = AwaitableContextManager(
                            mock_finalize_response
                        )
                        mock_session_instance.post = Mock(
                            return_value=mock_post_context
                        )

                        with patch(
                            "proximl.utils.transfer.ping_endpoint",
                            new_callable=AsyncMock,
                        ):
                            await specimen.download(
                                "example.com", "token", tmpdir
                            )

    @mark.asyncio
    async def test_download_content_disposition_filename_no_quotes(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                def mock_get(*args, **kwargs):
                    url = args[0] if args else kwargs.get("url", "")
                    # Handle /ping endpoint for ping_endpoint
                    if "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    # Handle /info endpoint
                    elif "/info" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.json = AsyncMock(
                            return_value={"archive": True}
                        )
                        mock_resp.text = AsyncMock(return_value="")
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    else:
                        # For /download endpoint, return awaitable that resolves to response
                        async def get_download_response():
                            mock_resp = AsyncMock()
                            mock_resp.status = 200
                            mock_resp.headers = {
                                "Content-Type": "application/zip",
                                "Content-Disposition": "attachment; filename=custom-name.zip",
                            }

                            async def chunk_iter():
                                yield b"zip data"
                                yield b""

                            mock_resp.content.iter_chunked = (
                                lambda size: chunk_iter()
                            )
                            mock_resp.close = Mock()
                            return mock_resp

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # Mock retry_request to actually call the function passed to it
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    with patch(
                        "proximl.utils.transfer.ping_endpoint",
                        new_callable=AsyncMock,
                    ):
                        mock_file_context = AsyncMock()
                        mock_file_context.__aenter__ = AsyncMock(
                            return_value=AsyncMock()
                        )
                        mock_file_context.__aexit__ = AsyncMock(
                            return_value=None
                        )
                        mock_file_context.__aenter__.return_value.write = (
                            AsyncMock()
                        )
                        with patch(
                            "aiofiles.open", return_value=mock_file_context
                        ) as mock_file:
                            mock_finalize_response = AsyncMock()
                            mock_finalize_response.status = 200
                            mock_finalize_response.json = AsyncMock(
                                return_value={"status": "ok"}
                            )

                        mock_finalize_response.__aenter__ = AsyncMock(
                            return_value=mock_finalize_response
                        )

                        mock_finalize_response.__aexit__ = AsyncMock(
                            return_value=None
                        )

                        # session.post() should return something that is both awaitable and an async context manager
                        class AwaitableContextManager:
                            def __init__(self, return_value):
                                self.return_value = return_value

                            def __await__(self):
                                yield
                                return self

                            async def __aenter__(self):
                                return self.return_value

                            async def __aexit__(self, *args):
                                return None

                        mock_post_context = AwaitableContextManager(
                            mock_finalize_response
                        )
                        mock_session_instance.post = Mock(
                            return_value=mock_post_context
                        )

                        with patch(
                            "proximl.utils.transfer.ping_endpoint",
                            new_callable=AsyncMock,
                        ):
                            await specimen.download(
                                "example.com", "token", tmpdir
                            )

    @mark.asyncio
    async def test_download_no_content_disposition_fallback(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                def mock_get(*args, **kwargs):
                    url = args[0] if args else kwargs.get("url", "")
                    if "/info" in url or "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.json = AsyncMock(
                            return_value={"archive": True}
                        )
                        mock_resp.text = AsyncMock(return_value="")
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    else:
                        # For /download endpoint, return awaitable that resolves to response
                        async def get_download_response():
                            mock_resp = AsyncMock()
                            mock_resp.status = 200
                            mock_resp.headers = {
                                "Content-Type": "application/zip",
                            }

                            async def chunk_iter():
                                yield b"zip data"
                                yield b""

                            mock_resp.content.iter_chunked = (
                                lambda size: chunk_iter()
                            )
                            mock_resp.close = Mock()
                            return mock_resp

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # Mock retry_request to actually call the function passed to it
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    mock_file_context = AsyncMock()
                    mock_file_context.__aenter__ = AsyncMock(
                        return_value=AsyncMock()
                    )
                    mock_file_context.__aexit__ = AsyncMock(return_value=None)
                    mock_file_context.__aenter__.return_value.write = (
                        AsyncMock()
                    )
                    with patch(
                        "aiofiles.open", return_value=mock_file_context
                    ) as mock_file:
                        mock_finalize_response = AsyncMock()
                        mock_finalize_response.status = 200
                        mock_finalize_response.json = AsyncMock(
                            return_value={"status": "ok"}
                        )

                        mock_finalize_response.__aenter__ = AsyncMock(
                            return_value=mock_finalize_response
                        )

                        mock_finalize_response.__aexit__ = AsyncMock(
                            return_value=None
                        )

                        # session.post() should return something that is both awaitable and an async context manager
                        class AwaitableContextManager:
                            def __init__(self, return_value):
                                self.return_value = return_value

                            def __await__(self):
                                yield
                                return self

                            async def __aenter__(self):
                                return self.return_value

                            async def __aexit__(self, *args):
                                return None

                        mock_post_context = AwaitableContextManager(
                            mock_finalize_response
                        )
                        mock_session_instance.post = Mock(
                            return_value=mock_post_context
                        )

                        with patch(
                            "proximl.utils.transfer.ping_endpoint",
                            new_callable=AsyncMock,
                        ):
                            await specimen.download(
                                "example.com", "token", tmpdir
                            )

    @mark.asyncio
    async def test_download_filename_no_zip_extension(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                def mock_get(*args, **kwargs):
                    url = args[0] if args else kwargs.get("url", "")
                    # Handle /ping endpoint for ping_endpoint
                    if "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    # Handle /info endpoint
                    elif "/info" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.json = AsyncMock(
                            return_value={"archive": True}
                        )
                        mock_resp.text = AsyncMock(return_value="")
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    else:
                        # For /download endpoint, return awaitable that resolves to response
                        async def get_download_response():
                            mock_resp = AsyncMock()
                            mock_resp.status = 200
                            mock_resp.headers = {
                                "Content-Type": "application/zip",
                                "Content-Disposition": 'attachment; filename="custom-name"',
                            }

                            async def chunk_iter():
                                yield b"zip data"
                                yield b""

                            mock_resp.content.iter_chunked = (
                                lambda size: chunk_iter()
                            )
                            mock_resp.close = Mock()
                            return mock_resp

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # Mock retry_request to actually call the function passed to it
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    with patch(
                        "proximl.utils.transfer.ping_endpoint",
                        new_callable=AsyncMock,
                    ):
                        mock_file_context = AsyncMock()
                        mock_file_context.__aenter__ = AsyncMock(
                            return_value=AsyncMock()
                        )
                        mock_file_context.__aexit__ = AsyncMock(
                            return_value=None
                        )
                        mock_file_context.__aenter__.return_value.write = (
                            AsyncMock()
                        )
                        with patch(
                            "aiofiles.open", return_value=mock_file_context
                        ) as mock_file:
                            mock_finalize_response = AsyncMock()
                            mock_finalize_response.status = 200
                            mock_finalize_response.json = AsyncMock(
                                return_value={"status": "ok"}
                            )

                        mock_finalize_response.__aenter__ = AsyncMock(
                            return_value=mock_finalize_response
                        )

                        mock_finalize_response.__aexit__ = AsyncMock(
                            return_value=None
                        )

                        # session.post() should return something that is both awaitable and an async context manager
                        class AwaitableContextManager:
                            def __init__(self, return_value):
                                self.return_value = return_value

                            def __await__(self):
                                yield
                                return self

                            async def __aenter__(self):
                                return self.return_value

                            async def __aexit__(self, *args):
                                return None

                        mock_post_context = AwaitableContextManager(
                            mock_finalize_response
                        )
                        mock_session_instance.post = Mock(
                            return_value=mock_post_context
                        )

                        with patch(
                            "proximl.utils.transfer.ping_endpoint",
                            new_callable=AsyncMock,
                        ):
                            await specimen.download(
                                "example.com", "token", tmpdir
                            )

    @mark.asyncio
    async def test_download_multiple_chunks(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                def mock_get(*args, **kwargs):
                    url = args[0] if args else kwargs.get("url", "")
                    # Handle /ping endpoint for ping_endpoint
                    if "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    # Handle /info endpoint
                    elif "/info" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.json = AsyncMock(
                            return_value={"archive": False}
                        )
                        mock_resp.text = AsyncMock(return_value="")
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    else:
                        # For /download endpoint, return awaitable that resolves to response
                        async def get_download_response():
                            mock_resp = AsyncMock()
                            mock_resp.status = 200
                            mock_resp.headers = {
                                "Content-Type": "application/x-tar"
                            }

                            # Simulate multiple chunks - iter_chunked should return an async iterator
                            async def chunk_iter():
                                yield b"chunk1"
                                yield b"chunk2"
                                yield b""

                            mock_resp.content.iter_chunked = (
                                lambda size: chunk_iter()
                            )
                            mock_resp.close = Mock()
                            return mock_resp

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # retry_request is called 3 times: _get_info, _download, _finalize
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    with patch(
                        "proximl.utils.transfer.ping_endpoint",
                        new_callable=AsyncMock,
                    ):
                        with patch(
                            "asyncio.create_subprocess_exec"
                        ) as mock_subprocess:
                            mock_process = AsyncMock()
                            mock_process.stdin = Mock()
                            mock_process.stdin.write = Mock()
                            mock_process.stdin.drain = AsyncMock()
                            mock_process.stdin.close = Mock()
                            mock_process.returncode = 0
                            mock_process.wait = AsyncMock(return_value=0)
                            mock_process.stderr.read = AsyncMock(
                                return_value=b""
                            )

                            # create_subprocess_exec is async, so return a coroutine
                            async def mock_create_subprocess_exec(
                                *args, **kwargs
                            ):
                                return mock_process

                            mock_subprocess.side_effect = (
                                mock_create_subprocess_exec
                            )

                            mock_finalize_response = AsyncMock()
                            mock_finalize_response.status = 200
                            mock_finalize_response.json = AsyncMock(
                                return_value={"status": "ok"}
                            )
                            mock_finalize_response.__aenter__ = AsyncMock(
                                return_value=mock_finalize_response
                            )
                            mock_finalize_response.__aexit__ = AsyncMock(
                                return_value=None
                            )

                            # session.post() should return something that is both awaitable and an async context manager
                            class AwaitableContextManager:
                                def __init__(self, return_value):
                                    self.return_value = return_value

                                def __await__(self):
                                    yield
                                    return self

                                async def __aenter__(self):
                                    return self.return_value

                                async def __aexit__(self, *args):
                                    return None

                            mock_post_context = AwaitableContextManager(
                                mock_finalize_response
                            )
                            mock_session_instance.post = Mock(
                                return_value=mock_post_context
                            )

                            await specimen.download(
                                "example.com", "token", tmpdir
                            )
                        # Verify stdin.write was called for each chunk (chunk1, chunk2, and empty)
                        # The empty chunk at the end also triggers a write
                        assert mock_process.stdin.write.call_count >= 2

    @mark.asyncio
    async def test_upload_chunk_retry_status_504(self):
        """Test upload_chunk retry on 504 status - this tests lines 106-113"""
        session = AsyncMock()
        response = AsyncMock()
        response.status = 504
        response.text = AsyncMock(return_value="Gateway Timeout")
        response.request_info = Mock()
        response.history = ()
        session.put = AsyncMock(return_value=response.__aenter__())
        response.__aenter__ = AsyncMock(return_value=response)
        response.__aexit__ = AsyncMock(return_value=None)

        async def _upload(*args, **kwargs):
            async with session.put(
                f"https://example.com/upload",
                headers={},
                data=b"data",
                timeout=30,
            ) as resp:
                if resp.status == 504:
                    text = await resp.text()
                    raise ClientResponseError(
                        request_info=resp.request_info,
                        history=resp.history,
                        status=resp.status,
                        message=text,
                    )

        # Mock the actual upload_chunk behavior with retry
        with patch("proximl.utils.transfer.retry_request") as mock_retry:
            # Simulate retry_request calling _upload which raises 504
            # The retry_request will retry, but we'll make it fail after max retries
            mock_retry.side_effect = ClientResponseError(
                request_info=Mock(),
                history=(),
                status=504,
                message="Gateway Timeout",
            )
            with patch("asyncio.sleep", new_callable=AsyncMock):
                # This should trigger the retry logic, but we'll let it fail after max retries
                with raises(ClientResponseError):
                    await specimen.upload_chunk(
                        session,
                        "https://example.com",
                        "token",
                        100,
                        b"data",
                        0,
                    )

    @mark.asyncio
    async def test_upload_finalize_success_logging(self):
        """Test upload finalize success logging"""
        with tempfile.NamedTemporaryFile() as tmp:
            tmp.write(b"test content")
            tmp.flush()

            with patch(
                "proximl.utils.transfer.ping_endpoint", new_callable=AsyncMock
            ):
                with patch(
                    "asyncio.create_subprocess_exec"
                ) as mock_subprocess:
                    mock_process = AsyncMock()
                    mock_process.stdout.read = AsyncMock(
                        side_effect=[b"data", b""]
                    )
                    mock_process.returncode = 0
                    mock_process.wait = AsyncMock(return_value=0)
                    mock_process.stderr.read = AsyncMock(return_value=b"")
                    mock_subprocess.return_value = mock_process

                    with patch("aiohttp.ClientSession") as mock_session:
                        mock_session_instance = AsyncMock()
                        mock_session.return_value.__aenter__ = AsyncMock(
                            return_value=mock_session_instance
                        )
                        mock_session.return_value.__aexit__ = AsyncMock(
                            return_value=None
                        )

                        with patch(
                            "proximl.utils.transfer.upload_chunk",
                            new_callable=AsyncMock,
                        ):
                            mock_finalize_response = AsyncMock()
                            mock_finalize_response.status = 200
                            mock_finalize_response.json = AsyncMock(
                                return_value={"status": "ok", "hash": "abc123"}
                            )
                            mock_finalize_response.__aenter__ = AsyncMock(
                                return_value=mock_finalize_response
                            )
                            mock_finalize_response.__aexit__ = AsyncMock(
                                return_value=None
                            )

                            # session.post() should return something that is both awaitable and an async context manager
                            class AwaitableContextManager:
                                def __init__(self, return_value):
                                    self.return_value = return_value

                                def __await__(self):
                                    yield
                                    return self

                                async def __aenter__(self):
                                    return self.return_value

                                async def __aexit__(self, *args):
                                    return None

                            mock_post_context = AwaitableContextManager(
                                mock_finalize_response
                            )
                            mock_session_instance.post = Mock(
                                return_value=mock_post_context
                            )

                            with patch("logging.debug") as mock_log:
                                await specimen.upload(
                                    "example.com", "token", tmp.name
                                )
                                # Verify logging.debug was called for finalize
                                mock_log.assert_called()

    @mark.asyncio
    async def test_download_info_endpoint_non_200_status(self):
        """Test download info endpoint non-200 status with error reading body"""
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                # Mock /info endpoint returning 500 with error reading body
                async def _get_info(*args, **kwargs):
                    mock_resp = AsyncMock()
                    mock_resp.status = 500
                    mock_resp.text = AsyncMock(
                        side_effect=Exception("Read error")
                    )
                    mock_resp.request_info = Mock()
                    mock_resp.history = ()
                    mock_resp.__aenter__ = AsyncMock(return_value=mock_resp)
                    mock_resp.__aexit__ = AsyncMock(return_value=None)
                    async with mock_resp:
                        if mock_resp.status != 200:
                            try:
                                error_text = await mock_resp.text()
                            except Exception:
                                error_text = f"Unable to read response body (status: {mock_resp.status})"
                            raise ConnectionError(
                                f"Failed to get server info (status {mock_resp.status}): {error_text}"
                            )

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=_get_info,
                ):
                    with patch(
                        "proximl.utils.transfer.ping_endpoint",
                        new_callable=AsyncMock,
                    ):
                        with raises(
                            ConnectionError, match="Failed to get server info"
                        ):
                            await specimen.download(
                                "example.com", "token", tmpdir
                            )

    @mark.asyncio
    async def test_download_non_404_error_in_download(self):
        """Test download endpoint non-404 error"""
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                async def mock_get(*args, **kwargs):
                    mock_resp = AsyncMock()
                    mock_resp.status = 200
                    mock_resp.json = AsyncMock(return_value={"archive": False})
                    mock_resp.__aenter__ = AsyncMock(return_value=mock_resp)
                    mock_resp.__aexit__ = AsyncMock(return_value=None)
                    mock_resp.text = AsyncMock(return_value="")
                    return mock_resp

                mock_session_instance.get = AsyncMock(side_effect=mock_get)

                # Mock _download to raise ClientResponseError for non-404 errors
                async def _download(*args, **kwargs):
                    # Simulate session.get() returning a response with status 500
                    mock_resp = AsyncMock()
                    mock_resp.status = 500
                    mock_resp.text = AsyncMock(return_value="Server error")
                    mock_resp.close = Mock()
                    mock_resp.request_info = Mock()
                    mock_resp.history = ()
                    # This simulates the code in _download that checks status
                    if mock_resp.status != 200:
                        text = await mock_resp.text()
                        mock_resp.close()
                        if mock_resp.status == 404:
                            raise ConnectionError(
                                "Download endpoint not available (404)"
                            )
                        raise ClientResponseError(
                            request_info=mock_resp.request_info,
                            history=mock_resp.history,
                            status=mock_resp.status,
                            message=text,
                        )
                    return mock_resp

                # Mock retry_request to raise ClientResponseError for the download call
                # First call returns info, second call (for download) raises
                def mock_retry_side_effect(func, *args, **kwargs):
                    # Check if this is the _download call by checking if func is callable
                    # For the info call, we return the dict
                    # For the download call, we raise
                    if callable(func):
                        # This is likely _download - raise the error
                        raise ClientResponseError(
                            request_info=Mock(),
                            history=(),
                            status=500,
                            message="Server error",
                        )
                    # This shouldn't happen, but return the info dict
                    return {"archive": False}

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=[
                        {"archive": False},
                        ClientResponseError(
                            request_info=Mock(),
                            history=(),
                            status=500,
                            message="Server error",
                        ),
                    ],
                ):
                    with patch(
                        "proximl.utils.transfer.ping_endpoint",
                        new_callable=AsyncMock,
                    ):
                        with raises(ClientResponseError):
                            await specimen.download(
                                "example.com", "token", tmpdir
                            )

    @mark.asyncio
    async def test_download_content_disposition_filename_no_quotes_fallback(
        self,
    ):
        """Test download filename parsing without quotes fallback"""
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                def mock_get(*args, **kwargs):
                    url = args[0] if args else kwargs.get("url", "")
                    # Handle /ping endpoint for ping_endpoint
                    if "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    # Handle /info endpoint
                    elif "/info" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.json = AsyncMock(
                            return_value={"archive": True}
                        )
                        mock_resp.text = AsyncMock(return_value="")
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    else:
                        # For /download endpoint, return awaitable that resolves to response
                        async def get_download_response():
                            mock_resp = AsyncMock()
                            mock_resp.status = 200
                            mock_resp.headers = {
                                "Content-Type": "application/zip",
                                "Content-Disposition": "attachment; filename=test-file.zip",
                            }

                            async def chunk_iter():
                                yield b"zip data"
                                yield b""

                            mock_resp.content.iter_chunked = (
                                lambda size: chunk_iter()
                            )
                            mock_resp.close = Mock()
                            return mock_resp

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # Mock retry_request to actually call the function passed to it
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    with patch(
                        "proximl.utils.transfer.ping_endpoint",
                        new_callable=AsyncMock,
                    ):
                        mock_file_context = AsyncMock()
                        mock_file_context.__aenter__ = AsyncMock(
                            return_value=AsyncMock()
                        )
                        mock_file_context.__aexit__ = AsyncMock(
                            return_value=None
                        )
                        mock_file_context.__aenter__.return_value.write = (
                            AsyncMock()
                        )
                        with patch(
                            "aiofiles.open", return_value=mock_file_context
                        ) as mock_file:
                            mock_finalize_response = AsyncMock()
                            mock_finalize_response.status = 200
                            mock_finalize_response.json = AsyncMock(
                                return_value={"status": "ok"}
                            )
                            mock_finalize_response.__aenter__ = AsyncMock(
                                return_value=mock_finalize_response
                            )
                            mock_finalize_response.__aexit__ = AsyncMock(
                                return_value=None
                            )

                        # session.post() should return something that is both awaitable and an async context manager
                        class AwaitableContextManager:
                            def __init__(self, return_value):
                                self.return_value = return_value

                            def __await__(self):
                                yield
                                return self

                            async def __aenter__(self):
                                return self.return_value

                            async def __aexit__(self, *args):
                                return None

                        mock_post_context = AwaitableContextManager(
                            mock_finalize_response
                        )
                        mock_session_instance.post = Mock(
                            return_value=mock_post_context
                        )

                        with patch(
                            "proximl.utils.transfer.ping_endpoint",
                            new_callable=AsyncMock,
                        ):
                            await specimen.download(
                                "example.com", "token", tmpdir
                            )

    @mark.asyncio
    async def test_download_finalize_success_logging(self):
        """Test download finalize success logging"""
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                # Mock /info endpoint returning success
                mock_info_response = AsyncMock()
                mock_info_response.status = 200
                mock_info_response.json = AsyncMock(
                    return_value={"archive": False}
                )
                mock_info_response.__aenter__ = AsyncMock(
                    return_value=mock_info_response
                )
                mock_info_response.__aexit__ = AsyncMock(return_value=None)

                mock_response = AsyncMock()
                mock_response.status = 200
                mock_response.headers = {"Content-Type": "application/x-tar"}

                async def chunk_iter():
                    yield b"tar data"
                    yield b""

                mock_response.content.iter_chunked = lambda size: chunk_iter()
                mock_response.close = Mock()
                mock_response.request_info = Mock()
                mock_response.history = ()

                call_count = 0

                def mock_get(*args, **kwargs):
                    nonlocal call_count
                    url = args[0] if args else kwargs.get("url", "")
                    # Handle /ping endpoint for ping_endpoint
                    if "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    call_count += 1
                    if call_count == 1:
                        # For /info endpoint, return async context manager (used with async with)
                        mock_get_response = AsyncMock()
                        mock_get_response.__aenter__ = AsyncMock(
                            return_value=mock_info_response
                        )
                        mock_get_response.__aexit__ = AsyncMock(
                            return_value=None
                        )
                        return mock_get_response
                    else:
                        # For /download endpoint, return awaitable that resolves to response (used with await)
                        async def get_download_response():
                            return mock_response

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # retry_request is called 3 times: _get_info, _download, _finalize
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    with patch(
                        "proximl.utils.transfer.ping_endpoint",
                        new_callable=AsyncMock,
                    ):
                        with patch(
                            "asyncio.create_subprocess_exec"
                        ) as mock_subprocess:
                            mock_process = AsyncMock()
                            mock_process.stdin = Mock()
                            mock_process.stdin.write = Mock()
                            mock_process.stdin.drain = AsyncMock()
                            mock_process.stdin.close = Mock()
                            mock_process.returncode = 0
                            mock_process.wait = AsyncMock(return_value=0)
                            mock_process.stderr.read = AsyncMock(
                                return_value=b""
                            )
                            mock_subprocess.return_value = mock_process

                            mock_finalize_response = AsyncMock()
                            mock_finalize_response.status = 200
                            mock_finalize_response.json = AsyncMock(
                                return_value={"status": "ok", "files": 10}
                            )
                            mock_finalize_response.__aenter__ = AsyncMock(
                                return_value=mock_finalize_response
                            )
                            mock_finalize_response.__aexit__ = AsyncMock(
                                return_value=None
                            )

                            # session.post() should return something that is both awaitable and an async context manager
                            class AwaitableContextManager:
                                def __init__(self, return_value):
                                    self.return_value = return_value

                                def __await__(self):
                                    yield
                                    return self

                                async def __aenter__(self):
                                    return self.return_value

                                async def __aexit__(self, *args):
                                    return None

                            mock_post_context = AwaitableContextManager(
                                mock_finalize_response
                            )
                            mock_session_instance.post = Mock(
                                return_value=mock_post_context
                            )

                            with patch("logging.debug") as mock_log:
                                await specimen.download(
                                    "example.com", "token", tmpdir
                                )
                                # Verify logging.debug was called for finalize
                                mock_log.assert_called()

    @mark.asyncio
    async def test_download_info_endpoint_error_direct(self):
        """Test download info endpoint error handling (lines 246-259) - direct execution"""
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                # Mock /info endpoint returning 500
                mock_info_response = AsyncMock()
                mock_info_response.status = 500
                mock_info_response.text = AsyncMock(
                    return_value="Server error"
                )
                mock_info_response.request_info = Mock()
                mock_info_response.history = ()
                mock_info_response.__aenter__ = AsyncMock(
                    return_value=mock_info_response
                )
                mock_info_response.__aexit__ = AsyncMock(return_value=None)

                # Make session.get return an async context manager
                mock_get_response = AsyncMock()
                mock_get_response.__aenter__ = AsyncMock(
                    return_value=mock_info_response
                )
                mock_get_response.__aexit__ = AsyncMock(return_value=None)
                mock_session_instance.get = Mock(
                    return_value=mock_get_response
                )

                # Mock retry_request to actually call the function passed to it
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    with patch(
                        "proximl.utils.transfer.ping_endpoint",
                        new_callable=AsyncMock,
                    ):
                        with raises(
                            ConnectionError, match="Failed to get server info"
                        ):
                            await specimen.download(
                                "example.com", "token", tmpdir
                            )

    @mark.asyncio
    async def test_download_info_endpoint_error_reading_body_direct(self):
        """Test download info endpoint error reading body (lines 252-255) - direct execution"""
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                # Mock /info endpoint returning 500 with error reading body
                mock_info_response = AsyncMock()
                mock_info_response.status = 500
                mock_info_response.text = AsyncMock(
                    side_effect=Exception("Read error")
                )
                mock_info_response.request_info = Mock()
                mock_info_response.history = ()
                mock_info_response.__aenter__ = AsyncMock(
                    return_value=mock_info_response
                )
                mock_info_response.__aexit__ = AsyncMock(return_value=None)

                # Make session.get return an async context manager
                def mock_get(*args, **kwargs):
                    url = args[0] if args else kwargs.get("url", "")
                    # Handle /ping endpoint for ping_endpoint
                    if "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    # Handle /info endpoint
                    mock_get_response = AsyncMock()
                    mock_get_response.__aenter__ = AsyncMock(
                        return_value=mock_info_response
                    )
                    mock_get_response.__aexit__ = AsyncMock(return_value=None)
                    return mock_get_response

                mock_session_instance.get = Mock(side_effect=mock_get)

                # Mock retry_request to actually call the function passed to it
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    with patch(
                        "proximl.utils.transfer.ping_endpoint",
                        new_callable=AsyncMock,
                    ):
                        with raises(
                            ConnectionError,
                            match="Failed to get server info.*Unable to read response body",
                        ):
                            await specimen.download(
                                "example.com", "token", tmpdir
                            )

    @mark.asyncio
    async def test_download_endpoint_404_error_direct(self):
        """Test download endpoint 404 error (lines 290-298) - direct execution"""
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                # Mock /info endpoint returning success
                mock_info_response = AsyncMock()
                mock_info_response.status = 200
                mock_info_response.json = AsyncMock(
                    return_value={"archive": False}
                )
                mock_info_response.__aenter__ = AsyncMock(
                    return_value=mock_info_response
                )
                mock_info_response.__aexit__ = AsyncMock(return_value=None)

                # Mock /download endpoint returning 404
                mock_download_response = AsyncMock()
                mock_download_response.status = 404
                mock_download_response.text = AsyncMock(
                    return_value="Not Found"
                )
                mock_download_response.close = Mock()
                mock_download_response.request_info = Mock()
                mock_download_response.history = ()

                call_count = 0

                def mock_get(*args, **kwargs):
                    nonlocal call_count
                    url = args[0] if args else kwargs.get("url", "")
                    # Handle /ping endpoint for ping_endpoint
                    if "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    call_count += 1
                    if call_count == 1:
                        # For /info endpoint, return async context manager (used with async with)
                        mock_get_response = AsyncMock()
                        mock_get_response.__aenter__ = AsyncMock(
                            return_value=mock_info_response
                        )
                        mock_get_response.__aexit__ = AsyncMock(
                            return_value=None
                        )
                        return mock_get_response
                    else:
                        # For /download endpoint, return awaitable that resolves to response (used with await)
                        async def get_download_response():
                            return mock_download_response

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # Mock retry_request to actually call the function passed to it
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    with patch(
                        "proximl.utils.transfer.ping_endpoint",
                        new_callable=AsyncMock,
                    ):
                        # The 404 error should be raised as ClientResponseError and retried, but eventually
                        # it will raise ConnectionError with the message about endpoint not available
                        with raises((ConnectionError, ClientResponseError)):
                            await specimen.download(
                                "example.com", "token", tmpdir
                            )

    @mark.asyncio
    async def test_download_endpoint_non_404_error_direct(self):
        """Test download endpoint non-404 error (lines 290-304) - direct execution"""
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                # Mock /info endpoint returning success
                mock_info_response = AsyncMock()
                mock_info_response.status = 200
                mock_info_response.json = AsyncMock(
                    return_value={"archive": False}
                )
                mock_info_response.__aenter__ = AsyncMock(
                    return_value=mock_info_response
                )
                mock_info_response.__aexit__ = AsyncMock(return_value=None)

                # Mock /download endpoint returning 500
                mock_download_response = AsyncMock()
                mock_download_response.status = 500
                mock_download_response.text = AsyncMock(
                    return_value="Server error"
                )
                mock_download_response.close = Mock()
                mock_download_response.request_info = Mock()
                mock_download_response.history = ()

                call_count = 0

                def mock_get(*args, **kwargs):
                    nonlocal call_count
                    url = args[0] if args else kwargs.get("url", "")
                    # Handle /ping endpoint for ping_endpoint
                    if "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    call_count += 1
                    if call_count == 1:
                        # For /info endpoint, return async context manager (used with async with)
                        mock_get_response = AsyncMock()
                        mock_get_response.__aenter__ = AsyncMock(
                            return_value=mock_info_response
                        )
                        mock_get_response.__aexit__ = AsyncMock(
                            return_value=None
                        )
                        return mock_get_response
                    else:
                        # For /download endpoint, return awaitable that resolves to response (used with await)
                        async def get_download_response():
                            return mock_download_response

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # Mock retry_request to actually call the function passed to it
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    with patch(
                        "proximl.utils.transfer.ping_endpoint",
                        new_callable=AsyncMock,
                    ):
                        with raises(ClientResponseError) as exc_info:
                            await specimen.download(
                                "example.com", "token", tmpdir
                            )
                    assert exc_info.value.status == 500

    @mark.asyncio
    async def test_download_filename_fallback_no_quotes_direct(self):
        """Test download filename parsing fallback without quotes (lines 339-343) - direct execution"""
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("aiohttp.ClientSession") as mock_session:
                mock_session_instance = AsyncMock()
                mock_session.return_value.__aenter__ = AsyncMock(
                    return_value=mock_session_instance
                )
                mock_session.return_value.__aexit__ = AsyncMock(
                    return_value=None
                )

                # Mock /info endpoint returning success
                mock_info_response = AsyncMock()
                mock_info_response.status = 200
                mock_info_response.json = AsyncMock(
                    return_value={"archive": True}
                )
                mock_info_response.__aenter__ = AsyncMock(
                    return_value=mock_info_response
                )
                mock_info_response.__aexit__ = AsyncMock(return_value=None)

                # Mock /download endpoint with Content-Disposition that forces fallback regex
                # To trigger the fallback path (lines 339-343), we need the first regex to not match
                # The first regex is r'filename="?([^"]+)"?' which matches filename=value
                # To make it not match, we'll patch the regex search to return None for the first attempt
                mock_download_response = AsyncMock()
                mock_download_response.status = 200
                mock_download_response.headers = {
                    "Content-Type": "application/zip",
                    "Content-Disposition": "attachment; filename=test-file.zip",
                }

                async def chunk_iter():
                    yield b"zip data"
                    yield b""

                mock_download_response.content.iter_chunked = (
                    lambda size: chunk_iter()
                )
                mock_download_response.close = Mock()
                mock_download_response.request_info = Mock()
                mock_download_response.history = ()

                call_count = 0

                def mock_get(*args, **kwargs):
                    nonlocal call_count
                    url = args[0] if args else kwargs.get("url", "")
                    # Handle /ping endpoint for ping_endpoint
                    if "/ping" in url:
                        mock_resp = AsyncMock()
                        mock_resp.status = 200
                        mock_resp.__aenter__ = AsyncMock(
                            return_value=mock_resp
                        )
                        mock_resp.__aexit__ = AsyncMock(return_value=None)
                        return mock_resp
                    call_count += 1
                    if call_count == 1:
                        # For /info endpoint, return async context manager (used with async with)
                        mock_get_response = AsyncMock()
                        mock_get_response.__aenter__ = AsyncMock(
                            return_value=mock_info_response
                        )
                        mock_get_response.__aexit__ = AsyncMock(
                            return_value=None
                        )
                        return mock_get_response
                    else:
                        # For /download endpoint, return awaitable that resolves to response (used with await)
                        async def get_download_response():
                            return mock_download_response

                        return get_download_response()

                mock_session_instance.get = Mock(side_effect=mock_get)

                # Mock retry_request to actually call the function passed to it
                async def mock_retry(func, *args, **kwargs):
                    return await func(*args, **kwargs)

                with patch(
                    "proximl.utils.transfer.retry_request",
                    side_effect=mock_retry,
                ):
                    with patch(
                        "proximl.utils.transfer.ping_endpoint",
                        new_callable=AsyncMock,
                    ):
                        # Patch re.search to return None for the first regex call (with quotes pattern)
                        # This forces the fallback regex to be used (lines 339-343)
                        original_search = re.search
                        search_call_count = 0

                        def mock_re_search(pattern, string, *args, **kwargs):
                            nonlocal search_call_count
                            search_call_count += 1
                            # For the first call (the quotes regex), return None to force fallback
                            if (
                                search_call_count == 1
                                and 'filename="?([^"]+)"?' in pattern
                            ):
                                return None
                            # For subsequent calls, use the real re.search
                            return original_search(
                                pattern, string, *args, **kwargs
                            )

                        mock_file_context = AsyncMock()
                        mock_file_context.__aenter__ = AsyncMock(
                            return_value=AsyncMock()
                        )
                        mock_file_context.__aexit__ = AsyncMock(
                            return_value=None
                        )
                        mock_file_context.__aenter__.return_value.write = (
                            AsyncMock()
                        )

                        mock_finalize_response = AsyncMock()
                        mock_finalize_response.status = 200
                        mock_finalize_response.json = AsyncMock(
                            return_value={"status": "ok"}
                        )
                        mock_finalize_response.__aenter__ = AsyncMock(
                            return_value=mock_finalize_response
                        )
                        mock_finalize_response.__aexit__ = AsyncMock(
                            return_value=None
                        )

                        # session.post() should return something that is both awaitable and an async context manager
                        class AwaitableContextManager:
                            def __init__(self, return_value):
                                self.return_value = return_value

                            def __await__(self):
                                yield
                                return self

                            async def __aenter__(self):
                                return self.return_value

                            async def __aexit__(self, *args):
                                return None

                        mock_post_context = AwaitableContextManager(
                            mock_finalize_response
                        )
                        mock_session_instance.post = Mock(
                            return_value=mock_post_context
                        )

                        with patch(
                            "proximl.utils.transfer.retry_request",
                            side_effect=mock_retry,
                        ):
                            with patch(
                                "proximl.utils.transfer.re.search",
                                side_effect=mock_re_search,
                            ):
                                with patch(
                                    "aiofiles.open",
                                    return_value=mock_file_context,
                                ):
                                    await specimen.download(
                                        "example.com", "token", tmpdir
                                    )
                                    # Verify the file was written (filename parsing worked)
                                    assert (
                                        mock_file_context.__aenter__.return_value.write.called
                                    )
