Files
Gnuboard7/tests/Unit/Extension/Helpers/ZipInstallHelperTest.php
T
2026-04-01 10:30:52 +09:00

288 lines
10 KiB
PHP

<?php
namespace Tests\Unit\Extension\Helpers;
use App\Extension\Helpers\ZipInstallHelper;
use Illuminate\Support\Facades\File;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
class ZipInstallHelperTest extends TestCase
{
private string $tempDir;
protected function setUp(): void
{
parent::setUp();
$this->tempDir = storage_path('app/temp/test-zip-helper-'.uniqid());
File::ensureDirectoryExists($this->tempDir);
}
protected function tearDown(): void
{
if (File::isDirectory($this->tempDir)) {
File::deleteDirectory($this->tempDir);
}
parent::tearDown();
}
// ──────────────────────────────────────────
// extractZip
// ──────────────────────────────────────────
#[Test]
public function extractZip_유효한_zip_파일을_추출합니다(): void
{
$zipPath = $this->createTestZip(['module.json' => '{"identifier":"test-module"}']);
$extractPath = $this->tempDir.'/extracted';
ZipInstallHelper::extractZip($zipPath, $extractPath);
$this->assertTrue(File::exists($extractPath.'/module.json'));
}
#[Test]
public function extractZip_존재하지_않는_파일이면_RuntimeException을_던집니다(): void
{
$this->expectException(\RuntimeException::class);
ZipInstallHelper::extractZip('/nonexistent/path.zip', $this->tempDir.'/extracted');
}
#[Test]
public function extractZip_손상된_zip이면_RuntimeException을_던집니다(): void
{
$fakePath = $this->tempDir.'/corrupted.zip';
File::put($fakePath, 'this is not a valid zip file');
$this->expectException(\RuntimeException::class);
ZipInstallHelper::extractZip($fakePath, $this->tempDir.'/extracted');
}
// ──────────────────────────────────────────
// findManifest
// ──────────────────────────────────────────
#[Test]
public function findManifest_루트에_있는_manifest를_찾습니다(): void
{
File::put($this->tempDir.'/module.json', '{}');
$result = ZipInstallHelper::findManifest($this->tempDir, 'module.json');
$this->assertNotNull($result);
$this->assertStringEndsWith('module.json', $result);
}
#[Test]
public function findManifest_1단계_하위_디렉토리에서_manifest를_찾습니다(): void
{
$subDir = $this->tempDir.'/owner-repo-abc123';
File::ensureDirectoryExists($subDir);
File::put($subDir.'/module.json', '{}');
$result = ZipInstallHelper::findManifest($this->tempDir, 'module.json');
$this->assertNotNull($result);
$this->assertStringContainsString('owner-repo-abc123', $result);
}
#[Test]
public function findManifest_manifest가_없으면_null을_반환합니다(): void
{
$result = ZipInstallHelper::findManifest($this->tempDir, 'module.json');
$this->assertNull($result);
}
#[Test]
public function findManifest_2단계_이상_하위에서는_찾지_않습니다(): void
{
$deepDir = $this->tempDir.'/level1/level2';
File::ensureDirectoryExists($deepDir);
File::put($deepDir.'/module.json', '{}');
$result = ZipInstallHelper::findManifest($this->tempDir, 'module.json');
$this->assertNull($result);
}
// ──────────────────────────────────────────
// findAndValidateManifest
// ──────────────────────────────────────────
#[Test]
public function findAndValidateManifest_유효한_모듈_manifest를_검증합니다(): void
{
File::put($this->tempDir.'/module.json', json_encode([
'identifier' => 'test-module',
'name' => 'Test Module',
]));
$result = ZipInstallHelper::findAndValidateManifest($this->tempDir, 'module.json', 'modules');
$this->assertSame('test-module', $result['identifier']);
$this->assertArrayHasKey('config', $result);
$this->assertArrayHasKey('sourcePath', $result);
$this->assertSame('Test Module', $result['config']['name']);
}
#[Test]
public function findAndValidateManifest_유효한_플러그인_manifest를_검증합니다(): void
{
File::put($this->tempDir.'/plugin.json', json_encode([
'identifier' => 'test-plugin',
'name' => 'Test Plugin',
]));
$result = ZipInstallHelper::findAndValidateManifest($this->tempDir, 'plugin.json', 'plugins');
$this->assertSame('test-plugin', $result['identifier']);
}
#[Test]
public function findAndValidateManifest_유효한_템플릿_manifest를_검증합니다(): void
{
File::put($this->tempDir.'/template.json', json_encode([
'identifier' => 'test-template',
'name' => 'Test Template',
]));
$result = ZipInstallHelper::findAndValidateManifest($this->tempDir, 'template.json', 'templates');
$this->assertSame('test-template', $result['identifier']);
}
#[Test]
public function findAndValidateManifest_manifest가_없으면_RuntimeException을_던집니다(): void
{
$this->expectException(\RuntimeException::class);
ZipInstallHelper::findAndValidateManifest($this->tempDir, 'module.json', 'modules');
}
#[Test]
public function findAndValidateManifest_identifier가_누락되면_RuntimeException을_던집니다(): void
{
File::put($this->tempDir.'/module.json', json_encode([
'name' => 'No Identifier Module',
]));
$this->expectException(\RuntimeException::class);
ZipInstallHelper::findAndValidateManifest($this->tempDir, 'module.json', 'modules');
}
#[Test]
public function findAndValidateManifest_잘못된_json이면_RuntimeException을_던집니다(): void
{
File::put($this->tempDir.'/module.json', 'this is not valid json {{{');
$this->expectException(\RuntimeException::class);
ZipInstallHelper::findAndValidateManifest($this->tempDir, 'module.json', 'modules');
}
#[Test]
public function findAndValidateManifest_github_아카이브_구조를_처리합니다(): void
{
// GitHub ZIP은 owner-repo-hash/ 형태로 추출됨
$subDir = $this->tempDir.'/owner-repo-abc123';
File::ensureDirectoryExists($subDir);
File::put($subDir.'/module.json', json_encode([
'identifier' => 'github-module',
]));
$result = ZipInstallHelper::findAndValidateManifest($this->tempDir, 'module.json', 'modules');
$this->assertSame('github-module', $result['identifier']);
$this->assertSame(realpath($subDir), realpath($result['sourcePath']));
}
// ──────────────────────────────────────────
// extractAndValidate
// ──────────────────────────────────────────
#[Test]
public function extractAndValidate_zip_추출_후_manifest를_검증합니다(): void
{
$zipPath = $this->createTestZip(['module.json' => json_encode([
'identifier' => 'zip-module',
'name' => 'ZIP Module',
])]);
$extractPath = $this->tempDir.'/extract';
$result = ZipInstallHelper::extractAndValidate($zipPath, $extractPath, 'module.json', 'modules');
$this->assertSame('zip-module', $result['identifier']);
$this->assertSame('ZIP Module', $result['config']['name']);
}
// ──────────────────────────────────────────
// moveToPending
// ──────────────────────────────────────────
#[Test]
public function moveToPending_소스를_pending_디렉토리로_이동합니다(): void
{
// 소스 준비
$sourcePath = $this->tempDir.'/source';
File::ensureDirectoryExists($sourcePath);
File::put($sourcePath.'/module.json', '{"identifier":"test"}');
$pendingBase = $this->tempDir.'/pending';
$result = ZipInstallHelper::moveToPending($sourcePath, $pendingBase, 'test-module');
$this->assertSame($pendingBase.'/test-module', $result);
$this->assertTrue(File::exists($result.'/module.json'));
$this->assertFalse(File::isDirectory($sourcePath));
}
#[Test]
public function moveToPending_이미_존재하는_대상을_덮어씁니다(): void
{
// 소스 준비
$sourcePath = $this->tempDir.'/source';
File::ensureDirectoryExists($sourcePath);
File::put($sourcePath.'/new_file.txt', 'new content');
// 기존 pending 대상 준비
$pendingBase = $this->tempDir.'/pending';
$existingTarget = $pendingBase.'/test-module';
File::ensureDirectoryExists($existingTarget);
File::put($existingTarget.'/old_file.txt', 'old content');
$result = ZipInstallHelper::moveToPending($sourcePath, $pendingBase, 'test-module');
$this->assertTrue(File::exists($result.'/new_file.txt'));
$this->assertFalse(File::exists($result.'/old_file.txt'));
}
// ──────────────────────────────────────────
// 헬퍼 메서드
// ──────────────────────────────────────────
/**
* 테스트용 ZIP 파일을 생성합니다.
*
* @param array<string, string> $files 파일명 => 내용 매핑
* @return string 생성된 ZIP 파일 경로
*/
private function createTestZip(array $files): string
{
$zipPath = $this->tempDir.'/test.zip';
$zip = new \ZipArchive;
$zip->open($zipPath, \ZipArchive::CREATE);
foreach ($files as $name => $content) {
$zip->addFromString($name, $content);
}
$zip->close();
return $zipPath;
}
}