docs/TESTING.md

# Testing Guide

ExLLM includes a comprehensive testing system with intelligent caching, semantic tagging, and 24 specialized Mix aliases for targeted test execution.

## Quick Start

```bash
# Run all tests (fast - uses cache when available)
mix test

# Run provider-specific tests
mix test.anthropic
mix test.openai
mix test.gemini

# Run integration tests with live APIs
mix test.integration --include live_api

# Run tests by capability
mix test.streaming
mix test.vision
mix test.oauth2

# Manage test cache
mix ex_llm.cache stats
mix ex_llm.cache clean --older-than 7d
```

## Test Organization

### Test Tags

ExLLM uses semantic tags to organize tests by requirements, capabilities, and providers:

#### **Requirement Tags**
- `:requires_api_key` - Tests needing API keys with automatic provider detection
- `:requires_oauth` - Tests needing OAuth2 authentication (e.g., Gemini APIs)
- `:requires_service` - Tests needing local services (Ollama, LM Studio)
- `:requires_resource` - Tests needing pre-existing resources (tuned models, corpora)

#### **Test Type Tags**
- `:live_api` - Tests that call live provider APIs
- `:integration` - Integration tests with external services
- `:external` - Tests making external network calls
- `:unit` - Unit tests (isolated, no external dependencies)

#### **Provider Tags**
- `:anthropic`, `:openai`, `:gemini`, `:groq`, `:mistral`
- `:openrouter`, `:perplexity`, `:ollama`, `:lmstudio`, `:bumblebee`

#### **Capability Tags**
- `:streaming` - Tests for streaming responses
- `:vision` - Tests for image/vision capabilities  
- `:multimodal` - Tests for multimodal inputs
- `:function_calling` - Tests for tool/function calling
- `:embedding` - Tests for embedding generation

## Mix Test Aliases

ExLLM provides 24 specialized test aliases for targeted execution:

### Provider-Specific Tests

```bash
# Test individual providers
mix test.anthropic       # Anthropic Claude tests
mix test.openai          # OpenAI GPT tests  
mix test.gemini          # Google Gemini tests
mix test.groq            # Groq tests
mix test.mistral         # Mistral AI tests
mix test.openrouter      # OpenRouter tests
mix test.perplexity      # Perplexity tests
mix test.ollama          # Ollama local tests
mix test.lmstudio        # LM Studio tests
mix test.bumblebee       # Bumblebee local tests
```

### Test Type Aliases

```bash
# By test type
mix test.unit            # Unit tests only
mix test.integration     # Integration tests
mix test.external        # Tests with external calls
mix test.oauth2          # OAuth2 authentication tests
```

### Capability-Based Tests

```bash
# By capability
mix test.streaming       # Streaming response tests
mix test.vision          # Vision/image processing tests
mix test.multimodal      # Multimodal input tests
mix test.function_calling # Function/tool calling tests
mix test.embedding       # Embedding generation tests
```

### Environment-Based Tests

```bash
# By environment needs
mix test.live_api        # Tests calling live APIs
mix test.local_only      # Local-only tests (no API calls)
mix test.fast            # Fast tests (cached/mocked)
mix test.all             # All tests including slow ones
```

## Test Caching System

ExLLM includes an advanced caching system that provides 25x speed improvements for integration tests.

### How It Works

1. **Automatic Detection**: Tests tagged with `:live_api` are automatically cached
2. **Smart Exclusions**: Destructive operations (create, delete, modify) are not cached
3. **TTL Management**: Cached responses expire after 7 days by default
4. **Fallback Strategies**: Multiple matching algorithms for cache hits

### Cache Management

```bash
# View cache statistics
mix ex_llm.cache stats

# Clean old cache entries  
mix ex_llm.cache clean --older-than 7d

# Clear all cache
mix ex_llm.cache clear

# Show cache details for a provider
mix ex_llm.cache show anthropic
```

### Configuration

Configure caching via environment variables:

```bash
# Enable/disable caching
export EX_LLM_TEST_CACHE_ENABLED=true

# Cache directory
export EX_LLM_TEST_CACHE_DIR="test/cache"

# TTL for cached responses (in seconds)
export EX_LLM_TEST_CACHE_TTL=604800  # 7 days

# Cache destructive operations
export EX_LLM_TEST_CACHE_DESTRUCTIVE_OPS=false
```

## Writing Tests

### Basic Test Structure

