---
title: "Test Implementation: Optimize Adapter Registry with Pre-computed Map (Issue #602)"
by: "khive-implementer"
created: "2025-04-22"
updated: "2025-04-22"
version: "1.0"
doc_type: "TI"
output_subdir: "tis"
description: "Test Implementation Plan for verifying the optimization of the adapter registry with a pre-computed map."
date: "2025-04-22"
---

# Test Implementation Plan: Optimize Adapter Registry with Pre-computed Map (Issue #602)

## 1. Overview

### 1.1 Component Under Test

The primary components under test are:

1. The `AdapterRegistry` class in `lionagi/adapters/adapter.py`
2. The `build_adapter_registry.py` script in `lionagi/scripts/`
3. The CLI entry point in `lionagi/scripts/cli.py`

### 1.2 Test Approach

This implementation focuses on **Unit Tests** to verify the functionality of the
pre-computed adapter registry. Additionally, a simple performance test will be
conducted to ensure the cold start time is reduced to less than 300ms.

### 1.3 Key Testing Goals

- Verify that the `build_adapter_registry.py` script correctly scans the
  adapters directory and generates a static mapping.
- Verify that the `AdapterRegistry` class correctly loads adapters from the
  pre-computed map.
- Verify that the `AdapterRegistry` class falls back to dynamic scanning if the
  map is not found.
- Verify that the CLI entry point correctly calls the
  `build_adapter_registry.py` script.
- Verify that the cold start time is reduced to less than 300ms.

## 2. Test Environment

### 2.1 Test Framework

```
pytest
pytest-asyncio
pytest-mock
pytest-cov
```

### 2.2 Mock Framework

```
unittest.mock
pytest-mock
```

### 2.3 Test Database

Not applicable for this issue.

## 3. Unit Tests

### 3.1 Test Suite: AdapterRegistry

#### 3.1.1 Test Case: get() - Unknown Key Raises MissingAdapterError

**Purpose:** To verify that calling `AdapterRegistry.get()` with a key that has
not been registered results in a `MissingAdapterError` being raised.

**Setup:**

- Clear the registry to ensure a clean test environment
- Initialize the registry

**Test Implementation:**

```python
def test_get_raises_missingadaptererror_for_unknown_key():
    """
    Test that AdapterRegistry.get() raises MissingAdapterError for an unknown key.
    """
    # Clear the registry to ensure a clean test environment
    AdapterRegistry._adapters = {}
    AdapterRegistry._adapter_map = {}
    AdapterRegistry._initialized = False
    
    with pytest.raises(MissingAdapterError) as excinfo:
        AdapterRegistry.get("bogus_key")
    
    # Assert that the correct exception type is raised
    assert excinfo.type is MissingAdapterError
    # Assert the content of the exception message
    assert "Adapter for key 'bogus_key' not found" in str(excinfo.value)
```

#### 3.1.2 Test Case: get() - Known Key Returns Adapter

**Purpose:** To ensure that the change to `get()` does not break the existing
functionality of retrieving registered adapters.

**Setup:**

- Clear the registry to ensure a clean test environment
- Register a mock adapter with a known key

**Test Implementation:**

```python
def test_get_returns_registered_adapter():
    """
    Test that AdapterRegistry.get() returns the correct adapter for a known key.
    """
    # Clear the registry to ensure a clean test environment
    AdapterRegistry._adapters = {}
    AdapterRegistry._adapter_map = {}
    AdapterRegistry._initialized = False
    
    # Create a mock adapter
    mock_adapter = Mock(spec=Adapter)
    mock_adapter.obj_key = "test_adapter"
    
    # Register the adapter
    AdapterRegistry.register(mock_adapter)
    
    # Act
    retrieved_adapter = AdapterRegistry.get("test_adapter")
    
    # Assert
    assert retrieved_adapter is mock_adapter
```

#### 3.1.3 Test Case: load_adapter_from_map

**Purpose:** To verify that the `AdapterRegistry` can load adapters from the
pre-computed map.

**Setup:**

- Clear the registry to ensure a clean test environment
- Create a temporary adapter map file
- Mock the package directory to point to the temporary file

**Test Implementation:**

