Files
Gnuboard7/tests/Unit/Services/LanguagePack/LanguagePackSeedInjectorTest.php
T
2026-07-01 10:30:32 +09:00

431 lines
17 KiB
PHP

<?php
namespace Tests\Unit\Services\LanguagePack;
use App\Enums\LanguagePackScope;
use App\Enums\LanguagePackStatus;
use App\Models\LanguagePack;
use App\Services\LanguagePack\LanguagePackRegistry;
use App\Services\LanguagePack\LanguagePackSeedInjector;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\File;
use Tests\TestCase;
/**
* LanguagePackSeedInjector 단위 테스트.
*
* 활성 코어 언어팩의 seed/*.json 데이터를 시드 입력 배열에 정확히 병합하는지 검증.
*/
class LanguagePackSeedInjectorTest extends TestCase
{
use RefreshDatabase;
private LanguagePackSeedInjector $injector;
private LanguagePackRegistry $registry;
private string $packRoot;
protected function setUp(): void
{
parent::setUp();
$this->registry = $this->app->make(LanguagePackRegistry::class);
$this->injector = $this->app->make(LanguagePackSeedInjector::class);
$this->packRoot = base_path('lang-packs/test-core-ja');
File::ensureDirectoryExists($this->packRoot.'/seed');
}
protected function tearDown(): void
{
File::deleteDirectory($this->packRoot);
parent::tearDown();
}
/**
* 활성 코어 ja 언어팩 1건 + seed 파일을 함께 생성합니다.
*
* @param string $entity 엔티티 이름 (permissions/roles/menus/notifications)
* @param array<string, mixed> $seedData seed 데이터
*/
private function setupCorePackWithSeed(string $entity, array $seedData): void
{
File::put($this->packRoot.'/seed/'.$entity.'.json', json_encode($seedData));
LanguagePack::query()->create([
'identifier' => 'test-core-ja',
'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',
]);
// singleton 인스턴스 재생성 (booted() 캐시 회피)
$this->app->forgetInstance(LanguagePackRegistry::class);
$this->app->forgetInstance(LanguagePackSeedInjector::class);
$this->registry = $this->app->make(LanguagePackRegistry::class);
$this->injector = $this->app->make(LanguagePackSeedInjector::class);
}
public function test_inject_core_permissions_adds_locale_keys(): void
{
$this->setupCorePackWithSeed('permissions', [
'core.users.read' => [
'name' => 'ユーザー閲覧',
'description' => 'ユーザー情報を閲覧する権限',
],
]);
$config = [
'module' => [
'identifier' => 'core',
'name' => ['ko' => '코어', 'en' => 'Core'],
'description' => ['ko' => '', 'en' => ''],
],
'categories' => [
[
'identifier' => 'core.users',
'name' => ['ko' => '사용자', 'en' => 'Users'],
'description' => ['ko' => '', 'en' => ''],
'permissions' => [
[
'identifier' => 'core.users.read',
'name' => ['ko' => '사용자 조회', 'en' => 'Read Users'],
'description' => ['ko' => '사용자 정보를 조회할 수 있습니다.', 'en' => 'Can view users.'],
],
],
],
],
];
$result = $this->injector->injectCorePermissions($config);
$this->assertSame(
'ユーザー閲覧',
$result['categories'][0]['permissions'][0]['name']['ja']
);
$this->assertSame(
'ユーザー情報を閲覧する権限',
$result['categories'][0]['permissions'][0]['description']['ja']
);
}
public function test_inject_notifications_3tier_definition_and_template(): void
{
$this->setupCorePackWithSeed('notifications', [
'welcome' => [
'definition' => ['name' => 'ようこそ', 'description' => '新規登録時の歓迎'],
'templates' => [
'mail' => ['subject' => '[アプリ] ご登録ありがとう', 'body' => 'こんにちは'],
],
],
]);
$defs = [
[
'type' => 'welcome',
'name' => ['ko' => '환영', 'en' => 'Welcome'],
'description' => ['ko' => '', 'en' => ''],
'templates' => [
[
'channel' => 'mail',
'subject' => ['ko' => '환영합니다', 'en' => 'Welcome'],
'body' => ['ko' => '안녕하세요', 'en' => 'Hi'],
],
],
],
];
$result = $this->injector->injectNotifications($defs);
$this->assertSame('ようこそ', $result[0]['name']['ja']);
$this->assertSame('[アプリ] ご登録ありがとう', $result[0]['templates'][0]['subject']['ja']);
$this->assertSame('こんにちは', $result[0]['templates'][0]['body']['ja']);
}
public function test_inject_skips_when_no_active_pack(): void
{
// active 팩 없음 → 입력 그대로 반환
$config = [
'module' => ['identifier' => 'core', 'name' => ['ko' => '코어']],
'categories' => [],
];
$result = $this->injector->injectCorePermissions($config);
$this->assertSame($config, $result);
}
public function test_inject_extension_entity_merges_module_seed(): void
{
$packRoot = base_path('lang-packs/acme-module-sirsoft-ecommerce-ja');
File::ensureDirectoryExists($packRoot.'/seed');
File::put($packRoot.'/seed/shipping_types.json', json_encode([
'parcel' => ['name' => '宅配便'],
]));
try {
LanguagePack::query()->create([
'identifier' => 'acme-module-sirsoft-ecommerce-ja',
'vendor' => 'acme',
'scope' => LanguagePackScope::Module->value,
'target_identifier' => 'sirsoft-ecommerce',
'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',
]);
$this->registry->invalidate();
$entries = [
['code' => 'parcel', 'name' => ['ko' => '택배', 'en' => 'Parcel']],
];
$result = $this->injector->injectExtensionEntity($entries, 'sirsoft-ecommerce', 'shipping_types', 'code');
$this->assertSame('宅配便', $result[0]['name']['ja']);
} finally {
File::deleteDirectory($packRoot);
}
}
public function test_inject_extension_notifications_merges_module_pack_seed(): void
{
$packRoot = base_path('lang-packs/g7-module-sirsoft-ecommerce-ja');
File::ensureDirectoryExists($packRoot.'/seed');
File::put($packRoot.'/seed/notifications.json', json_encode([
'order_paid' => [
'definition' => ['name' => '注文支払い完了', 'description' => '注文が支払われた時に送信'],
'templates' => [
'mail' => ['subject' => '【ショップ】ご注文ありがとうございます', 'body' => 'ご注文を承りました'],
],
],
]));
try {
LanguagePack::query()->create([
'identifier' => 'g7-module-sirsoft-ecommerce-ja',
'vendor' => 'g7',
'scope' => LanguagePackScope::Module->value,
'target_identifier' => 'sirsoft-ecommerce',
'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' => 'bundled',
]);
$this->registry->invalidate();
$defs = [
[
'type' => 'order_paid',
'name' => ['ko' => '주문 결제 완료', 'en' => 'Order Paid'],
'description' => ['ko' => '', 'en' => ''],
'templates' => [
[
'channel' => 'mail',
'subject' => ['ko' => '주문 감사합니다', 'en' => 'Thanks for your order'],
'body' => ['ko' => '주문이 접수되었습니다', 'en' => 'Order received'],
],
],
],
];
$result = $this->injector->injectExtensionNotifications($defs, 'sirsoft-ecommerce');
$this->assertSame('注文支払い完了', $result[0]['name']['ja']);
$this->assertSame('注文が支払われた時に送信', $result[0]['description']['ja']);
$this->assertSame('【ショップ】ご注文ありがとうございます', $result[0]['templates'][0]['subject']['ja']);
$this->assertSame('ご注文を承りました', $result[0]['templates'][0]['body']['ja']);
// 기존 ko/en 보존
$this->assertSame('주문 결제 완료', $result[0]['name']['ko']);
$this->assertSame('Order Paid', $result[0]['name']['en']);
} finally {
File::deleteDirectory($packRoot);
}
}
public function test_inject_extension_notifications_skips_other_target(): void
{
$packRoot = base_path('lang-packs/g7-module-sirsoft-ecommerce-ja');
File::ensureDirectoryExists($packRoot.'/seed');
File::put($packRoot.'/seed/notifications.json', json_encode([
'order_paid' => ['definition' => ['name' => '注文支払い完了']],
]));
try {
LanguagePack::query()->create([
'identifier' => 'g7-module-sirsoft-ecommerce-ja',
'vendor' => 'g7',
'scope' => LanguagePackScope::Module->value,
'target_identifier' => 'sirsoft-ecommerce',
'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' => 'bundled',
]);
$this->registry->invalidate();
$defs = [['type' => 'order_paid', 'name' => ['ko' => '주문 결제 완료']]];
// 다른 모듈(sirsoft-board) 대상 호출 → ja 병합 없음
$result = $this->injector->injectExtensionNotifications($defs, 'sirsoft-board');
$this->assertArrayNotHasKey('ja', $result[0]['name']);
} finally {
File::deleteDirectory($packRoot);
}
}
/**
* manifest seed(name/description)를 활성 ja 팩 locale 로 주입하고 ko/en 을 보존합니다.
*
* @param string $scope module|plugin|template
*/
private function setupExtensionManifestPack(string $scope, string $target, array $seed): string
{
$packRoot = base_path("lang-packs/g7-{$scope}-{$target}-ja");
File::ensureDirectoryExists($packRoot.'/seed');
File::put($packRoot.'/seed/manifest.json', json_encode($seed));
LanguagePack::query()->create([
'identifier' => "g7-{$scope}-{$target}-ja",
'vendor' => 'g7',
'scope' => $scope,
'target_identifier' => $target,
'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' => 'bundled',
]);
$this->registry->invalidate();
return $packRoot;
}
public function test_inject_extension_manifest_adds_ja_and_preserves_ko_en(): void
{
$packRoot = $this->setupExtensionManifestPack('plugin', 'sirsoft-gdpr', [
'name' => 'GDPR (一般データ保護規則)',
'description' => 'クッキー同意バナーを提供します。',
]);
try {
$manifest = [
'name' => ['ko' => 'GDPR', 'en' => 'GDPR'],
'description' => ['ko' => '쿠키 동의 배너', 'en' => 'Cookie consent banner'],
];
$result = $this->injector->injectExtensionManifest($manifest, 'sirsoft-gdpr', LanguagePackScope::Plugin->value);
$this->assertSame('GDPR (一般データ保護規則)', $result['name']['ja']);
$this->assertSame('クッキー同意バナーを提供します。', $result['description']['ja']);
// ko/en 보존
$this->assertSame('GDPR', $result['name']['ko']);
$this->assertSame('Cookie consent banner', $result['description']['en']);
} finally {
File::deleteDirectory($packRoot);
}
}
public function test_inject_extension_manifest_skips_when_no_pack(): void
{
$manifest = [
'name' => ['ko' => '게시판', 'en' => 'Board'],
'description' => ['ko' => '게시판 모듈', 'en' => 'Board module'],
];
// 활성 ja 팩 없음 → 입력 그대로
$result = $this->injector->injectExtensionManifest($manifest, 'sirsoft-board', LanguagePackScope::Module->value);
$this->assertSame($manifest, $result);
$this->assertArrayNotHasKey('ja', $result['name']);
}
public function test_inject_extension_manifest_skips_other_target(): void
{
$packRoot = $this->setupExtensionManifestPack('module', 'sirsoft-ecommerce', [
'name' => 'eコマース',
]);
try {
$manifest = ['name' => ['ko' => '게시판', 'en' => 'Board']];
// 다른 대상(sirsoft-board) 호출 → ja 병합 없음
$result = $this->injector->injectExtensionManifest($manifest, 'sirsoft-board', LanguagePackScope::Module->value);
$this->assertArrayNotHasKey('ja', $result['name']);
} finally {
File::deleteDirectory($packRoot);
}
}
public function test_inject_extension_manifest_handles_string_name_and_missing_description(): void
{
$packRoot = $this->setupExtensionManifestPack('template', 'sirsoft-basic', [
'name' => 'シルソフトベーシック',
'description' => 'ユーザーテンプレート',
]);
try {
// name 이 string(비배열) 이고 description 누락된 입력 → 배열로 정규화 후 ja 주입
$manifest = ['name' => 'sirsoft-basic'];
$result = $this->injector->injectExtensionManifest($manifest, 'sirsoft-basic', LanguagePackScope::Template->value);
$this->assertIsArray($result['name']);
$this->assertSame('シルソフトベーシック', $result['name']['ja']);
$this->assertSame('ユーザーテンプレート', $result['description']['ja']);
} finally {
File::deleteDirectory($packRoot);
}
}
public function test_inject_extension_manifest_skips_empty_seed_fields(): void
{
$packRoot = $this->setupExtensionManifestPack('plugin', 'sirsoft-marketing', [
'name' => '',
'description' => 'マーケティング同意',
]);
try {
$manifest = ['name' => ['ko' => '마케팅'], 'description' => ['ko' => '마케팅 동의']];
$result = $this->injector->injectExtensionManifest($manifest, 'sirsoft-marketing', LanguagePackScope::Plugin->value);
// 빈 name 은 주입 안 함, description 만 주입
$this->assertArrayNotHasKey('ja', $result['name']);
$this->assertSame('マーケティング同意', $result['description']['ja']);
} finally {
File::deleteDirectory($packRoot);
}
}
}