```elixir
defmodule MyProviderTest do
  use ExUnit.Case
  
  # Module-level tags
  @moduletag :integration
  @moduletag :live_api
  @moduletag :requires_api_key
  @moduletag provider: :anthropic
  
  # Import cache helpers
  import ExLLM.TestCacheHelpers
  
  setup_all do
    enable_cache_debug()
    :ok
  end
  
  setup context do
    setup_test_cache(context)
    on_exit(fn -> ExLLM.TestCacheDetector.clear_test_context() end)
    :ok
  end
  
  test "basic chat completion" do
    {:ok, response} = ExLLM.chat(:anthropic, [
      %{role: "user", content: "Hello!"}
    ])
    
    assert response.content != ""
  end
end
```

### Using ExLLM.Case for Automatic Requirements

```elixir
defmodule MyProviderTest do
  use ExLLM.Case, async: true
  
  @moduletag :requires_api_key
  @moduletag provider: :openai
  
  test "test with automatic API key checking", context do
    # Automatically skips if OPENAI_API_KEY not set
    check_test_requirements!(context)
    
    {:ok, response} = ExLLM.chat(:openai, [
      %{role: "user", content: "Test"}
    ])
    
    assert response.content != ""
  end
end
```

### OAuth2 Tests

```elixir
defmodule GeminiOAuth2Test do
  use ExLLM.Case, async: true
  
  @moduletag :requires_oauth
  @moduletag provider: :gemini
  
  test "OAuth2 API call", context do
    check_test_requirements!(context)
    
    # OAuth token automatically provided if available
    oauth_token = get_oauth_token(context)
    
    {:ok, response} = ExLLM.Gemini.Permissions.list_permissions(
      "tunedModels/test",
      oauth_token: oauth_token
    )
    
    assert is_list(response.permissions)
  end
end
```

## Test Exclusions

### Default Exclusions

By default, these tests are excluded unless explicitly included:

```bash
# In test_helper.exs
ExUnit.configure(exclude: [
  :live_api,           # Exclude live API calls by default
  :requires_api_key,   # Exclude tests needing API keys
  :requires_oauth,     # Exclude OAuth tests
  :requires_service,   # Exclude tests needing local services
  :integration,        # Exclude integration tests
  :external           # Exclude external network tests
])
```

### Running Excluded Tests

```bash
# Include specific tags
mix test --include live_api
mix test --include requires_api_key
mix test --include oauth2

# Include multiple tags
mix test --include live_api --include streaming

# Run only specific tags
mix test --only provider:anthropic
mix test --only streaming
```

## CI/CD Integration

### GitHub Actions Example

```yaml
name: Tests
on: [push, pull_request]

jobs:
  unit-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: erlef/setup-beam@v1
        with:
          elixir-version: '1.18'
          otp-version: '28'
      
      # Unit tests only (no API keys needed)
      - run: mix test.unit
  
  integration-tests:
    runs-on: ubuntu-latest
    if: github.event_name == 'push'
    steps:
      - uses: actions/checkout@v3
      - uses: erlef/setup-beam@v1
      
      # Integration tests with caching
      - run: mix test.integration --include live_api
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          EX_LLM_TEST_CACHE_ENABLED: true
```

## Performance Tips

1. **Use Caching**: Enable test caching for 25x faster integration tests
2. **Tag Appropriately**: Use semantic tags for precise test selection
3. **Run Targeted Tests**: Use Mix aliases to run only what you need
4. **Cache Management**: Regularly clean old cache entries
5. **Local Services**: Use Ollama/LM Studio for development without API costs

## Troubleshooting

### Common Issues

**Tests skipped with "API key required":**
```bash
# Set the required API key
export ANTHROPIC_API_KEY="your-key"
mix test.anthropic
```

**OAuth2 tests failing:**
```bash
# Setup OAuth2 first
elixir scripts/setup_oauth2.exs
# Then refresh token
elixir scripts/refresh_oauth2_token.exs
```

**Cache not working:**
```bash
# Check cache configuration
mix ex_llm.cache stats
# Enable debug logging
export EX_LLM_LOG_LEVEL=debug
```

**Tests timing out:**
```bash
# Use cached responses
export EX_LLM_TEST_CACHE_ENABLED=true
mix test --include live_api
```

### Debug Logging

Enable debug logging to troubleshoot test issues:

```bash
export EX_LLM_LOG_LEVEL=debug
export EX_LLM_LOG_COMPONENTS=http_client,cache,test_detector
mix test --include live_api
```

This comprehensive testing system ensures reliable, fast, and well-organized tests across all ExLLM functionality.