https://github.com/gnuboard/g7/issues/122 제보자의 추가 지적 2건(빌드가 게시본을 지움 / CLI 최초 게시 후 웹 재생성 불가)에 대응하고, 같은 근본 원인을 공유하는 결함을 전수조사로 함께 고친다. - 빌드가 자기 산출물만 교체한다: 루트 vite `emptyOutDir: false` 기본값 true 는 폴백 없는 코어 3번들과 배달된 immutable URL 의 게시본을 함께 지웠다 - 게시 트리 권한을 웹이 이어받는다: 병합 전 프리플라이트 + 부모 그룹 상속·g+w 종전 소유권 정상화는 root 축만 덮어 비-root CLI 계정은 무방비였다 - 갱신 → 생성 → 완전성 확인을 한 묶음으로: 기록 바이트 대조, .old 원자 스왑, rename 일시 거부 재시도, 프론트 JSON 파싱 검증 - 실패를 억제하고 기록한다: 버전 한정 실패 마커 + 사유별 대시보드 알림 + ext-static:status 점검 커맨드 - 파생: catch-all 제외 목록을 에셋 화이트리스트 합집합에서 파생(mjs·webp·otf 누락), 번들 디스크 쓰기 fail-soft·빈 번들 503, 활성 디렉토리 prune 회피
169 lines
5.2 KiB
PHP
169 lines
5.2 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Console;
|
|
|
|
use App\Console\Commands\Concerns\PrunesBuildOutput;
|
|
use Illuminate\Support\Facades\File;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* 빌드 산출물 정리의 경로 범위 테스트 (#122 C1).
|
|
*
|
|
* `pruneBuildOutput()` 은 빌드 **전에** 실행된다. 웹이 서빙 중인 활성 디렉토리의 `dist/` 를
|
|
* 비우면 prune~빌드 완료 구간 전체가 서빙 공백이 되고, 빌드가 실패하면 빈 채로 남는다.
|
|
* 그래서 소스 디렉토리(`_bundled` / `_pending`)만 정리 대상이다.
|
|
*/
|
|
class BuildCommandPruneScopeTest extends TestCase
|
|
{
|
|
private string $root;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->root = storage_path('framework/testing/prune-scope');
|
|
File::deleteDirectory($this->root);
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
File::deleteDirectory($this->root);
|
|
|
|
parent::tearDown();
|
|
}
|
|
|
|
/**
|
|
* 트레이트를 노출하는 테스트 더블 — 커맨드 전체를 띄우지 않고 판정만 검사한다.
|
|
*/
|
|
private function harness(): object
|
|
{
|
|
return new class
|
|
{
|
|
use PrunesBuildOutput;
|
|
|
|
/** @var array<int, string> */
|
|
public array $warnings = [];
|
|
|
|
/**
|
|
* 커맨드의 warn() 을 대체합니다.
|
|
*
|
|
* @param string $message 경고 문구
|
|
*/
|
|
public function warn($message, $verbosity = null): void
|
|
{
|
|
$this->warnings[] = (string) $message;
|
|
}
|
|
|
|
/**
|
|
* @param string $path 빌드 경로
|
|
* @return array<int, string> 삭제된 항목
|
|
*/
|
|
public function prune(string $path): array
|
|
{
|
|
return $this->pruneBuildOutput($path);
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 산출물이 있는 dist 를 만듭니다.
|
|
*
|
|
* @param string $buildPath 확장 루트 경로
|
|
*/
|
|
private function seedDist(string $buildPath): void
|
|
{
|
|
File::ensureDirectoryExists($buildPath.'/dist/js');
|
|
File::put($buildPath.'/dist/js/components.iife.js', '(function(){})()');
|
|
File::ensureDirectoryExists($buildPath.'/dist/vendor/lib/1.0.0');
|
|
File::put($buildPath.'/dist/vendor/lib/1.0.0/lib.js', 'vendor');
|
|
}
|
|
|
|
/**
|
|
* `_bundled` 경로는 종전대로 정리한다 (동봉 vendor 는 보존).
|
|
*
|
|
* @effects prune_cleans_bundled_source_path
|
|
*/
|
|
public function test_bundled_path_is_pruned_but_vendor_preserved(): void
|
|
{
|
|
$path = $this->root.'/templates/_bundled/vendor-tpl';
|
|
$this->seedDist($path);
|
|
|
|
$harness = $this->harness();
|
|
$removed = $harness->prune($path);
|
|
|
|
$this->assertContains('js', $removed);
|
|
$this->assertDirectoryDoesNotExist($path.'/dist/js');
|
|
$this->assertDirectoryExists($path.'/dist/vendor', '동봉 vendor 가 삭제됐다');
|
|
$this->assertSame([], $harness->warnings);
|
|
}
|
|
|
|
/**
|
|
* `_pending` 경로도 소스 보관소다 — 정리 대상.
|
|
*
|
|
* @effects prune_cleans_pending_source_path
|
|
*/
|
|
public function test_pending_path_is_pruned(): void
|
|
{
|
|
$path = $this->root.'/modules/_pending/vendor-mod';
|
|
$this->seedDist($path);
|
|
|
|
$removed = $this->harness()->prune($path);
|
|
|
|
$this->assertContains('js', $removed);
|
|
}
|
|
|
|
/**
|
|
* 활성 디렉토리는 정리하지 않고 경고한다 — 서빙 공백 차단.
|
|
*
|
|
* @effects prune_skips_active_serving_path
|
|
*/
|
|
public function test_active_directory_is_not_pruned_and_warns(): void
|
|
{
|
|
$path = $this->root.'/templates/vendor-tpl';
|
|
$this->seedDist($path);
|
|
|
|
$harness = $this->harness();
|
|
$removed = $harness->prune($path);
|
|
|
|
$this->assertSame([], $removed);
|
|
$this->assertDirectoryExists(
|
|
$path.'/dist/js',
|
|
'활성 디렉토리의 산출물이 삭제됐다 — 빌드 완료까지 서빙이 끊긴다'
|
|
);
|
|
$this->assertNotEmpty($harness->warnings, '조용히 건너뛰면 stale 누적을 알 수 없다');
|
|
}
|
|
|
|
/**
|
|
* Windows 역슬래시 경로도 같은 판정을 받는다.
|
|
*
|
|
* @effects prune_scope_normalizes_path_separators
|
|
*/
|
|
public function test_backslash_paths_are_normalized(): void
|
|
{
|
|
// 백슬래시 리터럴을 소스에 직접 두지 않는다 — 도구 체인마다 이스케이프 해석이
|
|
// 달라 픽스처 자신이 조용히 다른 문자열을 검사하게 된다.
|
|
$sep = chr(92);
|
|
$path = $this->root.'/templates/_bundled/vendor-tpl';
|
|
$this->seedDist($path);
|
|
|
|
$windowsStyle = str_replace('/', $sep, $path);
|
|
|
|
$removed = $this->harness()->prune($windowsStyle);
|
|
|
|
$this->assertContains('js', $removed, '역슬래시 경로가 _bundled 로 인식되지 않았다');
|
|
}
|
|
|
|
/**
|
|
* dist 가 없으면 아무 일도 하지 않는다 (경고도 없다).
|
|
*
|
|
* @effects prune_is_noop_without_dist
|
|
*/
|
|
public function test_missing_dist_is_noop(): void
|
|
{
|
|
$harness = $this->harness();
|
|
|
|
$this->assertSame([], $harness->prune($this->root.'/templates/vendor-tpl'));
|
|
$this->assertSame([], $harness->warnings);
|
|
}
|
|
}
|