Files
Gnuboard7/tests/Unit/Extension/ExtensionManagerZipTest.php
T
2026-04-20 20:37:49 +09:00

211 lines
7.6 KiB
PHP

<?php
namespace Tests\Unit\Extension;
use App\Extension\ExtensionManager;
use Illuminate\Support\Facades\File;
use Tests\TestCase;
/**
* ExtensionManager 의 외부 ZIP 추출/manifest 검증 단위 테스트
*
* - extractFromZip(): 평탄 루트 / wrapper 디렉토리 구조 모두 지원
* - prepareZipSource(): manifest 존재/해석/identifier/version 검증 경로
*/
class ExtensionManagerZipTest extends TestCase
{
private ExtensionManager $extensionManager;
/** @var array<string> 테스트 종료 시 정리할 임시 디렉토리 목록 */
private array $tempDirs = [];
protected function setUp(): void
{
parent::setUp();
$this->extensionManager = app(ExtensionManager::class);
}
protected function tearDown(): void
{
foreach ($this->tempDirs as $dir) {
if (File::isDirectory($dir)) {
File::deleteDirectory($dir);
}
}
parent::tearDown();
}
/**
* 루트 레벨에 manifest 가 있는 평탄 ZIP 을 추출하고 그 디렉토리를 반환하는지 검증합니다.
*/
public function test_extract_from_zip_returns_extract_dir_for_flat_layout(): void
{
if (! class_exists(\ZipArchive::class)) {
$this->markTestSkipped('ZipArchive not available');
}
$tempDir = $this->makeTempDir('flat');
$zipPath = $tempDir.DIRECTORY_SEPARATOR.'flat.zip';
$zip = new \ZipArchive;
$zip->open($zipPath, \ZipArchive::CREATE);
$zip->addFromString('module.json', '{"identifier":"sirsoft-example","version":"1.0.0"}');
$zip->addFromString('composer.json', '{}');
$zip->close();
$destDir = $this->makeTempDir('flat_dest');
$extractedDir = $this->extensionManager->extractFromZip($zipPath, $destDir);
$this->assertSame($destDir.DIRECTORY_SEPARATOR.'extracted', $extractedDir);
$this->assertTrue(File::exists($extractedDir.DIRECTORY_SEPARATOR.'module.json'));
}
/**
* owner-repo-hash/ 래퍼 디렉토리가 1개만 있는 ZIP 은 그 내부를 source 로 반환하는지 검증합니다.
*/
public function test_extract_from_zip_unwraps_single_wrapper_directory(): void
{
if (! class_exists(\ZipArchive::class)) {
$this->markTestSkipped('ZipArchive not available');
}
$tempDir = $this->makeTempDir('wrap');
$zipPath = $tempDir.DIRECTORY_SEPARATOR.'wrap.zip';
$zip = new \ZipArchive;
$zip->open($zipPath, \ZipArchive::CREATE);
$zip->addFromString('sirsoft-example-abc123/module.json', '{"identifier":"sirsoft-example","version":"1.0.0"}');
$zip->addFromString('sirsoft-example-abc123/README.md', '# example');
$zip->close();
$destDir = $this->makeTempDir('wrap_dest');
$extractedDir = $this->extensionManager->extractFromZip($zipPath, $destDir);
$expected = $destDir.DIRECTORY_SEPARATOR.'extracted'.DIRECTORY_SEPARATOR.'sirsoft-example-abc123';
$this->assertSame(realpath($expected), realpath($extractedDir));
$this->assertTrue(File::exists($extractedDir.DIRECTORY_SEPARATOR.'module.json'));
}
/**
* ZIP 파일이 존재하지 않으면 RuntimeException 이 발생하는지 검증합니다.
*/
public function test_extract_from_zip_throws_when_zip_missing(): void
{
$destDir = $this->makeTempDir('missing_dest');
$this->expectException(\RuntimeException::class);
$this->extensionManager->extractFromZip($destDir.DIRECTORY_SEPARATOR.'nonexistent.zip', $destDir);
}
/**
* prepareZipSource 가 유효한 ZIP + manifest 조합에서 version/manifest 를 반환하는지 검증합니다.
*/
public function test_prepare_zip_source_returns_version_and_manifest(): void
{
if (! class_exists(\ZipArchive::class)) {
$this->markTestSkipped('ZipArchive not available');
}
$tempDir = $this->makeTempDir('prep');
$zipPath = $tempDir.DIRECTORY_SEPARATOR.'ext.zip';
$zip = new \ZipArchive;
$zip->open($zipPath, \ZipArchive::CREATE);
$zip->addFromString('module.json', json_encode([
'identifier' => 'sirsoft-example',
'version' => '2.3.4',
'name' => 'Example',
]));
$zip->close();
$result = $this->extensionManager->prepareZipSource($zipPath, 'sirsoft-example', 'module.json');
$this->tempDirs[] = $result['temp_dir']; // 함수 내부에서 생성한 temp_dir 정리 등록
$this->assertSame('2.3.4', $result['to_version']);
$this->assertSame('sirsoft-example', $result['manifest']['identifier']);
$this->assertTrue(File::isDirectory($result['temp_dir']));
$this->assertTrue(File::exists($result['extracted_dir'].DIRECTORY_SEPARATOR.'module.json'));
}
/**
* manifest 파일이 없는 경우 RuntimeException + 임시 디렉토리 자동 정리가 발생하는지 검증합니다.
*/
public function test_prepare_zip_source_throws_when_manifest_missing(): void
{
if (! class_exists(\ZipArchive::class)) {
$this->markTestSkipped('ZipArchive not available');
}
$tempDir = $this->makeTempDir('no_manifest');
$zipPath = $tempDir.DIRECTORY_SEPARATOR.'no_manifest.zip';
$zip = new \ZipArchive;
$zip->open($zipPath, \ZipArchive::CREATE);
$zip->addFromString('README.md', '# no manifest');
$zip->close();
$this->expectException(\RuntimeException::class);
$this->extensionManager->prepareZipSource($zipPath, 'sirsoft-example', 'module.json');
}
/**
* identifier 가 일치하지 않으면 RuntimeException 이 발생하는지 검증합니다.
*/
public function test_prepare_zip_source_throws_on_identifier_mismatch(): void
{
if (! class_exists(\ZipArchive::class)) {
$this->markTestSkipped('ZipArchive not available');
}
$tempDir = $this->makeTempDir('id_mismatch');
$zipPath = $tempDir.DIRECTORY_SEPARATOR.'id_mismatch.zip';
$zip = new \ZipArchive;
$zip->open($zipPath, \ZipArchive::CREATE);
$zip->addFromString('module.json', json_encode([
'identifier' => 'sirsoft-other',
'version' => '1.0.0',
]));
$zip->close();
$this->expectException(\RuntimeException::class);
$this->extensionManager->prepareZipSource($zipPath, 'sirsoft-example', 'module.json');
}
/**
* manifest 에 version 필드가 없으면 RuntimeException 이 발생하는지 검증합니다.
*/
public function test_prepare_zip_source_throws_when_version_missing(): void
{
if (! class_exists(\ZipArchive::class)) {
$this->markTestSkipped('ZipArchive not available');
}
$tempDir = $this->makeTempDir('no_version');
$zipPath = $tempDir.DIRECTORY_SEPARATOR.'no_version.zip';
$zip = new \ZipArchive;
$zip->open($zipPath, \ZipArchive::CREATE);
$zip->addFromString('module.json', json_encode([
'identifier' => 'sirsoft-example',
]));
$zip->close();
$this->expectException(\RuntimeException::class);
$this->extensionManager->prepareZipSource($zipPath, 'sirsoft-example', 'module.json');
}
/**
* 임시 디렉토리를 생성하고 tearDown 시 정리되도록 등록합니다.
*/
private function makeTempDir(string $suffix): string
{
$dir = storage_path('test_ext_zip_'.$suffix.'_'.uniqid());
File::ensureDirectoryExists($dir);
$this->tempDirs[] = $dir;
return $dir;
}
}