from __future__ import annotations

import gc
import inspect
import os
import time
from pathlib import Path
from typing import List, Optional, Tuple
from unittest.mock import Mock, patch

import dill as pickle
import pytest
from inference.core.workflows.core_steps.composition.workflow_caller.v1 import (
    WORKFLOW_CALLER_BLOCK_TYPE, _check_workflow_for_circular_references,
    _extract_workflow_caller_ids_from_spec)
from inference.core.workflows.errors import ExecutionGraphStructureError


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, *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}######!')
    iteration = os.environ['CODEFLASH_TEST_ITERATION']
    with Path('{codeflash_run_tmp_dir_client_side}', f'test_return_values_{iteration}.bin').open('ab') as f:
        pickled_values = pickle.dumps((args, kwargs, exception)) if exception else pickle.dumps((args, kwargs, return_value))
        _test_name = f"{codeflash_test_module_name}:{(codeflash_test_class_name + '.' if codeflash_test_class_name else '')}{codeflash_test_name}:{codeflash_function_name}:{codeflash_line_id}".encode('ascii')
        f.write(len(_test_name).to_bytes(4, byteorder='big'))
        f.write(_test_name)
        f.write(codeflash_duration.to_bytes(8, byteorder='big'))
        f.write(len(pickled_values).to_bytes(4, byteorder='big'))
        f.write(pickled_values)
        f.write(codeflash_loop_index.to_bytes(8, byteorder='big'))
        f.write(len(invocation_id).to_bytes(4, byteorder='big'))
        f.write(invocation_id.encode('ascii'))
    if exception:
        raise exception
    return return_value

def test_no_circular_reference_single_workflow():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test that a workflow with no nested calls passes validation.'
    spec = {'steps': [{'type': 'some_other_step', 'name': 'step1'}]}
    _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec)
    _call__bound__arguments.apply_defaults()
    codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_no_circular_reference_single_workflow', '_check_workflow_for_circular_references', '2', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_no_circular_reference_linear_chain():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test that a linear chain of workflow calls (no cycles) passes validation.'
    spec_workflow_2 = {'steps': [{'type': 'some_other_step', 'name': 'step1'}]}
    spec_workflow_1 = {'steps': [{'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': 'workflow_2'}]}
    with patch('inference.core.workflows.core_steps.composition.workflow_caller.v1.get_workflow_specification') as mock_get_spec:
        mock_get_spec.return_value = spec_workflow_2
        _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec_workflow_1)
        _call__bound__arguments.apply_defaults()
        codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_no_circular_reference_linear_chain', '_check_workflow_for_circular_references', '3_1', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_circular_reference_self_call():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test that a workflow calling itself is detected as circular.'
    spec = {'steps': [{'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': 'workflow_1'}]}
    with pytest.raises(ExecutionGraphStructureError) as exc_info:
        _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec)
        _call__bound__arguments.apply_defaults()
        codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_circular_reference_self_call', '_check_workflow_for_circular_references', '2_0', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_circular_reference_two_workflow_cycle():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test that a cycle between two workflows is detected.'
    spec_workflow_1 = {'steps': [{'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': 'workflow_2'}]}
    spec_workflow_2 = {'steps': [{'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': 'workflow_1'}]}
    with patch('inference.core.workflows.core_steps.composition.workflow_caller.v1.get_workflow_specification') as mock_get_spec:

        def get_spec_side_effect(api_key, workspace_id, workflow_id, **kwargs):
            if workflow_id == 'workflow_1':
                return spec_workflow_1
            elif workflow_id == 'workflow_2':
                return spec_workflow_2
            else:
                raise ValueError(f'Unexpected workflow_id: {workflow_id}')
        mock_get_spec.side_effect = get_spec_side_effect
        with pytest.raises(ExecutionGraphStructureError) as exc_info:
            _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec_workflow_1)
            _call__bound__arguments.apply_defaults()
            codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_circular_reference_two_workflow_cycle', '_check_workflow_for_circular_references', '3_2_0', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_circular_reference_three_workflow_cycle():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test that a cycle among three workflows is detected.'
    spec_workflow_1 = {'steps': [{'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': 'workflow_2'}]}
    spec_workflow_2 = {'steps': [{'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': 'workflow_3'}]}
    spec_workflow_3 = {'steps': [{'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': 'workflow_1'}]}
    with patch('inference.core.workflows.core_steps.composition.workflow_caller.v1.get_workflow_specification') as mock_get_spec:

        def get_spec_side_effect(api_key, workspace_id, workflow_id, **kwargs):
            specs = {'workflow_1': spec_workflow_1, 'workflow_2': spec_workflow_2, 'workflow_3': spec_workflow_3}
            return specs.get(workflow_id, {})
        mock_get_spec.side_effect = get_spec_side_effect
        with pytest.raises(ExecutionGraphStructureError) as exc_info:
            _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec_workflow_1)
            _call__bound__arguments.apply_defaults()
            codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_circular_reference_three_workflow_cycle', '_check_workflow_for_circular_references', '4_2_0', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_visited_set_tracking():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test that the visited set is properly maintained during traversal.'
    spec_workflow_1 = {'steps': [{'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': 'workflow_2'}]}
    spec_workflow_2 = {'steps': []}
    with patch('inference.core.workflows.core_steps.composition.workflow_caller.v1.get_workflow_specification') as mock_get_spec:
        mock_get_spec.return_value = spec_workflow_2
        _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec_workflow_1)
        _call__bound__arguments.apply_defaults()
        codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_visited_set_tracking', '_check_workflow_for_circular_references', '3_1', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_prefetched_spec_avoids_api_call():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test that prefetched_spec parameter is used and avoids an API call.'
    spec = {'steps': [{'type': 'some_step', 'name': 'step1'}]}
    with patch('inference.core.workflows.core_steps.composition.workflow_caller.v1.get_workflow_specification') as mock_get_spec:
        _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec)
        _call__bound__arguments.apply_defaults()
        codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_prefetched_spec_avoids_api_call', '_check_workflow_for_circular_references', '2_0', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
        mock_get_spec.assert_not_called()

