# Recruitment Application Testing Guide

This guide provides comprehensive information about testing the Recruitment Application (ATS) system.

## Test Structure

The test suite is organized into several modules:

### 1. Basic Tests (`recruitment/tests.py`)
- **BaseTestCase**: Common setup for all tests
- **ModelTests**: Basic model functionality tests
- **ViewTests**: Standard view tests
- **FormTests**: Basic form validation tests
- **IntegrationTests**: Simple integration scenarios

### 2. Advanced Tests (`recruitment/tests_advanced.py`)
- **AdvancedModelTests**: Complex model scenarios and edge cases
- **AdvancedViewTests**: Complex view logic with multiple filters and workflows
- **AdvancedFormTests**: Complex form validation and dynamic fields
- **AdvancedIntegrationTests**: End-to-end workflows and concurrent operations
- **SecurityTests**: Security-focused testing

### 3. Configuration Files
- **`pytest.ini`**: Pytest configuration with coverage settings
- **`conftest.py`**: Pytest fixtures and common test setup

## Running Tests

### Basic Test Execution
```bash
# Run all tests
python manage.py test recruitment

# Run specific test class
python manage.py test recruitment.tests.AdvancedModelTests

# Run with verbose output
python manage.py test recruitment --verbosity=2

# Run tests with coverage
python manage.py test recruitment --coverage
```

### Using Pytest
```bash
# Install pytest and required packages
pip install pytest pytest-django pytest-cov

# Run all tests
pytest

# Run specific test file
pytest recruitment/tests.py

# Run with coverage
pytest --cov=recruitment --cov-report=html

# Run with markers
pytest -m unit          # Run only unit tests
pytest -m integration   # Run only integration tests
pytest -m "not slow"    # Skip slow tests
```

### Test Markers
- `@pytest.mark.unit`: For unit tests
- `@pytest.mark.integration`: For integration tests
- `@pytest.mark.security`: For security tests
- `@pytest.mark.api`: For API tests
- `@pytest.mark.slow`: For performance-intensive tests

## Test Coverage

The test suite aims for 80% code coverage. Coverage reports are generated in:
- HTML: `htmlcov/index.html`
- Terminal: Shows missing lines

### Improving Coverage
1. Add tests for untested branches
2. Test error conditions and edge cases
3. Use mocking for external dependencies

## Key Testing Areas

### 1. Model Testing
- **JobPosting**: ID generation, validation, methods
- **Candidate**: Stage transitions, relationships
- **ZoomMeeting**: Time validation, status handling
- **FormTemplate**: Template integrity, field ordering
- **InterviewSchedule**: Scheduling logic, slot generation

### 2. View Testing
- **Job Management**: CRUD operations, search, filtering
- **Candidate Management**: Stage updates, bulk operations
- **Meeting Management**: Scheduling, API integration
- **Form Handling**: Submission processing, validation

### 3. Form Testing
- **JobPostingForm**: Complex validation, field dependencies
- **CandidateForm**: File upload, validation
- **InterviewScheduleForm**: Dynamic fields, validation
- **MeetingCommentForm**: Comment creation/editing

### 4. Integration Testing
- **Complete Hiring Workflow**: Job → Application → Interview → Hire
- **Data Integrity**: Cross-component data consistency
- **API Integration**: Zoom API, LinkedIn integration
- **Concurrent Operations**: Multi-threading scenarios

### 5. Security Testing
- **Access Control**: Permission validation
- **CSRF Protection**: Form security
- **Input Validation**: SQL injection, XSS prevention
- **Authentication**: User authorization

## Test Fixtures

Common fixtures available in `conftest.py`:

- **User Fixtures**: `user`, `staff_user`, `profile`
- **Model Fixtures**: `job`, `candidate`, `zoom_meeting`, `form_template`
- **Form Data Fixtures**: `job_form_data`, `candidate_form_data`
- **Mock Fixtures**: `mock_zoom_api`, `mock_time_slots`
- **Client Fixtures**: `client`, `authenticated_client`, `authenticated_staff_client`

## Writing New Tests

### Test Naming Convention
- Use descriptive names: `test_user_can_create_job_posting`
- Follow the pattern: `test_[subject]_[action]_[expected_result]`

### Best Practices
1. **Use Fixtures**: Leverage existing fixtures instead of creating test data
2. **Mock External Dependencies**: Use `@patch` for API calls
3. **Test Edge Cases**: Include invalid data, boundary conditions
4. **Maintain Independence**: Each test should be runnable independently
5. **Use Assertions**: Be specific about expected outcomes

