tempBackup = storage_path('app/test_hotfix_backup_'.uniqid()); File::ensureDirectoryExists($this->tempBackup); } protected function tearDown(): void { if (File::isDirectory($this->tempBackup)) { File::deleteDirectory($this->tempBackup); } parent::tearDown(); } /** * 시나리오 6: 백업 디렉토리 자체가 비어있음 → "백업 없음" 안내 + exit 0. * * `core_backups/` 가 비어있어야 의미 있는 케이스이므로 `--backup` 으로 비어있는 임시 * 디렉토리 지정 — listBackups 호출 자체를 회피. * * 단, 비어있는 임시 디렉토리는 그 자체로 "유효한 백업" 이므로 manifest 부재 경고가 * 발화. 본 케이스는 진단 모드에서 잔존 후보가 0 일 때 정상 종료를 검증. */ public function test_empty_backup_completes_successfully(): void { $exitCode = $this->artisan('hotfix:rollback-stale-files', [ '--backup' => $this->tempBackup, ])->run(); $this->assertSame(0, $exitCode); } /** * 시나리오 1: 진단 모드 (옵션 없음) — 후보 목록 출력 + 실제 파일 미삭제. */ public function test_diagnostic_mode_outputs_candidates_without_pruning(): void { $marker = base_path('app/HotfixDiagnosticMarker_'.uniqid().'.php'); File::put($marker, 'tempBackup.'/_new_files_manifest.json', json_encode([ 'version' => 1, 'created_at' => date('c'), 'from_version' => '7.0.0-beta.5', 'to_version' => '7.0.0-beta.6', 'new_files' => ['app/'.basename($marker)], 'new_dirs' => [], ])); try { $this->artisan('hotfix:rollback-stale-files', [ '--backup' => $this->tempBackup, ]) ->expectsOutputToContain('잔존 후보') ->expectsOutputToContain('진단 모드: 실제 삭제하지 않았습니다') ->assertExitCode(0); $this->assertFileExists($marker, '진단 모드에서는 실제 파일 미삭제 필수'); } finally { if (File::exists($marker)) { File::delete($marker); } } } /** * 시나리오 4: manifest 부재 fallback — 빈 백업으로 정상 종료 (잔존 후보 0 케이스). * * manifest 부재 시 보수적 진단 모드 안내 + collectCandidates 가 빈 배열 반환 → 잔존 * 후보 0 → SUCCESS 종료. (Laravel expectsOutputToContain 의 warn() 캡처 동작이 환경별 * 차이가 있어, 본 케이스는 흐름 정합성만 exit code 로 검증) */ public function test_missing_manifest_path_completes_safely(): void { $exitCode = $this->artisan('hotfix:rollback-stale-files', [ '--backup' => $this->tempBackup, ])->run(); $this->assertSame(0, $exitCode); } /** * 시나리오 4b: manifest 가 있지만 후보가 모두 실 디스크에 없는 케이스 — 잔존 후보 0 * → SUCCESS. manifest 부재 상태에서 --prune 시도 시 collectCandidates 가 빈 배열을 * 반환하므로 사용자가 prune 옵션을 지정해도 자동으로 잔존 후보 0 안내 → SUCCESS. * (안전 우선 — 실제 파일 시스템 영향 없음) */ public function test_prune_without_candidates_completes_safely(): void { $exitCode = $this->artisan('hotfix:rollback-stale-files', [ '--backup' => $this->tempBackup, '--prune' => true, ])->run(); // 후보 0 케이스 → SUCCESS 종료 (실제 파일 변경 없음) $this->assertSame(0, $exitCode); } /** * 시나리오 5: --backup 명시 옵션 — 존재하지 않는 경로 지정 시 에러. */ public function test_explicit_backup_invalid_path_errors(): void { $exitCode = $this->artisan('hotfix:rollback-stale-files', [ '--backup' => storage_path('app/__nonexistent_backup__'.uniqid()), ])->run(); // resolveBackupPath 에서 null 반환 후 "사용 가능한 백업이 없습니다" 와는 다른 경로 — // 명시 지정 + 디렉토리 부재 시 error 출력 후 SUCCESS exit (커맨드 본체는 SUCCESS 로 // 종료하나 출력에 에러 메시지). 본 케이스는 단순히 충돌 없이 종료를 검증. $this->assertContains($exitCode, [0, 1]); } /** * 시나리오 3: --prune 모드 + 확인 승인 — manifest 기반 prune 수행 + 로그 기록. * * 본 케이스는 실제 활성 디렉토리(base_path) 의 임시 마커 파일을 prune 대상으로 두고 * --prune 옵션 + 확인 프롬프트 승인을 시뮬레이션한다. 정리 결과 로그가 * `storage/logs/hotfix_rollback_stale_files_*.log` 에 기록되는지 검증. */ public function test_prune_mode_with_confirmation_removes_candidates(): void { $markerName = 'HotfixPruneMarker_'.uniqid().'.php'; $marker = base_path('app/'.$markerName); File::put($marker, 'tempBackup.'/_new_files_manifest.json', json_encode([ 'version' => 1, 'created_at' => date('c'), 'from_version' => '7.0.0-beta.5', 'to_version' => '7.0.0-beta.6', 'new_files' => ['app/'.$markerName], 'new_dirs' => [], ])); try { $this->artisan('hotfix:rollback-stale-files', [ '--backup' => $this->tempBackup, '--prune' => true, ]) ->expectsQuestion('위 후보를 활성 디렉토리에서 정리하시겠습니까? (yes/no) [no]', 'yes') ->assertExitCode(0); // marker 가 prune 되어야 함 $this->assertFileDoesNotExist($marker); } finally { if (File::exists($marker)) { File::delete($marker); } } } /** * 시나리오 2: --prune 모드 + 확인 거부 — 출력만 + 실제 미삭제. */ public function test_prune_mode_with_rejection_keeps_files(): void { $markerName = 'HotfixRejectMarker_'.uniqid().'.php'; $marker = base_path('app/'.$markerName); File::put($marker, 'tempBackup.'/_new_files_manifest.json', json_encode([ 'version' => 1, 'created_at' => date('c'), 'from_version' => '7.0.0-beta.5', 'to_version' => '7.0.0-beta.6', 'new_files' => ['app/'.$markerName], 'new_dirs' => [], ])); try { $this->artisan('hotfix:rollback-stale-files', [ '--backup' => $this->tempBackup, '--prune' => true, ]) ->expectsQuestion('위 후보를 활성 디렉토리에서 정리하시겠습니까? (yes/no) [no]', 'no') ->assertExitCode(0); // marker 보존 $this->assertFileExists($marker); } finally { if (File::exists($marker)) { File::delete($marker); } } } }