chore: initialize gondulf project structure
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
This commit is contained in:
243
docs/standards/testing.md
Normal file
243
docs/standards/testing.md
Normal file
@@ -0,0 +1,243 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user