route:cache 가 걸리면 RouteServiceProvider 가 캐시 로드로 분기해 라우트 파일이 실행되지 않는다. routes/devtools.php 는 전역 함수 5개를 정의하고 핸들러가 호출했고, boost 의 browser-logs 는 프로바이더 boot 등록분이라 setCompiledRoutes 교체로 폐기됐다. 7.0.6 의 RouteCacheHelper 가 12개 라이프사이클 지점에서 캐시를 자동 생성하면서, 확장을 한 번이라도 건드린 사이트에서 항상 재현됐다. 로직을 오토로드되는 App\Support\DevTools\* 로 옮기고 browser-logs URI 를 G7 가 직접 소유해 캐시에 함께 구워지게 했다. 게이트가 참조하던 App\Models\Setting 은 저장소에 없는 유물이라 ConfigRepositoryInterface 로 교체했다. 같은 부류의 재발은 정적 검사(route-file-local-function)로 차단하고, 재현이 in-process 로 불가능하므로 서브프로세스 하네스로 회귀를 고정했다.
117 lines
4.1 KiB
PHP
117 lines
4.1 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\DevTools;
|
|
|
|
use App\Contracts\Repositories\ConfigRepositoryInterface;
|
|
use App\Support\DevTools\DebugGate;
|
|
use Mockery;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* DevTools 엔드포인트의 디버그 모드 게이트 회귀 테스트.
|
|
*
|
|
* 배경:
|
|
* 게이트는 원래 `config('app.debug') || \App\Models\Setting::getValue('debug.mode', false)`
|
|
* 였는데 `App\Models\Setting` 은 저장소에 존재하지 않는 유물이었다. `||` 단축평가 덕에
|
|
* `APP_DEBUG=true` 인 동안에는 두 번째 항이 평가되지 않아 드러나지 않았을 뿐이고,
|
|
* `APP_DEBUG=false` + settings JSON 의 `debug` 카테고리 미동기화가 겹치면
|
|
* **403 이어야 할 응답이 `Class "App\Models\Setting" not found` 500 이 됐다.**
|
|
*
|
|
* 검증 축:
|
|
* 1. app.debug=false + debug.mode=true → 200 (두 번째 항이 실제로 평가되어야 한다)
|
|
* 2. app.debug=false + debug.mode=false → 403 (500 이 아니어야 한다)
|
|
* 3. app.debug=true → 200 (환경설정 조회 없이 통과)
|
|
*/
|
|
class DevtoolsDebugGateTest extends TestCase
|
|
{
|
|
protected function tearDown(): void
|
|
{
|
|
Mockery::close();
|
|
parent::tearDown();
|
|
}
|
|
|
|
/**
|
|
* `debug.mode` 환경설정만 켜져 있어도 게이트를 통과해야 한다.
|
|
*
|
|
* 수정 전: HTTP 500 `Class "App\Models\Setting" not found`
|
|
*/
|
|
public function test_dump_state_passes_gate_via_settings_debug_mode(): void
|
|
{
|
|
config(['app.debug' => false]);
|
|
$this->mockConfigRepositoryDebugMode(true);
|
|
|
|
$response = $this->postJson('/_boost/g7-debug/dump-state', ['test' => true]);
|
|
|
|
$response->assertStatus(200);
|
|
$response->assertJson(['status' => 'success']);
|
|
}
|
|
|
|
/**
|
|
* 양쪽 모두 꺼져 있으면 403 이어야 한다 (500 이 아니라).
|
|
*/
|
|
public function test_dump_state_returns_403_when_debug_disabled(): void
|
|
{
|
|
config(['app.debug' => false]);
|
|
$this->mockConfigRepositoryDebugMode(false);
|
|
|
|
$response = $this->postJson('/_boost/g7-debug/dump-state', ['test' => true]);
|
|
|
|
$response->assertStatus(403);
|
|
$response->assertJson(['status' => 'error']);
|
|
|
|
// 번역 키가 해석되지 않으면 Laravel 은 키 문자열을 그대로 돌려준다 — 오류 없이
|
|
// 사용자에게 `devtools.debug_disabled` 가 노출되므로 해석 여부를 단언한다.
|
|
$this->assertNotSame(
|
|
'devtools.debug_disabled',
|
|
$response->json('message'),
|
|
'번역 키가 해석되지 않았습니다 (lang/{ko,en}/devtools.php 누락).'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* `browser-logs` 도 동일 게이트를 사용한다.
|
|
*/
|
|
public function test_browser_logs_returns_403_when_debug_disabled(): void
|
|
{
|
|
config(['app.debug' => false]);
|
|
$this->mockConfigRepositoryDebugMode(false);
|
|
|
|
$response = $this->postJson('/_boost/browser-logs', ['logs' => []]);
|
|
|
|
$response->assertStatus(403);
|
|
}
|
|
|
|
/**
|
|
* `.env` 의 `APP_DEBUG=true` 만으로도 통과한다 (환경설정 조회 불필요).
|
|
*/
|
|
public function test_gate_passes_on_app_debug_without_settings_lookup(): void
|
|
{
|
|
config(['app.debug' => true]);
|
|
|
|
$repository = Mockery::mock(ConfigRepositoryInterface::class);
|
|
$repository->shouldNotReceive('get');
|
|
$this->app->instance(ConfigRepositoryInterface::class, $repository);
|
|
|
|
$this->assertTrue(DebugGate::isEnabled());
|
|
}
|
|
|
|
/**
|
|
* `ConfigRepositoryInterface` 를 `debug.mode` 고정값으로 대체한다.
|
|
*
|
|
* @param bool $enabled debug.mode 값
|
|
* @return void
|
|
*/
|
|
private function mockConfigRepositoryDebugMode(bool $enabled): void
|
|
{
|
|
$repository = Mockery::mock(ConfigRepositoryInterface::class);
|
|
$repository->shouldReceive('get')
|
|
->with('debug.mode', false)
|
|
->andReturn($enabled);
|
|
$repository->shouldReceive('get')->andReturnUsing(
|
|
static fn (string $key, mixed $default = null): mixed => $default
|
|
);
|
|
|
|
$this->app->instance(ConfigRepositoryInterface::class, $repository);
|
|
}
|
|
}
|