INSTALLER_COMPLETED=true 가 적힌 .env 를 빈 DB 새 서버에 복사한 뒤
php artisan migrate 로 테이블을 만들기 전에 앱이 부팅되면, installer_completed
fast-path 가 테이블 존재를 잘못 전제하여 뒤따르는 쿼리가 table not found 로
부팅을 깨뜨렸다. 마이그레이션을 실행하려는 부팅에서 그 명령이 오히려 실패하는
닭-달걀 상태가 된다.
fast-path 자체(매 요청 information_schema 쿼리 제거 성능 최적화)는 유지하되,
이미 존재하던 참조 구현(LanguagePackServiceProvider::isRegistryReady)의 방어
패턴을 InstallerContext::isSchemaMutatingCommand 로 SSoT 화하여 나머지 5곳에
통일 적용했다.
- app/Support/InstallerContext.php: 마이그레이션 계열 콘솔 명령 판정 헬퍼
(migrate/migrate:fresh/migrate:refresh/migrate:rollback/migrate:reset/db:wipe)
- Caches{Module,Plugin,Template}Status: fast-path 앞에 && 가드
- {Module,Plugin}RouteServiceProvider: hasTable 검증 진입 조건에 || 가드로
무방비 pluck 도달 차단
- LanguagePackServiceProvider: 인라인 argv 검사를 헬퍼로 치환(migrate:refresh 보강)
부류 B(NotificationHookListener, IdentityPolicyRepository)는 fast-path 이후
쿼리가 이미 try/catch 로 보호되어 빈 DB 에서도 안전하므로 수정 제외.
최초 설치 흐름은 migrate 시점에 완료 플래그가 없어(false) 이번 가드의 영향권
밖이며 원래대로 hasTable 폴백을 탄다. 가드가 실제 발동하는 유일 케이스는
"플래그 true + 빈 DB" 뿐이고, 정상 운영 웹 요청은 fast-path 성능이 유지된다.
회귀 테스트: InstallerContextTest 8건 + InstallerCompletedGuardTest 5건 추가
(수정 전 5건 fail → 수정 후 green). 관련 스위트 355 passed.
7.0.3 출시 버전 bump: 코어 버전 SSoT 7곳 + CHANGELOG 동기화.
114 lines
3.6 KiB
PHP
114 lines
3.6 KiB
PHP
<?php
|
|
|
|
namespace Tests\Unit\Support;
|
|
|
|
use App\Support\InstallerContext;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* InstallerContext 회귀 테스트.
|
|
*
|
|
* `INSTALLER_COMPLETED=true` .env 를 빈 DB 새 서버에 복사한 뒤 `php artisan migrate`
|
|
* 부팅 시, installer_completed fast-path 가 테이블 존재를 잘못 전제해 부팅을 깨뜨리던
|
|
* 결함을 막기 위한 마이그레이션 계열 명령 판정 헬퍼를 검증한다.
|
|
*
|
|
* PHPUnit 실행 자체가 콘솔 컨텍스트(runningInConsole()===true)이므로,
|
|
* $_SERVER['argv'][1] 을 조작해 마이그레이션 명령 여부만 바꿔 판정을 검증한다.
|
|
* argv 는 finally 에서 반드시 원복해 다른 테스트 오염을 막는다.
|
|
*/
|
|
class InstallerContextTest extends TestCase
|
|
{
|
|
/** @var array<int, string>|null 원본 argv 백업 */
|
|
private ?array $originalArgv = null;
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
if ($this->originalArgv !== null) {
|
|
$_SERVER['argv'] = $this->originalArgv;
|
|
$this->originalArgv = null;
|
|
}
|
|
|
|
parent::tearDown();
|
|
}
|
|
|
|
/**
|
|
* 주어진 argv[1] 로 $_SERVER['argv'] 를 임시 설정하고 콜백을 실행합니다.
|
|
*
|
|
* @param string $command artisan 명령명 (argv[1] 위치)
|
|
* @param callable $callback argv 설정 상태에서 실행할 코드
|
|
*/
|
|
private function withArgv(string $command, callable $callback): void
|
|
{
|
|
if ($this->originalArgv === null) {
|
|
$this->originalArgv = $_SERVER['argv'] ?? [];
|
|
}
|
|
|
|
$_SERVER['argv'] = ['artisan', $command];
|
|
|
|
try {
|
|
$callback();
|
|
} finally {
|
|
$_SERVER['argv'] = $this->originalArgv;
|
|
$this->originalArgv = null;
|
|
}
|
|
}
|
|
|
|
public function test_returns_true_for_migrate(): void
|
|
{
|
|
$this->withArgv('migrate', function () {
|
|
$this->assertTrue(InstallerContext::isSchemaMutatingCommand());
|
|
});
|
|
}
|
|
|
|
public function test_returns_true_for_migrate_fresh(): void
|
|
{
|
|
$this->withArgv('migrate:fresh', function () {
|
|
$this->assertTrue(InstallerContext::isSchemaMutatingCommand());
|
|
});
|
|
}
|
|
|
|
public function test_returns_true_for_migrate_refresh(): void
|
|
{
|
|
// migrate:refresh 는 참조 구현(LanguagePackServiceProvider)에 누락돼 있던 명령.
|
|
// 스키마를 drop/recreate 하므로 동일한 빈 DB 구간을 만든다 → 반드시 포함.
|
|
$this->withArgv('migrate:refresh', function () {
|
|
$this->assertTrue(InstallerContext::isSchemaMutatingCommand());
|
|
});
|
|
}
|
|
|
|
public function test_returns_true_for_migrate_rollback(): void
|
|
{
|
|
$this->withArgv('migrate:rollback', function () {
|
|
$this->assertTrue(InstallerContext::isSchemaMutatingCommand());
|
|
});
|
|
}
|
|
|
|
public function test_returns_true_for_migrate_reset(): void
|
|
{
|
|
$this->withArgv('migrate:reset', function () {
|
|
$this->assertTrue(InstallerContext::isSchemaMutatingCommand());
|
|
});
|
|
}
|
|
|
|
public function test_returns_true_for_db_wipe(): void
|
|
{
|
|
$this->withArgv('db:wipe', function () {
|
|
$this->assertTrue(InstallerContext::isSchemaMutatingCommand());
|
|
});
|
|
}
|
|
|
|
public function test_returns_false_for_non_migration_command(): void
|
|
{
|
|
$this->withArgv('serve', function () {
|
|
$this->assertFalse(InstallerContext::isSchemaMutatingCommand());
|
|
});
|
|
}
|
|
|
|
public function test_returns_false_for_route_list(): void
|
|
{
|
|
$this->withArgv('route:list', function () {
|
|
$this->assertFalse(InstallerContext::isSchemaMutatingCommand());
|
|
});
|
|
}
|
|
}
|