```python
def test_load_adapter_from_map():
    """
    Test that AdapterRegistry can load adapters from the pre-computed map.
    """
    # Clear the registry to ensure a clean test environment
    AdapterRegistry._adapters = {}
    AdapterRegistry._adapter_map = {}
    AdapterRegistry._initialized = False
    
    # Create a temporary adapter map file
    with tempfile.NamedTemporaryFile(mode='w+', suffix='.json', delete=False) as temp_file:
        adapter_map = {
            "test_key": "lionagi.adapters.json_adapter.JsonAdapter"
        }
        json.dump(adapter_map, temp_file)
        temp_file_path = temp_file.name
    
    try:
        # Mock the package directory to point to the temporary file
        package_dir = os.path.dirname(temp_file_path)
        adapter_map_path = temp_file_path
        
        with patch('os.path.dirname', return_value=package_dir), \
             patch('os.path.join', return_value=adapter_map_path):
            
            # Initialize the registry
            AdapterRegistry._initialize()
            
            # Verify the adapter map was loaded
            assert "test_key" in AdapterRegistry._adapter_map
            assert AdapterRegistry._adapter_map["test_key"] == "lionagi.adapters.json_adapter.JsonAdapter"
    finally:
        # Clean up the temporary file
        os.unlink(temp_file_path)
```

#### 3.1.4 Test Case: import_adapter_from_map

**Purpose:** To verify that the `AdapterRegistry` can import adapters from the
pre-computed map.

**Setup:**

- Clear the registry to ensure a clean test environment
- Set up a mock adapter map
- Mock the importlib.import_module and getattr functions

**Test Implementation:**

```python
def test_import_adapter_from_map():
    """
    Test that AdapterRegistry can import adapters from the pre-computed map.
    """
    # Clear the registry to ensure a clean test environment
    AdapterRegistry._adapters = {}
    AdapterRegistry._adapter_map = {
        "json": "lionagi.adapters.json_adapter.JsonAdapter"
    }
    AdapterRegistry._initialized = True
    
    # Mock the importlib.import_module and getattr functions
    mock_adapter = Mock(spec=Adapter)
    mock_adapter.obj_key = "json"
    
    with patch('importlib.import_module') as mock_import, \
         patch('getattr', return_value=mock_adapter):
        
        # Get the adapter
        adapter = AdapterRegistry._import_adapter("json")
        
        # Verify the adapter was imported and registered
        assert adapter is not None
        assert mock_import.called
```

### 3.2 Test Suite: build_adapter_registry

#### 3.2.1 Test Case: is_adapter_class

**Purpose:** To verify that the `is_adapter_class` function correctly identifies
adapter classes.

**Setup:**

- Create a simple AST node for a class with an obj_key attribute

**Test Implementation:**

```python
def test_is_adapter_class():
    """Test the is_adapter_class function with a class that has an obj_key attribute."""
    # Create a simple AST node for a class with an obj_key attribute
    import ast

    class_def = ast.parse(
        """
class TestAdapter:
    obj_key = "test"
    """
    ).body[0]

    assert is_adapter_class(class_def)
```

#### 3.2.2 Test Case: is_adapter_class_negative

**Purpose:** To verify that the `is_adapter_class` function correctly identifies
non-adapter classes.

**Setup:**

- Create a simple AST node for a class without an obj_key attribute

**Test Implementation:**

```python
def test_is_adapter_class_negative():
    """Test the is_adapter_class function with a class that doesn't have an obj_key attribute."""
    # Create a simple AST node for a class without an obj_key attribute
    import ast

    class_def = ast.parse(
        """
class NotAnAdapter:
    some_attr = "test"
    """
    ).body[0]

    assert not is_adapter_class(class_def)
```

#### 3.2.3 Test Case: extract_obj_key

**Purpose:** To verify that the `extract_obj_key` function correctly extracts
the obj_key value.

**Setup:**

- Create a simple AST node for a class with an obj_key attribute

**Test Implementation:**

```python
def test_extract_obj_key():
    """Test the extract_obj_key function."""
    # Create a simple AST node for a class with an obj_key attribute
    import ast

    class_def = ast.parse(
        """
class TestAdapter:
    obj_key = "test"
    """
    ).body[0]

    assert extract_obj_key(class_def) == "test"
```

#### 3.2.4 Test Case: extract_obj_key_negative

