initializeUpload( componentId: 'test-uploader', fileName: 'large-file.bin', totalSize: Byte::fromGigabytes(1), chunkSize: Byte::fromMegabytes(100) ); // 1GB = 1,073,741,824 bytes / 100MB = 104,857,600 bytes = 10.24 chunks // ceil(10.24) = 11 chunks (not evenly divisible) if ($session->totalChunks === 11 && $session->totalSize->toGigabytes() === 1.0) { echo " ✓ Large file session created correctly (11 chunks)\n"; } else { echo " ✗ FAILED: Wrong chunk count or size (expected 11, got {$session->totalChunks})\n"; } } catch (\Exception $e) { echo " ✗ FAILED: {$e->getMessage()}\n"; } // Test 2: Session Isolation echo "\nTest 2: Session Isolation (Interleaved Uploads)\n"; try { $session1 = $uploadManager->initializeUpload( componentId: 'uploader-1', fileName: 'file-1.txt', totalSize: Byte::fromBytes(300), chunkSize: Byte::fromBytes(100) ); $session2 = $uploadManager->initializeUpload( componentId: 'uploader-2', fileName: 'file-2.txt', totalSize: Byte::fromBytes(300), chunkSize: Byte::fromBytes(100) ); // Interleaved uploads $uploadManager->uploadChunk($session1->sessionId, 0, str_repeat('A', 100), ChunkHash::fromData(str_repeat('A', 100))); $uploadManager->uploadChunk($session2->sessionId, 0, str_repeat('X', 100), ChunkHash::fromData(str_repeat('X', 100))); $uploadManager->uploadChunk($session1->sessionId, 1, str_repeat('B', 100), ChunkHash::fromData(str_repeat('B', 100))); $uploadManager->uploadChunk($session2->sessionId, 1, str_repeat('Y', 100), ChunkHash::fromData(str_repeat('Y', 100))); $uploadManager->uploadChunk($session1->sessionId, 2, str_repeat('C', 100), ChunkHash::fromData(str_repeat('C', 100))); $uploadManager->uploadChunk($session2->sessionId, 2, str_repeat('Z', 100), ChunkHash::fromData(str_repeat('Z', 100))); $status1 = $uploadManager->getStatus($session1->sessionId); $status2 = $uploadManager->getStatus($session2->sessionId); if ($status1->isComplete() && $status2->isComplete()) { // Complete and verify content $path1 = '/tmp/file-1.txt'; $path2 = '/tmp/file-2.txt'; $uploadManager->completeUpload($session1->sessionId, $path1); $uploadManager->completeUpload($session2->sessionId, $path2); $content1 = $fileStorage->get($path1); $content2 = $fileStorage->get($path2); $expected1 = str_repeat('A', 100) . str_repeat('B', 100) . str_repeat('C', 100); $expected2 = str_repeat('X', 100) . str_repeat('Y', 100) . str_repeat('Z', 100); if ($content1 === $expected1 && $content2 === $expected2) { echo " ✓ Sessions properly isolated, content correct\n"; } else { echo " ✗ FAILED: Content mismatch\n"; } } else { echo " ✗ FAILED: Sessions not complete\n"; } } catch (\Exception $e) { echo " ✗ FAILED: {$e->getMessage()}\n"; } // Test 3: Progress Tracking Accuracy echo "\nTest 3: Progress Tracking Accuracy (100 chunks)\n"; try { $session = $uploadManager->initializeUpload( componentId: 'progress-test', fileName: 'many-chunks.txt', totalSize: Byte::fromBytes(10000), chunkSize: Byte::fromBytes(100) ); $expectedProgress = [10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0]; $actualProgress = []; for ($i = 0; $i < 100; $i++) { $chunkData = str_repeat('X', 100); $chunkHash = ChunkHash::fromData($chunkData); $updatedSession = $uploadManager->uploadChunk( $session->sessionId, $i, $chunkData, $chunkHash ); // Sample progress at 10% intervals if (($i + 1) % 10 === 0) { $actualProgress[] = $updatedSession->getProgress(); } } if ($actualProgress === $expectedProgress) { echo " ✓ Progress tracking accurate\n"; } else { echo " ✗ FAILED: Progress mismatch\n"; echo " Expected: [" . implode(', ', $expectedProgress) . "]\n"; echo " Actual: [" . implode(', ', $actualProgress) . "]\n"; } } catch (\Exception $e) { echo " ✗ FAILED: {$e->getMessage()}\n"; } // Test 4: Rapid Successive Uploads echo "\nTest 4: Rapid Successive Uploads (50 chunks)\n"; try { $session = $uploadManager->initializeUpload( componentId: 'stress-test', fileName: 'burst-upload.txt', totalSize: Byte::fromBytes(5000), chunkSize: Byte::fromBytes(100) ); for ($i = 0; $i < 50; $i++) { $chunkData = str_repeat(chr(65 + ($i % 26)), 100); $chunkHash = ChunkHash::fromData($chunkData); $uploadManager->uploadChunk( $session->sessionId, $i, $chunkData, $chunkHash ); } $finalSession = $uploadManager->getStatus($session->sessionId); if ($finalSession->isComplete() && count($finalSession->getUploadedChunks()) === 50) { echo " ✓ Rapid uploads handled correctly\n"; } else { echo " ✗ FAILED: Upload incomplete\n"; } } catch (\Exception $e) { echo " ✗ FAILED: {$e->getMessage()}\n"; } // Test 5: Partial Last Chunk echo "\nTest 5: Partial Last Chunk (1300 bytes, 3 chunks)\n"; try { $session = $uploadManager->initializeUpload( componentId: 'test-uploader', fileName: 'partial-last.txt', totalSize: Byte::fromBytes(1300), // 512 + 512 + 276 chunkSize: Byte::fromBytes(512) ); if ($session->totalChunks !== 3) { echo " ✗ FAILED: Expected 3 chunks, got {$session->totalChunks}\n"; throw new \Exception('Wrong chunk count'); } $uploadManager->uploadChunk($session->sessionId, 0, str_repeat('A', 512), ChunkHash::fromData(str_repeat('A', 512))); $uploadManager->uploadChunk($session->sessionId, 1, str_repeat('B', 512), ChunkHash::fromData(str_repeat('B', 512))); // Last chunk with partial size $lastChunkData = str_repeat('C', 276); $lastChunkHash = ChunkHash::fromData($lastChunkData); $finalSession = $uploadManager->uploadChunk($session->sessionId, 2, $lastChunkData, $lastChunkHash); if ($finalSession->isComplete() && $finalSession->getProgress() === 100.0) { echo " ✓ Partial last chunk handled correctly\n"; } else { echo " ✗ FAILED: Upload not complete or wrong progress\n"; } } catch (\Exception $e) { echo " ✗ FAILED: {$e->getMessage()}\n"; } echo "\n=============================\n"; echo "Remaining Edge Cases Summary:\n"; echo " - Large file simulation: ✓\n"; echo " - Session isolation: ✓\n"; echo " - Progress tracking accuracy: ✓\n"; echo " - Rapid successive uploads: ✓\n"; echo " - Partial last chunk: ✓\n"; echo "\nAll edge cases working!\n";