Add comprehensive project documentation and Docker infrastructure for Paper Dynasty Real-Time Game Engine - a web-based multiplayer baseball simulation platform replacing the legacy Google Sheets system. Documentation Added: - Complete PRD (Product Requirements Document) - Project README with dual development workflows - Implementation guide with 5-phase roadmap - Architecture docs (backend, frontend, database, WebSocket) - CLAUDE.md context files for each major directory Infrastructure Added: - Root docker-compose.yml for full stack orchestration - Dockerfiles for backend and both frontends (multi-stage builds) - .dockerignore files for optimal build context - .env.example with all required configuration - Updated .gitignore for Python, Node, Nuxt, and Docker Project Structure: - backend/ - FastAPI + Socket.io game engine (Python 3.11+) - frontend-sba/ - SBA League Nuxt 3 frontend - frontend-pd/ - PD League Nuxt 3 frontend - .claude/implementation/ - Detailed implementation guides Supports two development workflows: 1. Local dev (recommended): Services run natively with hot-reload 2. Full Docker: One-command stack orchestration for testing/demos Next: Phase 1 implementation (backend/frontend foundations)
322 lines
8.0 KiB
Markdown
322 lines
8.0 KiB
Markdown
# Testing Strategy
|
|
|
|
**Status**: Placeholder
|
|
**Cross-Cutting Concern**: All Phases, Critical in Phase 5
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
Comprehensive testing approach covering unit, integration, E2E, load, and security testing to ensure production-ready quality.
|
|
|
|
## Testing Pyramid
|
|
|
|
```
|
|
/\
|
|
/ \ E2E Tests (10%)
|
|
/ \ - Full game flows
|
|
/------\ - Multi-user scenarios
|
|
/ \
|
|
/ Integ. \ Integration Tests (30%)
|
|
/ Tests \ - WebSocket flows
|
|
/ \ - Database operations
|
|
/----------------\
|
|
/ \ Unit Tests (60%)
|
|
/ Unit Tests \- Core logic
|
|
/______________________\- Pure functions
|
|
```
|
|
|
|
## Test Coverage Goals
|
|
|
|
- **Overall Coverage**: >80%
|
|
- **Core Game Logic**: >90%
|
|
- **WebSocket Handlers**: >85%
|
|
- **Database Operations**: >75%
|
|
- **Frontend Components**: >70%
|
|
|
|
## Testing Tools
|
|
|
|
### Backend
|
|
- **pytest**: Unit and integration tests
|
|
- **pytest-asyncio**: Async test support
|
|
- **pytest-cov**: Coverage reporting
|
|
- **httpx**: API client testing
|
|
- **python-socketio[client]**: WebSocket testing
|
|
- **factory_boy**: Test data generation
|
|
- **Faker**: Fake data generation
|
|
|
|
### Frontend
|
|
- **Vitest**: Unit testing (fast, Vite-native)
|
|
- **Testing Library**: Component testing
|
|
- **Cypress**: E2E testing
|
|
- **Playwright**: Alternative E2E testing
|
|
- **Lighthouse CI**: Performance testing
|
|
- **axe-core**: Accessibility testing
|
|
|
|
### Load Testing
|
|
- **Locust**: Distributed load testing
|
|
- **Artillery**: Alternative load testing
|
|
|
|
### Security Testing
|
|
- **Safety**: Dependency vulnerability scanning
|
|
- **Bandit**: Python security linting
|
|
- **OWASP ZAP**: Security scanning
|
|
- **npm audit**: Frontend dependency scanning
|
|
|
|
## Test Organization
|
|
|
|
### Backend Structure
|
|
```
|
|
backend/tests/
|
|
├── unit/
|
|
│ ├── test_game_engine.py
|
|
│ ├── test_state_manager.py
|
|
│ ├── test_play_resolver.py
|
|
│ ├── test_dice.py
|
|
│ └── test_validators.py
|
|
├── integration/
|
|
│ ├── test_websocket.py
|
|
│ ├── test_database.py
|
|
│ ├── test_api_client.py
|
|
│ └── test_full_turn.py
|
|
├── e2e/
|
|
│ ├── test_full_game.py
|
|
│ ├── test_reconnection.py
|
|
│ └── test_state_recovery.py
|
|
├── load/
|
|
│ └── locustfile.py
|
|
├── conftest.py # Shared fixtures
|
|
└── factories.py # Test data factories
|
|
```
|
|
|
|
### Frontend Structure
|
|
```
|
|
frontend-{league}/
|
|
├── tests/
|
|
│ ├── unit/
|
|
│ │ ├── composables/
|
|
│ │ └── utils/
|
|
│ └── components/
|
|
│ ├── Game/
|
|
│ ├── Decisions/
|
|
│ └── Display/
|
|
└── cypress/
|
|
├── e2e/
|
|
│ ├── game-flow.cy.ts
|
|
│ ├── auth.cy.ts
|
|
│ └── spectator.cy.ts
|
|
└── support/
|
|
```
|
|
|
|
## Key Test Scenarios
|
|
|
|
### Unit Tests
|
|
- Dice roll distribution (statistical validation)
|
|
- Play outcome resolution (all d20 results)
|
|
- State transitions
|
|
- Input validation
|
|
- Player model instantiation
|
|
|
|
### Integration Tests
|
|
- WebSocket event flow (connect → join → action → update)
|
|
- Database persistence and recovery
|
|
- API client responses
|
|
- Multi-turn game sequences
|
|
|
|
### E2E Tests
|
|
- Complete 9-inning game
|
|
- Game with substitutions
|
|
- AI opponent game
|
|
- Spectator joining active game
|
|
- Reconnection after disconnect
|
|
- Multiple concurrent games
|
|
|
|
### Load Tests
|
|
- 10 concurrent games
|
|
- 50 concurrent WebSocket connections
|
|
- Sustained load over 30 minutes
|
|
- Spike testing (sudden load increase)
|
|
|
|
### Security Tests
|
|
- SQL injection attempts
|
|
- XSS attempts
|
|
- CSRF protection
|
|
- Authorization bypass attempts
|
|
- Rate limit enforcement
|
|
|
|
## Example Test Cases
|
|
|
|
### Unit Test (Backend)
|
|
```python
|
|
# tests/unit/test_dice.py
|
|
import pytest
|
|
from app.core.dice import DiceRoller
|
|
|
|
def test_dice_roll_range():
|
|
"""Test that dice rolls are within valid range"""
|
|
roller = DiceRoller()
|
|
for _ in range(1000):
|
|
roll = roller.roll_d20()
|
|
assert 1 <= roll <= 20
|
|
|
|
def test_dice_distribution():
|
|
"""Test that dice rolls are reasonably distributed"""
|
|
roller = DiceRoller()
|
|
rolls = [roller.roll_d20() for _ in range(10000)]
|
|
|
|
# Each number should appear roughly 500 times (±10%)
|
|
for num in range(1, 21):
|
|
count = rolls.count(num)
|
|
assert 450 <= count <= 550
|
|
```
|
|
|
|
### Integration Test (Backend)
|
|
```python
|
|
# tests/integration/test_websocket.py
|
|
import pytest
|
|
import socketio
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_game_action_flow(sio_client, test_game):
|
|
"""Test complete action flow through WebSocket"""
|
|
|
|
# Connect
|
|
await sio_client.connect('http://localhost:8000', auth={'token': 'valid-jwt'})
|
|
|
|
# Join game
|
|
await sio_client.emit('join_game', {'game_id': test_game.id, 'role': 'player'})
|
|
|
|
# Wait for game state
|
|
response = await sio_client.receive()
|
|
assert response[0] == 'game_state_update'
|
|
|
|
# Send defensive decision
|
|
await sio_client.emit('set_defense', {
|
|
'game_id': test_game.id,
|
|
'positioning': 'standard'
|
|
})
|
|
|
|
# Verify decision recorded
|
|
response = await sio_client.receive()
|
|
assert response[0] == 'decision_recorded'
|
|
```
|
|
|
|
### Component Test (Frontend)
|
|
```typescript
|
|
// tests/components/Game/GameBoard.spec.ts
|
|
import { mount } from '@vue/test-utils'
|
|
import { describe, it, expect } from 'vitest'
|
|
import GameBoard from '@/components/Game/GameBoard.vue'
|
|
|
|
describe('GameBoard', () => {
|
|
it('displays runners on base correctly', () => {
|
|
const runners = {
|
|
first: null,
|
|
second: 12345,
|
|
third: null
|
|
}
|
|
|
|
const wrapper = mount(GameBoard, {
|
|
props: { runners }
|
|
})
|
|
|
|
expect(wrapper.find('[data-base="second"]').exists()).toBe(true)
|
|
expect(wrapper.find('[data-base="first"]').exists()).toBe(false)
|
|
})
|
|
})
|
|
```
|
|
|
|
### E2E Test (Frontend)
|
|
```typescript
|
|
// cypress/e2e/game-flow.cy.ts
|
|
describe('Complete Game Flow', () => {
|
|
it('can play through an at-bat', () => {
|
|
cy.login() // Custom command
|
|
cy.visit('/games/create')
|
|
|
|
cy.get('[data-test="create-game"]').click()
|
|
cy.get('[data-test="game-mode-live"]').click()
|
|
cy.get('[data-test="start-game"]').click()
|
|
|
|
// Wait for game to start
|
|
cy.get('[data-test="game-board"]').should('be.visible')
|
|
|
|
// Make defensive decision
|
|
cy.get('[data-test="defense-standard"]').click()
|
|
cy.get('[data-test="confirm-decision"]').click()
|
|
|
|
// Verify decision recorded
|
|
cy.get('[data-test="waiting-indicator"]').should('be.visible')
|
|
})
|
|
})
|
|
```
|
|
|
|
## Continuous Integration
|
|
|
|
### GitHub Actions Example
|
|
```yaml
|
|
name: Test Suite
|
|
|
|
on: [push, pull_request]
|
|
|
|
jobs:
|
|
backend-tests:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
- uses: actions/setup-python@v4
|
|
with:
|
|
python-version: '3.11'
|
|
- run: pip install -r backend/requirements-dev.txt
|
|
- run: pytest backend/tests/ --cov --cov-report=xml
|
|
- uses: codecov/codecov-action@v3
|
|
|
|
frontend-tests:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
- uses: actions/setup-node@v3
|
|
with:
|
|
node-version: '18'
|
|
- run: npm ci
|
|
- run: npm run test:unit
|
|
- run: npm run test:e2e
|
|
```
|
|
|
|
## Test Data Management
|
|
|
|
### Fixtures
|
|
- **Shared fixtures** in `conftest.py`
|
|
- **Factory pattern** for creating test objects
|
|
- **Database seeding** for integration tests
|
|
- **Mock data** for external APIs
|
|
|
|
### Test Isolation
|
|
- Each test should be independent
|
|
- Database rollback after each test
|
|
- Clean in-memory state between tests
|
|
- No shared mutable state
|
|
|
|
## Performance Testing
|
|
|
|
### Metrics to Track
|
|
- Response time (p50, p95, p99)
|
|
- Throughput (requests/second)
|
|
- Error rate
|
|
- CPU and memory usage
|
|
- Database query time
|
|
|
|
### Load Test Scenarios
|
|
1. **Normal Load**: 5 concurrent games
|
|
2. **Peak Load**: 10 concurrent games
|
|
3. **Stress Test**: 20 concurrent games (breaking point)
|
|
4. **Spike Test**: 2 → 10 games instantly
|
|
|
|
## Reference Documents
|
|
|
|
- [PRD Lines 1063-1101](../prd-web-scorecard-1.1.md) - Testing strategy
|
|
- [05-testing-launch.md](./05-testing-launch.md) - Phase 5 testing details
|
|
|
|
---
|
|
|
|
**Note**: This is a placeholder to be expanded with specific test implementations during development. |