def test_multiple_workflow_calls_in_single_spec():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test spec with multiple workflow_caller steps.'
    spec = {'steps': [{'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': 'workflow_2'}, {'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': 'workflow_3'}]}
    spec_nested = {'steps': []}
    with patch('inference.core.workflows.core_steps.composition.workflow_caller.v1.get_workflow_specification') as mock_get_spec:
        mock_get_spec.return_value = spec_nested
        _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec)
        _call__bound__arguments.apply_defaults()
        codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_multiple_workflow_calls_in_single_spec', '_check_workflow_for_circular_references', '3_1', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_empty_steps_list():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test spec with empty steps list.'
    spec = {'steps': []}
    _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec)
    _call__bound__arguments.apply_defaults()
    codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_empty_steps_list', '_check_workflow_for_circular_references', '2', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_missing_steps_key_in_spec():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    "Test spec without 'steps' key."
    spec = {'some_other_key': 'value'}
    _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec)
    _call__bound__arguments.apply_defaults()
    codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_missing_steps_key_in_spec', '_check_workflow_for_circular_references', '2', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_workflow_id_is_selector_not_literal():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test that dynamic workflow IDs (selectors) are skipped.'
    spec = {'steps': [{'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': '$inputs.workflow_id'}]}
    with patch('inference.core.workflows.core_steps.composition.workflow_caller.v1.get_workflow_specification') as mock_get_spec:
        _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec)
        _call__bound__arguments.apply_defaults()
        codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_workflow_id_is_selector_not_literal', '_check_workflow_for_circular_references', '2_0', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
        mock_get_spec.assert_not_called()

def test_workflow_id_not_string():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test that non-string workflow IDs are skipped.'
    spec = {'steps': [{'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': 123}]}
    with patch('inference.core.workflows.core_steps.composition.workflow_caller.v1.get_workflow_specification') as mock_get_spec:
        _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec)
        _call__bound__arguments.apply_defaults()
        codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_workflow_id_not_string', '_check_workflow_for_circular_references', '2_0', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
        mock_get_spec.assert_not_called()