**Purpose:** To verify that the `extract_obj_key` function returns None for
classes without an obj_key attribute.

**Setup:**

- Create a simple AST node for a class without an obj_key attribute

**Test Implementation:**

```python
def test_extract_obj_key_negative():
    """Test the extract_obj_key function with a class that doesn't have an obj_key attribute."""
    # Create a simple AST node for a class without an obj_key attribute
    import ast

    class_def = ast.parse(
        """
class NotAnAdapter:
    some_attr = "test"
    """
    ).body[0]

    assert extract_obj_key(class_def) is None
```

#### 3.2.5 Test Case: find_adapter_classes

**Purpose:** To verify that the `find_adapter_classes` function correctly finds
adapter classes in a file.

**Setup:**

- Create a temporary file with adapter and non-adapter classes

**Test Implementation:**

```python
def test_find_adapter_classes():
    """Test the find_adapter_classes function."""
    # Create a temporary file with a class that has an obj_key attribute
    with tempfile.NamedTemporaryFile(mode="w+", suffix=".py", delete=False) as temp_file:
        temp_file.write(
            """
class TestAdapter:
    obj_key = "test"

class AnotherAdapter:
    obj_key = "another"

class NotAnAdapter:
    some_attr = "test"
"""
        )
        temp_file_path = temp_file.name

    try:
        # Find adapter classes in the temporary file
        adapter_classes = find_adapter_classes(temp_file_path)

        # Verify the adapter classes were found
        assert len(adapter_classes) == 2
        assert ("TestAdapter", "test") in adapter_classes
        assert ("AnotherAdapter", "another") in adapter_classes
    finally:
        # Clean up the temporary file
        os.unlink(temp_file_path)
```

#### 3.2.6 Test Case: scan_adapters_directory

**Purpose:** To verify that the `scan_adapters_directory` function correctly
scans a directory for adapter classes.

**Setup:**

- Create a temporary directory with adapter and non-adapter files

**Test Implementation:**

```python
def test_scan_adapters_directory():
    """Test the scan_adapters_directory function."""
    # Create a temporary directory with adapter files
    with tempfile.TemporaryDirectory() as temp_dir:
        # Create a subdirectory
        subdir = os.path.join(temp_dir, "subdir")
        os.makedirs(subdir)

        # Create adapter files
        with open(os.path.join(temp_dir, "adapter1.py"), "w") as f:
            f.write(
                """
class Adapter1:
    obj_key = "adapter1"
"""
            )

        with open(os.path.join(subdir, "adapter2.py"), "w") as f:
            f.write(
                """
class Adapter2:
    obj_key = "adapter2"
"""
            )

        # Create a non-adapter file
        with open(os.path.join(temp_dir, "not_adapter.py"), "w") as f:
            f.write(
                """
class NotAnAdapter:
    some_attr = "test"
"""
            )

        # Create a file that should be ignored
        with open(os.path.join(temp_dir, "__init__.py"), "w") as f:
            f.write("")

        # Scan the temporary directory
        adapter_map = scan_adapters_directory(temp_dir)

        # Verify the adapter map
        assert len(adapter_map) == 2
        assert "adapter1" in adapter_map
        assert "adapter2" in adapter_map
        assert adapter_map["adapter1"].endswith(".Adapter1")
        assert adapter_map["adapter2"].endswith("subdir.adapter2.Adapter2")
```

#### 3.2.7 Test Case: main_function

**Purpose:** To verify that the `main` function correctly creates an adapter map
file.

**Setup:**

- Create a temporary directory with adapter files
- Create a temporary output file
- Mock the command-line arguments

**Test Implementation:**

```python
def test_main_function():
    """Test the main function of the build_adapter_registry script."""
    # Create a temporary directory with adapter files
    with tempfile.TemporaryDirectory() as temp_dir:
        # Create adapter files
        with open(os.path.join(temp_dir, "adapter1.py"), "w") as f:
            f.write(
                """
class Adapter1:
    obj_key = "adapter1"
"""
            )

        # Create a temporary output file
        output_file = os.path.join(temp_dir, "adapter_map.json")

        # Mock the command-line arguments
        with patch("sys.argv", ["build_adapter_registry", "--adapters-dir", temp_dir, "--output", output_file]):
            # Import and run the main function
            from lionagi.scripts.build_adapter_registry import main
            main()

        # Verify the output file was created
        assert os.path.exists(output_file)

        # Verify the content of the output file
        with open(output_file, "r") as f:
            adapter_map = json.load(f)
            assert len(adapter_map) == 1
            assert "adapter1" in adapter_map
            assert adapter_map["adapter1"].endswith(".Adapter1")
```

