|
| 1 | +<?php |
| 2 | + |
| 3 | +declare(strict_types=1); |
| 4 | + |
| 5 | +namespace Internal\DLoad\Tests\Integration\Module\Archive; |
| 6 | + |
| 7 | +use Internal\DLoad\Module\Archive\ArchiveFactory; |
| 8 | +use PHPUnit\Framework\Attributes\DataProvider; |
| 9 | +use PHPUnit\Framework\Attributes\Group; |
| 10 | +use PHPUnit\Framework\TestCase; |
| 11 | + |
| 12 | +/** |
| 13 | + * Integration tests for Archive module |
| 14 | + * |
| 15 | + * These tests verify that the Archive module components work together correctly. |
| 16 | + * They require the phar extension to be enabled and temporary files to be created. |
| 17 | + */ |
| 18 | +#[Group('integration')] |
| 19 | +final class ArchiveIntegrationTest extends TestCase |
| 20 | +{ |
| 21 | + private string $tempDir; |
| 22 | + private ArchiveFactory $factory; |
| 23 | + |
| 24 | + public static function provideArchiveTypes(): \Generator |
| 25 | + { |
| 26 | + yield 'zip' => ['zip', 'Internal\DLoad\Module\Archive\Internal\ZipPharArchive']; |
| 27 | + yield 'tar.gz' => ['tar.gz', 'Internal\DLoad\Module\Archive\Internal\TarPharArchive']; |
| 28 | + yield 'phar' => ['phar', 'Internal\DLoad\Module\Archive\Internal\PharArchive']; |
| 29 | + } |
| 30 | + |
| 31 | + #[DataProvider('provideArchiveTypes')] |
| 32 | + public function testFactoryCreateReturnsCorrectImplementation( |
| 33 | + string $extension, |
| 34 | + string $className, |
| 35 | + ): void { |
| 36 | + // Skip if we can't verify the implementation type |
| 37 | + if (!\class_exists($className)) { |
| 38 | + self::markTestSkipped("Class $className not available"); |
| 39 | + } |
| 40 | + |
| 41 | + // Arrange - create mock file with extension |
| 42 | + $file = $this->createMock(\SplFileInfo::class); |
| 43 | + $file->method('getFilename')->willReturn('test.' . $extension); |
| 44 | + $file->method('isFile')->willReturn(true); |
| 45 | + $file->method('isReadable')->willReturn(true); |
| 46 | + |
| 47 | + // Act - create archive handler |
| 48 | + try { |
| 49 | + $archive = $this->factory->create($file); |
| 50 | + |
| 51 | + // Assert - check implementation type |
| 52 | + self::assertInstanceOf($className, $archive); |
| 53 | + } catch (\InvalidArgumentException $e) { |
| 54 | + // If creation fails due to real file requirements, just verify supported extensions |
| 55 | + $extensions = $this->factory->getSupportedExtensions(); |
| 56 | + self::assertContains($extension, $extensions); |
| 57 | + } |
| 58 | + } |
| 59 | + |
| 60 | + public function testFactoryExtendWithCustomImplementation(): void |
| 61 | + { |
| 62 | + // Arrange - create custom archive mock |
| 63 | + $customArchive = $this->createMock('Internal\DLoad\Module\Archive\Archive'); |
| 64 | + |
| 65 | + // Register custom implementation for .custom extension |
| 66 | + $this->factory->extend( |
| 67 | + static fn(\SplFileInfo $file) => |
| 68 | + \str_ends_with($file->getFilename(), '.custom') ? $customArchive : null, |
| 69 | + ['custom'], |
| 70 | + ); |
| 71 | + |
| 72 | + // Create mock file with custom extension |
| 73 | + $file = $this->createMock(\SplFileInfo::class); |
| 74 | + $file->method('getFilename')->willReturn('test.custom'); |
| 75 | + $file->method('isFile')->willReturn(true); |
| 76 | + $file->method('isReadable')->willReturn(true); |
| 77 | + |
| 78 | + // Act |
| 79 | + $archive = $this->factory->create($file); |
| 80 | + |
| 81 | + // Assert |
| 82 | + self::assertSame($customArchive, $archive); |
| 83 | + } |
| 84 | + |
| 85 | + protected function setUp(): void |
| 86 | + { |
| 87 | + // Skip tests if phar extension is not available |
| 88 | + if (!\class_exists(\PharData::class)) { |
| 89 | + self::markTestSkipped('Phar extension is not available'); |
| 90 | + } |
| 91 | + |
| 92 | + // Create temporary directory for test files in project runtime |
| 93 | + $projectRoot = \dirname(__DIR__, 4); // Four levels up from this file |
| 94 | + $this->tempDir = $projectRoot . '/runtime/tests/archive-integration-' . \uniqid(); |
| 95 | + \mkdir($this->tempDir, 0777, true); |
| 96 | + |
| 97 | + // Create factory |
| 98 | + $this->factory = new ArchiveFactory(); |
| 99 | + } |
| 100 | + |
| 101 | + protected function tearDown(): void |
| 102 | + { |
| 103 | + // Clean up temporary directory |
| 104 | + if (\is_dir($this->tempDir)) { |
| 105 | + $this->removeDirectory($this->tempDir); |
| 106 | + } |
| 107 | + } |
| 108 | + |
| 109 | + /** |
| 110 | + * Recursively remove a directory and its contents |
| 111 | + */ |
| 112 | + private function removeDirectory(string $dir): void |
| 113 | + { |
| 114 | + if (!\is_dir($dir)) { |
| 115 | + return; |
| 116 | + } |
| 117 | + |
| 118 | + $items = \scandir($dir); |
| 119 | + foreach ($items as $item) { |
| 120 | + if ($item === '.' || $item === '..') { |
| 121 | + continue; |
| 122 | + } |
| 123 | + |
| 124 | + $path = $dir . '/' . $item; |
| 125 | + if (\is_dir($path)) { |
| 126 | + $this->removeDirectory($path); |
| 127 | + } else { |
| 128 | + \unlink($path); |
| 129 | + } |
| 130 | + } |
| 131 | + |
| 132 | + \rmdir($dir); |
| 133 | + } |
| 134 | +} |
0 commit comments