### Example Test Structure
```python
from django.test import TestCase
from recruitment.models import JobPosting
from recruitment.tests import BaseTestCase

class JobPostingTests(BaseTestCase):

    def test_job_creation_minimal_data(self):
        """Test job creation with minimal required fields"""
        job = JobPosting.objects.create(
            title='Minimal Job',
            department='IT',
            job_type='FULL_TIME',
            workplace_type='REMOTE',
            created_by=self.user
        )
        self.assertEqual(job.title, 'Minimal Job')
        self.assertIsNotNone(job.slug)

    def test_job_posting_validation_invalid_data(self):
        """Test that invalid data raises validation errors"""
        with self.assertRaises(ValueError):
            JobPosting.objects.create(
                title='',  # Empty title
                department='IT',
                job_type='FULL_TIME',
                workplace_type='REMOTE',
                created_by=self.user
            )
```

## Testing External Integrations

### Zoom API Integration
```python
@patch('recruitment.views.create_zoom_meeting')
def test_meeting_creation(self, mock_zoom):
    """Test Zoom meeting creation with mocked API"""
    mock_zoom.return_value = {
        'status': 'success',
        'meeting_details': {
            'meeting_id': '123456789',
            'join_url': 'https://zoom.us/j/123456789'
        }
    }

    # Test meeting creation logic
    result = create_zoom_meeting('Test Meeting', start_time, duration)
    self.assertEqual(result['status'], 'success')
    mock_zoom.assert_called_once()
```

### LinkedIn Integration
```python
@patch('recruitment.views.LinkedinService')
def test_linkedin_posting(self, mock_linkedin):
    """Test LinkedIn job posting with mocked service"""
    mock_service = mock_linkedin.return_value
    mock_service.create_job_post.return_value = {
        'success': True,
        'post_id': 'linkedin123',
        'post_url': 'https://linkedin.com/jobs/view/linkedin123'
    }

    # Test LinkedIn posting logic
    result = mock_service.create_job_post(job)
    self.assertTrue(result['success'])
```

## Performance Testing

### Running Performance Tests
```bash
# Run slow tests only
pytest -m slow

# Profile test execution
pytest --profile
```

### Performance Considerations
1. Use `TransactionTestCase` for tests that require database commits
2. Mock external API calls to avoid network delays
3. Use `select_related` and `prefetch_related` in queries
4. Test with realistic data volumes

## Continuous Integration

### GitHub Actions Integration
```yaml
name: Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: [3.9, 3.10, 3.11]
    steps:
    - uses: actions/checkout@v2
    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
        pip install pytest pytest-django pytest-cov
    - name: Run tests
      run: |
        pytest --cov=recruitment --cov-report=xml
    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v1
```

## Troubleshooting Common Issues

### Database Issues
```python
# Use TransactionTestCase for tests that modify database structure
from django.test import TransactionTestCase

class MyTests(TransactionTestCase):
    def test_database_modification(self):
        # This test will properly clean up the database
        pass
```

### Mocking Issues
```python
# Correct way to mock imports
from unittest.mock import patch

@patch('recruitment.views.zoom_api.ZoomClient')
def test_zoom_integration(self, mock_zoom_client):
    mock_instance = mock_zoom_client.return_value
    mock_instance.create_meeting.return_value = {'success': True}

    # Test code
```

### HTMX Testing
```python
# Test HTMX responses
def test_htmx_partial_update(self):
    response = self.client.get('/some-url/', HTTP_HX_REQUEST='true')
    self.assertEqual(response.status_code, 200)
    self.assertIn('partial-content', response.content)
```

## Contributing to Tests

### Adding New Tests
1. Place tests in appropriate test modules
2. Use existing fixtures when possible
3. Add descriptive docstrings
4. Mark tests with appropriate markers
5. Ensure new tests maintain coverage requirements

### Test Review Checklist
- [ ] Tests are properly isolated
- [ ] Fixtures are used effectively
- [ ] External dependencies are mocked
- [ ] Edge cases are covered
- [ ] Naming conventions are followed
- [ ] Documentation is clear

## Resources

- [Django Testing Documentation](https://docs.djangoproject.com/en/stable/topics/testing/)
- [Pytest Documentation](https://docs.pytest.org/)
- [Test-Driven Development](https://testdriven.io/blog/tdd-with-django-and-react/)
- [Code Coverage Best Practices](https://pytest-cov.readthedocs.io/)
