- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
651 lines
16 KiB
Markdown
651 lines
16 KiB
Markdown
# LiveComponents Chunked Upload E2E Tests
|
|
|
|
Comprehensive end-to-end testing suite for the LiveComponents chunked upload system.
|
|
|
|
## Overview
|
|
|
|
This test suite validates the complete chunked upload functionality including:
|
|
- Upload session initialization and management
|
|
- Chunk splitting and parallel uploads
|
|
- SHA-256 integrity verification
|
|
- Progress tracking via SSE
|
|
- Resume capability for interrupted uploads
|
|
- Retry logic with exponential backoff
|
|
- Quarantine system integration
|
|
- Component state synchronization
|
|
|
|
## Quick Start
|
|
|
|
### Prerequisites
|
|
|
|
```bash
|
|
# Ensure Playwright is installed
|
|
npm install
|
|
|
|
# Install browser binaries
|
|
npx playwright install chromium
|
|
|
|
# Ensure development server is running
|
|
make up
|
|
```
|
|
|
|
### Running Tests
|
|
|
|
```bash
|
|
# Run all chunked upload tests
|
|
npm run test:upload
|
|
|
|
# Run with visible browser (for debugging)
|
|
npm run test:upload:headed
|
|
|
|
# Run with debug mode
|
|
npm run test:upload:debug
|
|
|
|
# Run specific test
|
|
npx playwright test chunked-upload.spec.js --grep "should upload chunks in parallel"
|
|
```
|
|
|
|
## Test Scenarios
|
|
|
|
### 1. Upload Session Initialization
|
|
|
|
**Tests:** Session creation with UUID format validation
|
|
|
|
**Validates:**
|
|
- Unique session ID generation
|
|
- UUID format (RFC 4122)
|
|
- Session metadata storage
|
|
- Client-server session sync
|
|
|
|
**Expected Behavior:**
|
|
- Session ID: `^[a-f0-9-]{36}$` (UUID v4 format)
|
|
- Session created within 500ms
|
|
- Session accessible via `window.__uploadSession`
|
|
|
|
### 2. Chunk Splitting Verification
|
|
|
|
**Tests:** File split into correct number of chunks based on size
|
|
|
|
**Validates:**
|
|
- Chunk size calculation (default: 512KB)
|
|
- Total chunks = ceil(fileSize / chunkSize)
|
|
- Chunk boundaries alignment
|
|
|
|
**Example:**
|
|
- 2MB file → 4 chunks (2048KB / 512KB = 4)
|
|
- 1MB file → 2 chunks (1024KB / 512KB = 2)
|
|
- 600KB file → 2 chunks (ceil(600KB / 512KB) = 2)
|
|
|
|
### 3. Parallel Chunk Uploads
|
|
|
|
**Tests:** Chunks upload concurrently (default: 3 parallel)
|
|
|
|
**Validates:**
|
|
- Multiple chunks upload simultaneously
|
|
- Concurrent request detection (time window analysis)
|
|
- Upload parallelization efficiency
|
|
|
|
**Metrics:**
|
|
- Expected concurrency: 3 simultaneous uploads
|
|
- Time window: Requests within 100ms considered parallel
|
|
|
|
### 4. Progress Tracking Accuracy
|
|
|
|
**Tests:** Real-time progress updates from 0% to 100%
|
|
|
|
**Validates:**
|
|
- Monotonic progress increase
|
|
- Accurate percentage calculation
|
|
- No progress regression
|
|
- Final state: 100% completion
|
|
|
|
**Progress Formula:**
|
|
```javascript
|
|
progress = (uploadedChunks / totalChunks) * 100
|
|
```
|
|
|
|
### 5. SHA-256 Integrity Verification
|
|
|
|
**Tests:** Hash calculation and verification for each chunk
|
|
|
|
**Validates:**
|
|
- Client-side hash generation (SHA-256)
|
|
- Server-side hash verification
|
|
- Hash format: 64 hexadecimal characters
|
|
- Upload rejection on hash mismatch
|
|
|
|
**Hash Calculation:**
|
|
```javascript
|
|
const hash = await crypto.subtle.digest('SHA-256', chunkData);
|
|
const hashHex = Array.from(new Uint8Array(hash))
|
|
.map(b => b.toString(16).padStart(2, '0'))
|
|
.join('');
|
|
```
|
|
|
|
### 6. Upload Interruption and Resume
|
|
|
|
**Tests:** Resume capability after page reload or connection loss
|
|
|
|
**Validates:**
|
|
- Uploaded chunks persistence
|
|
- Resume from last successful chunk
|
|
- Skip already uploaded chunks
|
|
- Session restoration
|
|
|
|
**Resume Workflow:**
|
|
1. Upload starts, some chunks complete
|
|
2. Interruption occurs (reload/disconnect)
|
|
3. Session restored with uploaded chunks info
|
|
4. Upload resumes from next chunk
|
|
5. Completion without re-uploading existing chunks
|
|
|
|
### 7. Retry Logic with Exponential Backoff
|
|
|
|
**Tests:** Failed chunks retry with increasing delays
|
|
|
|
**Validates:**
|
|
- Automatic retry on failure
|
|
- Exponential backoff strategy
|
|
- Maximum retry attempts (default: 3)
|
|
- Success after retries
|
|
|
|
**Backoff Strategy:**
|
|
```javascript
|
|
delay = baseDelay * Math.pow(2, attemptNumber)
|
|
// Attempt 1: 100ms
|
|
// Attempt 2: 200ms
|
|
// Attempt 3: 400ms
|
|
```
|
|
|
|
### 8. Concurrent Multi-File Uploads
|
|
|
|
**Tests:** Multiple files upload simultaneously with queue management
|
|
|
|
**Validates:**
|
|
- Independent upload sessions per file
|
|
- Progress tracking per file
|
|
- Queue management (max concurrent files)
|
|
- No session conflicts
|
|
|
|
**Expected Behavior:**
|
|
- Each file: unique session ID
|
|
- Independent progress tracking
|
|
- Proper resource cleanup
|
|
|
|
### 9. Real-time SSE Progress Updates
|
|
|
|
**Tests:** Server-Sent Events for live progress updates
|
|
|
|
**Validates:**
|
|
- SSE connection establishment
|
|
- Progress events received
|
|
- Event format and data accuracy
|
|
- Connection persistence
|
|
|
|
**SSE Event Format:**
|
|
```javascript
|
|
{
|
|
type: 'upload-progress',
|
|
sessionId: 'uuid',
|
|
progress: 0-100,
|
|
uploadedChunks: number,
|
|
totalChunks: number,
|
|
bytesUploaded: number,
|
|
totalBytes: number
|
|
}
|
|
```
|
|
|
|
### 10. Upload Cancellation
|
|
|
|
**Tests:** User-initiated upload cancellation
|
|
|
|
**Validates:**
|
|
- Cancel button functionality
|
|
- Pending chunk requests abortion
|
|
- Session cleanup
|
|
- UI state reset
|
|
|
|
**Cancellation Steps:**
|
|
1. Upload in progress
|
|
2. Cancel button clicked
|
|
3. All pending requests aborted
|
|
4. Session marked as cancelled
|
|
5. Temporary chunks deleted
|
|
|
|
### 11. File Size Validation
|
|
|
|
**Tests:** File size limits enforcement
|
|
|
|
**Validates:**
|
|
- Maximum file size check (configurable)
|
|
- Client-side pre-validation
|
|
- Server-side validation
|
|
- Clear error messaging
|
|
|
|
**Default Limits:**
|
|
- Max file size: 100MB (configurable)
|
|
- Validation occurs before session creation
|
|
|
|
### 12. File Type Validation
|
|
|
|
**Tests:** Allowed file types enforcement
|
|
|
|
**Validates:**
|
|
- MIME type checking
|
|
- File extension validation
|
|
- Blocked file types rejection
|
|
- Whitelist/blacklist support
|
|
|
|
**Example Allowed Types:**
|
|
```javascript
|
|
allowedTypes: [
|
|
'image/jpeg', 'image/png', 'image/gif',
|
|
'application/pdf', 'application/zip'
|
|
]
|
|
```
|
|
|
|
### 13. Uploaded File Display
|
|
|
|
**Tests:** File appears in component after successful upload
|
|
|
|
**Validates:**
|
|
- Fragment update with file list
|
|
- File metadata display (name, size, date)
|
|
- DOM update verification
|
|
- Component state consistency
|
|
|
|
**Expected DOM:**
|
|
```html
|
|
<div data-lc-fragment="file-list">
|
|
<div class="file-item" data-file-id="uuid">
|
|
<span class="file-name">example.pdf</span>
|
|
<span class="file-size">2.4 MB</span>
|
|
<span class="file-date">2025-01-19</span>
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
### 14. Quarantine System Integration
|
|
|
|
**Tests:** Uploaded files move to quarantine for virus scanning
|
|
|
|
**Validates:**
|
|
- File moved to quarantine directory
|
|
- Virus scan initiated (ClamAV/VirusTotal)
|
|
- Quarantine status tracking
|
|
- Clean/infected file handling
|
|
|
|
**Quarantine Workflow:**
|
|
1. Upload completes successfully
|
|
2. File moved to quarantine directory
|
|
3. Virus scan job queued
|
|
4. Component shows "Scanning..." status
|
|
5. Result: Clean → Available, Infected → Deleted
|
|
|
|
### 15. Component State Updates
|
|
|
|
**Tests:** Component state synchronizes after upload
|
|
|
|
**Validates:**
|
|
- State updates via LiveComponents protocol
|
|
- Fragment rendering with new data
|
|
- State persistence across interactions
|
|
- No state corruption
|
|
|
|
### 16. Network Interruption Handling
|
|
|
|
**Tests:** Graceful handling of network failures
|
|
|
|
**Validates:**
|
|
- Connection loss detection
|
|
- Automatic reconnection attempts
|
|
- Resume after reconnection
|
|
- User notification of connection issues
|
|
|
|
**Failure Scenarios:**
|
|
- Temporary network loss (WiFi disconnect)
|
|
- Server unavailability (503 responses)
|
|
- Timeout errors (slow connections)
|
|
|
|
### 17. Performance Tests
|
|
|
|
**Tests:** Upload performance benchmarks
|
|
|
|
**Validates:**
|
|
- 10MB file uploads in <30 seconds
|
|
- Memory efficiency for large files (50MB)
|
|
- No memory leaks during uploads
|
|
- CPU usage within acceptable limits
|
|
|
|
**Performance Targets:**
|
|
- **Throughput**: >2MB/s on localhost
|
|
- **Memory**: <100MB for 50MB file upload
|
|
- **CPU**: <50% average during upload
|
|
|
|
## Test Page Requirements
|
|
|
|
Tests assume the following test page structure at `https://localhost/livecomponents/test/upload`:
|
|
|
|
### Required HTML Elements
|
|
|
|
```html
|
|
<div data-component-id="upload:test">
|
|
<!-- File Input -->
|
|
<input type="file" id="file-input" multiple />
|
|
|
|
<!-- Upload Control Buttons -->
|
|
<button id="upload-btn">Upload</button>
|
|
<button id="resume-upload-btn" style="display:none">Resume Upload</button>
|
|
<button id="cancel-upload-btn" style="display:none">Cancel Upload</button>
|
|
|
|
<!-- Progress Display -->
|
|
<div id="upload-progress">
|
|
<div id="progress-bar">
|
|
<div id="progress-fill" style="width: 0%"></div>
|
|
</div>
|
|
<span id="progress-text">0%</span>
|
|
<span id="upload-speed">0 MB/s</span>
|
|
</div>
|
|
|
|
<!-- Status Display -->
|
|
<div id="upload-status">Ready</div>
|
|
|
|
<!-- File List (Fragment) -->
|
|
<div data-lc-fragment="file-list">
|
|
<!-- Uploaded files will be rendered here -->
|
|
</div>
|
|
|
|
<!-- Quarantine Status -->
|
|
<div data-lc-fragment="quarantine-status">
|
|
<!-- Virus scan status will be rendered here -->
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
### Required Component Actions
|
|
|
|
```php
|
|
final readonly class UploadTestComponent extends LiveComponent
|
|
{
|
|
#[Action]
|
|
public function initializeUpload(string $filename, int $fileSize): array
|
|
{
|
|
// Return session ID and chunk configuration
|
|
return [
|
|
'sessionId' => Uuid::generate(),
|
|
'chunkSize' => 512 * 1024, // 512KB
|
|
'totalChunks' => ceil($fileSize / (512 * 1024))
|
|
];
|
|
}
|
|
|
|
#[Action]
|
|
public function uploadChunk(
|
|
string $sessionId,
|
|
int $chunkIndex,
|
|
string $chunkData,
|
|
string $hash
|
|
): array {
|
|
// Verify hash, store chunk, return progress
|
|
return [
|
|
'success' => true,
|
|
'uploadedChunks' => $chunkIndex + 1,
|
|
'progress' => (($chunkIndex + 1) / $totalChunks) * 100
|
|
];
|
|
}
|
|
|
|
#[Action]
|
|
public function completeUpload(string $sessionId): array
|
|
{
|
|
// Assemble file, move to quarantine, trigger scan
|
|
return [
|
|
'success' => true,
|
|
'fileId' => $fileId,
|
|
'quarantineStatus' => 'scanning'
|
|
];
|
|
}
|
|
|
|
#[Action]
|
|
public function cancelUpload(string $sessionId): array
|
|
{
|
|
// Clean up session and temporary chunks
|
|
return ['success' => true];
|
|
}
|
|
}
|
|
```
|
|
|
|
### SSE Endpoint
|
|
|
|
```php
|
|
#[Route(path: '/upload-progress/{sessionId}', method: Method::GET)]
|
|
public function uploadProgress(string $sessionId): SseStream
|
|
{
|
|
return new SseStream(function() use ($sessionId) {
|
|
while ($session = $this->getUploadSession($sessionId)) {
|
|
yield [
|
|
'type' => 'upload-progress',
|
|
'sessionId' => $sessionId,
|
|
'progress' => $session->getProgress(),
|
|
'uploadedChunks' => $session->getUploadedChunks(),
|
|
'totalChunks' => $session->getTotalChunks()
|
|
];
|
|
|
|
sleep(1);
|
|
|
|
if ($session->isComplete()) {
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
```
|
|
|
|
## Configuration
|
|
|
|
### Environment Variables
|
|
|
|
```env
|
|
# Chunked Upload Configuration
|
|
UPLOAD_CHUNK_SIZE=524288 # 512KB in bytes
|
|
UPLOAD_MAX_FILE_SIZE=104857600 # 100MB in bytes
|
|
UPLOAD_PARALLEL_CHUNKS=3 # Concurrent uploads
|
|
UPLOAD_RETRY_ATTEMPTS=3 # Max retries per chunk
|
|
UPLOAD_RETRY_BASE_DELAY=100 # Base delay in ms
|
|
UPLOAD_QUARANTINE_PATH=/var/quarantine
|
|
UPLOAD_VIRUS_SCAN_ENABLED=true
|
|
```
|
|
|
|
### Test Configuration
|
|
|
|
```javascript
|
|
// In chunked-upload.spec.js
|
|
const TEST_CONFIG = {
|
|
baseUrl: 'https://localhost',
|
|
testPagePath: '/livecomponents/test/upload',
|
|
testFilesDir: './tests/tmp/upload-test-files',
|
|
timeouts: {
|
|
uploadSmall: 10000, // 10s for small files
|
|
uploadMedium: 30000, // 30s for medium files
|
|
uploadLarge: 120000 // 120s for large files
|
|
}
|
|
};
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Tests Timing Out
|
|
|
|
**Symptoms:**
|
|
- Upload tests exceed timeout limits
|
|
- Progress stuck at certain percentage
|
|
|
|
**Solutions:**
|
|
```javascript
|
|
// Increase timeout for specific test
|
|
test('large file upload', async ({ page }) => {
|
|
test.setTimeout(120000); // 120 seconds for large files
|
|
// ...
|
|
});
|
|
```
|
|
|
|
### File Generation Issues
|
|
|
|
**Symptoms:**
|
|
- Test files not created
|
|
- Permission errors in tmp directory
|
|
|
|
**Solutions:**
|
|
```bash
|
|
# Ensure tmp directory exists and is writable
|
|
mkdir -p tests/tmp/upload-test-files
|
|
chmod 755 tests/tmp/upload-test-files
|
|
|
|
# Clean up before running tests
|
|
rm -rf tests/tmp/upload-test-files/*
|
|
```
|
|
|
|
### Network Request Monitoring Failures
|
|
|
|
**Symptoms:**
|
|
- Parallel upload detection fails
|
|
- Request interception not working
|
|
|
|
**Solutions:**
|
|
```javascript
|
|
// Set up request monitoring before navigation
|
|
await page.route('**/live-component/**', route => {
|
|
// Your intercept logic
|
|
route.continue();
|
|
});
|
|
|
|
await page.goto('https://localhost/livecomponents/test/upload');
|
|
```
|
|
|
|
### SSE Connection Issues
|
|
|
|
**Symptoms:**
|
|
- Progress updates not received
|
|
- SSE event listener not triggering
|
|
|
|
**Solutions:**
|
|
```javascript
|
|
// Verify SSE connection
|
|
await page.evaluate(() => {
|
|
const eventSource = new EventSource('/upload-progress/session-id');
|
|
eventSource.onmessage = (event) => {
|
|
console.log('SSE Event:', event.data);
|
|
};
|
|
eventSource.onerror = (error) => {
|
|
console.error('SSE Error:', error);
|
|
};
|
|
});
|
|
```
|
|
|
|
### Memory Issues with Large Files
|
|
|
|
**Symptoms:**
|
|
- Browser crashes during large file uploads
|
|
- Out of memory errors
|
|
|
|
**Solutions:**
|
|
```javascript
|
|
// Use smaller test files or increase browser memory
|
|
const browser = await chromium.launch({
|
|
args: ['--max-old-space-size=4096'] // 4GB memory limit
|
|
});
|
|
|
|
// Or reduce test file sizes
|
|
const testFile = await createTestFile('test-medium.bin', 10); // 10MB instead of 50MB
|
|
```
|
|
|
|
## Continuous Integration
|
|
|
|
### GitHub Actions Integration
|
|
|
|
```yaml
|
|
name: Chunked Upload E2E Tests
|
|
|
|
on:
|
|
push:
|
|
branches: [main, develop]
|
|
pull_request:
|
|
branches: [main]
|
|
|
|
jobs:
|
|
upload-tests:
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
|
|
- uses: actions/setup-node@v3
|
|
with:
|
|
node-version: '18'
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- name: Install Playwright
|
|
run: npx playwright install --with-deps chromium
|
|
|
|
- name: Start dev server
|
|
run: make up
|
|
|
|
- name: Run chunked upload tests
|
|
run: npm run test:upload
|
|
|
|
- name: Upload test results
|
|
if: always()
|
|
uses: actions/upload-artifact@v3
|
|
with:
|
|
name: upload-test-results
|
|
path: test-results/
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### 1. Test File Management
|
|
- Always clean up test files after tests
|
|
- Use unique filenames to avoid conflicts
|
|
- Store test files in `tests/tmp/` directory
|
|
- Add `tests/tmp/` to `.gitignore`
|
|
|
|
### 2. Network Simulation
|
|
- Test with realistic network conditions
|
|
- Simulate network failures and recovery
|
|
- Test on slow connections (throttling)
|
|
- Validate behavior during high latency
|
|
|
|
### 3. State Management
|
|
- Verify component state after each action
|
|
- Test state persistence across interactions
|
|
- Ensure proper cleanup on errors
|
|
- Validate state synchronization
|
|
|
|
### 4. Performance Testing
|
|
- Test with various file sizes (1MB, 10MB, 50MB)
|
|
- Monitor memory usage during uploads
|
|
- Measure throughput and latency
|
|
- Validate resource cleanup
|
|
|
|
### 5. Error Handling
|
|
- Test all failure scenarios
|
|
- Verify error messages are user-friendly
|
|
- Ensure proper cleanup on errors
|
|
- Test recovery mechanisms
|
|
|
|
## Resources
|
|
|
|
- [LiveComponents Upload Guide](../../../src/Framework/LiveComponents/docs/UPLOAD-GUIDE.md)
|
|
- [Playwright Testing Documentation](https://playwright.dev/docs/intro)
|
|
- [Web Crypto API - SubtleCrypto](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto)
|
|
- [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events)
|
|
|
|
## Support
|
|
|
|
For issues or questions:
|
|
1. Review this documentation
|
|
2. Check test output for specific errors
|
|
3. Consult LiveComponents upload guide
|
|
4. Review Playwright documentation for browser automation issues
|
|
5. Create GitHub issue with test output and error logs
|