Files
job-forge/docs/testing/qa_procedures.md
l3ocho b646e2f5df Major documentation overhaul: Transform to Python/FastAPI web application
This comprehensive update transforms Job Forge from a generic MVP concept to a
production-ready Python/FastAPI web application prototype with complete documentation,
testing infrastructure, and deployment procedures.

## 🏗️ Architecture Changes
- Updated all documentation to reflect Python/FastAPI + Dash + PostgreSQL stack
- Transformed from MVP concept to deployable web application prototype
- Added comprehensive multi-tenant architecture with Row Level Security (RLS)
- Integrated Claude API and OpenAI API for AI-powered document generation

## 📚 Documentation Overhaul
- **CLAUDE.md**: Complete rewrite as project orchestrator for 4 specialized agents
- **README.md**: New centralized documentation hub with organized navigation
- **API Specification**: Updated with comprehensive FastAPI endpoint documentation
- **Database Design**: Enhanced schema with RLS policies and performance optimization
- **Architecture Guide**: Transformed to web application focus with deployment strategy

## 🏗️ New Documentation Structure
- **docs/development/**: Python/FastAPI coding standards and development guidelines
- **docs/infrastructure/**: Docker setup and server deployment procedures
- **docs/testing/**: Comprehensive QA procedures with pytest integration
- **docs/ai/**: AI prompt templates and examples (preserved from original)

## 🎯 Team Structure Updates
- **.claude/agents/**: 4 new Python/FastAPI specialized agents
  - simplified_technical_lead.md: Architecture and technical guidance
  - fullstack_developer.md: FastAPI backend + Dash frontend implementation
  - simplified_qa.md: pytest testing and quality assurance
  - simplified_devops.md: Docker deployment and server infrastructure

## 🧪 Testing Infrastructure
- **pytest.ini**: Complete pytest configuration with coverage requirements
- **tests/conftest.py**: Comprehensive test fixtures and database setup
- **tests/unit/**: Example unit tests for auth and application services
- **tests/integration/**: API integration test examples
- Support for async testing, AI service mocking, and database testing

## 🧹 Cleanup
- Removed 9 duplicate/outdated documentation files
- Eliminated conflicting technology references (Node.js/TypeScript)
- Consolidated overlapping content into comprehensive guides
- Cleaned up project structure for professional development workflow

## 🚀 Production Ready Features
- Docker containerization for development and production
- Server deployment procedures for prototype hosting
- Security best practices with JWT authentication and RLS
- Performance optimization with database indexing and caching
- Comprehensive testing strategy with quality gates

This update establishes Job Forge as a professional Python/FastAPI web application
prototype ready for development and deployment.

🤖 Generated with Claude Code (https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-02 11:33:32 -04:00

769 lines
20 KiB
Markdown

# QA Procedures - Job Forge
## Overview
This document outlines the Quality Assurance procedures for Job Forge, including testing strategies, quality gates, bug reporting, and release validation processes.
## Testing Strategy
### Test Pyramid for Job Forge
```
/\ E2E Tests (10%)
/ \ - Critical user workflows
/____\ - Cross-browser testing
/ \ Integration Tests (20%)
/ \ - API endpoint testing
\ / - Database RLS validation
\______/ - AI service integration
\ / Unit Tests (70%)
\ / - Business logic
\/ - Authentication
- Data validation
```
### 1. Unit Testing (70% of tests)
#### Test Categories
- **Authentication & Security**: Login, JWT tokens, password hashing
- **Business Logic**: Application CRUD operations, status transitions
- **Data Validation**: Pydantic model validation, input sanitization
- **AI Integration**: Service mocking, error handling, rate limiting
- **Database Operations**: RLS policies, query optimization
#### Running Unit Tests
```bash
# Run all unit tests
pytest tests/unit/ -v
# Run specific test file
pytest tests/unit/test_auth_service.py -v
# Run with coverage
pytest tests/unit/ --cov=app --cov-report=html
# Run tests matching pattern
pytest -k "test_auth" -v
```
#### Unit Test Example
```python
# tests/unit/test_application_service.py
import pytest
from unittest.mock import AsyncMock
@pytest.mark.asyncio
async def test_create_application_with_ai_generation(test_db, test_user, mock_claude_service):
"""Test application creation with AI cover letter generation."""
# Arrange
mock_claude_service.generate_cover_letter.return_value = "Generated cover letter"
app_data = ApplicationCreate(
company_name="AI Corp",
role_title="ML Engineer",
job_description="Python ML position",
status="draft"
)
# Act
with patch('app.services.ai.claude_service.ClaudeService', return_value=mock_claude_service):
application = await create_application(test_db, app_data, test_user.id)
# Assert
assert application.company_name == "AI Corp"
assert application.cover_letter == "Generated cover letter"
mock_claude_service.generate_cover_letter.assert_called_once()
```
### 2. Integration Testing (20% of tests)
#### Test Categories
- **API Integration**: Full request/response testing with authentication
- **Database Integration**: Multi-tenant isolation, RLS policy validation
- **AI Service Integration**: Real API calls with mocking strategies
- **Service Layer Integration**: Complete workflow testing
#### Running Integration Tests
```bash
# Run integration tests
pytest tests/integration/ -v
# Run with test database
pytest tests/integration/ --db-url=postgresql://test:test@localhost:5432/jobforge_test
# Run specific integration test
pytest tests/integration/test_api_auth.py::TestAuthenticationEndpoints::test_complete_registration_flow -v
```
#### Integration Test Example
```python
# tests/integration/test_api_applications.py
@pytest.mark.asyncio
async def test_complete_application_workflow(async_client, test_user_token):
"""Test complete application workflow from creation to update."""
headers = {"Authorization": f"Bearer {test_user_token}"}
# 1. Create application
app_data = {
"company_name": "Integration Test Corp",
"role_title": "Software Engineer",
"job_description": "Full-stack developer position",
"status": "draft"
}
create_response = await async_client.post(
"/api/v1/applications/",
json=app_data,
headers=headers
)
assert create_response.status_code == 201
app_id = create_response.json()["id"]
# 2. Get application
get_response = await async_client.get(
f"/api/v1/applications/{app_id}",
headers=headers
)
assert get_response.status_code == 200
# 3. Update application status
update_response = await async_client.put(
f"/api/v1/applications/{app_id}",
json={"status": "applied"},
headers=headers
)
assert update_response.status_code == 200
assert update_response.json()["status"] == "applied"
```
### 3. End-to-End Testing (10% of tests)
#### Test Categories
- **Critical User Journeys**: Registration → Login → Create Application → Generate Cover Letter
- **Cross-browser Compatibility**: Chrome, Firefox, Safari, Edge
- **Performance Testing**: Response times, concurrent users
- **Error Scenario Testing**: Network failures, service outages
#### E2E Test Tools Setup
```bash
# Install Playwright for E2E testing
pip install playwright
playwright install
# Run E2E tests
pytest tests/e2e/ -v --headed # With browser UI
pytest tests/e2e/ -v # Headless mode
```
#### E2E Test Example
```python
# tests/e2e/test_user_workflows.py
import pytest
from playwright.async_api import async_playwright
@pytest.mark.asyncio
async def test_complete_user_journey():
"""Test complete user journey from registration to application creation."""
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page()
try:
# 1. Navigate to registration
await page.goto("http://localhost:8000/register")
# 2. Fill registration form
await page.fill('[data-testid="email-input"]', 'e2e@test.com')
await page.fill('[data-testid="password-input"]', 'E2EPassword123!')
await page.fill('[data-testid="first-name-input"]', 'E2E')
await page.fill('[data-testid="last-name-input"]', 'User')
# 3. Submit registration
await page.click('[data-testid="register-button"]')
# 4. Verify redirect to dashboard
await page.wait_for_url("**/dashboard")
# 5. Create application
await page.click('[data-testid="new-application-button"]')
await page.fill('[data-testid="company-input"]', 'E2E Test Corp')
await page.fill('[data-testid="role-input"]', 'Test Engineer')
# 6. Submit application
await page.click('[data-testid="save-application-button"]')
# 7. Verify application appears
await page.wait_for_selector('[data-testid="application-card"]')
# 8. Verify application details
company_text = await page.text_content('[data-testid="company-name"]')
assert company_text == "E2E Test Corp"
finally:
await browser.close()
```
## Quality Gates
### 1. Code Quality Gates
#### Pre-commit Hooks
```bash
# Install pre-commit hooks
pip install pre-commit
pre-commit install
# Run hooks manually
pre-commit run --all-files
```
#### .pre-commit-config.yaml
```yaml
repos:
- repo: https://github.com/psf/black
rev: 23.7.0
hooks:
- id: black
language_version: python3.12
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.284
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.5.1
hooks:
- id: mypy
additional_dependencies: [pydantic, sqlalchemy]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
```
#### Quality Metrics Thresholds
```bash
# Code coverage minimum: 80%
pytest --cov=app --cov-fail-under=80
# Complexity maximum: 10
ruff check --select=C901
# Type coverage minimum: 90%
mypy app/ --strict
```
### 2. Functional Quality Gates
#### API Response Time Requirements
- **Authentication endpoints**: < 200ms
- **CRUD operations**: < 500ms
- **AI generation endpoints**: < 30 seconds
- **Dashboard loading**: < 2 seconds
#### Reliability Requirements
- **Uptime**: > 99% during testing
- **Error rate**: < 1% for non-AI operations
- **AI service fallback**: Must handle service failures gracefully
### 3. Security Quality Gates
#### Security Testing Checklist
```yaml
authentication_security:
- [ ] JWT tokens expire correctly
- [ ] Password hashing is secure (bcrypt)
- [ ] Session management is stateless
- [ ] Rate limiting prevents brute force
authorization_security:
- [ ] RLS policies enforce user isolation
- [ ] API endpoints require proper authentication
- [ ] Users cannot access other users' data
- [ ] Admin endpoints are properly protected
input_validation:
- [ ] All API inputs are validated
- [ ] SQL injection prevention works
- [ ] XSS prevention is implemented
- [ ] File upload validation is secure
data_protection:
- [ ] Sensitive data is encrypted
- [ ] API keys are properly secured
- [ ] Environment variables contain no secrets
- [ ] Database connections are secure
```
## Bug Reporting and Management
### 1. Bug Classification
#### Severity Levels
- **Critical**: Application crashes, data loss, security vulnerabilities
- **High**: Major features not working, authentication failures
- **Medium**: Minor features broken, UI issues, performance problems
- **Low**: Cosmetic issues, minor improvements, documentation errors
#### Priority Levels
- **P0**: Fix immediately (< 2 hours)
- **P1**: Fix within 24 hours
- **P2**: Fix within 1 week
- **P3**: Fix in next release cycle
### 2. Bug Report Template
#### GitHub Issue Template
```markdown
## Bug Report
### Summary
Brief description of the bug
### Environment
- **OS**: macOS 14.0 / Windows 11 / Ubuntu 22.04
- **Browser**: Chrome 118.0 / Firefox 119.0 / Safari 17.0
- **Python Version**: 3.12.0
- **FastAPI Version**: 0.104.1
### Steps to Reproduce
1. Go to '...'
2. Click on '...'
3. Enter data '...'
4. See error
### Expected Behavior
What should happen
### Actual Behavior
What actually happens
### Screenshots/Logs
```
Error logs or screenshots
```
### Additional Context
Any other context about the problem
### Severity/Priority
- [ ] Critical
- [ ] High
- [ ] Medium
- [ ] Low
```
### 3. Bug Triage Process
#### Weekly Bug Triage Meeting
1. **Review new bugs**: Assign severity and priority
2. **Update existing bugs**: Check progress and blockers
3. **Close resolved bugs**: Verify fixes and close tickets
4. **Plan bug fixes**: Assign to sprints based on priority
#### Bug Assignment Criteria
- **Critical/P0**: Technical Lead + DevOps
- **High/P1**: Full-stack Developer
- **Medium/P2**: QA Engineer + Developer collaboration
- **Low/P3**: Next available developer
## Test Data Management
### 1. Test Data Strategy
#### Test Database Setup
```bash
# Create test database
createdb jobforge_test
# Run test migrations
DATABASE_URL=postgresql://test:test@localhost/jobforge_test alembic upgrade head
# Seed test data
python scripts/seed_test_data.py
```
#### Test Data Factory
```python
# tests/factories.py
import factory
from app.models.user import User
from app.models.application import Application
class UserFactory(factory.Factory):
class Meta:
model = User
email = factory.Sequence(lambda n: f"user{n}@test.com")
password_hash = "$2b$12$hash"
first_name = "Test"
last_name = factory.Sequence(lambda n: f"User{n}")
class ApplicationFactory(factory.Factory):
class Meta:
model = Application
company_name = factory.Faker('company')
role_title = factory.Faker('job')
job_description = factory.Faker('text', max_nb_chars=500)
status = "draft"
user = factory.SubFactory(UserFactory)
```
### 2. Test Environment Management
#### Environment Isolation
```yaml
# docker-compose.test.yml
version: '3.8'
services:
test-db:
image: pgvector/pgvector:pg16
environment:
- POSTGRES_DB=jobforge_test
- POSTGRES_USER=test
- POSTGRES_PASSWORD=test
ports:
- "5433:5432"
tmpfs:
- /var/lib/postgresql/data # In-memory for speed
test-app:
build: .
environment:
- DATABASE_URL=postgresql://test:test@test-db:5432/jobforge_test
- TESTING=true
depends_on:
- test-db
```
#### Test Data Cleanup
```python
# tests/conftest.py
@pytest.fixture(autouse=True)
async def cleanup_test_data(test_db):
"""Clean up test data after each test."""
yield
# Truncate all tables
await test_db.execute("TRUNCATE TABLE applications CASCADE")
await test_db.execute("TRUNCATE TABLE users CASCADE")
await test_db.commit()
```
## Performance Testing
### 1. Load Testing with Locust
#### Installation and Setup
```bash
# Install locust
pip install locust
# Run load tests
locust -f tests/performance/locustfile.py --host=http://localhost:8000
```
#### Load Test Example
```python
# tests/performance/locustfile.py
from locust import HttpUser, task, between
import json
class JobForgeUser(HttpUser):
wait_time = between(1, 3)
def on_start(self):
"""Login user on start."""
response = self.client.post("/api/auth/login", data={
"username": "test@example.com",
"password": "testpass123"
})
if response.status_code == 200:
self.token = response.json()["access_token"]
self.headers = {"Authorization": f"Bearer {self.token}"}
@task(3)
def get_applications(self):
"""Get user applications."""
self.client.get("/api/v1/applications/", headers=self.headers)
@task(1)
def create_application(self):
"""Create new application."""
app_data = {
"company_name": "Load Test Corp",
"role_title": "Test Engineer",
"job_description": "Performance testing position",
"status": "draft"
}
self.client.post(
"/api/v1/applications/",
json=app_data,
headers=self.headers
)
@task(1)
def generate_cover_letter(self):
"""Generate AI cover letter (expensive operation)."""
# Get first application
response = self.client.get("/api/v1/applications/", headers=self.headers)
if response.status_code == 200:
applications = response.json()
if applications:
app_id = applications[0]["id"]
self.client.post(
f"/api/v1/applications/{app_id}/generate-cover-letter",
headers=self.headers
)
```
### 2. Performance Benchmarks
#### Response Time Targets
```python
# tests/performance/test_benchmarks.py
import pytest
import time
import statistics
@pytest.mark.performance
@pytest.mark.asyncio
async def test_api_response_times(async_client, test_user_token):
"""Test API response time benchmarks."""
headers = {"Authorization": f"Bearer {test_user_token}"}
# Test multiple requests
response_times = []
for _ in range(50):
start_time = time.time()
response = await async_client.get("/api/v1/applications/", headers=headers)
assert response.status_code == 200
response_time = (time.time() - start_time) * 1000 # Convert to ms
response_times.append(response_time)
# Analyze results
avg_time = statistics.mean(response_times)
p95_time = statistics.quantiles(response_times, n=20)[18] # 95th percentile
# Assert performance requirements
assert avg_time < 200, f"Average response time {avg_time}ms exceeds 200ms limit"
assert p95_time < 500, f"95th percentile {p95_time}ms exceeds 500ms limit"
print(f"Average response time: {avg_time:.2f}ms")
print(f"95th percentile: {p95_time:.2f}ms")
```
## Release Testing Procedures
### 1. Pre-Release Testing Checklist
#### Functional Testing
```yaml
authentication_testing:
- [ ] User registration works
- [ ] User login/logout works
- [ ] JWT token validation works
- [ ] Password reset works (if implemented)
application_management:
- [ ] Create application works
- [ ] View applications works
- [ ] Update application works
- [ ] Delete application works
- [ ] Application status transitions work
ai_integration:
- [ ] Cover letter generation works
- [ ] AI service error handling works
- [ ] Rate limiting is enforced
- [ ] Fallback mechanisms work
data_security:
- [ ] User data isolation works
- [ ] RLS policies are enforced
- [ ] No data leakage between users
- [ ] Sensitive data is protected
```
#### Cross-Browser Testing
```yaml
browsers_to_test:
chrome:
- [ ] Latest version
- [ ] Previous major version
firefox:
- [ ] Latest version
- [ ] ESR version
safari:
- [ ] Latest version (macOS/iOS)
edge:
- [ ] Latest version
mobile_testing:
- [ ] iOS Safari
- [ ] Android Chrome
- [ ] Responsive design works
- [ ] Touch interactions work
```
### 2. Release Validation Process
#### Staging Environment Testing
```bash
# Deploy to staging
docker-compose -f docker-compose.staging.yml up -d
# Run full test suite against staging
pytest tests/ --base-url=https://staging.jobforge.com
# Run smoke tests
pytest tests/smoke/ -v
# Performance testing
locust -f tests/performance/locustfile.py --host=https://staging.jobforge.com --users=50 --spawn-rate=5 --run-time=5m
```
#### Production Deployment Checklist
```yaml
pre_deployment:
- [ ] All tests passing in CI/CD
- [ ] Code review completed
- [ ] Database migrations tested
- [ ] Environment variables updated
- [ ] SSL certificates valid
- [ ] Backup created
deployment:
- [ ] Deploy with zero downtime
- [ ] Health checks passing
- [ ] Database migrations applied
- [ ] Cache cleared if needed
- [ ] CDN updated if needed
post_deployment:
- [ ] Smoke tests passing
- [ ] Performance metrics normal
- [ ] Error rates acceptable
- [ ] User workflows tested
- [ ] Rollback plan ready
```
## Continuous Testing Integration
### 1. CI/CD Pipeline Testing
#### GitHub Actions Workflow
```yaml
# .github/workflows/test.yml
name: Test Suite
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: pgvector/pgvector:pg16
env:
POSTGRES_PASSWORD: test
POSTGRES_DB: jobforge_test
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.12'
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Run linting
run: |
black --check .
ruff check .
mypy app/
- name: Run tests
run: |
pytest tests/unit/ tests/integration/ --cov=app --cov-report=xml
env:
DATABASE_URL: postgresql://postgres:test@localhost:5432/jobforge_test
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
```
### 2. Quality Metrics Dashboard
#### Test Results Tracking
```python
# scripts/generate_test_report.py
import json
import subprocess
from datetime import datetime
def generate_test_report():
"""Generate comprehensive test report."""
# Run tests with JSON output
result = subprocess.run([
'pytest', 'tests/', '--json-report', '--json-report-file=test_report.json'
], capture_output=True, text=True)
# Load test results
with open('test_report.json') as f:
test_data = json.load(f)
# Generate summary
summary = {
'timestamp': datetime.now().isoformat(),
'total_tests': test_data['summary']['total'],
'passed': test_data['summary']['passed'],
'failed': test_data['summary']['failed'],
'skipped': test_data['summary']['skipped'],
'duration': test_data['duration'],
'pass_rate': test_data['summary']['passed'] / test_data['summary']['total'] * 100
}
print(f"Test Summary: {summary['passed']}/{summary['total']} passed ({summary['pass_rate']:.1f}%)")
return summary
if __name__ == "__main__":
generate_test_report()
```
This comprehensive QA procedure ensures that Job Forge maintains high quality through systematic testing, monitoring, and validation processes.