Added documentation for API, architecture, configuration, database, development guide, testing, and navigation. Includes helper scripts, diagrams, and guides for NFO files and migration.
147 lines
4.3 KiB
Markdown
147 lines
4.3 KiB
Markdown
# 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] = {}
|
|
```
|
|
|
|
### Testing SetupService
|
|
|
|
SetupService handles series key resolution from folder names during library setup. Test file: `tests/unit/test_setup_service.py`.
|
|
|
|
Key methods tested:
|
|
- `_extract_year_from_folder_name()` — parses `(YYYY)` suffix
|
|
- `_extract_title_from_folder_name()` — strips year suffix
|
|
- `_resolve_key_via_search()` — resolves provider key via fuzzy title matching
|
|
|
|
```python
|
|
@pytest.mark.asyncio
|
|
async def test_returns_key_when_single_exact_match(self):
|
|
"""Search returns 1 result with same name → returns key."""
|
|
mock_series_app = AsyncMock()
|
|
mock_series_app.search.return_value = [
|
|
{'title': 'Attack on Titan', 'link': '/anime/stream/attack-on-titan'}
|
|
]
|
|
|
|
with patch('src.server.services.setup_service.get_series_app', return_value=mock_series_app):
|
|
result = await SetupService._resolve_key_via_search("Attack on Titan")
|
|
|
|
assert result == 'attack-on-titan'
|
|
```
|
|
|
|
### 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
|