신규 설치/복구 시 비-ko/en 번들 언어팩(ja 등)이 자동으로 채워지지 않아
런타임에 오류 없이 조용히 ko 로 폴백하고, 운영자가 이를 알아채기 어려웠다.
두 결손을 함께 해소한다.
프로비저닝(SSoT):
- language-pack:provision 멱등 커맨드 신설. supported_locales ∖ {ko,en} 의
미설치 번들 팩을 설치하고, 설치본 파일이 사라진 드리프트 팩(슬롯 점유로
getUninstalledBundledPacks 가 못 잡는)도 복구 경로로 겸한다. 재실행 시 0건 수렴.
발견성:
- CLI language-pack:list 를 Service::list 경유로 전환해 미설치(uninstalled)와
드리프트(active·파일 없음)를 함께 표면화.
- files_missing / bundled_source_available 파생 플래그를 Resource 에 노출.
- 관리자 언어팩 목록에 "파일 없음" 배지 + 원클릭 재설치(기존 install-bundled 모달
재사용). 재설치 경로만 auto_activate 로 바인딩해 복구 후 active 를 유지한다.
회귀 방지:
- 정적 룰 lang-pack-supported-locale-has-bundle(선언 로케일 ↔ 번들 소스 부재) 신설,
lang-pack-installed-version-drift 를 프로비저닝된 환경 한정으로 확장.
- 백엔드/레이아웃/E2E/시나리오 테스트 + 룰 fixture 추가.
부수 수정:
- 프로비저닝 작업 중 표면화된 ecommerce 언어팩 시더 테스트 격리결함 수정
(modules 등록 행을 매 테스트가 직접 보장 — 프로세스당 1회 시딩 전제 붕괴 대응).
88 lines
3.3 KiB
PHP
88 lines
3.3 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\LanguagePack;
|
|
|
|
use App\Enums\LanguagePackScope;
|
|
use App\Enums\LanguagePackStatus;
|
|
use App\Models\LanguagePack;
|
|
use App\Services\LanguagePackService;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* LanguagePackService::list() 의 드리프트 파생 상태(files_missing) 검증 (이슈 #496 결손 2).
|
|
*
|
|
* DB 에 active 로 기록됐으나 설치본 디렉토리(lang-packs/{id})가 부재한 행은
|
|
* 런타임에 조용히 base locale 로 폴백한다. list() 가 이를 files_missing=true 로 표면화해야 한다.
|
|
*/
|
|
class ListDriftDetectionTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
private LanguagePackService $service;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
$this->service = app(LanguagePackService::class);
|
|
}
|
|
|
|
/**
|
|
* @effects active_row_with_missing_install_dir_flagged_files_missing,
|
|
* third_party_pack_without_bundle_source_not_offered_for_reinstall
|
|
*/
|
|
public function test_active_row_with_missing_install_dir_is_flagged(): void
|
|
{
|
|
// 설치본 디렉토리(lang-packs/{id})가 실재하지 않는 식별자로 active 행 생성 → 드리프트
|
|
$id = 'test-drift-missing-ja';
|
|
LanguagePack::query()->create([
|
|
'identifier' => $id,
|
|
'vendor' => 'test',
|
|
'scope' => LanguagePackScope::Core->value,
|
|
'target_identifier' => null,
|
|
'locale' => 'ja',
|
|
'locale_name' => 'Japanese',
|
|
'locale_native_name' => '日本語',
|
|
'text_direction' => 'ltr',
|
|
'version' => '1.0.0',
|
|
'status' => LanguagePackStatus::Active->value,
|
|
'is_protected' => false,
|
|
'manifest' => [],
|
|
'source_type' => 'zip',
|
|
]);
|
|
|
|
$paginator = $this->service->list([], 100);
|
|
$found = collect($paginator->items())->firstWhere('identifier', $id);
|
|
|
|
$this->assertNotNull($found, 'active 행이 목록에 나타나야 함');
|
|
$this->assertTrue(
|
|
(bool) $found->getAttribute('files_missing'),
|
|
'설치본 디렉토리가 없는 active 행은 files_missing=true 여야 함'
|
|
);
|
|
|
|
// 이 식별자는 lang-packs/_bundled/ 에 대응 소스가 없다 → 번들 재설치로 복구할 수 없다.
|
|
// 화면은 이 플래그로 재설치 버튼 노출을 가르므로, 복구 불가 팩에 버튼이 뜨면 안 된다.
|
|
$this->assertFalse(
|
|
(bool) $found->getAttribute('bundled_source_available'),
|
|
'번들 소스가 없는 팩은 bundled_source_available=false 여야 함'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @effects virtual_builtin_rows_not_flagged
|
|
*/
|
|
public function test_virtual_builtin_rows_are_not_flagged(): void
|
|
{
|
|
// 코어 ko/en 가상 보호 행은 lang/{locale}/ 로 항상 존재 → 드리프트 아님
|
|
$paginator = $this->service->list([], 100);
|
|
$koPack = collect($paginator->items())
|
|
->first(fn ($p) => $p->scope === LanguagePackScope::Core->value && $p->locale === 'ko');
|
|
|
|
$this->assertNotNull($koPack, '코어 ko 가상 행이 존재해야 함');
|
|
$this->assertFalse(
|
|
(bool) $koPack->getAttribute('files_missing'),
|
|
'가상 보호 행은 files_missing 대상이 아님'
|
|
);
|
|
}
|
|
}
|