### 3.3 Test Suite: CLI

#### 3.3.1 Test Case: cli_build_registry

**Purpose:** To verify that the CLI `build-registry` command correctly calls the
`build_adapter_registry.main` function.

**Setup:**

- Create temporary directories for adapters and output
- Mock the command-line arguments
- Mock the `build_adapter_registry.main` function

**Test Implementation:**

```python
def test_cli_build_registry():
    """Test the CLI build-registry command."""
    # Create a temporary directory for the output
    with tempfile.TemporaryDirectory() as temp_dir:
        output_file = os.path.join(temp_dir, "adapter_map.json")
        
        # Create a temporary directory with adapter files
        with tempfile.TemporaryDirectory() as adapters_dir:
            # Create an adapter file
            with open(os.path.join(adapters_dir, "test_adapter.py"), "w") as f:
                f.write(
                    """
class TestAdapter:
    obj_key = "test"
"""
                )
            
            # Mock the command-line arguments
            with patch("sys.argv", ["lionagi", "build-registry", "--adapters-dir", adapters_dir, "--output", output_file]):
                # Mock the build_registry_main function to verify it's called with the correct arguments
                with patch("lionagi.scripts.build_adapter_registry.main") as mock_build_registry:
                    # Run the CLI
                    main()
                    
                    # Verify the build_registry_main function was called
                    assert mock_build_registry.called
```

## 4. Integration Tests

No new integration tests are required for this specific change, as the unit
tests cover the functionality comprehensively.

## 5. API Tests

Not applicable for this issue.

## 6. Error Handling Tests

Error handling is covered by the unit tests for each component.

## 7. Performance Tests

### 7.1 Test Case: cold_start_time

**Purpose:** To verify that the cold start time for importing `lionagi` and
initializing `AdapterRegistry` is less than 300ms.

**Setup:**

- Clean Python environment
- Pre-computed adapter map

**Test Implementation:**

```python
def test_cold_start_time():
    """Test that the cold start time is less than 300ms."""
    # This test should be run in a clean Python environment
    # with the pre-computed adapter map in place
    
    import time
    
    # Measure the time it takes to import lionagi
    start_time = time.time()
    import lionagi
    from lionagi.adapters.adapter import AdapterRegistry
    
    # Initialize the registry
    AdapterRegistry._initialize()
    
    # Calculate the elapsed time
    elapsed_time = time.time() - start_time
    
    # Verify the elapsed time is less than 300ms
    assert elapsed_time < 0.3, f"Cold start time is {elapsed_time:.3f}s, which is greater than 0.3s"
```

## 8. Mock Implementation Details

The tests use the following mocks:

- `unittest.mock.Mock` for creating mock adapter objects
- `unittest.mock.patch` for patching functions and methods

## 9. Test Data

The tests use the following test data:

- Temporary files with adapter and non-adapter classes
- Temporary directories with adapter and non-adapter files
- Mock adapter objects

## 10. Helper Functions

No new helper functions are required for these tests.

## 11. Test Coverage Targets

- **Line Coverage Target:** 90% for the modified files
- **Branch Coverage Target:** 80% for the modified files
- **Critical Modules:** `lionagi/adapters/adapter.py`,
  `lionagi/scripts/build_adapter_registry.py`, `lionagi/scripts/cli.py`

## 12. Continuous Integration

The standard CI workflow (`khive ci`) will be used to run these tests and verify
coverage targets before merging.

## 13. Notes and Caveats

### 13.1 Known Limitations

- The performance test may be environment-dependent and may not be reliable on
  all systems.
- The pre-computed adapter map may become out of sync with the actual adapters
  if new adapters are added without rebuilding the map.

### 13.2 Future Improvements

- Add a mechanism to automatically detect when the adapter map is out of sync
  with the actual adapters.
- Add a warning when the adapter map is not found or is outdated.
- Add a mechanism to automatically rebuild the adapter map when new adapters are
  added.