def test_workflow_version_id_selector():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test that dynamic workflow_version_id (selector) is handled correctly.'
    spec = {'steps': [{'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': 'workflow_2', 'workflow_version_id': '$inputs.version'}]}
    spec_nested = {'steps': []}
    with patch('inference.core.workflows.core_steps.composition.workflow_caller.v1.get_workflow_specification') as mock_get_spec:
        mock_get_spec.return_value = spec_nested
        _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec)
        _call__bound__arguments.apply_defaults()
        codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_workflow_version_id_selector', '_check_workflow_for_circular_references', '3_1', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_fetch_error_silently_returns():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test that API fetch errors are silently caught and function returns.'
    spec = {'steps': [{'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': 'workflow_2'}]}
    with patch('inference.core.workflows.core_steps.composition.workflow_caller.v1.get_workflow_specification') as mock_get_spec:
        mock_get_spec.side_effect = Exception('API Error')
        _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec)
        _call__bound__arguments.apply_defaults()
        codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_fetch_error_silently_returns', '_check_workflow_for_circular_references', '2_1', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_api_key_none():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test that api_key can be None.'
    spec = {'steps': []}
    _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key=None, workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec)
    _call__bound__arguments.apply_defaults()
    codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_api_key_none', '_check_workflow_for_circular_references', '2', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_workflow_version_id_none():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test that workflow_version_id can be None.'
    spec = {'steps': []}
    _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), target_workflow_version_id=None, prefetched_spec=spec)
    _call__bound__arguments.apply_defaults()
    codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_workflow_version_id_none', '_check_workflow_for_circular_references', '2', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_workflow_version_id_provided():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test that workflow_version_id is passed correctly.'
    spec_main = {'steps': []}
    with patch('inference.core.workflows.core_steps.composition.workflow_caller.v1.get_workflow_specification') as mock_get_spec:
        _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), target_workflow_version_id='v1.0', prefetched_spec=spec_main)
        _call__bound__arguments.apply_defaults()
        codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_workflow_version_id_provided', '_check_workflow_for_circular_references', '2_0', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_empty_workflow_id_string():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test that empty workflow_id strings are handled.'
    spec = {'steps': [{'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': ''}]}
    with patch('inference.core.workflows.core_steps.composition.workflow_caller.v1.get_workflow_specification') as mock_get_spec:
        _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec)
        _call__bound__arguments.apply_defaults()
        codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_empty_workflow_id_string', '_check_workflow_for_circular_references', '2_0', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_step_missing_type_field():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    "Test spec with steps missing 'type' field."
    spec = {'steps': [{'workflow_id': 'workflow_2'}]}
    _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec)
    _call__bound__arguments.apply_defaults()
    codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_step_missing_type_field', '_check_workflow_for_circular_references', '2', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_step_missing_workflow_id_field():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    "Test spec with workflow_caller step missing 'workflow_id' field."
    spec = {'steps': [{'type': WORKFLOW_CALLER_BLOCK_TYPE}]}
    _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec)
    _call__bound__arguments.apply_defaults()
    codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_step_missing_workflow_id_field', '_check_workflow_for_circular_references', '2', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_context_and_message_in_error():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test that ExecutionGraphStructureError has correct context and message.'
    spec = {'steps': [{'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': 'workflow_1'}]}
    with pytest.raises(ExecutionGraphStructureError) as exc_info:
        _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec)
        _call__bound__arguments.apply_defaults()
        codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_context_and_message_in_error', '_check_workflow_for_circular_references', '2_0', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)
    error = exc_info.value

