Files
michaelschiemer/tests/Unit/Framework/LiveComponents/Services/ChunkAssemblerTest.php
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- 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.
2025-10-25 19:18:37 +02:00

201 lines
6.7 KiB
PHP

<?php
declare(strict_types=1);
use App\Framework\Core\ValueObjects\Byte;
use App\Framework\Filesystem\FileStorage;
use App\Framework\LiveComponents\Services\ChunkAssembler;
describe('ChunkAssembler Service', function () {
beforeEach(function () {
// Create temporary directory for test files
$this->testDir = sys_get_temp_dir() . '/chunk_assembler_test_' . uniqid();
mkdir($this->testDir);
$this->fileStorage = new FileStorage();
$this->assembler = new ChunkAssembler($this->fileStorage);
});
afterEach(function () {
// Clean up test directory
if (is_dir($this->testDir)) {
$files = glob($this->testDir . '/*');
foreach ($files as $file) {
if (is_file($file)) {
unlink($file);
}
}
rmdir($this->testDir);
}
});
it('assembles multiple chunks into single file', function () {
// Create 3 test chunks
$chunk1Path = $this->testDir . '/chunk_0';
$chunk2Path = $this->testDir . '/chunk_1';
$chunk3Path = $this->testDir . '/chunk_2';
file_put_contents($chunk1Path, 'chunk 1 data');
file_put_contents($chunk2Path, 'chunk 2 data');
file_put_contents($chunk3Path, 'chunk 3 data');
$targetPath = $this->testDir . '/assembled.txt';
$this->assembler->assemble(
[$chunk1Path, $chunk2Path, $chunk3Path],
$targetPath
);
expect(file_exists($targetPath))->toBeTrue();
expect(file_get_contents($targetPath))->toBe('chunk 1 datachunk 2 datachunk 3 data');
});
it('assembles single chunk', function () {
$chunkPath = $this->testDir . '/chunk_0';
file_put_contents($chunkPath, 'single chunk data');
$targetPath = $this->testDir . '/assembled.txt';
$this->assembler->assemble([$chunkPath], $targetPath);
expect(file_exists($targetPath))->toBeTrue();
expect(file_get_contents($targetPath))->toBe('single chunk data');
});
it('throws exception for empty chunk array', function () {
$targetPath = $this->testDir . '/assembled.txt';
expect(fn() => $this->assembler->assemble([], $targetPath))
->toThrow(InvalidArgumentException::class);
});
it('throws exception for missing chunk file', function () {
$existingChunk = $this->testDir . '/chunk_0';
$missingChunk = $this->testDir . '/chunk_1_missing';
file_put_contents($existingChunk, 'chunk data');
$targetPath = $this->testDir . '/assembled.txt';
expect(fn() => $this->assembler->assemble(
[$existingChunk, $missingChunk],
$targetPath
))->toThrow(InvalidArgumentException::class);
});
it('assembles large chunks efficiently', function () {
// Create 3 chunks of 1MB each
$chunk1Path = $this->testDir . '/chunk_0';
$chunk2Path = $this->testDir . '/chunk_1';
$chunk3Path = $this->testDir . '/chunk_2';
file_put_contents($chunk1Path, str_repeat('A', 1024 * 1024));
file_put_contents($chunk2Path, str_repeat('B', 1024 * 1024));
file_put_contents($chunk3Path, str_repeat('C', 1024 * 1024));
$targetPath = $this->testDir . '/assembled_large.bin';
$this->assembler->assemble(
[$chunk1Path, $chunk2Path, $chunk3Path],
$targetPath
);
expect(file_exists($targetPath))->toBeTrue();
expect(filesize($targetPath))->toBe(3 * 1024 * 1024);
});
it('calculates total size correctly', function () {
$chunk1Path = $this->testDir . '/chunk_0';
$chunk2Path = $this->testDir . '/chunk_1';
file_put_contents($chunk1Path, str_repeat('A', 1024)); // 1KB
file_put_contents($chunk2Path, str_repeat('B', 2048)); // 2KB
$totalSize = $this->assembler->calculateTotalSize([$chunk1Path, $chunk2Path]);
expect($totalSize->toBytes())->toBe(3072); // 3KB
expect($totalSize->toKilobytes())->toBe(3.0);
});
it('handles empty chunk files', function () {
$chunk1Path = $this->testDir . '/chunk_0';
$chunk2Path = $this->testDir . '/chunk_1';
file_put_contents($chunk1Path, 'data');
file_put_contents($chunk2Path, ''); // Empty chunk
$targetPath = $this->testDir . '/assembled.txt';
$this->assembler->assemble([$chunk1Path, $chunk2Path], $targetPath);
expect(file_exists($targetPath))->toBeTrue();
expect(file_get_contents($targetPath))->toBe('data');
});
it('preserves chunk order during assembly', function () {
$paths = [];
for ($i = 0; $i < 5; $i++) {
$path = $this->testDir . "/chunk_$i";
file_put_contents($path, "chunk$i");
$paths[] = $path;
}
$targetPath = $this->testDir . '/assembled.txt';
$this->assembler->assemble($paths, $targetPath);
expect(file_get_contents($targetPath))->toBe('chunk0chunk1chunk2chunk3chunk4');
});
it('assembles binary data correctly', function () {
$chunk1Path = $this->testDir . '/chunk_0';
$chunk2Path = $this->testDir . '/chunk_1';
$binaryData1 = random_bytes(256);
$binaryData2 = random_bytes(256);
file_put_contents($chunk1Path, $binaryData1);
file_put_contents($chunk2Path, $binaryData2);
$targetPath = $this->testDir . '/assembled.bin';
$this->assembler->assemble([$chunk1Path, $chunk2Path], $targetPath);
$assembled = file_get_contents($targetPath);
expect($assembled)->toBe($binaryData1 . $binaryData2);
});
it('returns zero size for no chunks', function () {
$totalSize = $this->assembler->calculateTotalSize([]);
expect($totalSize->toBytes())->toBe(0);
expect($totalSize->isEmpty())->toBeTrue();
});
it('skips missing chunks in size calculation', function () {
$chunk1Path = $this->testDir . '/chunk_0';
$missingPath = $this->testDir . '/chunk_missing';
file_put_contents($chunk1Path, str_repeat('A', 1024));
$totalSize = $this->assembler->calculateTotalSize([$chunk1Path, $missingPath]);
expect($totalSize->toBytes())->toBe(1024); // Only existing chunk counted
});
it('uses custom buffer size when provided', function () {
$customBufferSize = Byte::fromKilobytes(16);
$assembler = new ChunkAssembler($this->fileStorage, $customBufferSize);
$chunkPath = $this->testDir . '/chunk_0';
file_put_contents($chunkPath, str_repeat('A', 32 * 1024)); // 32KB
$targetPath = $this->testDir . '/assembled.txt';
$assembler->assemble([$chunkPath], $targetPath);
expect(file_exists($targetPath))->toBeTrue();
expect(filesize($targetPath))->toBe(32 * 1024);
});
});