신규 설치/복구 시 비-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회 시딩 전제 붕괴 대응).
84 lines
3.4 KiB
PHP
84 lines
3.4 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\LanguagePack;
|
|
|
|
use App\Enums\LanguagePackScope;
|
|
use App\Enums\LanguagePackStatus;
|
|
use App\Models\LanguagePack;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Artisan;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* language-pack:list 커맨드 회귀 테스트 (이슈 #496 결손 2 — B1).
|
|
*
|
|
* 커맨드가 repository->paginate() 직접 호출에서 Service::list() 경유로 전환되어,
|
|
* 설치된 DB 행뿐 아니라 (1) 번들에 있으나 미설치(uninstalled) 및 (2) DB active 인데
|
|
* 설치본 파일 부재(드리프트)를 함께 표면화하는지 출력으로 검증한다.
|
|
*
|
|
* repository->paginate() 로 되돌리면 미설치/드리프트가 출력에서 사라져 이 테스트가 회귀를 잡는다.
|
|
*
|
|
* Symfony Table 출력은 PendingCommand::expectsOutputToContain 으로 신뢰성 있게 캡처되지 않으므로
|
|
* Artisan::call() + Artisan::output() 로 렌더된 원문을 직접 검사한다.
|
|
*/
|
|
class ListLanguagePackCommandTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
/**
|
|
* @effects cli_list_surfaces_uninstalled_and_drift
|
|
*/
|
|
public function test_lists_drift_row_with_files_missing_marker(): 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',
|
|
]);
|
|
|
|
$exit = Artisan::call('language-pack:list');
|
|
$out = Artisan::output();
|
|
|
|
$this->assertSame(0, $exit);
|
|
$this->assertStringContainsString($id, $out, '드리프트 행이 목록에 나타나야 함');
|
|
$this->assertStringContainsString('파일 없음', $out, 'active + 설치본 부재는 "(파일 없음)" 으로 표기되어야 함');
|
|
}
|
|
|
|
/**
|
|
* @effects cli_list_surfaces_uninstalled_and_drift
|
|
*/
|
|
public function test_lists_uninstalled_bundle_pack(): void
|
|
{
|
|
// RefreshDatabase 상태 → 설치된 팩 없음. 번들 소스(g7-core-ja)는 uninstalled 가상행으로 노출.
|
|
$exit = Artisan::call('language-pack:list');
|
|
$out = Artisan::output();
|
|
|
|
$this->assertSame(0, $exit);
|
|
$this->assertStringContainsString('g7-core-ja', $out, '미설치 번들 팩이 목록에 나타나야 함');
|
|
$this->assertStringContainsString('uninstalled', $out, '미설치 번들은 uninstalled 상태로 표기되어야 함');
|
|
}
|
|
|
|
public function test_scope_filter_limits_output(): void
|
|
{
|
|
$exit = Artisan::call('language-pack:list', ['--scope' => 'core']);
|
|
$out = Artisan::output();
|
|
|
|
$this->assertSame(0, $exit);
|
|
// core 스코프만 → 모듈/플러그인/템플릿 팩은 제외
|
|
$this->assertStringContainsString('g7-core-ja', $out);
|
|
$this->assertStringNotContainsString('sirsoft-module-sirsoft-board', $out, 'core 스코프에 모듈 팩이 섞이면 안 됨');
|
|
}
|
|
}
|