# Unit Tests for dynabots-core

Comprehensive test suite for the dynabots-core package with 99 tests covering all protocols, value objects, and providers.

## Test Coverage

### Test Files Created

1. **`tests/__init__.py`** - Test package initialization
2. **`tests/conftest.py`** - Pytest configuration and shared fixtures
3. **`tests/test_task_result.py`** - 47 tests for TaskResult value object
4. **`tests/test_protocols.py`** - 31 tests for protocols and value objects
5. **`tests/test_providers.py`** - 21 tests for LLM providers (mocked, no network)

**Total: 99 tests, all passing**

## Test Organization

### test_task_result.py (47 tests)

Tests for `TaskResult` value object and `TaskOutcome` enum:

#### TaskOutcome Enum
- Enum value verification
- Value-based enum creation

#### TaskResult Factories
- `success()` - Basic and with metadata
- `failure()` - Basic and with metadata
- `no_action_needed()` - Basic and with metadata
- `skipped()` - Basic and with metadata
- `partial()` - Basic and with metadata

#### TaskResult Properties
- `is_success`, `is_failure`, `is_skipped`, `is_no_action_needed`
- `is_actionable` for all outcome types
- Should continue flag behavior

#### Serialization/Deserialization
- `to_dict()` for all outcome types
- `from_dict()` roundtrip for all outcomes
- Timestamp handling (ISO format, UTC timezone)
- Metadata preservation

#### Context Extraction
- `get_context_for_downstream()` basic functionality
- Metadata spreading to context
- Context for failure results

#### Immutability
- Frozen dataclass prevents mutation
- All field types tested (str, enum, dict, bool, optional)

#### Edge Cases
- Complex nested data structures
- None data values
- Empty error messages
- Very large duration values
- Zero duration
- Default metadata initialization
- UTC timezone verification

#### Integration Tests
- Workflow with conditional execution
- Error recovery with retries
- Batch processing with partial success

### test_protocols.py (31 tests)

Tests for protocols and value objects:

#### Agent Protocol
- Runtime checkable protocol verification
- Basic agent implementation
- Agent with optional domains property
- LegacyAgent protocol with `execute_capability`

#### LLMMessage & LLMResponse
- LLMMessage creation with all fields
- LLMMessage with tool calls
- LLMResponse creation and properties
- LLMResponse with token usage
- LLMResponse with tool calls
- ToolDefinition structure
- LLMProvider protocol implementation

#### Tool Protocol & Formatters
- Tool protocol implementation
- `tool_to_openai_format()` conversion
- `tool_to_anthropic_format()` conversion
- Format differences between OpenAI and Anthropic

#### Verdict & Submission
- Verdict creation with scores
- `is_tie` property (empty, lowercase "tie", non-tie cases)
- `to_dict()` serialization
- Submission creation with metrics
- Submission with latency and cost
- Submission with TaskResult conversion

#### Judge Protocol
- Judge protocol implementation
- ScoringJudge protocol with score method

#### Storage Protocols
- ExecutionStore (save, get, list workflows)
- AuditStore (log workflow, task, error)
- CacheStore (get, set, delete)
- ReputationStore (get, update, leaderboard)

#### Protocol Combinations
- Agent with judge integration
- LLM with tools integration

### test_providers.py (21 tests)

Tests for LLM provider implementations (all mocked, no network calls):

#### OpenAI Provider (5 tests)
- Provider instantiation
- Basic completion call
- JSON mode request
- Tool calling with function definitions
- Temperature and max_tokens parameters

#### Anthropic Provider (7 tests)
- Provider instantiation
- Basic completion call
- System message extraction (single and multiple)
- JSON mode instruction appending
- Tool use block handling
- Mixed content (text + tool use)

#### Ollama Provider (7 tests)
- Provider instantiation
- Basic completion call
- JSON mode format flag
- Tool definitions handling
- Temperature and max_tokens
- Custom host configuration
- Custom options merging

#### Common Behavior (2 tests)
- All providers return LLMResponse objects
- Missing usage information handling

## Running the Tests

### All Tests
```bash
pytest packages/core/tests/ -v
```

### Specific Test File
```bash
pytest packages/core/tests/test_task_result.py -v
pytest packages/core/tests/test_protocols.py -v
pytest packages/core/tests/test_providers.py -v
```

### Specific Test Class
```bash
pytest packages/core/tests/test_task_result.py::TestTaskResultFactory -v
```

### Specific Test
```bash
pytest packages/core/tests/test_task_result.py::TestTaskResultFactory::test_success_factory -v
```

### With Coverage
```bash
pytest packages/core/tests/ --cov=dynabots_core --cov-report=html
```

## Test Design Principles

### No Network Calls
- All tests are self-contained and offline
- Providers use mocked clients (AsyncMock)
- No real API keys or credentials needed

### Comprehensive Coverage
- Factory methods and properties tested
- Serialization/deserialization roundtrips verified
- Edge cases and error conditions covered
- Integration scenarios demonstrated

### Protocol Testing
- `isinstance()` checks verify `@runtime_checkable` protocols work
- Multiple implementations per protocol tested
- Protocol composition scenarios covered

### Mock Strategy
- AsyncMock for async providers
- MagicMock for complex response objects
- unittest.mock.patch for import mocking (e.g., ollama.AsyncClient)

### Async Testing
- pytest-asyncio with asyncio_mode=auto
- All async tests use `@pytest.mark.asyncio`
- Natural async/await syntax

## Test Fixtures

### conftest.py Provides
- `sample_metadata` fixture with common test metadata

### Test-Specific Fixtures
- `mock_openai_client` - AsyncMock OpenAI client
- `mock_anthropic_client` - AsyncMock Anthropic client
- `mock_ollama_client` - AsyncMock Ollama client

## Key Testing Insights

### TaskResult Immutability
TaskResult is a frozen dataclass, so all tests verify that fields cannot be mutated after creation. This is critical for its use as a value object.

### Protocol Runtime Checking
Protocols decorated with `@runtime_checkable` allow `isinstance()` checks at runtime. Tests verify this works for Agent, LLMProvider, Tool, Judge, and storage protocols.

### Provider System Message Handling
Anthropic separates system messages from conversation messages, while OpenAI and Ollama handle them differently. Tests verify each provider's specific behavior.

### Tool Format Differences
OpenAI uses `{"type": "function", "function": {...}}` format while Anthropic uses `{"name": ..., "input_schema": ...}` format. Tests verify both formatters and show the differences.

## Coverage Summary

- **TaskResult**: All factory methods, properties, serialization, context extraction, immutability, edge cases
- **TaskOutcome**: Enum values and creation
- **Protocols**: Agent, LLMProvider, LLMMessage, LLMResponse, Tool, Judge, Verdict, Submission, Storage protocols
- **Providers**: OpenAI, Anthropic, Ollama (mocked)
- **Tool Formatters**: OpenAI and Anthropic formats
- **Integration**: Multi-protocol scenarios, async operations

## Notes for Developers

### Running Tests During Development
```bash
# Watch mode (requires pytest-watch)
ptw packages/core/tests/

# Run with print output
pytest packages/core/tests/ -v -s

# Run with detailed tracebacks
pytest packages/core/tests/ -v --tb=long
```

### Adding New Tests
1. Follow existing test class organization
2. Use descriptive test names starting with `test_`
3. Group related tests in TestClasses
4. Document expected behavior in docstrings
5. Verify new tests pass and don't break existing tests

### Test Maintenance
- Tests are self-documenting through clear names and docstrings
- Use fixtures for repeated setup
- Mock external dependencies completely
- Keep tests focused and independent