def test_spec_is_none_with_prefetch():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test handling when spec structure is unusual.'
    spec = None
    with pytest.raises((TypeError, AttributeError)):
        _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec)
        _call__bound__arguments.apply_defaults()
        codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_spec_is_none_with_prefetch', '_check_workflow_for_circular_references', '2_0', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_workspace_name_with_special_characters():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test that workspace_name with special characters is handled.'
    spec = {'steps': []}
    _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='workspace-with-dashes_and_underscores', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec)
    _call__bound__arguments.apply_defaults()
    codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_workspace_name_with_special_characters', '_check_workflow_for_circular_references', '2', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_workflow_id_with_special_characters():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test that workflow_id with special characters is handled.'
    spec = {'steps': []}
    _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow-with-dashes_and_numbers_123', visited=set(), prefetched_spec=spec)
    _call__bound__arguments.apply_defaults()
    codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_workflow_id_with_special_characters', '_check_workflow_for_circular_references', '2', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_deep_linear_chain_100_workflows():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test that a deep linear chain of 100 workflows (no cycle) is validated.'
    specs = {}
    for i in range(1, 101):
        if i == 100:
            specs[f'workflow_{i}'] = {'steps': []}
        else:
            specs[f'workflow_{i}'] = {'steps': [{'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': f'workflow_{i + 1}'}]}
    with patch('inference.core.workflows.core_steps.composition.workflow_caller.v1.get_workflow_specification') as mock_get_spec:

        def get_spec_side_effect(api_key, workspace_id, workflow_id, **kwargs):
            return specs.get(workflow_id, {'steps': []})
        mock_get_spec.side_effect = get_spec_side_effect
        _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=specs['workflow_1'])
        _call__bound__arguments.apply_defaults()
        codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_deep_linear_chain_100_workflows', '_check_workflow_for_circular_references', '3_2', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_wide_branching_multiple_children():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test that a workflow with many child workflows is validated correctly.'
    steps = []
    for i in range(1, 101):
        steps.append({'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': f'workflow_child_{i}'})
    spec_main = {'steps': steps}
    spec_child = {'steps': []}
    with patch('inference.core.workflows.core_steps.composition.workflow_caller.v1.get_workflow_specification') as mock_get_spec:
        mock_get_spec.return_value = spec_child
        _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec_main)
        _call__bound__arguments.apply_defaults()
        codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_wide_branching_multiple_children', '_check_workflow_for_circular_references', '5_1', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_complex_dag_no_cycle():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test complex directed acyclic graph (DAG) with no cycles.'
    specs = {'workflow_1': {'steps': [{'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': 'workflow_2'}, {'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': 'workflow_3'}]}, 'workflow_2': {'steps': [{'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': 'workflow_4'}, {'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': 'workflow_5'}]}, 'workflow_3': {'steps': [{'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': 'workflow_5'}, {'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': 'workflow_6'}]}, 'workflow_4': {'steps': []}, 'workflow_5': {'steps': []}, 'workflow_6': {'steps': []}}
    with patch('inference.core.workflows.core_steps.composition.workflow_caller.v1.get_workflow_specification') as mock_get_spec:

        def get_spec_side_effect(api_key, workspace_id, workflow_id, **kwargs):
            return specs.get(workflow_id, {'steps': []})
        mock_get_spec.side_effect = get_spec_side_effect
        _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=specs['workflow_1'])
        _call__bound__arguments.apply_defaults()
        codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_complex_dag_no_cycle', '_check_workflow_for_circular_references', '2_2', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_large_spec_with_many_non_caller_steps():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test spec with 500 non-workflow_caller steps.'
    steps = []
    for i in range(500):
        steps.append({'type': f'some_step_type_{i}', 'name': f'step_{i}'})
    spec = {'steps': steps}
    _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec)
    _call__bound__arguments.apply_defaults()
    codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_large_spec_with_many_non_caller_steps', '_check_workflow_for_circular_references', '4', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_circular_detection_with_large_visited_set():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test circular detection when visited set already contains many items.'
    spec = {'steps': [{'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': 'workflow_new'}]}
    large_visited = {f'workflow_{i}' for i in range(100)}
    large_visited.add('workflow_new')
    with pytest.raises(ExecutionGraphStructureError) as exc_info:
        _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_from_large_set', visited=large_visited, prefetched_spec=spec)
        _call__bound__arguments.apply_defaults()
        codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_circular_detection_with_large_visited_set', '_check_workflow_for_circular_references', '4_0', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)

def test_extract_workflow_caller_ids_from_spec_large():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test _extract_workflow_caller_ids_from_spec with 500 workflow_caller steps.'
    steps = []
    expected_workflows = []
    for i in range(500):
        workflow_id = f'workflow_{i}'
        version_id = f'v{i}' if i % 2 == 0 else None
        steps.append({'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': workflow_id, 'workflow_version_id': version_id})
        expected_workflows.append((workflow_id, version_id))
    spec = {'steps': steps}
    result = _extract_workflow_caller_ids_from_spec(spec)

def test_mixed_spec_with_500_steps_and_50_callers():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test spec with 500 total steps, 50 of which are workflow_caller.'
    steps = []
    caller_count = 0
    for i in range(500):
        if i % 10 == 0:
            steps.append({'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': f'workflow_called_{caller_count}'})
            caller_count += 1
        else:
            steps.append({'type': f'other_step_type', 'name': f'step_{i}'})
    spec = {'steps': steps}
    result = _extract_workflow_caller_ids_from_spec(spec)

def test_nested_json_structure_large():
    codeflash_loop_index = int(os.environ['CODEFLASH_LOOP_INDEX'])
    'Test that deeply nested JSON structures are handled gracefully.'
    spec = {'steps': [{'type': WORKFLOW_CALLER_BLOCK_TYPE, 'workflow_id': 'workflow_2', 'nested': {'deep': {'structure': {'with': {'many': {'levels': 'value'}}}}}}]}
    spec_nested = {'steps': []}
    with patch('inference.core.workflows.core_steps.composition.workflow_caller.v1.get_workflow_specification') as mock_get_spec:
        mock_get_spec.return_value = spec_nested
        _call__bound__arguments = inspect.signature(_check_workflow_for_circular_references).bind(api_key='test_key', workspace_name='test_workspace', target_workflow_id='workflow_1', visited=set(), prefetched_spec=spec)
        _call__bound__arguments.apply_defaults()
        codeflash_return_value = codeflash_wrap(_check_workflow_for_circular_references, 'tests.inference.unit_tests.test__check_workflow_for_circular_references__unit_test_1', None, 'test_nested_json_structure_large', '_check_workflow_for_circular_references', '3_1', codeflash_loop_index, *_call__bound__arguments.args, **_call__bound__arguments.kwargs)