import gc
import inspect
import json
import os
import sqlite3
import time
from typing import Type
from unittest import mock
from unittest.mock import MagicMock

import aiohttp
import dill as pickle
import pytest
import requests.exceptions
from aioresponses import aioresponses
from requests_mock import Mocker
from yarl import URL

from inference.core import roboflow_api
from inference.core.cache import MemoryCache
from inference.core.env import API_BASE_URL
from inference.core.exceptions import (MalformedRoboflowAPIResponseError,
                                       MalformedWorkflowResponseError,
                                       MissingDefaultModelError,
                                       PaymentRequiredError, RetryRequestError,
                                       RoboflowAPIConnectionError,
                                       RoboflowAPIIAlreadyAnnotatedError,
                                       RoboflowAPIIAnnotationRejectionError,
                                       RoboflowAPIImageUploadRejectionError,
                                       RoboflowAPINotAuthorizedError,
                                       RoboflowAPINotNotFoundError,
                                       RoboflowAPITimeoutError,
                                       RoboflowAPIUnsuccessfulRequestError,
                                       RoboflowAPIUsagePausedError,
                                       WorkspaceLoadError)
from inference.core.roboflow_api import (
    ModelEndpointType, annotate_image_at_roboflow, build_roboflow_api_headers,
    delete_cached_workflow_response_if_exists, get_from_url,
    get_model_metadata_from_inference_models_registry,
    get_roboflow_active_learning_configuration, get_roboflow_dataset_type,
    get_roboflow_labeling_batches, get_roboflow_labeling_jobs,
    get_roboflow_model_data, get_roboflow_model_type, get_roboflow_workspace,
    get_roboflow_workspace_async, get_workflow_specification,
    raise_from_lambda, register_image_at_roboflow, wrap_roboflow_api_errors,
    wrap_roboflow_api_errors_async)
from inference.core.utils.url_utils import wrap_url
from inference.core.version import __version__


def codeflash_wrap(codeflash_wrapped, codeflash_test_module_name, codeflash_test_class_name, codeflash_test_name, codeflash_function_name, codeflash_line_id, codeflash_loop_index, codeflash_cur, codeflash_con, *args, **kwargs):
    test_id = f'{codeflash_test_module_name}:{codeflash_test_class_name}:{codeflash_test_name}:{codeflash_line_id}:{codeflash_loop_index}'
    if not hasattr(codeflash_wrap, 'index'):
        codeflash_wrap.index = {}
    if test_id in codeflash_wrap.index:
        codeflash_wrap.index[test_id] += 1
    else:
        codeflash_wrap.index[test_id] = 0
    codeflash_test_index = codeflash_wrap.index[test_id]
    invocation_id = f'{codeflash_line_id}_{codeflash_test_index}'
    test_stdout_tag = f"{codeflash_test_module_name}:{(codeflash_test_class_name + '.' if codeflash_test_class_name else '')}{codeflash_test_name}:{codeflash_function_name}:{codeflash_loop_index}:{invocation_id}"
    print(f'!$######{test_stdout_tag}######$!')
    exception = None
    gc.disable()
    try:
        counter = time.perf_counter_ns()
        return_value = codeflash_wrapped(*args, **kwargs)
        codeflash_duration = time.perf_counter_ns() - counter
    except Exception as e:
        codeflash_duration = time.perf_counter_ns() - counter
        exception = e
    gc.enable()
    print(f'!######{test_stdout_tag}######!')
    pickled_return_value = pickle.dumps(exception) if exception else pickle.dumps(return_value)
    codeflash_cur.execute('INSERT INTO test_results VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', (codeflash_test_module_name, codeflash_test_class_name, codeflash_test_name, codeflash_function_name, codeflash_loop_index, invocation_id, codeflash_duration, pickled_return_value, 'function_call'))
    codeflash_con.commit()
    if exception:
        raise exception
    return return_value

class TestException(Exception):
    pass

def test_wrap_roboflow_api_errors_when_no_error_occurs() -> None:

    @wrap_roboflow_api_errors()
    def my_fun(a: int, b: int) -> int:
        return a + b
    result = my_fun(2, 3)
    assert result == 5

def test_wrap_roboflow_api_errors_when_given_up_on_timeout_error() -> None:

    @wrap_roboflow_api_errors()
    def my_fun(a: int, b: int) -> int:
        raise RetryRequestError('some', inner_error=requests.Timeout())
    with pytest.raises(RoboflowAPITimeoutError):
        _ = my_fun(2, 3)

def test_wrap_roboflow_api_errors_when_given_up_on_http_error() -> None:

    @wrap_roboflow_api_errors()
    def my_fun(a: int, b: int) -> int:
        response = requests.Response()
        response.status_code = 404
        raise RetryRequestError('some', inner_error=requests.exceptions.HTTPError(response=response))
    with pytest.raises(RoboflowAPINotNotFoundError):
        _ = my_fun(2, 3)

@pytest.mark.parametrize('exception_class', [ConnectionError, requests.exceptions.ConnectionError])
def test_wrap_roboflow_api_errors_when_connection_error_occurs(exception_class: Type[Exception]) -> None:

    @wrap_roboflow_api_errors()
    def my_fun(a: int, b: int) -> int:
        raise exception_class('some')
    with pytest.raises(RoboflowAPIConnectionError):
        _ = my_fun(2, 3)

def test_wrap_roboflow_api_errors_when_timeout_error_is_raised() -> None:

    @wrap_roboflow_api_errors()
    def my_fun(a: int, b: int) -> int:
        raise requests.Timeout('some')
    with pytest.raises(RoboflowAPITimeoutError):
        _ = my_fun(2, 3)

def test_wrap_roboflow_api_errors_when_http_401_error_occurs_and_default_handlers_used() -> None:

    @wrap_roboflow_api_errors()
    def my_fun(a: int, b: int) -> int:
        response = requests.Response()
        response.status_code = 401
        raise requests.exceptions.HTTPError('some', response=response)
    with pytest.raises(RoboflowAPINotAuthorizedError):
        _ = my_fun(2, 3)

def test_wrap_roboflow_api_errors_when_http_402_error_occurs_and_default_handlers_used() -> None:

    @wrap_roboflow_api_errors()
    def my_fun(a: int, b: int) -> int:
        response = requests.Response()
        response.status_code = 402
        raise requests.exceptions.HTTPError('some', response=response)
    with pytest.raises(PaymentRequiredError):
        _ = my_fun(2, 3)

def test_wrap_roboflow_api_errors_when_http_404_error_occurs_and_default_handlers_used() -> None:

    @wrap_roboflow_api_errors()
    def my_fun(a: int, b: int) -> int:
        response = requests.Response()
        response.status_code = 404
        raise requests.exceptions.HTTPError('some', response=response)
    with pytest.raises(RoboflowAPINotNotFoundError):
        _ = my_fun(2, 3)

def test_wrap_roboflow_api_errors_when_http_423_error_occurs_and_default_handlers_used() -> None:

    @wrap_roboflow_api_errors()
    def my_fun(a: int, b: int) -> int:
        response = requests.Response()
        response.status_code = 423
        raise requests.exceptions.HTTPError('some', response=response)
    with pytest.raises(RoboflowAPIUsagePausedError):
        _ = my_fun(2, 3)

@pytest.mark.asyncio
async def test_wrap_roboflow_api_errors_async_when_http_423_error_occurs_and_default_handlers_used() -> None:

    @wrap_roboflow_api_errors_async()
    async def my_fun(a: int, b: int) -> int:
        raise aiohttp.ClientResponseError(request_info=aiohttp.RequestInfo(url=URL('http://some'), method='GET', headers={}, real_url=URL('http://some')), history=(), status=423, message='some')
    with pytest.raises(RoboflowAPIUsagePausedError):
        _ = await my_fun(2, 3)

def test_wrap_roboflow_api_errors_when_http_error_occurs_and_custom_handlers_used() -> None:

    @wrap_roboflow_api_errors(http_errors_handlers={500: lambda e: raise_from_lambda(e, TestException, 'Some')})
    def my_fun(a: int, b: int) -> int:
        response = requests.Response()
        response.status_code = 500
        raise requests.exceptions.HTTPError('some', response=response)
    with pytest.raises(TestException):
        _ = my_fun(2, 3)

def test_wrap_roboflow_api_errors_when_http_error_occurs_and_custom_handlers_override_defaults() -> None:

    @wrap_roboflow_api_errors(http_errors_handlers={401: lambda e: raise_from_lambda(e, TestException, 'Some')})
    def my_fun(a: int, b: int) -> int:
        response = requests.Response()
        response.status_code = 401
        raise requests.exceptions.HTTPError('some', response=response)
    with pytest.raises(TestException):
        _ = my_fun(2, 3)

def test_wrap_roboflow_api_errors_when_http_error_occurs_and_default_handlers_used() -> None:

    @wrap_roboflow_api_errors()
    def my_fun(a: int, b: int) -> int:
        response = requests.Response()
        response.status_code = 401
        raise requests.exceptions.HTTPError('some', response=response)
    with pytest.raises(RoboflowAPIUnsuccessfulRequestError):
        _ = my_fun(2, 3)

def test_wrap_roboflow_api_errors_when_connection_json_parsing_error_occurs() -> None:

    @wrap_roboflow_api_errors()
    def my_fun(a: int, b: int) -> int:
        raise requests.exceptions.InvalidJSONError('some')
    with pytest.raises(MalformedRoboflowAPIResponseError):
        _ = my_fun(2, 3)

