# Testing Documentation

## Document Purpose

This document describes the testing strategy, guidelines, and practices for the Aniworld project.

### What This Document Contains

-   **Testing Strategy**: Overall approach to quality assurance
-   **Test Categories**: Unit, integration, API, performance, security tests
-   **Test Structure**: Organization of test files and directories
-   **Writing Tests**: Guidelines for writing effective tests
-   **Fixtures and Mocking**: Shared test utilities and mock patterns
-   **Running Tests**: Commands and configurations
-   **Coverage Requirements**: Minimum coverage thresholds
-   **CI/CD Integration**: How tests run in automation
-   **Test Data Management**: Managing test fixtures and data
-   **Best Practices**: Do's and don'ts for testing

### What This Document Does NOT Contain

-   Production deployment (see [DEPLOYMENT.md](DEPLOYMENT.md))
-   Security audit procedures (see [SECURITY.md](SECURITY.md))
-   Bug tracking and issue management
-   Performance benchmarking results

### Target Audience

-   Developers writing tests
-   QA Engineers
-   CI/CD Engineers
-   Code reviewers

---

## Sections to Document

1. Testing Philosophy
    - Test pyramid approach
    - Quality gates
2. Test Categories
    - Unit Tests (`tests/unit/`)
    - Integration Tests (`tests/integration/`)
    - API Tests (`tests/api/`)
    - Frontend Tests (`tests/frontend/`)
    - Performance Tests (`tests/performance/`)
    - Security Tests (`tests/security/`)
3. Test Structure and Naming
    - File naming conventions
    - Test function naming
    - Test class organization
4. Running Tests
    - pytest commands
    - Running specific tests
    - Verbose output
    - Coverage reports
5. Fixtures and Conftest
    - Shared fixtures
    - Database fixtures
    - Mock services
6. Mocking Guidelines
    - What to mock
    - Mock patterns
    - External service mocks

### Mocking the Download Queue

Use `MockQueueRepository` for testing download queue functionality:

```python
from src.server.models.download import DownloadItem, EpisodeIdentifier

class MockQueueRepository:
    def __init__(self):
        self._items: Dict[str, DownloadItem] = {}

    async def save_item(self, item: DownloadItem) -> DownloadItem:
        self._items[item.id] = item
        return item

    async def get_item(self, item_id: str) -> Optional[DownloadItem]:
        return self._items.get(item_id)

    async def get_all_items(self) -> List[DownloadItem]:
        return list(self._items.values())

    async def set_error(self, item_id: str, error: str) -> bool:
        if item_id in self._items:
            self._items[item_id].error = error
            return True
        return False

    async def delete_item(self, item_id: str) -> bool:
        if item_id in self._items:
            del self._items[item_id]
            return True
        return False

    async def clear_all(self) -> int:
        count = len(self._items)
        self._items.clear()
        return count
```

**Key points:**
- The mock uses in-memory storage, no database required
- All async methods are implemented (even if just pass-through)
- `save_item` uses `item.id` as key (must be set before calling)
- Suitable for unit tests only (no persistence)

### Mocking aiohttp Sessions

When testing code that uses `aiohttp.ClientSession`:

```python
from unittest.mock import AsyncMock, MagicMock, patch
from aiohttp import ClientSession

# Mock aiohttp session for testing
class MockAiohttpSession:
    def __init__(self):
        self.closed = False
    
    async def close(self):
        self.closed = True
    
    def get(self, url, **kwargs):
        mock_response = AsyncMock()
        mock_response.status = 200
        mock_response.json = AsyncMock(return_value={"data": "test"})
        mock_response.__aenter__ = AsyncMock(return_value=mock_response)
        mock_response.__aexit__ = AsyncMock(return_value=None)
        return mock_response

# Use in fixture
@pytest.fixture
async def mock_tmdb_session():
    session = MockAiohttpSession()
    yield session
    # Cleanup verification
    assert session.closed, "Session was not closed"
```

**Key points:**
- Always verify `session.closed` is `True` after context manager exits
- Mock `__aenter__` and `__aexit__` for response context managers
- Set `closed = False` on mock session for unclosed warning tests

7. Coverage Requirements
8. CI/CD Integration
9. Writing Good Tests
    - Arrange-Act-Assert pattern
    - Test isolation
    - Edge cases
10. Common Pitfalls to Avoid
