Files
Gnuboard7/tests/Feature/Console/Hotfix/RollbackStaleFilesCommandTest.php
T
2026-05-14 11:13:08 +09:00

209 lines
7.9 KiB
PHP

<?php
namespace Tests\Feature\Console\Hotfix;
use Illuminate\Support\Facades\File;
use Tests\TestCase;
/**
* `hotfix:rollback-stale-files` Artisan 커맨드 통합 테스트.
*
* 본 커맨드는 운영자가 명시 실행하는 단발성 회복 도구이며, 기본은 진단 모드 (실제 삭제
* 없이 후보 목록만 출력), `--prune` 옵션 + 확인 프롬프트 시 정리 수행.
*
* 본 테스트는 실제 base_path() 의 활성 파일을 prune 하지 않도록 항상 `--backup` 옵션을
* 사용하거나 임시 디렉토리 fixture 만 다룬다.
*/
class RollbackStaleFilesCommandTest extends TestCase
{
private string $tempBackup;
protected function setUp(): void
{
parent::setUp();
$this->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, '<?php // test marker');
File::put($this->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, '<?php // test marker');
File::put($this->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, '<?php // test marker');
File::put($this->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);
}
}
}
}