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 $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); } } }