feat(verification_kginicis): 라이브 MID 자동완성 방지 + SRB 프리픽스 전환
- 라이브 자격증명 입력칸 브라우저 자동완성 자동 채움 차단 (live_mid: one-time-code / live_api_key: new-password — 크롬이 autoComplete=off 를 무시함을 실측 후 강화) - 라이브 가맹점 MID 프리픽스를 이니시스 정책 변경에 맞춰 SRA→SRB 전환. 런타임 프리픽스를 InicisIdentityProvider::LIVE_MID_PREFIX 상수로 추출, 표시 문자열(라벨·Span·힌트 ko/en/ja)은 값 교체. - 설정 저장 테스트가 실제 스토리지 설정 파일을 오염시키던 결함 격리 (PluginTestCase 에서 plugins 디스크를 테스트 전용 경로로 격리) - 설정 화면 배너 아이콘 크기 시맨틱 정정 (w-N h-N → text-*, 정렬 회귀 해결) - 프리픽스 상수 ↔ 표시 문자열 정합 audit 룰 추가 (이중관리 불일치 차단)
This commit is contained in:
@@ -4,6 +4,12 @@
|
||||
형식은 [Keep a Changelog](https://keepachangelog.com/ko/1.1.0/)를 따르며,
|
||||
[Semantic Versioning](https://semver.org/lang/ko/)을 준수합니다.
|
||||
|
||||
## [1.0.1] - 2026-07-09
|
||||
|
||||
### Changed
|
||||
|
||||
- 라이브 가맹점 MID 프리픽스 표기를 이니시스 정책 변경에 맞춰 SRB 로 갱신했습니다 (라이브 MID 라벨·입력 안내 문구).
|
||||
|
||||
## [1.0.0] - 2026-07-01
|
||||
|
||||
### Added
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ return [
|
||||
],
|
||||
'settings' => [
|
||||
'test_mode' => 'テストモード',
|
||||
'live_mid' => 'ライブMID(SRAプレフィックス)',
|
||||
'live_mid' => 'ライブMID(SRBプレフィックス)',
|
||||
'live_api_key' => 'ライブAPIキー',
|
||||
'duplicate_field' => '同一人識別基準',
|
||||
'duplicate_block_enabled' => [
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"live_mode_warning_body": "ライブモードは実際の加盟店MIDで本人認証を実行します。ライブMIDとAPIキーを入力してから運用してください。",
|
||||
"section_live_keys": "ライブ資格情報",
|
||||
"live_mid": "ライブMID",
|
||||
"live_mid_hint": "イニシスライブ加盟店MIDはSRAで始まります (例:SRA1234567)。運用モード使用前に必須入力です。",
|
||||
"live_mid_hint": "イニシスライブ加盟店MIDはSRBで始まります (例:SRB1234567)。運用モード使用前に必須入力です。",
|
||||
"live_api_key": "ライブAPIキー",
|
||||
"section": {
|
||||
"duplicate_field": "同一人識別"
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"en": "G7 plugin (sirsoft-verification_kginicis) Japanese language pack (bundled)",
|
||||
"ja": "G7 プラグイン (sirsoft-verification_kginicis) 日本語 言語パック(バンドル)"
|
||||
},
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"license": "MIT",
|
||||
"scope": "plugin",
|
||||
"target_identifier": "sirsoft-verification_kginicis",
|
||||
|
||||
@@ -4,6 +4,16 @@ All notable changes to this plugin will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
## [1.0.1] - 2026-07-09
|
||||
|
||||
### Changed
|
||||
|
||||
- 라이브 가맹점 MID 프리픽스를 이니시스 정책 변경에 맞춰 SRB 로 갱신했습니다 (설정 화면 고정 프리픽스 표시·입력 안내 문구 포함).
|
||||
|
||||
### Fixed
|
||||
|
||||
- 라이브 자격증명 입력칸(라이브 MID·라이브 API 키)에 브라우저 자동완성으로 계정 이메일 등 의도치 않은 값이 자동으로 채워지던 문제를 수정했습니다. 잘못 채워진 값이 저장되어 라이브 본인인증이 실패하는 상황을 방지합니다.
|
||||
|
||||
## [1.0.0] - 2026-07-01
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "plugins/sirsoft-verification_kginicis",
|
||||
"description": "KG Inicis Identity Verification provider for G7",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"type": "library",
|
||||
"authors": [
|
||||
{
|
||||
|
||||
@@ -12,9 +12,9 @@ return [
|
||||
|
||||
'settings' => [
|
||||
'test_mode' => 'Test Mode',
|
||||
'live_mid' => 'Live MID (SRA prefix)',
|
||||
'live_mid' => 'Live MID (SRB prefix)',
|
||||
'live_api_key' => 'Live API Key',
|
||||
// Short field labels for validation error messages (without "(SRA prefix)" hint).
|
||||
// Short field labels for validation error messages (without "(SRB prefix)" hint).
|
||||
'live_mid_attribute' => 'Live MID',
|
||||
'live_api_key_attribute' => 'Live API Key',
|
||||
'duplicate_field' => 'Duplicate Identifier',
|
||||
|
||||
@@ -12,9 +12,9 @@ return [
|
||||
|
||||
'settings' => [
|
||||
'test_mode' => '테스트 모드',
|
||||
'live_mid' => '라이브 MID (SRA 프리픽스)',
|
||||
'live_mid' => '라이브 MID (SRB 프리픽스)',
|
||||
'live_api_key' => '라이브 API 키',
|
||||
// 검증 에러 메시지의 필드 이름용 짧은 라벨 (화면 라벨의 부가 설명 "(SRA 프리픽스)" 제외)
|
||||
// 검증 에러 메시지의 필드 이름용 짧은 라벨 (화면 라벨의 부가 설명 "(SRB 프리픽스)" 제외)
|
||||
'live_mid_attribute' => '라이브 MID',
|
||||
'live_api_key_attribute' => '라이브 API 키',
|
||||
'duplicate_field' => '동일인 식별 기준',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@g7/sirsoft-verification_kginicis",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"ko": "KG이니시스 본인인증",
|
||||
"en": "KG Inicis Identity Verification"
|
||||
},
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"license": "MIT",
|
||||
"description": {
|
||||
"ko": "KG이니시스 통합인증의 본인확인(reqSvcCd=03)을 G7 코어 IDV 인프라에 Provider 로 등록하는 플러그인",
|
||||
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* @file pluginSettingsLiveMid.test.tsx
|
||||
* @description sirsoft-verification_kginicis 플러그인 환경설정 라이브 MID 입력칸 회귀 테스트 (#458)
|
||||
*
|
||||
* 작업 A — 라이브 MID 입력칸(live_mid)에 autoComplete="off" 가 적용되어 브라우저 자동완성
|
||||
* (계정 이메일 등)이 채워지지 않도록 한다.
|
||||
* 작업 B — 라이브 MID 고정 프리픽스 Span 이 이니시스 정책 변경값 SRB 로 표시된다.
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import pluginSettingsLayout from '../../../layouts/admin/plugin_settings.json';
|
||||
|
||||
/** 레이아웃 트리에서 props.name 이 일치하는 첫 노드를 찾는다. */
|
||||
function findByPropName(node: unknown, name: string): Record<string, unknown> | undefined {
|
||||
if (!node || typeof node !== 'object') {
|
||||
return undefined;
|
||||
}
|
||||
const value = node as Record<string, unknown>;
|
||||
const props = (value.props ?? {}) as Record<string, unknown>;
|
||||
if (props.name === name) {
|
||||
return value;
|
||||
}
|
||||
for (const child of Object.values(value)) {
|
||||
const found = findByPropName(child, name);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** 레이아웃 트리에서 text 가 일치하는 첫 노드를 찾는다. */
|
||||
function findByText(node: unknown, text: string): Record<string, unknown> | undefined {
|
||||
if (!node || typeof node !== 'object') {
|
||||
return undefined;
|
||||
}
|
||||
const value = node as Record<string, unknown>;
|
||||
if (value.text === text) {
|
||||
return value;
|
||||
}
|
||||
for (const child of Object.values(value)) {
|
||||
const found = findByText(child, text);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
describe('plugin_settings 라이브 MID 입력칸 (#458)', () => {
|
||||
it('live_mid input 에 autoComplete="one-time-code" 가 적용되어 있어야 한다 (크롬 autofill/제안 차단)', () => {
|
||||
// 크롬은 autoComplete="off" 를 무시하고 저장된 계정(이메일)을 채운다. live_mid 는 비밀번호가
|
||||
// 아닌 일반 텍스트 MID 칸이므로, autofill/제안을 차단하되 "비밀번호 생성" 제안이 뜨지 않는
|
||||
// one-time-code 토큰을 사용한다 (new-password 는 live_api_key 처럼 실제 비번 칸에만) (#458).
|
||||
const liveMid = findByPropName(pluginSettingsLayout, 'live_mid');
|
||||
expect(liveMid).toBeDefined();
|
||||
|
||||
const props = (liveMid?.props ?? {}) as Record<string, unknown>;
|
||||
expect(props.autoComplete).toBe('one-time-code');
|
||||
});
|
||||
|
||||
it('live_api_key input 에 autoComplete="new-password" 가 적용되어 있어야 한다 (저장 비번 autofill 차단)', () => {
|
||||
// live_api_key 는 실제 새 API 키(비밀번호 성격) 입력칸이므로 의미상으로도 정확한 new-password 사용.
|
||||
const liveApiKey = findByPropName(pluginSettingsLayout, 'live_api_key');
|
||||
expect(liveApiKey).toBeDefined();
|
||||
|
||||
const props = (liveApiKey?.props ?? {}) as Record<string, unknown>;
|
||||
expect(props.autoComplete).toBe('new-password');
|
||||
});
|
||||
|
||||
it('라이브 MID 고정 프리픽스 Span 이 SRB 로 표시되어야 한다', () => {
|
||||
// 정책 전환 후 SRA 는 존재하지 않아야 한다.
|
||||
expect(findByText(pluginSettingsLayout, 'SRA')).toBeUndefined();
|
||||
expect(findByText(pluginSettingsLayout, 'SRB')).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -20,7 +20,7 @@
|
||||
"live_mode_warning_body": "Live mode performs verification with your real merchant MID. Enter your Live MID and API key before going live.",
|
||||
"section_live_keys": "Live Credentials",
|
||||
"live_mid": "Live MID",
|
||||
"live_mid_hint": "Inicis live merchant MIDs start with SRA (e.g., SRA1234567). Required before switching to Live mode.",
|
||||
"live_mid_hint": "Inicis live merchant MIDs start with SRB (e.g., SRB1234567). Required before switching to Live mode.",
|
||||
"live_api_key": "Live API Key",
|
||||
"section": {
|
||||
"duplicate_field": "Duplicate Check Basis"
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"live_mode_warning_body": "라이브 모드는 실제 가맹점 MID 로 본인인증을 수행합니다. 라이브 MID 와 API 키를 입력한 뒤 운영하세요.",
|
||||
"section_live_keys": "라이브 자격증명",
|
||||
"live_mid": "라이브 MID",
|
||||
"live_mid_hint": "이니시스 라이브 가맹점 MID 는 SRA 로 시작합니다 (예: SRA1234567). 운영 모드 사용 전 필수 입력.",
|
||||
"live_mid_hint": "이니시스 라이브 가맹점 MID 는 SRB 로 시작합니다 (예: SRB1234567). 운영 모드 사용 전 필수 입력.",
|
||||
"live_api_key": "라이브 API 키",
|
||||
"section": {
|
||||
"duplicate_field": "동일인 식별"
|
||||
|
||||
+6
-5
@@ -63,7 +63,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "circle-info",
|
||||
"className": "w-5 h-5 text-blue-600 dark:text-blue-400 flex-shrink-0 mt-0.5"
|
||||
"className": "text-xl text-blue-600 dark:text-blue-400 flex-shrink-0 mt-0.5"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -98,7 +98,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "triangle-exclamation",
|
||||
"className": "w-5 h-5 text-red-600 dark:text-red-400 flex-shrink-0 mt-0.5"
|
||||
"className": "text-xl text-red-600 dark:text-red-400 flex-shrink-0 mt-0.5"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -203,7 +203,7 @@
|
||||
"name": "Icon",
|
||||
"props": {
|
||||
"name": "triangle-exclamation",
|
||||
"className": "w-5 h-5 text-amber-600 dark:text-amber-400 flex-shrink-0 mt-0.5"
|
||||
"className": "text-xl text-amber-600 dark:text-amber-400 flex-shrink-0 mt-0.5"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -297,7 +297,7 @@
|
||||
"props": {
|
||||
"className": "inline-flex items-center px-3 bg-gray-100 dark:bg-gray-600 border-r border-gray-300 dark:border-gray-600 text-body font-mono font-bold select-none"
|
||||
},
|
||||
"text": "SRA"
|
||||
"text": "SRB"
|
||||
},
|
||||
{
|
||||
"type": "basic",
|
||||
@@ -305,6 +305,7 @@
|
||||
"props": {
|
||||
"type": "text",
|
||||
"name": "live_mid",
|
||||
"autoComplete": "one-time-code",
|
||||
"placeholder": "1234567",
|
||||
"className": "flex-1 font-mono outline-none border-0"
|
||||
}
|
||||
@@ -353,7 +354,7 @@
|
||||
"props": {
|
||||
"type": "password",
|
||||
"name": "live_api_key",
|
||||
"autoComplete": "off",
|
||||
"autoComplete": "new-password",
|
||||
"placeholder": "••••••••••••••••",
|
||||
"className": "{{_local.errors?.live_api_key ? 'dark:focus:ring-red-400 font-mono border border-red-500 dark:border-red-500 focus:ring-red-500' : 'dark:focus:ring-blue-400 focus:border-transparent font-mono'}}"
|
||||
}
|
||||
|
||||
+17
-8
@@ -36,6 +36,15 @@ class InicisIdentityProvider implements IdentityVerificationInterface
|
||||
/** Provider 식별자 (코어 표준) */
|
||||
public const PROVIDER_ID = 'inicis';
|
||||
|
||||
/**
|
||||
* 라이브 가맹점 MID 프리픽스 (이니시스 정책값).
|
||||
*
|
||||
* 라이브 MID = 이 프리픽스 + 운영자 입력값 (예: SRB1234567).
|
||||
* 이니시스 프리픽스 정책 변경 시 이 상수 1곳만 갱신하면 런타임 로직 전체에 반영된다.
|
||||
* (UI Span·언어팩 힌트 같은 정적 표시 문자열은 상수 참조가 불가하므로 값만 함께 교체)
|
||||
*/
|
||||
public const LIVE_MID_PREFIX = 'SRB';
|
||||
|
||||
/**
|
||||
* 성인인증 purpose 키 (Plugin::getIdentityPurposes 선언값과 동일 — 단일 출처).
|
||||
*
|
||||
@@ -143,7 +152,7 @@ class InicisIdentityProvider implements IdentityVerificationInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* 운영 가능 여부 — 모드별 MID/API key + 라이브 모드의 SRA 프리픽스 검증.
|
||||
* 운영 가능 여부 — 모드별 MID/API key + 라이브 모드의 프리픽스 검증.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@@ -157,7 +166,7 @@ class InicisIdentityProvider implements IdentityVerificationInterface
|
||||
|
||||
return $liveMid !== ''
|
||||
&& ! empty($this->config['live_api_key'])
|
||||
&& str_starts_with($liveMid, 'SRA');
|
||||
&& str_starts_with($liveMid, self::LIVE_MID_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -500,7 +509,7 @@ class InicisIdentityProvider implements IdentityVerificationInterface
|
||||
'default' => 'TGdxb2l3enJDWFRTbTgvREU3MGYwUT09',
|
||||
],
|
||||
'live_mid' => [
|
||||
'label' => 'Live MID (SRA prefix)',
|
||||
'label' => 'Live MID (SRB prefix)',
|
||||
'type' => 'text',
|
||||
'default' => '',
|
||||
],
|
||||
@@ -568,13 +577,13 @@ class InicisIdentityProvider implements IdentityVerificationInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* 라이브 MID 에 SRA 프리픽스를 보장한다.
|
||||
* 라이브 MID 에 프리픽스(self::LIVE_MID_PREFIX)를 보장한다.
|
||||
*
|
||||
* 운영자가 settings UI 에서 SRA 분리형 input 으로 입력하므로 일반적으로 SRA 미포함 값이
|
||||
* 들어오지만, 직접 SRA 포함하여 입력한 경우에도 안전하게 처리한다.
|
||||
* 운영자가 settings UI 에서 프리픽스 분리형 input 으로 입력하므로 일반적으로 프리픽스
|
||||
* 미포함 값이 들어오지만, 직접 프리픽스를 포함하여 입력한 경우에도 안전하게 처리한다.
|
||||
*
|
||||
* @param string $value 운영자 입력 값
|
||||
* @return string SRA 가 보장된 라이브 MID
|
||||
* @return string 프리픽스가 보장된 라이브 MID
|
||||
*/
|
||||
protected function buildLiveMid(string $value): string
|
||||
{
|
||||
@@ -583,7 +592,7 @@ class InicisIdentityProvider implements IdentityVerificationInterface
|
||||
return '';
|
||||
}
|
||||
|
||||
return str_starts_with($value, 'SRA') ? $value : 'SRA'.$value;
|
||||
return str_starts_with($value, self::LIVE_MID_PREFIX) ? $value : self::LIVE_MID_PREFIX.$value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ use Illuminate\Support\Facades\Lang;
|
||||
* 자격증명에 required 규칙을 동적 부여한다.
|
||||
*
|
||||
* 검증 기준은 InicisIdentityProvider::isAvailable() 의 라이브 운영 가능 조건
|
||||
* (live_mid 비어있지 않음 + live_api_key 비어있지 않음) 과 일치시킨다. live_mid 의 SRA
|
||||
* (live_mid 비어있지 않음 + live_api_key 비어있지 않음) 과 일치시킨다. live_mid 의 SRB
|
||||
* 프리픽스는 buildLiveMid() 가 자동 부착하므로 별도 형식 검증은 두지 않는다.
|
||||
*
|
||||
* @since 1.0.0-beta.1
|
||||
|
||||
+6
-3
@@ -8,6 +8,7 @@ use App\Models\Role;
|
||||
use App\Models\User;
|
||||
use App\Services\PluginSettingsService;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Plugins\Sirsoft\VerificationKginicis\Plugin;
|
||||
use Plugins\Sirsoft\VerificationKginicis\Tests\PluginTestCase;
|
||||
|
||||
@@ -219,9 +220,11 @@ class InicisLiveModeSettingsValidationTest extends PluginTestCase
|
||||
])->assertStatus(200);
|
||||
|
||||
// 저장 파일에 평문이 남지 않아야 한다 (sensitive 암호화).
|
||||
$settingsPath = storage_path('app/plugins/'.self::IDENTIFIER.'/settings/setting.json');
|
||||
$this->assertFileExists($settingsPath);
|
||||
$raw = file_get_contents($settingsPath);
|
||||
// PluginTestCase 가 'plugins' 디스크를 임시 디렉토리로 격리하므로 실제 storage_path 를
|
||||
// 하드코딩하지 않고 격리된 디스크를 통해 파일을 확인한다.
|
||||
$settingsFile = self::IDENTIFIER.'/settings/setting.json';
|
||||
$this->assertTrue(Storage::disk('plugins')->exists($settingsFile));
|
||||
$raw = Storage::disk('plugins')->get($settingsFile);
|
||||
$this->assertStringNotContainsString('live-secret-key', $raw);
|
||||
|
||||
// 복호화 왕복은 원문과 일치해야 한다.
|
||||
|
||||
@@ -4,7 +4,9 @@ namespace Plugins\Sirsoft\VerificationKginicis\Tests;
|
||||
|
||||
use App\Extension\HookListenerRegistrar;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Plugins\Sirsoft\VerificationKginicis\Listeners\AssertNoDuplicateInicisIdentity;
|
||||
use Plugins\Sirsoft\VerificationKginicis\Listeners\CleanInicisRecordOnUserDelete;
|
||||
use Plugins\Sirsoft\VerificationKginicis\Listeners\CleanInicisRecordOnUserWithdraw;
|
||||
@@ -25,6 +27,9 @@ abstract class PluginTestCase extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
/** 테스트 격리용 임시 plugins 스토리지 루트 (setUp 에서 생성, tearDown 에서 제거). */
|
||||
private ?string $isolatedPluginsRoot = null;
|
||||
|
||||
protected function migrateFreshUsing(): array
|
||||
{
|
||||
return [
|
||||
@@ -42,6 +47,8 @@ abstract class PluginTestCase extends TestCase
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->isolatePluginStorage();
|
||||
|
||||
$this->registerPluginAutoload();
|
||||
|
||||
// 플러그인 lang 네임스페이스 등록 — 실 환경에서는 코어 TranslationServiceProvider::boot()
|
||||
@@ -84,6 +91,54 @@ abstract class PluginTestCase extends TestCase
|
||||
$this->registerPluginHookListeners();
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$this->restorePluginStorage();
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* 플러그인 스토리지('plugins' 디스크)를 테스트 전용 임시 디렉토리로 격리한다.
|
||||
*
|
||||
* 설정 저장 테스트(PluginSettingsService::save)는 코어 'plugins' 디스크
|
||||
* (root = storage_path('app/plugins'))에 setting.json 을 쓴다. 격리하지 않으면
|
||||
* 테스트가 실제 로컬 런타임 설정 파일을 덮어써 라이브 모드/자격증명이 오염된다
|
||||
* (RefreshDatabase 는 DB 만 롤백하고 파일시스템은 되돌리지 않음). 디스크 root 를
|
||||
* 임시 경로로 바꾸고 resolved 인스턴스를 purge 하여 실제 파일을 원천적으로 못
|
||||
* 건드리게 한다.
|
||||
*/
|
||||
private function isolatePluginStorage(): void
|
||||
{
|
||||
// Laravel 이 테스트용 쓰기 공간으로 보장하는 storage/framework/testing 하위를 사용한다.
|
||||
// sys_get_temp_dir() 은 CI/컨테이너/open_basedir 제약 환경에서 위치가 다르거나
|
||||
// 쓰기 불가일 수 있어 프로젝트 내부의 격리 경로를 쓴다 (Laravel 규약 준수).
|
||||
$this->isolatedPluginsRoot = storage_path(
|
||||
'framework/testing/plugin-storage-'.uniqid('', true)
|
||||
);
|
||||
|
||||
File::ensureDirectoryExists($this->isolatedPluginsRoot);
|
||||
|
||||
config(['filesystems.disks.plugins.root' => $this->isolatedPluginsRoot]);
|
||||
|
||||
// 이미 resolve 된 'plugins' 디스크 인스턴스를 폐기해 새 root 로 재생성되게 한다.
|
||||
Storage::forgetDisk('plugins');
|
||||
}
|
||||
|
||||
/**
|
||||
* 격리 임시 디렉토리를 제거한다 (테스트 간 잔여 파일 격리).
|
||||
*/
|
||||
private function restorePluginStorage(): void
|
||||
{
|
||||
if ($this->isolatedPluginsRoot !== null && is_dir($this->isolatedPluginsRoot)) {
|
||||
File::deleteDirectory($this->isolatedPluginsRoot);
|
||||
}
|
||||
|
||||
$this->isolatedPluginsRoot = null;
|
||||
|
||||
Storage::forgetDisk('plugins');
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin 의 hook listener 들을 코어 HookManager 에 등록.
|
||||
*
|
||||
|
||||
+2
-2
@@ -99,7 +99,7 @@ class InicisIdentityProviderWithConfigTest extends TestCase
|
||||
['is_test_mode' => true, 'test_mid' => 'INIiasTest', 'duplicate_field' => 'di'],
|
||||
);
|
||||
|
||||
$cloned = $provider->withConfig(['is_test_mode' => false, 'live_mid' => 'SRA1234567']);
|
||||
$cloned = $provider->withConfig(['is_test_mode' => false, 'live_mid' => 'SRB1234567']);
|
||||
|
||||
$reflection = new \ReflectionClass($cloned);
|
||||
foreach (['gateway', 'mappingRepository', 'recordRepository', 'cache', 'config'] as $prop) {
|
||||
@@ -113,7 +113,7 @@ class InicisIdentityProviderWithConfigTest extends TestCase
|
||||
|
||||
$this->assertSame(false, $injectedConfig['is_test_mode']);
|
||||
$this->assertSame('INIiasTest', $injectedConfig['test_mid']);
|
||||
$this->assertSame('SRA1234567', $injectedConfig['live_mid']);
|
||||
$this->assertSame('SRB1234567', $injectedConfig['live_mid']);
|
||||
$this->assertSame('di', $injectedConfig['duplicate_field']);
|
||||
|
||||
$gatewayProp = $reflection->getProperty('gateway');
|
||||
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
namespace Plugins\Sirsoft\VerificationKginicis\Tests\Unit\Identity;
|
||||
|
||||
use App\Contracts\Extension\CacheInterface;
|
||||
use Mockery;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Plugins\Sirsoft\VerificationKginicis\Identity\InicisIdentityProvider;
|
||||
use Plugins\Sirsoft\VerificationKginicis\Repositories\InicisChallengeMappingRepositoryInterface;
|
||||
use Plugins\Sirsoft\VerificationKginicis\Repositories\InicisIdentityRecordRepositoryInterface;
|
||||
use Plugins\Sirsoft\VerificationKginicis\Services\InicisGatewayInterface;
|
||||
|
||||
/**
|
||||
* 라이브 MID 프리픽스(LIVE_MID_PREFIX) 회귀 테스트.
|
||||
*
|
||||
* 배경 (#458):
|
||||
* 이니시스 정책 변경으로 라이브 가맹점 MID 프리픽스가 SRA → SRB 로 전환된다.
|
||||
* 프리픽스를 InicisIdentityProvider::LIVE_MID_PREFIX 클래스 상수 1곳으로 추출하여
|
||||
* 런타임 로직(buildLiveMid / isAvailable)이 상수를 참조하도록 했다. 본 테스트는
|
||||
* 상수값이 실제 프리픽스 부착·검증에 일관되게 적용되는지 고정한다 (다음 정책 변경 시
|
||||
* 상수만 바꾸면 이 스위트가 전 지점을 함께 검증).
|
||||
*/
|
||||
class InicisLiveMidPrefixTest extends TestCase
|
||||
{
|
||||
protected function tearDown(): void
|
||||
{
|
||||
Mockery::close();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* 프리픽스 상수는 현재 정책값 SRB 여야 한다.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_live_mid_prefix_constant_is_srb(): void
|
||||
{
|
||||
$this->assertSame('SRB', InicisIdentityProvider::LIVE_MID_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* buildLiveMid 는 프리픽스 미포함 입력값 앞에 상수 프리픽스를 부착해야 한다.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_build_live_mid_prepends_prefix_when_absent(): void
|
||||
{
|
||||
$result = $this->invokeBuildLiveMid('1234567');
|
||||
|
||||
$this->assertSame(InicisIdentityProvider::LIVE_MID_PREFIX.'1234567', $result);
|
||||
$this->assertSame('SRB1234567', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* buildLiveMid 는 이미 프리픽스를 포함한 입력값에 중복 부착하지 않아야 한다.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_build_live_mid_does_not_double_prepend_prefix(): void
|
||||
{
|
||||
$result = $this->invokeBuildLiveMid('SRB1234567');
|
||||
|
||||
$this->assertSame('SRB1234567', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* buildLiveMid 는 빈 입력값(공백 포함)을 빈 문자열로 반환해야 한다.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_build_live_mid_returns_empty_for_blank_input(): void
|
||||
{
|
||||
$this->assertSame('', $this->invokeBuildLiveMid(''));
|
||||
$this->assertSame('', $this->invokeBuildLiveMid(' '));
|
||||
}
|
||||
|
||||
/**
|
||||
* 라이브 모드에서 프리픽스가 부착된 라이브 MID + API 키가 있으면 운영 가능해야 한다.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_is_available_true_in_live_mode_with_prefixed_mid(): void
|
||||
{
|
||||
$provider = $this->makeProvider([
|
||||
'is_test_mode' => false,
|
||||
'live_mid' => '1234567',
|
||||
'live_api_key' => 'live-api-key',
|
||||
]);
|
||||
|
||||
$this->assertTrue($provider->isAvailable());
|
||||
}
|
||||
|
||||
/**
|
||||
* 라이브 모드에서 API 키가 비어 있으면 운영 불가여야 한다.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_is_available_false_in_live_mode_without_api_key(): void
|
||||
{
|
||||
$provider = $this->makeProvider([
|
||||
'is_test_mode' => false,
|
||||
'live_mid' => '1234567',
|
||||
'live_api_key' => '',
|
||||
]);
|
||||
|
||||
$this->assertFalse($provider->isAvailable());
|
||||
}
|
||||
|
||||
/**
|
||||
* 라이브 MID 를 리플렉션으로 protected buildLiveMid() 에 통과시킨다.
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
protected function invokeBuildLiveMid(string $value): string
|
||||
{
|
||||
$provider = $this->makeProvider(['is_test_mode' => true, 'test_mid' => 'INIiasTest']);
|
||||
|
||||
$method = new \ReflectionMethod($provider, 'buildLiveMid');
|
||||
$method->setAccessible(true);
|
||||
|
||||
return $method->invoke($provider, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $config
|
||||
* @return InicisIdentityProvider
|
||||
*/
|
||||
protected function makeProvider(array $config): InicisIdentityProvider
|
||||
{
|
||||
return new InicisIdentityProvider(
|
||||
Mockery::mock(InicisGatewayInterface::class),
|
||||
Mockery::mock(InicisChallengeMappingRepositoryInterface::class),
|
||||
Mockery::mock(InicisIdentityRecordRepositoryInterface::class),
|
||||
Mockery::mock(CacheInterface::class),
|
||||
$config,
|
||||
);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -59,7 +59,7 @@ design_rationale: |
|
||||
- 채택: (a) 스키마를 plugin.php 로 이전(결제 플러그인 동형 + sensitive) + (b) 라이브 조건부
|
||||
required 는 코어가 이미 발행하는 core.plugin_settings.update_rules 필터로 주입. 코어 무수정.
|
||||
- 라이브 검증 기준은 InicisIdentityProvider::isAvailable()(live_mid + live_api_key 비어있지 않음)
|
||||
과 일치. live_mid 의 SRA 프리픽스는 buildLiveMid() 가 자동 부착하므로 별도 형식 검증 미도입.
|
||||
과 일치. live_mid 의 SRB 프리픽스는 buildLiveMid() 가 자동 부착하므로 별도 형식 검증 미도입.
|
||||
- (라벨 로케일 보강) ServiceProvider 의 validation.attributes 라벨 등록이 ['ko','en'] 하드코딩이라
|
||||
ja 로케일에서 일본어 라벨이 노출되지 않던 결함을 config('app.supported_locales') 동적 처리로 정정.
|
||||
ja 번역(ライブ MID 등)은 이미 언어팩에 존재했으나 등록 로케일 누락으로 미사용 상태였다.
|
||||
|
||||
Reference in New Issue
Block a user