Set up Python project with uv environment management and FastAPI stack. Project structure: - src/gondulf/ - Main application package - tests/ - Test suite directory - pyproject.toml - Project configuration with dependencies - README.md - Project documentation - uv.lock - Dependency lock file Dependencies configured: - FastAPI + Uvicorn for web framework - SQLAlchemy for database ORM - pytest + coverage for testing - ruff, black, mypy, flake8 for code quality - Development environment using uv direct execution model All project standards reviewed and implemented per: - /docs/standards/coding.md - /docs/standards/testing.md - /docs/standards/git.md - /docs/standards/development-environment.md - /docs/standards/versioning.md
243 lines
5.9 KiB
Markdown
243 lines
5.9 KiB
Markdown
# Testing Standard
|
|
|
|
## Overview
|
|
|
|
Testing is mandatory for all code. This project maintains high quality through comprehensive automated testing at multiple levels.
|
|
|
|
## Testing Philosophy
|
|
|
|
- Write tests first when possible (TDD approach)
|
|
- Test behavior, not implementation
|
|
- Keep tests simple and focused
|
|
- Fast tests are better tests
|
|
- Clear test names that describe what is being tested
|
|
|
|
## Test Coverage Requirements
|
|
|
|
### Minimum Coverage
|
|
- Overall: 80% code coverage minimum
|
|
- Critical paths (auth, token, security): 95% coverage
|
|
- New code: 90% coverage required
|
|
|
|
### Coverage Exclusions
|
|
Acceptable to exclude from coverage:
|
|
- Logging statements
|
|
- Debug utilities
|
|
- Main entry points (if trivial)
|
|
- Third-party integration boilerplate
|
|
|
|
## Testing Pyramid
|
|
|
|
### Unit Tests (70% of tests)
|
|
- Test individual functions and methods
|
|
- No external dependencies (use mocks)
|
|
- Should run in milliseconds
|
|
- Located in `tests/unit/`
|
|
|
|
### Integration Tests (20% of tests)
|
|
- Test component interactions
|
|
- May use test database
|
|
- Test API endpoints with real HTTP requests
|
|
- Located in `tests/integration/`
|
|
|
|
### End-to-End Tests (10% of tests)
|
|
- Test complete IndieAuth flows
|
|
- Verify compliance with W3C specification
|
|
- Include client registration and authentication flows
|
|
- Located in `tests/e2e/`
|
|
|
|
## Test Structure
|
|
|
|
### Test File Organization
|
|
```
|
|
tests/
|
|
├── unit/
|
|
│ ├── test_auth.py
|
|
│ ├── test_token.py
|
|
│ └── test_client.py
|
|
├── integration/
|
|
│ ├── test_auth_endpoint.py
|
|
│ ├── test_token_endpoint.py
|
|
│ └── test_client_registration.py
|
|
├── e2e/
|
|
│ ├── test_full_auth_flow.py
|
|
│ └── test_client_self_registration.py
|
|
├── fixtures/
|
|
│ └── test_data.py
|
|
└── conftest.py # pytest configuration
|
|
```
|
|
|
|
### Test Naming Convention
|
|
- Test files: `test_*.py`
|
|
- Test classes: `Test*`
|
|
- Test methods: `test_*`
|
|
|
|
Examples:
|
|
```python
|
|
# Good test names
|
|
def test_token_expires_after_configured_duration():
|
|
def test_client_registration_requires_redirect_uri():
|
|
def test_authorization_code_is_single_use():
|
|
|
|
# Bad test names
|
|
def test_auth(): # Too vague
|
|
def test_1(): # Meaningless
|
|
```
|
|
|
|
## Python Testing Tools
|
|
|
|
### Core Testing Framework
|
|
- **pytest**: Primary testing framework
|
|
- Use fixtures for test setup
|
|
- Use parametrize for testing multiple cases
|
|
- Use markers for test categorization
|
|
|
|
### Required Testing Libraries
|
|
```python
|
|
# requirements-test.txt
|
|
pytest>=7.0.0
|
|
pytest-cov>=4.0.0 # Coverage reporting
|
|
pytest-asyncio>=0.20.0 # Async test support
|
|
pytest-mock>=3.10.0 # Mocking utilities
|
|
freezegun>=1.2.0 # Time mocking
|
|
factory-boy>=3.2.0 # Test data factories
|
|
responses>=0.22.0 # HTTP response mocking
|
|
```
|
|
|
|
### Test Fixtures
|
|
```python
|
|
# Example fixture structure
|
|
@pytest.fixture
|
|
def auth_client():
|
|
"""Returns authenticated test client."""
|
|
|
|
@pytest.fixture
|
|
def test_user():
|
|
"""Returns test user with domain."""
|
|
|
|
@pytest.fixture
|
|
def registered_client():
|
|
"""Returns pre-registered OAuth client."""
|
|
```
|
|
|
|
## Writing Tests
|
|
|
|
### Test Structure (AAA Pattern)
|
|
```python
|
|
def test_authorization_code_exchange():
|
|
# Arrange - Set up test data and conditions
|
|
code = generate_auth_code()
|
|
client = create_test_client()
|
|
|
|
# Act - Execute the behavior being tested
|
|
token = exchange_code_for_token(code, client)
|
|
|
|
# Assert - Verify the outcome
|
|
assert token.is_valid()
|
|
assert token.client_id == client.id
|
|
```
|
|
|
|
### Mocking Guidelines
|
|
- Mock external services (HTTP calls, databases in unit tests)
|
|
- Don't mock what you're testing
|
|
- Prefer dependency injection over patching
|
|
|
|
### Testing IndieAuth Compliance
|
|
Special test suite for W3C specification compliance:
|
|
```python
|
|
class TestIndieAuthCompliance:
|
|
"""Tests that verify W3C IndieAuth specification compliance."""
|
|
|
|
def test_authorization_endpoint_supports_response_type_code(self):
|
|
def test_token_endpoint_requires_code_verifier_with_pkce(self):
|
|
def test_client_id_must_be_valid_url(self):
|
|
```
|
|
|
|
## Test Execution
|
|
|
|
### Running Tests
|
|
```bash
|
|
# Run all tests
|
|
pytest
|
|
|
|
# Run with coverage
|
|
pytest --cov=indieauth --cov-report=html
|
|
|
|
# Run specific test level
|
|
pytest tests/unit/
|
|
pytest tests/integration/
|
|
pytest tests/e2e/
|
|
|
|
# Run tests matching pattern
|
|
pytest -k "test_token"
|
|
|
|
# Run with verbose output
|
|
pytest -vv
|
|
```
|
|
|
|
### Continuous Integration
|
|
All tests must pass before merging:
|
|
```yaml
|
|
# Example CI configuration
|
|
test:
|
|
- pytest tests/unit/ --cov=indieauth
|
|
- pytest tests/integration/
|
|
- pytest tests/e2e/
|
|
- coverage report --fail-under=80
|
|
```
|
|
|
|
## Performance Testing
|
|
|
|
### Response Time Requirements
|
|
- Authorization endpoint: < 200ms
|
|
- Token endpoint: < 100ms
|
|
- Client registration: < 500ms
|
|
|
|
### Load Testing
|
|
- Support 100 concurrent authentications
|
|
- Handle 1000 registered clients
|
|
- Token validation: 10,000 requests/minute
|
|
|
|
## Security Testing
|
|
|
|
### Required Security Tests
|
|
- Test for timing attacks in token validation
|
|
- Test rate limiting on all endpoints
|
|
- Test PKCE validation
|
|
- Test redirect URI validation
|
|
- Test for open redirect vulnerabilities
|
|
- Test token entropy and uniqueness
|
|
|
|
## Test Data Management
|
|
|
|
### Test Database
|
|
- Use in-memory SQLite for unit tests
|
|
- Use PostgreSQL/MySQL for integration tests (same as production)
|
|
- Reset database between test runs
|
|
- Use transactions for test isolation
|
|
|
|
### Test Secrets
|
|
- Never use production secrets in tests
|
|
- Generate test keys and tokens dynamically
|
|
- Use consistent test data for reproducibility
|
|
|
|
## Documentation of Tests
|
|
|
|
Each test should be self-documenting:
|
|
```python
|
|
def test_expired_token_is_rejected():
|
|
"""
|
|
Verify that tokens past their expiration time are rejected.
|
|
|
|
This prevents replay attacks and ensures tokens have limited lifetime
|
|
as required by OAuth 2.0 security best practices.
|
|
"""
|
|
```
|
|
|
|
## Test Maintenance
|
|
|
|
- Review and update tests when requirements change
|
|
- Remove obsolete tests
|
|
- Refactor tests that become brittle
|
|
- Keep test execution time under 1 minute for unit tests
|
|
- Keep full test suite under 5 minutes |