def test_get_roboflow_workspace_when_wrong_api_key_used(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/'), status_code=401)
    with pytest.raises(RoboflowAPINotAuthorizedError):
        _ = get_roboflow_workspace(api_key='my_api_key')
    assert requests_mock.last_request.query == 'api_key=my_api_key&nocache=true'

@pytest.mark.asyncio
async def test_get_roboflow_workspace_async_when_wrong_api_key_used() -> None:
    with aioresponses() as request_mock:
        request_mock.get(f'{API_BASE_URL}/?api_key=my_api_key&nocache=true', payload={'error': {'message': 'This API key does not exist (or has been revoked).', 'status': 401, 'type': 'OAuthException', 'hint': 'You may retrieve your API key via the Roboflow Dashboard.', 'key': 'my_api_key'}}, status=401)
        with pytest.raises(RoboflowAPINotAuthorizedError):
            _ = await get_roboflow_workspace_async(api_key='my_api_key')

@mock.patch.object(roboflow_api.requests, 'get')
def test_get_roboflow_workspace_when_connection_error_occurs(get_mock: MagicMock) -> None:
    get_mock.side_effect = ConnectionError()
    with pytest.raises(RoboflowAPIConnectionError):
        _ = get_roboflow_workspace(api_key='my_api_key')

@pytest.mark.asyncio
async def test_get_roboflow_workspace_async_when_connection_error_occurs() -> None:
    with aioresponses():
        with pytest.raises(RoboflowAPIConnectionError):
            _ = await get_roboflow_workspace_async(api_key='my_api_key')

@mock.patch.object(roboflow_api, 'RETRY_CONNECTION_ERRORS_TO_ROBOFLOW_API', True)
@pytest.mark.asyncio
async def test_get_roboflow_workspace_async_when_connection_error_occurs_and_retries_enforced() -> None:
    with aioresponses():
        with pytest.raises(RoboflowAPIConnectionError):
            _ = await get_roboflow_workspace_async(api_key='my_api_key')

@mock.patch.object(roboflow_api, 'TRANSIENT_ROBOFLOW_API_ERRORS', {503})
@pytest.mark.asyncio
async def test_get_roboflow_workspace_async_when_transient_errors_occur_and_finally_request_succeed() -> None:
    with aioresponses() as request_mock:
        request_mock.get(f'{API_BASE_URL}/?api_key=my_api_key&nocache=true', status=503)
        request_mock.get(f'{API_BASE_URL}/?api_key=my_api_key&nocache=true', status=503)
        request_mock.get(f'{API_BASE_URL}/?api_key=my_api_key&nocache=true', payload={'workspace': 'my-workspace'})
        result = await get_roboflow_workspace_async(api_key='my_api_key')
        assert result == 'my-workspace'

def test_get_roboflow_workspace_when_response_parsing_error_occurs(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/'), content=b'For sure not a JSON payload')
    with pytest.raises(MalformedRoboflowAPIResponseError):
        _ = get_roboflow_workspace(api_key='my_api_key')
    assert requests_mock.last_request.query == 'api_key=my_api_key&nocache=true'

@pytest.mark.asyncio
async def test_get_roboflow_workspace_async_when_response_parsing_error_occurs() -> None:
    with aioresponses() as request_mock:
        request_mock.get(f'{API_BASE_URL}/?api_key=my_api_key&nocache=true', body=b'For sure not a JSON payload')
        with pytest.raises(MalformedRoboflowAPIResponseError):
            _ = await get_roboflow_workspace_async(api_key='my_api_key')

def test_get_roboflow_workspace_when_workspace_id_is_empty(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/'), json={'some': 'payload'})
    with pytest.raises(WorkspaceLoadError):
        _ = get_roboflow_workspace(api_key='my_api_key')
    assert requests_mock.last_request.query == 'api_key=my_api_key&nocache=true'

@pytest.mark.asyncio
async def test_get_roboflow_workspace_async_when_workspace_id_is_empty() -> None:
    with aioresponses() as request_mock:
        request_mock.get(f'{API_BASE_URL}/?api_key=my_api_key&nocache=true', payload={'some': 'payload'})
        with pytest.raises(WorkspaceLoadError):
            _ = await get_roboflow_workspace_async(api_key='my_api_key')

@mock.patch.object(roboflow_api, 'ROBOFLOW_API_EXTRA_HEADERS', json.dumps({'extra': 'header'}))
def test_get_roboflow_workspace_when_response_is_valid(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/'), json={'workspace': 'my_workspace'})
    result = get_roboflow_workspace(api_key='my_api_key')
    assert requests_mock.last_request.query == 'api_key=my_api_key&nocache=true'
    assert result == 'my_workspace'

@mock.patch.object(roboflow_api, 'ROBOFLOW_API_EXTRA_HEADERS', json.dumps({'extra': 'header'}))
@pytest.mark.asyncio
async def test_get_roboflow_workspace_async_when_response_is_valid() -> None:
    with aioresponses() as request_mock:
        request_mock.get(f'{API_BASE_URL}/?api_key=my_api_key&nocache=true', payload={'workspace': 'my_workspace'})
        result = await get_roboflow_workspace_async(api_key='my_api_key')
        assert result == 'my_workspace'
        registered_requests = request_mock.requests['GET', URL(f'{API_BASE_URL}/?api_key=my_api_key&nocache=true')]
        assert registered_requests[0].kwargs['headers'] == {'extra': 'header', roboflow_api.ROBOFLOW_INFERENCE_VERSION_HEADER: __version__, roboflow_api.ALLOW_CHUNKED_RESPONSE_HEADER: 'true'}

def test_get_roboflow_dataset_type_when_wrong_key_used(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/coins_detection'), status_code=401)
    with pytest.raises(RoboflowAPINotAuthorizedError):
        _ = get_roboflow_dataset_type(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key&nocache=true'

def test_get_roboflow_dataset_type_when_project_not_found_used(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/coins_detection'), status_code=404)
    with pytest.raises(RoboflowAPINotNotFoundError):
        _ = get_roboflow_dataset_type(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key&nocache=true'

@mock.patch.object(roboflow_api.requests, 'get')
def test_get_roboflow_dataset_type_when_connection_error_occurs(get_mock: MagicMock) -> None:
    get_mock.side_effect = ConnectionError()
    with pytest.raises(RoboflowAPIConnectionError):
        _ = get_roboflow_dataset_type(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection')

def test_get_roboflow_dataset_type_when_response_parsing_error_occurs(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/coins_detection'), content=b'For sure not a JSON payload')
    with pytest.raises(MalformedRoboflowAPIResponseError):
        _ = get_roboflow_dataset_type(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key&nocache=true'

def test_get_roboflow_dataset_type_when_project_is_empty(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/coins_detection'), json={'project': {}})
    result = get_roboflow_dataset_type(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key&nocache=true'
    assert result == 'object-detection'

def test_get_roboflow_dataset_type_when_response_is_valid(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/coins_detection'), json={'project': {'type': 'classification'}})
    result = get_roboflow_dataset_type(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key&nocache=true'
    assert result == 'classification'

def test_get_roboflow_model_type_when_wrong_api_key_used(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/coins_detection/1'), status_code=401)
    with pytest.raises(RoboflowAPINotAuthorizedError):
        _ = get_roboflow_model_type(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection', version_id='1', project_task_type='object-detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key&nocache=true'

def test_get_roboflow_model_type_when_wrong_project_used(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/coins_detection/1'), status_code=404)
    with pytest.raises(RoboflowAPINotNotFoundError):
        _ = get_roboflow_model_type(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection', version_id='1', project_task_type='object-detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key&nocache=true'

def test_get_roboflow_model_type_when_wrong_version_used(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/coins_detection/invalid'), status_code=500)
    with pytest.raises(RoboflowAPINotNotFoundError):
        _ = get_roboflow_model_type(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection', version_id='invalid', project_task_type='object-detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key&nocache=true'

@mock.patch.object(roboflow_api.requests, 'get')
def test_get_roboflow_model_type_when_connection_error_occurs(get_mock: MagicMock) -> None:
    get_mock.side_effect = ConnectionError()
    with pytest.raises(RoboflowAPIConnectionError):
        _ = get_roboflow_model_type(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection', version_id='1', project_task_type='object-detection')

def test_get_roboflow_model_type_when_response_parsing_error_occurs(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/coins_detection/1'), content=b'For sure not a JSON payload')
    with pytest.raises(MalformedRoboflowAPIResponseError):
        _ = get_roboflow_model_type(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection', version_id='1', project_task_type='object-detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key&nocache=true'

def test_get_roboflow_model_type_when_default_model_can_be_chosen(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/coins_detection/1'), json={'version': {}})
    result = get_roboflow_model_type(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection', version_id='1', project_task_type='object-detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key&nocache=true'
    assert result == 'yolov5v2s'

def test_get_roboflow_model_type_when_default_model_cannot_be_chosen(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/coins_detection/1'), json={'version': {}})
    with pytest.raises(MissingDefaultModelError):
        _ = get_roboflow_model_type(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection', version_id='1', project_task_type='unknown')
    assert requests_mock.last_request.query == 'api_key=my_api_key&nocache=true'

def test_get_roboflow_model_type_when_response_is_valid(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/coins_detection/1'), json={'version': {'modelType': 'yolov8n'}})
    result = get_roboflow_model_type(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection', version_id='1', project_task_type='object-detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key&nocache=true'
    assert result == 'yolov8n'

@mock.patch.object(roboflow_api.requests, 'get')
def test_get_roboflow_model_data_when_connection_error_occurs(get_mock: MagicMock) -> None:
    get_mock.side_effect = ConnectionError()
    with pytest.raises(RoboflowAPIConnectionError):
        _ = get_roboflow_model_data(api_key='my_api_key', model_id='coins_detection/1', endpoint_type=ModelEndpointType.ORT, device_id='some')

@mock.patch.object(roboflow_api.requests, 'get')
def test_get_model_metadata_from_inference_models_registry_when_connection_error_occurs(get_mock: MagicMock) -> None:
    get_mock.side_effect = ConnectionError()
    with pytest.raises(RoboflowAPIConnectionError):
        _ = get_model_metadata_from_inference_models_registry(api_key='my_api_key', model_id='coins_detection/1')

def test_get_roboflow_model_data_when_wrong_api_key_used(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/ort/coins_detection/1'), status_code=401)
    with pytest.raises(RoboflowAPINotAuthorizedError):
        _ = get_roboflow_model_data(api_key='my_api_key', model_id='coins_detection/1', endpoint_type=ModelEndpointType.ORT, device_id='some')
    params = ['api_key=my_api_key', 'nocache=true', 'device=some', 'dynamic=true']
    for param in params:
        assert param in requests_mock.last_request.query

def test_get_model_metadata_from_inference_models_registry_when_wrong_api_key_used(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/models/v1/external/weights'), status_code=401)
    with pytest.raises(RoboflowAPINotAuthorizedError):
        _ = get_model_metadata_from_inference_models_registry(api_key='my_api_key', model_id='coins_detection/1')
    assert 'modelid=coins_detection%2f1' in requests_mock.last_request.query
    assert requests_mock.last_request.headers['Authorization'] == 'Bearer my_api_key'

def test_get_roboflow_model_data_when_wrong_model_used(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/ort/coins_detection/1'), status_code=404)
    with pytest.raises(RoboflowAPINotNotFoundError):
        _ = get_roboflow_model_data(api_key='my_api_key', model_id='coins_detection/1', endpoint_type=ModelEndpointType.ORT, device_id='some')
    params = ['api_key=my_api_key', 'nocache=true', 'device=some', 'dynamic=true']
    for param in params:
        assert param in requests_mock.last_request.query

def test_get_model_metadata_from_inference_models_registry_when_wrong_model_used(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/models/v1/external/weights'), status_code=404)
    with pytest.raises(RoboflowAPINotNotFoundError):
        _ = get_model_metadata_from_inference_models_registry(api_key='my_api_key', model_id='coins_detection/1')
    assert 'modelid=coins_detection%2f1' in requests_mock.last_request.query
    assert requests_mock.last_request.headers['Authorization'] == 'Bearer my_api_key'

def test_get_roboflow_model_data_when_http_error_occurs(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/ort/coins_detection/1'), status_code=500)
    with pytest.raises(RoboflowAPIUnsuccessfulRequestError):
        _ = get_roboflow_model_data(api_key='my_api_key', model_id='coins_detection/1', endpoint_type=ModelEndpointType.ORT, device_id='some')
    params = ['api_key=my_api_key', 'nocache=true', 'device=some', 'dynamic=true']
    for param in params:
        assert param in requests_mock.last_request.query

def test_get_model_metadata_from_inference_models_registry_when_http_error_occurs(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/models/v1/external/weights'), status_code=500)
    with pytest.raises(RoboflowAPIUnsuccessfulRequestError):
        _ = get_model_metadata_from_inference_models_registry(api_key='my_api_key', model_id='coins_detection/1')
    assert 'modelid=coins_detection%2f1' in requests_mock.last_request.query
    assert requests_mock.last_request.headers['Authorization'] == 'Bearer my_api_key'

def test_get_roboflow_model_data_when_response_parsing_error_occurs(requests_mock: Mocker) -> None:
    expected_response = b'For sure not a JSON payload'
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/ort/coins_detection/1'), content=expected_response)
    with pytest.raises(MalformedRoboflowAPIResponseError):
        _ = get_roboflow_model_data(api_key='my_api_key', model_id='coins_detection/1', endpoint_type=ModelEndpointType.ORT, device_id='some')
    params = ['api_key=my_api_key', 'nocache=true', 'device=some', 'dynamic=true']
    for param in params:
        assert param in requests_mock.last_request.query

def test_get_model_metadata_from_inference_models_registry_when_response_parsing_error_occurs(requests_mock: Mocker) -> None:
    expected_response = b'For sure not a JSON payload'
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/models/v1/external/weights'), content=expected_response)
    with pytest.raises(MalformedRoboflowAPIResponseError):
        _ = get_model_metadata_from_inference_models_registry(api_key='my_api_key', model_id='coins_detection/1')
    assert 'modelid=coins_detection%2f1' in requests_mock.last_request.query
    assert requests_mock.last_request.headers['Authorization'] == 'Bearer my_api_key'

def test_get_roboflow_model_data_when_valid_response_expected(requests_mock: Mocker) -> None:
    expected_response = {'ort': {'name': 'barbel-detection', 'type': 'object-detection', 'icon': 'XXX', 'iconHasAnnotation': False, 'annotation': 'person-barbell-edge', 'environment': 'some-link', 'classes': ['barbell-edge', 'person', 'plate'], 'colors': {'coin': '#C7FC00'}, 'model': 'some-link', 'modelType': 'yolov8s'}}
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/ort/coins_detection/1'), json=expected_response)
    result = get_roboflow_model_data(api_key='my_api_key', model_id='coins_detection/1', endpoint_type=ModelEndpointType.ORT, device_id='some')
    params = ['api_key=my_api_key', 'nocache=true', 'device=some', 'dynamic=true']
    for param in params:
        assert param in requests_mock.last_request.query
    assert result == expected_response

def test_get_roboflow_model_data_excludes_api_key_when_local(requests_mock: Mocker) -> None:
    expected_response = {'ort': {'name': 'barbel-detection', 'type': 'object-detection'}}
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/ort/local_test_model/1'), json=expected_response)
    result = get_roboflow_model_data(api_key='local', model_id='local_test_model/1', endpoint_type=ModelEndpointType.ORT, device_id='some')
    assert 'api_key' not in requests_mock.last_request.query
    assert 'nocache=true' in requests_mock.last_request.query
    assert result == expected_response

def test_get_model_metadata_from_inference_models_registry_when_valid_response_expected(requests_mock: Mocker) -> None:
    expected_response = {'modelMetadata': {'modelArchitecture': 'yolov8', 'taskType': 'object-detection'}}
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/models/v1/external/weights'), json=expected_response)
    result = get_model_metadata_from_inference_models_registry(api_key='my_api_key', model_id='coins_detection/1')
    assert 'modelid=coins_detection%2f1' in requests_mock.last_request.query
    assert requests_mock.last_request.headers['Authorization'] == 'Bearer my_api_key'
    assert result == {'modelType': 'yolov8', 'taskType': 'object-detection'}

@mock.patch.object(roboflow_api, 'GCP_SERVERLESS', True)
@mock.patch.object(roboflow_api, 'ENFORCE_CREDITS_VERIFICATION', True)
@mock.patch.object(roboflow_api, 'MODELS_CACHE_AUTH_ENABLED', True)
def test_get_model_metadata_from_inference_models_registry_when_valid_response_expected_enforce_credits(requests_mock: Mocker) -> None:
    expected_response = {'modelMetadata': {'modelArchitecture': 'yolov8', 'taskType': 'object-detection'}}
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/models/v1/external/weights'), json=expected_response)
    result = get_model_metadata_from_inference_models_registry(api_key='my_api_key', model_id='coins_detection/1')
    assert 'modelid=coins_detection%2f1' in requests_mock.last_request.query
    assert requests_mock.last_request.headers['Authorization'] == 'Bearer my_api_key'
    assert requests_mock.last_request.headers['x-enforce-credits-verification'] == 'true'
    assert requests_mock.last_request.headers['x-enforce-internal-artefacts-urls'] == 'true'
    assert result == {'modelType': 'yolov8', 'taskType': 'object-detection'}

@mock.patch.object(roboflow_api, 'GCP_SERVERLESS', True)
@mock.patch.object(roboflow_api, 'ENFORCE_CREDITS_VERIFICATION', True)
@mock.patch.object(roboflow_api, 'ROBOFLOW_SERVICE_SECRET', 'dummy-secret')
@mock.patch.object(roboflow_api, 'MODELS_CACHE_AUTH_ENABLED', True)
def test_get_model_metadata_from_inference_models_registry_when_valid_response_expected_enforce_credits_overruled(requests_mock: Mocker) -> None:
    expected_response = {'modelMetadata': {'modelArchitecture': 'yolov8', 'taskType': 'object-detection'}}
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/models/v1/external/weights'), json=expected_response)
    result = get_model_metadata_from_inference_models_registry(api_key='my_api_key', model_id='coins_detection/1', countinference=False, service_secret='dummy-secret')
    assert result == {'modelType': 'yolov8', 'taskType': 'object-detection'}
    assert 'x-enforce-credits-verification' not in requests_mock.last_request.headers

@mock.patch.object(roboflow_api.requests, 'post')
def test_register_image_at_roboflow_when_connection_error_occurs(post_mock: MagicMock) -> None:
    post_mock.side_effect = ConnectionError()
    with pytest.raises(RoboflowAPIConnectionError):
        _ = register_image_at_roboflow(api_key='my_api_key', dataset_id='coins_detection', local_image_id='local_id', image_bytes=b'SOME_IMAGE_BYTES', batch_name='my-batch', tags=['a', 'b', 'c/d'])

def test_register_image_at_roboflow_when_wrong_api_key_used(requests_mock: Mocker) -> None:
    requests_mock.post(url=wrap_url(f'{API_BASE_URL}/dataset/coins_detection/upload'), status_code=401)
    with pytest.raises(RoboflowAPINotAuthorizedError):
        _ = register_image_at_roboflow(api_key='my_api_key', dataset_id='coins_detection', local_image_id='local_id', image_bytes=b'SOME_IMAGE_BYTES', batch_name='my-batch', tags=['a', 'b', 'c/d'])
    assert requests_mock.last_request.query == 'api_key=my_api_key&batch=my-batch&tag=a&tag=b&tag=c%2fd'
    assert requests_mock.last_request.text.fields['name'] == 'local_id.jpg'
    assert requests_mock.last_request.text.fields['file'] == ('imageToUpload', b'SOME_IMAGE_BYTES', 'image/jpeg')

def test_register_image_at_roboflow_when_wrong_dataset_used(requests_mock: Mocker) -> None:
    requests_mock.post(url=wrap_url(f'{API_BASE_URL}/dataset/invalid/upload'), status_code=404)
    with pytest.raises(RoboflowAPINotNotFoundError):
        _ = register_image_at_roboflow(api_key='my_api_key', dataset_id='invalid', local_image_id='local_id', image_bytes=b'SOME_IMAGE_BYTES', batch_name='my-batch', tags=['a', 'b', 'c/d'])
    assert requests_mock.last_request.query == 'api_key=my_api_key&batch=my-batch&tag=a&tag=b&tag=c%2fd'
    assert requests_mock.last_request.text.fields['name'] == 'local_id.jpg'
    assert requests_mock.last_request.text.fields['file'] == ('imageToUpload', b'SOME_IMAGE_BYTES', 'image/jpeg')

def test_register_image_at_roboflow_when_http_error_occurs(requests_mock: Mocker) -> None:
    requests_mock.post(url=wrap_url(f'{API_BASE_URL}/dataset/coins_detection/upload'), status_code=500)
    with pytest.raises(RoboflowAPIUnsuccessfulRequestError):
        _ = register_image_at_roboflow(api_key='my_api_key', dataset_id='coins_detection', local_image_id='local_id', image_bytes=b'SOME_IMAGE_BYTES', batch_name='my-batch', tags=['a', 'b', 'c/d'])
    assert requests_mock.last_request.query == 'api_key=my_api_key&batch=my-batch&tag=a&tag=b&tag=c%2fd'
    assert requests_mock.last_request.text.fields['name'] == 'local_id.jpg'
    assert requests_mock.last_request.text.fields['file'] == ('imageToUpload', b'SOME_IMAGE_BYTES', 'image/jpeg')

def test_register_image_at_roboflow_when_response_parsing_error_occurs(requests_mock: Mocker) -> None:
    requests_mock.post(url=wrap_url(f'{API_BASE_URL}/dataset/coins_detection/upload'), content=b'For sure not a JSON payload')
    with pytest.raises(MalformedRoboflowAPIResponseError):
        _ = register_image_at_roboflow(api_key='my_api_key', dataset_id='coins_detection', local_image_id='local_id', image_bytes=b'SOME_IMAGE_BYTES', batch_name='my-batch', tags=['a', 'b', 'c/d'])
    assert requests_mock.last_request.query == 'api_key=my_api_key&batch=my-batch&tag=a&tag=b&tag=c%2fd'
    assert requests_mock.last_request.text.fields['name'] == 'local_id.jpg'
    assert requests_mock.last_request.text.fields['file'] == ('imageToUpload', b'SOME_IMAGE_BYTES', 'image/jpeg')

def test_register_image_at_roboflow_when_valid_response_returned(requests_mock: Mocker) -> None:
    requests_mock.post(url=wrap_url(f'{API_BASE_URL}/dataset/coins_detection/upload'), json={'success': True, 'id': 'xxx'})
    response = register_image_at_roboflow(api_key='my_api_key', dataset_id='coins_detection', local_image_id='local_id', image_bytes=b'SOME_IMAGE_BYTES', batch_name='my-batch', tags=['a', 'b', 'c/d'])
    assert requests_mock.last_request.query == 'api_key=my_api_key&batch=my-batch&tag=a&tag=b&tag=c%2fd'
    assert requests_mock.last_request.text.fields['name'] == 'local_id.jpg'
    assert requests_mock.last_request.text.fields['file'] == ('imageToUpload', b'SOME_IMAGE_BYTES', 'image/jpeg')
    assert response == {'success': True, 'id': 'xxx'}

def test_register_image_at_roboflow_when_valid_response_returned_and_no_tags_used(requests_mock: Mocker) -> None:
    requests_mock.post(url=wrap_url(f'{API_BASE_URL}/dataset/coins_detection/upload'), json={'success': True, 'id': 'xxx'})
    response = register_image_at_roboflow(api_key='my_api_key', dataset_id='coins_detection', local_image_id='local_id', image_bytes=b'SOME_IMAGE_BYTES', batch_name='my-batch')
    assert requests_mock.last_request.query == 'api_key=my_api_key&batch=my-batch'
    assert requests_mock.last_request.text.fields['name'] == 'local_id.jpg'
    assert requests_mock.last_request.text.fields['file'] == ('imageToUpload', b'SOME_IMAGE_BYTES', 'image/jpeg')
    assert response == {'success': True, 'id': 'xxx'}

def test_register_image_at_roboflow_when_duplicate_response_returned(requests_mock: Mocker) -> None:
    requests_mock.post(url=wrap_url(f'{API_BASE_URL}/dataset/coins_detection/upload'), json={'duplicate': True, 'id': 'xxx'})
    response = register_image_at_roboflow(api_key='my_api_key', dataset_id='coins_detection', local_image_id='local_id', image_bytes=b'SOME_IMAGE_BYTES', batch_name='my-batch')
    assert requests_mock.last_request.query == 'api_key=my_api_key&batch=my-batch'
    assert requests_mock.last_request.text.fields['name'] == 'local_id.jpg'
    assert requests_mock.last_request.text.fields['file'] == ('imageToUpload', b'SOME_IMAGE_BYTES', 'image/jpeg')
    assert response == {'duplicate': True, 'id': 'xxx'}

def test_register_image_at_roboflow_when_error_response_returned(requests_mock: Mocker) -> None:
    requests_mock.post(url=wrap_url(f'{API_BASE_URL}/dataset/coins_detection/upload'), json={'error': 'some'})
    with pytest.raises(RoboflowAPIImageUploadRejectionError) as e:
        _ = register_image_at_roboflow(api_key='my_api_key', dataset_id='coins_detection', local_image_id='local_id', image_bytes=b'SOME_IMAGE_BYTES', batch_name='my-batch')
    assert requests_mock.last_request.query == 'api_key=my_api_key&batch=my-batch'
    assert requests_mock.last_request.text.fields['name'] == 'local_id.jpg'
    assert requests_mock.last_request.text.fields['file'] == ('imageToUpload', b'SOME_IMAGE_BYTES', 'image/jpeg')
    assert str({'error': 'some'}) in str(e.value)

def test_register_image_at_roboflow_when_lack_of_success_reported(requests_mock: Mocker) -> None:
    requests_mock.post(url=wrap_url(f'{API_BASE_URL}/dataset/coins_detection/upload'), json={'success': False})
    with pytest.raises(RoboflowAPIImageUploadRejectionError) as e:
        _ = register_image_at_roboflow(api_key='my_api_key', dataset_id='coins_detection', local_image_id='local_id', image_bytes=b'SOME_IMAGE_BYTES', batch_name='my-batch')
    assert requests_mock.last_request.query == 'api_key=my_api_key&batch=my-batch'
    assert requests_mock.last_request.text.fields['name'] == 'local_id.jpg'
    assert requests_mock.last_request.text.fields['file'] == ('imageToUpload', b'SOME_IMAGE_BYTES', 'image/jpeg')
    assert str({'success': False}) in str(e.value)

@mock.patch.object(roboflow_api.requests, 'post')
def test_annotate_image_at_roboflow_when_connection_error_occurs(post_mock: MagicMock) -> None:
    post_mock.side_effect = ConnectionError()
    with pytest.raises(RoboflowAPIConnectionError):
        _ = annotate_image_at_roboflow(api_key='my_api_key', dataset_id='coins_detection', local_image_id='local_id', roboflow_image_id='roboflow_id', annotation_content='some', annotation_file_type='txt', is_prediction=True)

def test_annotate_image_at_roboflow_when_wrong_api_key_used(requests_mock: Mocker) -> None:
    requests_mock.post(url=wrap_url(f'{API_BASE_URL}/dataset/coins_detection/annotate/roboflow_id'), status_code=401)
    with pytest.raises(RoboflowAPINotAuthorizedError):
        _ = annotate_image_at_roboflow(api_key='my_api_key', dataset_id='coins_detection', local_image_id='local_id', roboflow_image_id='roboflow_id', annotation_content='some', annotation_file_type='txt', is_prediction=True)
    assert requests_mock.last_request.query == 'api_key=my_api_key&name=local_id.txt&prediction=true'

def test_annotate_image_at_roboflow_when_wrong_dataset_used(requests_mock: Mocker) -> None:
    requests_mock.post(url=wrap_url(f'{API_BASE_URL}/dataset/invalid/annotate/roboflow_id'), status_code=404)
    with pytest.raises(RoboflowAPINotNotFoundError):
        _ = annotate_image_at_roboflow(api_key='my_api_key', dataset_id='invalid', local_image_id='local_id', roboflow_image_id='roboflow_id', annotation_content='some', annotation_file_type='txt', is_prediction=True)
    assert requests_mock.last_request.query == 'api_key=my_api_key&name=local_id.txt&prediction=true'

def test_annotate_image_at_roboflow_when_wrong_image_id_selected_used(requests_mock: Mocker) -> None:
    requests_mock.post(url=wrap_url(f'{API_BASE_URL}/dataset/coins_detection/annotate/invalid'), status_code=404)
    with pytest.raises(RoboflowAPINotNotFoundError):
        _ = annotate_image_at_roboflow(api_key='my_api_key', dataset_id='coins_detection', local_image_id='local_id', roboflow_image_id='invalid', annotation_content='some', annotation_file_type='txt', is_prediction=False)
    assert requests_mock.last_request.query == 'api_key=my_api_key&name=local_id.txt&prediction=false'

def test_annotate_image_at_roboflow_when_image_already_annotated(requests_mock: Mocker) -> None:
    requests_mock.post(url=wrap_url(f'{API_BASE_URL}/dataset/coins_detection/annotate/roboflow_id'), status_code=409)
    with pytest.raises(RoboflowAPIIAlreadyAnnotatedError):
        _ = annotate_image_at_roboflow(api_key='my_api_key', dataset_id='coins_detection', local_image_id='local_id', roboflow_image_id='roboflow_id', annotation_content='some', annotation_file_type='txt', is_prediction=True)
    assert requests_mock.last_request.query == 'api_key=my_api_key&name=local_id.txt&prediction=true'

def test_annotate_image_at_roboflow_when_http_error_occurs(requests_mock: Mocker) -> None:
    requests_mock.post(url=wrap_url(f'{API_BASE_URL}/dataset/coins_detection/annotate/roboflow_id'), status_code=500)
    with pytest.raises(RoboflowAPIUnsuccessfulRequestError):
        _ = annotate_image_at_roboflow(api_key='my_api_key', dataset_id='coins_detection', local_image_id='local_id', roboflow_image_id='roboflow_id', annotation_content='some', annotation_file_type='txt', is_prediction=True)
    assert requests_mock.last_request.query == 'api_key=my_api_key&name=local_id.txt&prediction=true'

def test_annotate_image_at_roboflow_when_response_parsing_error_occurs(requests_mock: Mocker) -> None:
    requests_mock.post(url=wrap_url(f'{API_BASE_URL}/dataset/coins_detection/annotate/roboflow_id'), content=b'For sure not a JSON payload')
    with pytest.raises(MalformedRoboflowAPIResponseError):
        _ = annotate_image_at_roboflow(api_key='my_api_key', dataset_id='coins_detection', local_image_id='local_id', roboflow_image_id='roboflow_id', annotation_content='some', annotation_file_type='txt', is_prediction=True)
    assert requests_mock.last_request.query == 'api_key=my_api_key&name=local_id.txt&prediction=true'

def test_annotate_image_at_roboflow_when_error_description_returned(requests_mock: Mocker) -> None:
    requests_mock.post(url=wrap_url(f'{API_BASE_URL}/dataset/coins_detection/annotate/roboflow_id'), json={'error': 'some'})
    with pytest.raises(RoboflowAPIIAnnotationRejectionError) as error:
        _ = annotate_image_at_roboflow(api_key='my_api_key', dataset_id='coins_detection', local_image_id='local_id', roboflow_image_id='roboflow_id', annotation_content='some', annotation_file_type='txt', is_prediction=True)
    assert requests_mock.last_request.query == 'api_key=my_api_key&name=local_id.txt&prediction=true'
    assert str({'error': 'some'}) in str(error.value)

def test_annotate_image_at_roboflow_when_lack_of_success_reported(requests_mock: Mocker) -> None:
    requests_mock.post(url=wrap_url(f'{API_BASE_URL}/dataset/coins_detection/annotate/roboflow_id'), json={'success': False})
    with pytest.raises(RoboflowAPIIAnnotationRejectionError) as error:
        _ = annotate_image_at_roboflow(api_key='my_api_key', dataset_id='coins_detection', local_image_id='local_id', roboflow_image_id='roboflow_id', annotation_content='some', annotation_file_type='txt', is_prediction=True)
    assert requests_mock.last_request.query == 'api_key=my_api_key&name=local_id.txt&prediction=true'
    assert str({'success': False}) in str(error.value)

def test_annotate_image_at_roboflow_when_successful_response_expected(requests_mock: Mocker) -> None:
    requests_mock.post(url=wrap_url(f'{API_BASE_URL}/dataset/coins_detection/annotate/roboflow_id'), json={'success': True})
    result = annotate_image_at_roboflow(api_key='my_api_key', dataset_id='coins_detection', local_image_id='local_id', roboflow_image_id='roboflow_id', annotation_content='some', annotation_file_type='txt', is_prediction=True)
    assert requests_mock.last_request.query == 'api_key=my_api_key&name=local_id.txt&prediction=true'
    assert result == {'success': True}

@mock.patch.object(roboflow_api.requests, 'get')
def test_get_roboflow_labeling_batches_when_connection_error_occurs(get_mock: MagicMock) -> None:
    get_mock.side_effect = ConnectionError()
    with pytest.raises(RoboflowAPIConnectionError):
        _ = get_roboflow_labeling_batches(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection')

def test_get_roboflow_labeling_batches_when_wrong_api_key_used(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/coins_detection/batches'), status_code=401)
    with pytest.raises(RoboflowAPINotAuthorizedError):
        _ = get_roboflow_labeling_batches(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key'

def test_get_roboflow_labeling_batches_when_wrong_dataset_used(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/invalid/batches'), status_code=404)
    with pytest.raises(RoboflowAPINotNotFoundError):
        _ = get_roboflow_labeling_batches(api_key='my_api_key', workspace_id='my_workspace', dataset_id='invalid')
    assert requests_mock.last_request.query == 'api_key=my_api_key'

def test_get_roboflow_labeling_batches_when_wrong_workspace_used(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/invalid/coins_detection/batches'), status_code=404)
    with pytest.raises(RoboflowAPINotNotFoundError):
        _ = get_roboflow_labeling_batches(api_key='my_api_key', workspace_id='invalid', dataset_id='coins_detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key'

def test_get_roboflow_labeling_batches_when_http_error_occurred(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/invalid/coins_detection/batches'), status_code=500)
    with pytest.raises(RoboflowAPIUnsuccessfulRequestError):
        _ = get_roboflow_labeling_batches(api_key='my_api_key', workspace_id='invalid', dataset_id='coins_detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key'

def test_get_roboflow_labeling_batches_when_malformed_response_returned(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/invalid/coins_detection/batches'), content=b'Not a JSON :)')
    with pytest.raises(MalformedRoboflowAPIResponseError):
        _ = get_roboflow_labeling_batches(api_key='my_api_key', workspace_id='invalid', dataset_id='coins_detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key'

def test_get_roboflow_labeling_batches_when_valid_response_returned(requests_mock: Mocker) -> None:
    expected_result = {'batches': [{'name': 'Pip Package Upload', 'numJobs': 0, 'uploaded': {'_seconds': 1698060510, '_nanoseconds': 403000000}, 'images': 1, 'id': 'XXX'}, {'name': 'active-learning-23-10-2023', 'uploaded': {'_seconds': 1698060589, '_nanoseconds': 416000000}, 'numJobs': 1, 'images': 2, 'id': 'XXX'}]}
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/coins_detection/batches'), json=expected_result)
    result = get_roboflow_labeling_batches(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection')
    assert result == expected_result
    assert requests_mock.last_request.query == 'api_key=my_api_key'

@mock.patch.object(roboflow_api.requests, 'get')
def test_get_roboflow_labeling_jobs_when_connection_error_occurs(get_mock: MagicMock) -> None:
    get_mock.side_effect = ConnectionError()
    with pytest.raises(RoboflowAPIConnectionError):
        _ = get_roboflow_labeling_jobs(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection')

def test_get_roboflow_labeling_jobs_when_wrong_api_key_used(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/coins_detection/jobs'), status_code=401)
    with pytest.raises(RoboflowAPINotAuthorizedError):
        _ = get_roboflow_labeling_jobs(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key'

def test_get_roboflow_labeling_jobs_when_wrong_dataset_used(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/invalid/jobs'), status_code=404)
    with pytest.raises(RoboflowAPINotNotFoundError):
        _ = get_roboflow_labeling_jobs(api_key='my_api_key', workspace_id='my_workspace', dataset_id='invalid')
    assert requests_mock.last_request.query == 'api_key=my_api_key'

def test_get_roboflow_labeling_jobs_when_wrong_workspace_used(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/invalid/coins_detection/jobs'), status_code=404)
    with pytest.raises(RoboflowAPINotNotFoundError):
        _ = get_roboflow_labeling_jobs(api_key='my_api_key', workspace_id='invalid', dataset_id='coins_detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key'

def test_get_roboflow_labeling_jobs_when_http_error_occurred(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/invalid/coins_detection/jobs'), status_code=500)
    with pytest.raises(RoboflowAPIUnsuccessfulRequestError):
        _ = get_roboflow_labeling_jobs(api_key='my_api_key', workspace_id='invalid', dataset_id='coins_detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key'

def test_get_roboflow_labeling_jobs_when_malformed_response_returned(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/invalid/coins_detection/jobs'), content=b'Not a JSON :)')
    with pytest.raises(MalformedRoboflowAPIResponseError):
        _ = get_roboflow_labeling_jobs(api_key='my_api_key', workspace_id='invalid', dataset_id='coins_detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key'

def test_get_roboflow_labeling_jobs_when_valid_response_returned(requests_mock: Mocker) -> None:
    expected_result = {'jobs': [{'owner': 'XXX', 'rejected': 0, 'project': 'XXX', 'reviewer': 'XXX', 'sourceBatch': 'XXX/YYY', 'approved': 0, 'instructionsText': '', 'createdBy': 'XXX', 'name': 'Uploaded on 10/11/23 at 7:20 pm', 'labeler': 'XXX', 'created': {'_seconds': 1697044995, '_nanoseconds': 375000000}, 'numImages': 161, 'status': 'complete', 'unannotated': 0, 'annotated': 161, 'id': 'XXX'}]}
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/invalid/coins_detection/jobs'), json=expected_result)
    result = get_roboflow_labeling_jobs(api_key='my_api_key', workspace_id='invalid', dataset_id='coins_detection')
    assert result == expected_result
    assert requests_mock.last_request.query == 'api_key=my_api_key'

@mock.patch.object(roboflow_api.requests, 'get')
def test_get_roboflow_active_learning_configuration_when_connection_error_occurs(get_mock: MagicMock) -> None:
    get_mock.side_effect = ConnectionError()
    with pytest.raises(RoboflowAPIConnectionError):
        _ = get_roboflow_active_learning_configuration(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection')

def test_get_roboflow_active_learning_configuration_when_wrong_api_key_used(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/coins_detection/active_learning'), status_code=401)
    with pytest.raises(RoboflowAPINotAuthorizedError):
        _ = get_roboflow_active_learning_configuration(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key', 'API key must be given in query'

def test_get_roboflow_active_learning_configuration_when_not_found_returned(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/coins_detection/active_learning'), status_code=404)
    with pytest.raises(RoboflowAPINotNotFoundError):
        _ = get_roboflow_active_learning_configuration(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key', 'API key must be given in query'

def test_get_roboflow_active_learning_configuration_when_internal_error_returned(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/coins_detection/active_learning'), status_code=500)
    with pytest.raises(RoboflowAPIUnsuccessfulRequestError):
        _ = get_roboflow_active_learning_configuration(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key', 'API key must be given in query'

def test_get_roboflow_active_learning_configuration_when_malformed_response_returned(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/coins_detection/active_learning'), content=b'Not a JSON :)')
    with pytest.raises(MalformedRoboflowAPIResponseError):
        _ = get_roboflow_active_learning_configuration(api_key='my_api_key', workspace_id='my_workspace', dataset_id='coins_detection')
    assert requests_mock.last_request.query == 'api_key=my_api_key', 'API key must be given in query'

@mock.patch.object(roboflow_api.requests, 'get')
def test_get_workflow_specification_when_connection_error_occurs_and_no_cache_to_be_used(get_mock: MagicMock) -> None:
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    codeflash_iteration = os.environ['CODEFLASH_TEST_ITERATION']
    codeflash_con = sqlite3.connect(f'/tmp/codeflash_bs0b086a/test_return_values_{codeflash_iteration}.sqlite')
    codeflash_cur = codeflash_con.cursor()
    codeflash_cur.execute('CREATE TABLE IF NOT EXISTS test_results (test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, runtime INTEGER, return_value BLOB, verification_type TEXT)')
    delete_cached_workflow_response_if_exists(workspace_id='my_workspace', workflow_id='some_workflow', api_key='my_api_key')
    get_mock.side_effect = ConnectionError()
    with pytest.raises(RoboflowAPIConnectionError):
        _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', use_cache=False)
        _call__bound__arguments.apply_defaults()
        _ = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_when_connection_error_occurs_and_no_cache_to_be_used', 'get_workflow_specification', '2_0', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    codeflash_con.close()

@mock.patch.object(roboflow_api.requests, 'get')
def test_get_workflow_specification_when_connection_error_occurs_but_file_is_cached_in_file(get_mock: MagicMock) -> None:
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    codeflash_iteration = os.environ['CODEFLASH_TEST_ITERATION']
    codeflash_con = sqlite3.connect(f'/tmp/codeflash_bs0b086a/test_return_values_{codeflash_iteration}.sqlite')
    codeflash_cur = codeflash_con.cursor()
    codeflash_cur.execute('CREATE TABLE IF NOT EXISTS test_results (test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, runtime INTEGER, return_value BLOB, verification_type TEXT)')
    delete_cached_workflow_response_if_exists(workspace_id='my_workspace', workflow_id='some_workflow', api_key='my_api_key')
    get_mock.return_value = MagicMock(status_code=200, json=MagicMock(return_value={'workflow': {'config': json.dumps({'specification': {'some': 'some'}})}}))
    _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', ephemeral_cache=MemoryCache())
    _call__bound__arguments.apply_defaults()
    _ = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_when_connection_error_occurs_but_file_is_cached_in_file', 'get_workflow_specification', '2', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    get_mock.side_effect = ConnectionError()
    _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', ephemeral_cache=MemoryCache())
    _call__bound__arguments.apply_defaults()
    result = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_when_connection_error_occurs_but_file_is_cached_in_file', 'get_workflow_specification', '4', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    assert result == {'some': 'some', 'id': None}, 'Expected workflow specification to be retrieved from file'
    codeflash_con.close()

@mock.patch.object(roboflow_api.requests, 'get')
def test_get_workflow_specification_when_consecutive_request_hits_ephemeral_cache(get_mock: MagicMock) -> None:
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    codeflash_iteration = os.environ['CODEFLASH_TEST_ITERATION']
    codeflash_con = sqlite3.connect(f'/tmp/codeflash_bs0b086a/test_return_values_{codeflash_iteration}.sqlite')
    codeflash_cur = codeflash_con.cursor()
    codeflash_cur.execute('CREATE TABLE IF NOT EXISTS test_results (test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, runtime INTEGER, return_value BLOB, verification_type TEXT)')
    delete_cached_workflow_response_if_exists(workspace_id='my_workspace', workflow_id='some_workflow', api_key='my_api_key')
    get_mock.return_value = MagicMock(status_code=200, json=MagicMock(return_value={'workflow': {'config': json.dumps({'specification': {'some': 'some'}})}}))
    ephemeral_cache = MemoryCache()
    _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', ephemeral_cache=ephemeral_cache)
    _call__bound__arguments.apply_defaults()
    _ = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_when_consecutive_request_hits_ephemeral_cache', 'get_workflow_specification', '3', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', ephemeral_cache=ephemeral_cache)
    _call__bound__arguments.apply_defaults()
    result = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_when_consecutive_request_hits_ephemeral_cache', 'get_workflow_specification', '4', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    assert result == {'some': 'some', 'id': None}, 'Expected workflow specification to be retrieved from file'
    assert get_mock.call_count == 1, 'Expected remote API to be only called once'
    codeflash_con.close()

def test_get_workflow_specification_when_wrong_api_key_used_and_no_cache_allowed_to_be_used(requests_mock: Mocker) -> None:
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    codeflash_iteration = os.environ['CODEFLASH_TEST_ITERATION']
    codeflash_con = sqlite3.connect(f'/tmp/codeflash_bs0b086a/test_return_values_{codeflash_iteration}.sqlite')
    codeflash_cur = codeflash_con.cursor()
    codeflash_cur.execute('CREATE TABLE IF NOT EXISTS test_results (test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, runtime INTEGER, return_value BLOB, verification_type TEXT)')
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/workflows/some_workflow'), status_code=401)
    with pytest.raises(RoboflowAPINotAuthorizedError):
        _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', use_cache=False)
        _call__bound__arguments.apply_defaults()
        _ = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_when_wrong_api_key_used_and_no_cache_allowed_to_be_used', 'get_workflow_specification', '1_0', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    assert requests_mock.last_request.query == 'api_key=my_api_key', 'API key must be given in query'
    codeflash_con.close()

def test_get_workflow_specification_when_wrong_api_key_used_and_ephemeral_cache_miss_detected(requests_mock: Mocker) -> None:
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    codeflash_iteration = os.environ['CODEFLASH_TEST_ITERATION']
    codeflash_con = sqlite3.connect(f'/tmp/codeflash_bs0b086a/test_return_values_{codeflash_iteration}.sqlite')
    codeflash_cur = codeflash_con.cursor()
    codeflash_cur.execute('CREATE TABLE IF NOT EXISTS test_results (test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, runtime INTEGER, return_value BLOB, verification_type TEXT)')
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/workflows/some_workflow'), status_code=401)
    with pytest.raises(RoboflowAPINotAuthorizedError):
        _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', ephemeral_cache=MemoryCache())
        _call__bound__arguments.apply_defaults()
        _ = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_when_wrong_api_key_used_and_ephemeral_cache_miss_detected', 'get_workflow_specification', '1_0', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    assert requests_mock.last_request.query == 'api_key=my_api_key', 'API key must be given in query'
    codeflash_con.close()

def test_get_workflow_specification_when_not_found_returned_and_cache_disabled(requests_mock: Mocker) -> None:
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    codeflash_iteration = os.environ['CODEFLASH_TEST_ITERATION']
    codeflash_con = sqlite3.connect(f'/tmp/codeflash_bs0b086a/test_return_values_{codeflash_iteration}.sqlite')
    codeflash_cur = codeflash_con.cursor()
    codeflash_cur.execute('CREATE TABLE IF NOT EXISTS test_results (test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, runtime INTEGER, return_value BLOB, verification_type TEXT)')
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/workflows/some_workflow'), status_code=404)
    with pytest.raises(RoboflowAPINotNotFoundError):
        _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', use_cache=False)
        _call__bound__arguments.apply_defaults()
        _ = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_when_not_found_returned_and_cache_disabled', 'get_workflow_specification', '1_0', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    assert requests_mock.last_request.query == 'api_key=my_api_key', 'API key must be given in query'
    codeflash_con.close()

def test_get_workflow_specification_when_not_found_returned_and_ephemeral_cache_miss_detected(requests_mock: Mocker) -> None:
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    codeflash_iteration = os.environ['CODEFLASH_TEST_ITERATION']
    codeflash_con = sqlite3.connect(f'/tmp/codeflash_bs0b086a/test_return_values_{codeflash_iteration}.sqlite')
    codeflash_cur = codeflash_con.cursor()
    codeflash_cur.execute('CREATE TABLE IF NOT EXISTS test_results (test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, runtime INTEGER, return_value BLOB, verification_type TEXT)')
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/workflows/some_workflow'), status_code=404)
    with pytest.raises(RoboflowAPINotNotFoundError):
        _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', ephemeral_cache=MemoryCache())
        _call__bound__arguments.apply_defaults()
        _ = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_when_not_found_returned_and_ephemeral_cache_miss_detected', 'get_workflow_specification', '1_0', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    assert requests_mock.last_request.query == 'api_key=my_api_key', 'API key must be given in query'
    codeflash_con.close()

def test_get_workflow_specification_when_internal_error_returned_and_cache_disabled(requests_mock: Mocker) -> None:
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    codeflash_iteration = os.environ['CODEFLASH_TEST_ITERATION']
    codeflash_con = sqlite3.connect(f'/tmp/codeflash_bs0b086a/test_return_values_{codeflash_iteration}.sqlite')
    codeflash_cur = codeflash_con.cursor()
    codeflash_cur.execute('CREATE TABLE IF NOT EXISTS test_results (test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, runtime INTEGER, return_value BLOB, verification_type TEXT)')
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/workflows/some_workflow'), status_code=500)
    with pytest.raises(RoboflowAPIUnsuccessfulRequestError):
        _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', use_cache=False)
        _call__bound__arguments.apply_defaults()
        _ = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_when_internal_error_returned_and_cache_disabled', 'get_workflow_specification', '1_0', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    assert requests_mock.last_request.query == 'api_key=my_api_key', 'API key must be given in query'
    codeflash_con.close()

def test_get_workflow_specification_when_internal_error_returned_and_ephemeral_cache_miss_detected(requests_mock: Mocker) -> None:
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    codeflash_iteration = os.environ['CODEFLASH_TEST_ITERATION']
    codeflash_con = sqlite3.connect(f'/tmp/codeflash_bs0b086a/test_return_values_{codeflash_iteration}.sqlite')
    codeflash_cur = codeflash_con.cursor()
    codeflash_cur.execute('CREATE TABLE IF NOT EXISTS test_results (test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, runtime INTEGER, return_value BLOB, verification_type TEXT)')
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/workflows/some_workflow'), status_code=500)
    ephemeral_cache = MemoryCache()
    with pytest.raises(RoboflowAPIUnsuccessfulRequestError):
        _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', ephemeral_cache=ephemeral_cache)
        _call__bound__arguments.apply_defaults()
        _ = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_when_internal_error_returned_and_ephemeral_cache_miss_detected', 'get_workflow_specification', '2_0', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    assert requests_mock.last_request.query == 'api_key=my_api_key', 'API key must be given in query'
    assert len(ephemeral_cache.cache) == 0, 'Expected nothing saved to cache'
    codeflash_con.close()

def test_get_workflow_specification_when_malformed_response_returned_and_cache_disabled(requests_mock: Mocker) -> None:
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    codeflash_iteration = os.environ['CODEFLASH_TEST_ITERATION']
    codeflash_con = sqlite3.connect(f'/tmp/codeflash_bs0b086a/test_return_values_{codeflash_iteration}.sqlite')
    codeflash_cur = codeflash_con.cursor()
    codeflash_cur.execute('CREATE TABLE IF NOT EXISTS test_results (test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, runtime INTEGER, return_value BLOB, verification_type TEXT)')
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/workflows/some_workflow'), content=b'Not a JSON :)')
    with pytest.raises(MalformedRoboflowAPIResponseError):
        _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', use_cache=False)
        _call__bound__arguments.apply_defaults()
        _ = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_when_malformed_response_returned_and_cache_disabled', 'get_workflow_specification', '1_0', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    assert requests_mock.last_request.query == 'api_key=my_api_key', 'API key must be given in query'
    codeflash_con.close()

def test_get_workflow_specification_when_malformed_response_returned_and_ephemeral_cache_miss_detected(requests_mock: Mocker) -> None:
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    codeflash_iteration = os.environ['CODEFLASH_TEST_ITERATION']
    codeflash_con = sqlite3.connect(f'/tmp/codeflash_bs0b086a/test_return_values_{codeflash_iteration}.sqlite')
    codeflash_cur = codeflash_con.cursor()
    codeflash_cur.execute('CREATE TABLE IF NOT EXISTS test_results (test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, runtime INTEGER, return_value BLOB, verification_type TEXT)')
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/workflows/some_workflow'), content=b'Not a JSON :)')
    ephemeral_cache = MemoryCache()
    with pytest.raises(MalformedRoboflowAPIResponseError):
        _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', ephemeral_cache=ephemeral_cache)
        _call__bound__arguments.apply_defaults()
        _ = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_when_malformed_response_returned_and_ephemeral_cache_miss_detected', 'get_workflow_specification', '2_0', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    assert requests_mock.last_request.query == 'api_key=my_api_key', 'API key must be given in query'
    assert len(ephemeral_cache.cache) == 0, 'Expected nothing saved to cache'
    codeflash_con.close()

def test_get_workflow_specification_when_config_not_provided_and_cache_disabled(requests_mock: Mocker) -> None:
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    codeflash_iteration = os.environ['CODEFLASH_TEST_ITERATION']
    codeflash_con = sqlite3.connect(f'/tmp/codeflash_bs0b086a/test_return_values_{codeflash_iteration}.sqlite')
    codeflash_cur = codeflash_con.cursor()
    codeflash_cur.execute('CREATE TABLE IF NOT EXISTS test_results (test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, runtime INTEGER, return_value BLOB, verification_type TEXT)')
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/workflows/some_workflow'), json={})
    with pytest.raises(MalformedWorkflowResponseError):
        _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', use_cache=False)
        _call__bound__arguments.apply_defaults()
        _ = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_when_config_not_provided_and_cache_disabled', 'get_workflow_specification', '1_0', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    assert requests_mock.last_request.query == 'api_key=my_api_key', 'API key must be given in query'
    codeflash_con.close()

def test_get_workflow_specification_when_config_not_provided_and_ephemeral_cache_miss_detected(requests_mock: Mocker) -> None:
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    codeflash_iteration = os.environ['CODEFLASH_TEST_ITERATION']
    codeflash_con = sqlite3.connect(f'/tmp/codeflash_bs0b086a/test_return_values_{codeflash_iteration}.sqlite')
    codeflash_cur = codeflash_con.cursor()
    codeflash_cur.execute('CREATE TABLE IF NOT EXISTS test_results (test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, runtime INTEGER, return_value BLOB, verification_type TEXT)')
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/workflows/some_workflow'), json={})
    ephemeral_cache = MemoryCache()
    with pytest.raises(MalformedWorkflowResponseError):
        _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', ephemeral_cache=ephemeral_cache)
        _call__bound__arguments.apply_defaults()
        _ = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_when_config_not_provided_and_ephemeral_cache_miss_detected', 'get_workflow_specification', '2_0', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    assert requests_mock.last_request.query == 'api_key=my_api_key', 'API key must be given in query'
    assert len(ephemeral_cache.cache) == 0, 'Expected nothing saved to cache'
    codeflash_con.close()

def test_get_workflow_specification_when_config_not_parsable_and_cache_disabled(requests_mock: Mocker) -> None:
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    codeflash_iteration = os.environ['CODEFLASH_TEST_ITERATION']
    codeflash_con = sqlite3.connect(f'/tmp/codeflash_bs0b086a/test_return_values_{codeflash_iteration}.sqlite')
    codeflash_cur = codeflash_con.cursor()
    codeflash_cur.execute('CREATE TABLE IF NOT EXISTS test_results (test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, runtime INTEGER, return_value BLOB, verification_type TEXT)')
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/workflows/some_workflow'), json={'config': 'Not a JSON'})
    with pytest.raises(MalformedWorkflowResponseError):
        _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', use_cache=False)
        _call__bound__arguments.apply_defaults()
        _ = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_when_config_not_parsable_and_cache_disabled', 'get_workflow_specification', '1_0', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    assert requests_mock.last_request.query == 'api_key=my_api_key', 'API key must be given in query'
    codeflash_con.close()

def test_get_workflow_specification_when_valid_response_given_and_cache_disabled(requests_mock: Mocker) -> None:
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    codeflash_iteration = os.environ['CODEFLASH_TEST_ITERATION']
    codeflash_con = sqlite3.connect(f'/tmp/codeflash_bs0b086a/test_return_values_{codeflash_iteration}.sqlite')
    codeflash_cur = codeflash_con.cursor()
    codeflash_cur.execute('CREATE TABLE IF NOT EXISTS test_results (test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, runtime INTEGER, return_value BLOB, verification_type TEXT)')
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/workflows/some_workflow'), json={'workflow': {'owner': '50hbxrck9m8nKykOhCEq', 'name': 'Thermal', 'url': 'thermal', 'config': '{"specification":{"version":"1.0","inputs":[{"type":"InferenceImage","name":"image"}],"steps":[{"type":"CVModel","name":"step_1","image":"$inputs.image","model_id":"thermal dogs and people/18"}],"outputs":[{"type":"JsonField","name":"a","selector":"$steps.step_1.predictions"}]},"preset":"single-model"}', 'id': 'Har3FW34j1Rjc4p8IX4B'}, 'status': 'ok'})
    _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', use_cache=False)
    _call__bound__arguments.apply_defaults()
    result = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_when_valid_response_given_and_cache_disabled', 'get_workflow_specification', '1', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    assert requests_mock.last_request.query == 'api_key=my_api_key', 'API key must be given in query'
    assert result == {'version': '1.0', 'inputs': [{'type': 'InferenceImage', 'name': 'image'}], 'steps': [{'type': 'CVModel', 'name': 'step_1', 'image': '$inputs.image', 'model_id': 'thermal dogs and people/18'}], 'outputs': [{'type': 'JsonField', 'name': 'a', 'selector': '$steps.step_1.predictions'}], 'id': 'Har3FW34j1Rjc4p8IX4B'}
    codeflash_con.close()

def test_get_workflow_specification_when_valid_response_given_on_consecutive_requests(requests_mock: Mocker) -> None:
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    codeflash_iteration = os.environ['CODEFLASH_TEST_ITERATION']
    codeflash_con = sqlite3.connect(f'/tmp/codeflash_bs0b086a/test_return_values_{codeflash_iteration}.sqlite')
    codeflash_cur = codeflash_con.cursor()
    codeflash_cur.execute('CREATE TABLE IF NOT EXISTS test_results (test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, runtime INTEGER, return_value BLOB, verification_type TEXT)')
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/workflows/some_workflow'), json={'workflow': {'owner': '50hbxrck9m8nKykOhCEq', 'name': 'Thermal', 'url': 'thermal', 'config': '{"specification":{"version":"1.0","inputs":[{"type":"InferenceImage","name":"image"}],"steps":[{"type":"CVModel","name":"step_1","image":"$inputs.image","model_id":"thermal dogs and people/18"}],"outputs":[{"type":"JsonField","name":"a","selector":"$steps.step_1.predictions"}]},"preset":"single-model"}', 'id': 'Har3FW34j1Rjc4p8IX4B'}, 'status': 'ok'})
    ephemeral_cache = MemoryCache()
    _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', ephemeral_cache=ephemeral_cache)
    _call__bound__arguments.apply_defaults()
    result_1 = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_when_valid_response_given_on_consecutive_requests', 'get_workflow_specification', '2', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', ephemeral_cache=ephemeral_cache)
    _call__bound__arguments.apply_defaults()
    result_2 = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_when_valid_response_given_on_consecutive_requests', 'get_workflow_specification', '3', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    assert requests_mock.call_count == 1, 'Expected remote API to be called only once'
    assert requests_mock.last_request.query == 'api_key=my_api_key', 'API key must be given in query'
    assert result_1 == result_2 == {'version': '1.0', 'inputs': [{'type': 'InferenceImage', 'name': 'image'}], 'steps': [{'type': 'CVModel', 'name': 'step_1', 'image': '$inputs.image', 'model_id': 'thermal dogs and people/18'}], 'outputs': [{'type': 'JsonField', 'name': 'a', 'selector': '$steps.step_1.predictions'}], 'id': 'Har3FW34j1Rjc4p8IX4B'}
    assert len(ephemeral_cache.cache) == 1, 'Expected cache content to appear'
    codeflash_con.close()

def test_get_workflow_specification_with_workflow_version_id(requests_mock: Mocker) -> None:
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    codeflash_iteration = os.environ['CODEFLASH_TEST_ITERATION']
    codeflash_con = sqlite3.connect(f'/tmp/codeflash_bs0b086a/test_return_values_{codeflash_iteration}.sqlite')
    codeflash_cur = codeflash_con.cursor()
    codeflash_cur.execute('CREATE TABLE IF NOT EXISTS test_results (test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, runtime INTEGER, return_value BLOB, verification_type TEXT)')
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/workflows/some_workflow'), json={'workflow': {'owner': '50hbxrck9m8nKykOhCEq', 'name': 'Thermal', 'url': 'thermal', 'config': '{"specification":{"version":"1.0","inputs":[{"type":"InferenceImage","name":"image"}],"steps":[{"type":"CVModel","name":"step_1","image":"$inputs.image","model_id":"thermal dogs and people/18"}],"outputs":[{"type":"JsonField","name":"a","selector":"$steps.step_1.predictions"}]},"preset":"single-model"}', 'id': 'Har3FW34j1Rjc4p8IX4B'}, 'status': 'ok'})
    _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', workflow_version_id='1771122946631', use_cache=False)
    _call__bound__arguments.apply_defaults()
    result = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_with_workflow_version_id', 'get_workflow_specification', '1', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    assert 'workflow_version=1771122946631' in requests_mock.last_request.query, 'Workflow version must be given in query'
    assert 'api_key=my_api_key' in requests_mock.last_request.query, 'API key must be given in query'
    assert result == {'version': '1.0', 'inputs': [{'type': 'InferenceImage', 'name': 'image'}], 'steps': [{'type': 'CVModel', 'name': 'step_1', 'image': '$inputs.image', 'model_id': 'thermal dogs and people/18'}], 'outputs': [{'type': 'JsonField', 'name': 'a', 'selector': '$steps.step_1.predictions'}], 'id': 'Har3FW34j1Rjc4p8IX4B'}
    codeflash_con.close()

def test_get_workflow_specification_with_version_id_uses_separate_cache(requests_mock: Mocker) -> None:
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    codeflash_iteration = os.environ['CODEFLASH_TEST_ITERATION']
    codeflash_con = sqlite3.connect(f'/tmp/codeflash_bs0b086a/test_return_values_{codeflash_iteration}.sqlite')
    codeflash_cur = codeflash_con.cursor()
    codeflash_cur.execute('CREATE TABLE IF NOT EXISTS test_results (test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, runtime INTEGER, return_value BLOB, verification_type TEXT)')
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/my_workspace/workflows/some_workflow'), json={'workflow': {'owner': '50hbxrck9m8nKykOhCEq', 'name': 'Thermal', 'url': 'thermal', 'config': '{"specification":{"version":"1.0","inputs":[{"type":"InferenceImage","name":"image"}],"steps":[{"type":"CVModel","name":"step_1","image":"$inputs.image","model_id":"thermal dogs and people/18"}],"outputs":[{"type":"JsonField","name":"a","selector":"$steps.step_1.predictions"}]},"preset":"single-model"}', 'id': 'Har3FW34j1Rjc4p8IX4B'}, 'status': 'ok'})
    ephemeral_cache = MemoryCache()
    _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', ephemeral_cache=ephemeral_cache)
    _call__bound__arguments.apply_defaults()
    codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_with_version_id_uses_separate_cache', 'get_workflow_specification', '2', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', workflow_version_id='1771122946631', ephemeral_cache=ephemeral_cache)
    _call__bound__arguments.apply_defaults()
    codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_with_version_id_uses_separate_cache', 'get_workflow_specification', '3', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    assert requests_mock.call_count == 2, 'Expected two API calls since versioned and unversioned use separate cache keys'
    assert len(ephemeral_cache.cache) == 2, 'Expected two cache entries'
    codeflash_con.close()

@mock.patch.object(roboflow_api, 'ROBOFLOW_API_EXTRA_HEADERS', None)
def test_build_roboflow_api_headers_when_no_extra_headers() -> None:
    result = build_roboflow_api_headers()
    assert result == {roboflow_api.ROBOFLOW_INFERENCE_VERSION_HEADER: __version__, roboflow_api.ALLOW_CHUNKED_RESPONSE_HEADER: 'true'}

@mock.patch.object(roboflow_api, 'ROBOFLOW_API_EXTRA_HEADERS', None)
def test_build_roboflow_api_headers_when_no_extra_headers_but_explicit_headers_given() -> None:
    result = build_roboflow_api_headers(explicit_headers={'my': 'header'})
    assert result == {'my': 'header', roboflow_api.ROBOFLOW_INFERENCE_VERSION_HEADER: __version__, roboflow_api.ALLOW_CHUNKED_RESPONSE_HEADER: 'true'}, 'Expected to preserve explicit header and inject version'

@mock.patch.object(roboflow_api, 'ROBOFLOW_API_EXTRA_HEADERS', json.dumps({'extra': 'header', 'another': 'extra'}))
def test_build_roboflow_api_headers_when_extra_headers_given() -> None:
    result = build_roboflow_api_headers()
    assert result == {'extra': 'header', 'another': 'extra', roboflow_api.ROBOFLOW_INFERENCE_VERSION_HEADER: __version__, roboflow_api.ALLOW_CHUNKED_RESPONSE_HEADER: 'true'}, 'Expected extra headers to be decoded'

@mock.patch.object(roboflow_api, 'ROBOFLOW_API_EXTRA_HEADERS', json.dumps({'extra': 'header', 'another': 'extra'}))
def test_build_roboflow_api_headers_when_extra_headers_given_and_explicit_headers_present() -> None:
    result = build_roboflow_api_headers(explicit_headers={'my': 'header'})
    assert result == {'my': 'header', 'extra': 'header', 'another': 'extra', roboflow_api.ROBOFLOW_INFERENCE_VERSION_HEADER: __version__, roboflow_api.ALLOW_CHUNKED_RESPONSE_HEADER: 'true'}, 'Expected extra headers to be decoded and shipped along with explicit headers'

@mock.patch.object(roboflow_api, 'ROBOFLOW_API_EXTRA_HEADERS', 'For sure not a JSON :)')
def test_build_roboflow_api_headers_when_extra_headers_given_as_invalid_json() -> None:
    result = build_roboflow_api_headers(explicit_headers={'my': 'header'})
    assert result == {'my': 'header', roboflow_api.ROBOFLOW_INFERENCE_VERSION_HEADER: __version__, roboflow_api.ALLOW_CHUNKED_RESPONSE_HEADER: 'true'}, 'Expected extra headers to be decoded and shipped along with explicit headers'

@mock.patch.object(roboflow_api, 'ROBOFLOW_API_EXTRA_HEADERS', json.dumps({'extra': 'header', 'another': 'extra'}))
def test_build_roboflow_api_headers_when_extra_headers_given_and_explicit_headers_collide_with_extras() -> None:
    result = build_roboflow_api_headers(explicit_headers={'extra': 'explicit-is-better', 'my': 'header'})
    assert result == {'another': 'extra', 'extra': 'explicit-is-better', 'my': 'header', roboflow_api.ROBOFLOW_INFERENCE_VERSION_HEADER: __version__, roboflow_api.ALLOW_CHUNKED_RESPONSE_HEADER: 'true'}, 'Expected extra headers to be decoded and explicit header to override implicit one while keeping version header'

def test_build_roboflow_api_headers_always_sets_version_header() -> None:
    result = build_roboflow_api_headers(explicit_headers={roboflow_api.ROBOFLOW_INFERENCE_VERSION_HEADER: 'should-be-overwritten', 'custom': 'value'})
    assert result[roboflow_api.ROBOFLOW_INFERENCE_VERSION_HEADER] == __version__
    assert result[roboflow_api.ALLOW_CHUNKED_RESPONSE_HEADER] == 'true'
    assert result['custom'] == 'value'

@mock.patch.object(roboflow_api, 'RETRY_CONNECTION_ERRORS_TO_ROBOFLOW_API', False)
@mock.patch.object(roboflow_api, 'TRANSIENT_ROBOFLOW_API_ERRORS', set())
def test_get_from_url_when_no_retires_possible(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/some'), json={'status': 'fail'}, status_code=500)
    with pytest.raises(RoboflowAPIUnsuccessfulRequestError):
        _ = get_from_url(url=wrap_url(f'{API_BASE_URL}/some'), json_response=True)

@mock.patch.object(roboflow_api, 'RETRY_CONNECTION_ERRORS_TO_ROBOFLOW_API', True)
@mock.patch.object(roboflow_api, 'TRANSIENT_ROBOFLOW_API_ERRORS', {503})
def test_get_from_url_when_retires_possible(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/some'), response_list=[{'json': {'status': 'fail'}, 'status_code': 503}, {'json': {'status': 'ok'}, 'status_code': 200}])
    result = get_from_url(url=wrap_url(f'{API_BASE_URL}/some'), json_response=True)
    assert result == {'status': 'ok'}

@mock.patch.object(roboflow_api, 'TRANSIENT_ROBOFLOW_API_ERRORS_RETRIES', 1)
@mock.patch.object(roboflow_api, 'RETRY_CONNECTION_ERRORS_TO_ROBOFLOW_API', True)
@mock.patch.object(roboflow_api, 'TRANSIENT_ROBOFLOW_API_ERRORS', {503})
def test_get_from_url_when_retires_possible_but_given_up(requests_mock: Mocker) -> None:
    requests_mock.get(url=wrap_url(f'{API_BASE_URL}/some'), response_list=[{'json': {'status': 'fail'}, 'status_code': 503}, {'json': {'status': 'fail'}, 'status_code': 503}])
    with pytest.raises(RoboflowAPIUnsuccessfulRequestError):
        _ = get_from_url(url=wrap_url(f'{API_BASE_URL}/some'), json_response=True)

@mock.patch.object(roboflow_api, 'MD5_VERIFICATION_ENABLED', True)
@mock.patch.object(roboflow_api, 'RETRY_CONNECTION_ERRORS_TO_ROBOFLOW_API', False)
@mock.patch.object(roboflow_api, 'TRANSIENT_ROBOFLOW_API_ERRORS', set())
def test_get_from_url_when_md5_verification_enabled_but_x_goog_hash_header_missing(requests_mock: Mocker) -> None:
    request_url = wrap_url(f'{API_BASE_URL}/some')
    requests_mock.get(url=request_url, json={'status': 'ok'}, status_code=200, headers={})
    with mock.patch.object(roboflow_api, 'logger') as logger_mock:
        result = get_from_url(url=request_url, json_response=True)
    assert result == {'status': 'ok'}
    logger_mock.warning.assert_called_once()
    call_args = logger_mock.warning.call_args[0][0]
    assert 'x-goog-hash' in call_args
    assert request_url in call_args

@mock.patch.object(roboflow_api, 'MD5_VERIFICATION_ENABLED', True)
@mock.patch.object(roboflow_api, 'RETRY_CONNECTION_ERRORS_TO_ROBOFLOW_API', False)
@mock.patch.object(roboflow_api, 'TRANSIENT_ROBOFLOW_API_ERRORS', set())
def test_get_from_url_when_md5_verification_enabled_but_x_goog_hash_missing_does_not_log_api_key(requests_mock: Mocker) -> None:
    secret_api_key = 'my-secret-api-key-12345'
    request_url = wrap_url(f'{API_BASE_URL}/some?api_key={secret_api_key}')
    requests_mock.get(url=request_url, json={'status': 'ok'}, status_code=200, headers={})
    with mock.patch.object(roboflow_api, 'logger') as logger_mock:
        get_from_url(url=request_url, json_response=True)
    logged_message = logger_mock.warning.call_args[0][0]
    assert secret_api_key not in logged_message
    assert 'x-goog-hash' in logged_message
    assert '/some' in logged_message

@mock.patch.object(roboflow_api, 'MD5_VERIFICATION_ENABLED', True)
@mock.patch.object(roboflow_api, 'RETRY_CONNECTION_ERRORS_TO_ROBOFLOW_API', False)
@mock.patch.object(roboflow_api, 'TRANSIENT_ROBOFLOW_API_ERRORS', set())
def test_get_from_url_when_md5_verification_enabled_but_x_goog_hash_has_no_md5_part(requests_mock: Mocker) -> None:
    request_url = wrap_url(f'{API_BASE_URL}/some')
    header_value = 'crc32c=abc123'
    requests_mock.get(url=request_url, json={'status': 'ok'}, status_code=200, headers={'x-goog-hash': header_value})
    with mock.patch.object(roboflow_api, 'logger') as logger_mock:
        result = get_from_url(url=request_url, json_response=True)
    assert result == {'status': 'ok'}
    logger_mock.warning.assert_called_once()
    logged_message = logger_mock.warning.call_args[0][0]
    assert 'no md5= part' in logged_message
    assert '/some' in logged_message

@mock.patch.object(roboflow_api, 'MD5_VERIFICATION_ENABLED', True)
@mock.patch.object(roboflow_api, 'RETRY_CONNECTION_ERRORS_TO_ROBOFLOW_API', False)
@mock.patch.object(roboflow_api, 'TRANSIENT_ROBOFLOW_API_ERRORS', set())
def test_get_from_url_when_md5_verification_enabled_but_md5_part_is_invalid_base64(requests_mock: Mocker) -> None:
    request_url = wrap_url(f'{API_BASE_URL}/some')
    requests_mock.get(url=request_url, json={'status': 'ok'}, status_code=200, headers={'x-goog-hash': 'md5=!!!invalid-base64!!!'})
    with pytest.raises(RoboflowAPIUnsuccessfulRequestError) as exc_info:
        get_from_url(url=request_url, json_response=True)
    assert 'not valid base64' in str(exc_info.value)
    assert exc_info.value.__cause__ is not None

@mock.patch.object(roboflow_api.requests, 'get')
def test_get_workflow_specification_falls_back_to_cache_on_timeout(get_mock: MagicMock) -> None:
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    codeflash_iteration = os.environ['CODEFLASH_TEST_ITERATION']
    codeflash_con = sqlite3.connect(f'/tmp/codeflash_bs0b086a/test_return_values_{codeflash_iteration}.sqlite')
    codeflash_cur = codeflash_con.cursor()
    codeflash_cur.execute('CREATE TABLE IF NOT EXISTS test_results (test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, runtime INTEGER, return_value BLOB, verification_type TEXT)')
    delete_cached_workflow_response_if_exists(workspace_id='my_workspace', workflow_id='some_workflow', api_key='my_api_key')
    get_mock.return_value = MagicMock(status_code=200, json=MagicMock(return_value={'workflow': {'config': json.dumps({'specification': {'timeout': 'fallback'}})}}))
    _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', ephemeral_cache=MemoryCache())
    _call__bound__arguments.apply_defaults()
    _ = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_falls_back_to_cache_on_timeout', 'get_workflow_specification', '2', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    get_mock.side_effect = requests.exceptions.Timeout()
    _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='some_workflow', ephemeral_cache=MemoryCache())
    _call__bound__arguments.apply_defaults()
    result = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_falls_back_to_cache_on_timeout', 'get_workflow_specification', '4', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    assert result == {'timeout': 'fallback', 'id': None}, 'Expected workflow specification to be retrieved from file cache on Timeout'
    codeflash_con.close()

@mock.patch.object(roboflow_api.requests, 'get')
def test_get_workflow_specification_raises_timeout_when_no_cache(get_mock: MagicMock) -> None:
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    codeflash_iteration = os.environ['CODEFLASH_TEST_ITERATION']
    codeflash_con = sqlite3.connect(f'/tmp/codeflash_bs0b086a/test_return_values_{codeflash_iteration}.sqlite')
    codeflash_cur = codeflash_con.cursor()
    codeflash_cur.execute('CREATE TABLE IF NOT EXISTS test_results (test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, runtime INTEGER, return_value BLOB, verification_type TEXT)')
    delete_cached_workflow_response_if_exists(workspace_id='my_workspace', workflow_id='timeout_no_cache_workflow', api_key='my_api_key')
    get_mock.side_effect = requests.exceptions.Timeout()
    with pytest.raises(RoboflowAPITimeoutError):
        _call__bound__arguments = inspect.signature(get_workflow_specification).bind(api_key='my_api_key', workspace_id='my_workspace', workflow_id='timeout_no_cache_workflow', use_cache=False)
        _call__bound__arguments.apply_defaults()
        _ = codeflash_wrap(get_workflow_specification, 'tests.inference.unit_tests.core.test_roboflow_api', None, 'test_get_workflow_specification_raises_timeout_when_no_cache', 'get_workflow_specification', '2_0', codeflash_loop_index, codeflash_cur, codeflash_con, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    codeflash_con.close()
