# Testing Guide
This document describes how to test the rek2_httpserver project using Rust's built-in testing framework.
## Test Structure
The project includes comprehensive testing with proper separation of concerns:
### Unit Tests (`src/lib.rs`)
Located in the library module with organized test modules:
- **`filename_extraction`** - Tests filename extraction from various sources
- **`content_disposition_parsing`** - Tests HTTP header parsing logic
- **`edge_cases`** - Tests edge cases like unicode, special characters, long names
### Integration Tests (`tests/upload_tests.rs`)
Full end-to-end testing of the upload functionality:
- File upload with different methods
- Directory structure preservation
- Binary file integrity
- Content-length verification
- Error handling scenarios
## Running Tests
### Basic Commands
```bash
# Run all tests (unit + integration)
cargo test
# Run only unit tests
cargo test --lib
# Run only integration tests
cargo test --test upload_tests
# Run with verbose output
cargo test -- --nocapture
# Run tests in parallel (default) or single-threaded
cargo test -- --test-threads=1
```
### Specific Test Categories
```bash
# Run filename extraction tests
cargo test filename_extraction
# Run content disposition parsing tests
cargo test content_disposition_parsing
# Run edge case tests
cargo test edge_cases
# Run a specific test
cargo test test_upload_with_url_filename
```
### Test Coverage
```bash
# Generate test coverage report (requires cargo-tarpaulin)
cargo install cargo-tarpaulin
cargo tarpaulin --out Html
# Or with llvm-cov (requires nightly)
cargo +nightly test --coverage
```
## Test Details
### Unit Tests (17 tests)
#### Filename Extraction Tests
- `test_url_path_priority` - URL path takes precedence
- `test_url_decoding` - Percent-encoded URLs are decoded
- `test_x_filename_header` - X-Filename header extraction
- `test_content_disposition` - Content-Disposition header parsing
- `test_priority_order` - Verifies URL > X-Filename > Content-Disposition priority
- `test_fallback_generation` - Auto-generated filename when none provided
#### Content-Disposition Parsing Tests
- `test_simple_filename` - Basic filename extraction
- `test_quoted_filename` - Filenames with quotes
- `test_rfc5987_encoded` - RFC 5987 encoded filenames
- `test_no_filename` - Headers without filename
- `test_malformed` - Malformed headers
- `test_empty_filename` - Empty filename values
#### Edge Case Tests
- `test_unicode_filename` - Unicode characters in filenames
- `test_special_characters` - Special characters (@#$%^&)
- `test_very_long_filename` - Very long filenames (300+ chars)
- `test_windows_path` - Windows-style paths
- `test_path_traversal` - Path traversal attempts
### Integration Tests (10 tests)
#### Upload Functionality Tests
- `test_basic_upload` - Basic file upload without filename
- `test_upload_with_url_filename` - Upload with filename in URL path
- `test_upload_with_x_filename_header` - Upload with X-Filename header
- `test_upload_with_content_disposition` - Upload with Content-Disposition
- `test_upload_preserves_directory_structure` - Directory structure preservation
- `test_content_length_verification` - Content-Length header validation
- `test_empty_file_upload` - Zero-byte file uploads
- `test_binary_file_integrity` - Binary data integrity
- `test_url_encoded_filename` - URL-encoded filenames
- `test_filename_priority` - Priority order verification
## Writing New Tests
### Unit Test Example
```rust
#[test]
fn test_custom_functionality() {
let result = extract_filename("test.txt", None, None);
assert_eq!(result, "test.txt");
}
```
### Integration Test Example
```rust
#[test]
fn test_custom_upload() {
let temp_dir = TempDir::new().unwrap();
let output_dir = temp_dir.path().to_str().unwrap();
let body = Bytes::from("test data");
let result = handle_upload_internal(
"custom.txt",
None,
None,
None,
None,
body,
output_dir,
);
assert_eq!(result.status, StatusCode::OK);
assert!(result.message.contains("custom.txt"));
}
```
## Test Guidelines
### Best Practices
1. **Isolation** - Each test uses temporary directories
2. **Deterministic** - Tests don't depend on external state
3. **Descriptive Names** - Test names clearly describe what's being tested
4. **Assertions** - Use specific assertions for better error messages
5. **Edge Cases** - Include tests for boundary conditions
### Test Data
- Use `tempfile::TempDir` for temporary directories
- Use `bytes::Bytes` for binary test data
- Test with various file sizes (0 bytes to large files)
- Include special characters and unicode in test filenames
### Performance Considerations
- Tests run in parallel by default
- Use `-- --test-threads=1` if tests interfere with each other
- Keep test data small for fast execution
- Use `#[ignore]` for slow tests that should be run separately
## Continuous Integration
For CI environments, add this to your workflow:
```yaml
- name: Run tests
run: cargo test --all-features
- name: Run tests with coverage
run: cargo tarpaulin --out Xml
```
## Debugging Tests
### Common Issues
1. **File permissions** - Ensure test has write access to temp directories
2. **Path separators** - Use `std::path::Path` for cross-platform compatibility
3. **Timing issues** - Avoid depending on exact timestamps in tests
### Debug Output
```bash
# Show test output
cargo test -- --nocapture
# Show which tests are running
cargo test -- --show-output
# Run specific test with debug info
RUST_LOG=debug cargo test test_name -- --nocapture
```
## Test Maintenance
- Run tests before every commit
- Update tests when changing functionality
- Add tests for new features
- Remove or update obsolete tests
- Keep test documentation current
## Performance Benchmarks
For performance testing, consider adding benchmarks:
```bash
# Install criterion for benchmarks
cargo install cargo-criterion
# Run benchmarks
cargo criterion
```
This ensures the upload server maintains good performance characteristics under various load conditions.