extensionManager = app(ExtensionManager::class); $this->coreUpdateService = app(CoreUpdateService::class); } protected function tearDown(): void { foreach ($this->tempDirs as $dir) { if (File::exists($dir)) { File::deleteDirectory($dir); } } parent::tearDown(); } /** * 테스트용 디렉토리를 생성하고 추적합니다. */ private function createTempDir(string $name): string { $dir = storage_path("app/testing/{$name}_".uniqid()); File::ensureDirectoryExists($dir); $this->tempDirs[] = $dir; return $dir; } // ======================================================================== // hasComposerDependenciesAt 테스트 // ======================================================================== /** * 지정 경로의 composer.json에서 외부 의존성을 감지합니다. */ public function test_has_composer_dependencies_at_detects_external_packages(): void { $dir = $this->createTempDir('deps-at'); File::put($dir.'/composer.json', json_encode([ 'require' => [ 'php' => '^8.2', 'vendor/package' => '^1.0', ], ])); $this->assertTrue($this->extensionManager->hasComposerDependenciesAt($dir)); } /** * php와 ext-*만 있으면 외부 의존성이 없다고 판단합니다. */ public function test_has_composer_dependencies_at_returns_false_for_php_only(): void { $dir = $this->createTempDir('no-deps-at'); File::put($dir.'/composer.json', json_encode([ 'require' => [ 'php' => '^8.2', 'ext-json' => '*', ], ])); $this->assertFalse($this->extensionManager->hasComposerDependenciesAt($dir)); } /** * composer.json이 없으면 외부 의존성이 없다고 판단합니다. */ public function test_has_composer_dependencies_at_returns_false_when_no_composer_json(): void { $dir = $this->createTempDir('no-json'); $this->assertFalse($this->extensionManager->hasComposerDependenciesAt($dir)); } // ======================================================================== // isComposerUnchanged 테스트 // ======================================================================== /** * composer.json과 composer.lock이 모두 동일하면 true를 반환합니다. */ public function test_is_composer_unchanged_returns_true_when_files_identical(): void { $staging = $this->createTempDir('staging'); $active = $this->createTempDir('active'); $composerJson = json_encode(['require' => ['vendor/pkg' => '^1.0']]); $composerLock = json_encode(['packages' => [['name' => 'vendor/pkg', 'version' => '1.0.0']]]); File::put($staging.'/composer.json', $composerJson); File::put($staging.'/composer.lock', $composerLock); File::put($active.'/composer.json', $composerJson); File::put($active.'/composer.lock', $composerLock); File::ensureDirectoryExists($active.'/vendor'); $this->assertTrue($this->extensionManager->isComposerUnchanged($staging, $active)); } /** * composer.json이 다르면 false를 반환합니다. */ public function test_is_composer_unchanged_returns_false_when_json_differs(): void { $staging = $this->createTempDir('staging'); $active = $this->createTempDir('active'); File::put($staging.'/composer.json', json_encode(['require' => ['vendor/pkg' => '^2.0']])); File::put($active.'/composer.json', json_encode(['require' => ['vendor/pkg' => '^1.0']])); File::ensureDirectoryExists($active.'/vendor'); $this->assertFalse($this->extensionManager->isComposerUnchanged($staging, $active)); } /** * composer.lock이 다르면 false를 반환합니다. */ public function test_is_composer_unchanged_returns_false_when_lock_differs(): void { $staging = $this->createTempDir('staging'); $active = $this->createTempDir('active'); $composerJson = json_encode(['require' => ['vendor/pkg' => '^1.0']]); File::put($staging.'/composer.json', $composerJson); File::put($active.'/composer.json', $composerJson); File::put($staging.'/composer.lock', json_encode(['hash' => 'new'])); File::put($active.'/composer.lock', json_encode(['hash' => 'old'])); File::ensureDirectoryExists($active.'/vendor'); $this->assertFalse($this->extensionManager->isComposerUnchanged($staging, $active)); } /** * 활성 디렉토리가 없으면 false를 반환합니다. */ public function test_is_composer_unchanged_returns_false_when_active_not_exists(): void { $staging = $this->createTempDir('staging'); File::put($staging.'/composer.json', json_encode(['require' => []])); $this->assertFalse( $this->extensionManager->isComposerUnchanged($staging, '/nonexistent/path') ); } /** * 활성 vendor/가 없으면 false를 반환합니다. */ public function test_is_composer_unchanged_returns_false_when_active_vendor_missing(): void { $staging = $this->createTempDir('staging'); $active = $this->createTempDir('active'); $composerJson = json_encode(['require' => ['vendor/pkg' => '^1.0']]); File::put($staging.'/composer.json', $composerJson); File::put($active.'/composer.json', $composerJson); // vendor/ 디렉토리 없음 $this->assertFalse($this->extensionManager->isComposerUnchanged($staging, $active)); } /** * 한쪽에만 composer.lock이 있으면 false를 반환합니다. */ public function test_is_composer_unchanged_returns_false_when_lock_exists_only_in_one(): void { $staging = $this->createTempDir('staging'); $active = $this->createTempDir('active'); $composerJson = json_encode(['require' => ['vendor/pkg' => '^1.0']]); File::put($staging.'/composer.json', $composerJson); File::put($staging.'/composer.lock', json_encode(['hash' => 'abc'])); File::put($active.'/composer.json', $composerJson); // active에 lock 없음 File::ensureDirectoryExists($active.'/vendor'); $this->assertFalse($this->extensionManager->isComposerUnchanged($staging, $active)); } /** * 양쪽 모두 composer.lock이 없으면 json만 비교하여 true를 반환합니다. */ public function test_is_composer_unchanged_returns_true_when_both_no_lock(): void { $staging = $this->createTempDir('staging'); $active = $this->createTempDir('active'); $composerJson = json_encode(['require' => ['vendor/pkg' => '^1.0']]); File::put($staging.'/composer.json', $composerJson); File::put($active.'/composer.json', $composerJson); // 양쪽 모두 lock 없음 File::ensureDirectoryExists($active.'/vendor'); $this->assertTrue($this->extensionManager->isComposerUnchanged($staging, $active)); } // ======================================================================== // copyVendorFromActive 테스트 // ======================================================================== /** * 활성 vendor/를 스테이징으로 복사합니다. */ public function test_copy_vendor_from_active_copies_vendor_directory(): void { $active = $this->createTempDir('active'); $staging = $this->createTempDir('staging'); // 활성 vendor/ 생성 File::ensureDirectoryExists($active.'/vendor/some-package'); File::put($active.'/vendor/autoload.php', 'assertTrue(File::isDirectory($staging.'/vendor')); $this->assertTrue(File::exists($staging.'/vendor/autoload.php')); $this->assertTrue(File::exists($staging.'/vendor/some-package/file.php')); } /** * 활성 vendor/가 없으면 예외 없이 무시합니다. */ public function test_copy_vendor_from_active_skips_when_no_vendor(): void { $active = $this->createTempDir('active'); $staging = $this->createTempDir('staging'); // vendor/ 없는 상태에서 호출 ExtensionPendingHelper::copyVendorFromActive($active, $staging); $this->assertFalse(File::isDirectory($staging.'/vendor')); } // ======================================================================== // isComposerUnchangedForCore 테스트 // ======================================================================== /** * 코어: composer.json과 composer.lock이 동일하면 true를 반환합니다. */ public function test_core_is_composer_unchanged_returns_true_when_identical(): void { $pending = $this->createTempDir('pending'); // 운영 디렉토리의 파일을 _pending에 복사 (동일하게) File::copy(base_path('composer.json'), $pending.'/composer.json'); File::copy(base_path('composer.lock'), $pending.'/composer.lock'); $this->assertTrue($this->coreUpdateService->isComposerUnchangedForCore($pending)); } /** * 코어: composer.json이 다르면 false를 반환합니다. */ public function test_core_is_composer_unchanged_returns_false_when_json_differs(): void { $pending = $this->createTempDir('pending'); File::put($pending.'/composer.json', json_encode(['name' => 'different/project'])); File::copy(base_path('composer.lock'), $pending.'/composer.lock'); $this->assertFalse($this->coreUpdateService->isComposerUnchangedForCore($pending)); } /** * 코어: composer.lock이 다르면 false를 반환합니다. */ public function test_core_is_composer_unchanged_returns_false_when_lock_differs(): void { $pending = $this->createTempDir('pending'); File::copy(base_path('composer.json'), $pending.'/composer.json'); File::put($pending.'/composer.lock', json_encode(['content-hash' => 'different'])); $this->assertFalse($this->coreUpdateService->isComposerUnchangedForCore($pending)); } /** * 코어: _pending에 composer.json이 없으면 false를 반환합니다. */ public function test_core_is_composer_unchanged_returns_false_when_pending_json_missing(): void { $pending = $this->createTempDir('pending'); $this->assertFalse($this->coreUpdateService->isComposerUnchangedForCore($pending)); } }