7.0.0 정식 출시 커밋에서 PG 3종이 함께 제거되었으나, 그중 토스페이먼츠만 번들로 되돌린다. 삭제 직전 트리를 기준으로 플러그인 40개 파일과 일본어 언어팩 6개 파일을 복원하고, 삭제에 연동되어 변경되었던 README 목록·언어팩 빌드 스크립트 엔트리·이커머스 편집기 샘플의 PG provider 를 함께 되살렸다. 복원된 코드는 삭제 시점에 멈춰 있어 현행 코드베이스와 어긋나는 지점이 있어 함께 정합화했다. - PluginTestCase 가 활성 디렉토리 경로를 참조해 _bundled 단독 실행 시 라우트가 등록되지 않던 문제를 자기 기준 경로로 교체 - SettingsServiceProvider 의 site_url override 로 app.url 이 오염되어 assertRedirect 가 간헐 실패하던 문제를 APP_URL 명시 리셋으로 차단 - 환경설정 레이아웃의 시맨틱 클래스가 한 칸씩 밀려 순환 배치되어 2열 폼이 무너지고 섹션 제목이 에러 색으로 표시되던 결함을 정정 (회귀 테스트 신설) - 편집기 저장 시 silent drop 되던 미표준 root 키 제거, Icon 을 size prop 으로 전환
287 lines
9.0 KiB
PHP
287 lines
9.0 KiB
PHP
<?php
|
|
|
|
namespace Plugins\Sirsoft\Tosspayments\Tests;
|
|
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* 토스페이먼츠 플러그인 테스트 베이스 클래스
|
|
*
|
|
* 모든 토스페이먼츠 플러그인 테스트는 이 클래스를 상속받아야 합니다.
|
|
* 모듈/플러그인 오토로드, ServiceProvider 등록, 마이그레이션, 라우트 등록을 자동으로 처리합니다.
|
|
*/
|
|
abstract class PluginTestCase extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
/**
|
|
* 시딩 활성화
|
|
*
|
|
* @return bool
|
|
*/
|
|
protected function shouldSeed(): bool
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 테스트용 시더 클래스
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function seeder(): string
|
|
{
|
|
return \Modules\Sirsoft\Ecommerce\Database\Seeders\TestingSeeder::class;
|
|
}
|
|
|
|
/**
|
|
* 마이그레이션 경로를 반환합니다.
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function migrateFreshUsing(): array
|
|
{
|
|
// RefreshDatabase 는 첫 테스트의 migrateFreshUsing 만 적용 — 모든 번들 확장
|
|
// migrations 를 포함시켜 Plugin suite 전체 실행 시에도 스키마 보장.
|
|
$paths = ['database/migrations'];
|
|
foreach (glob(base_path('modules/_bundled/*/database/migrations'), GLOB_ONLYDIR) as $p) {
|
|
$paths[] = str_replace(base_path().DIRECTORY_SEPARATOR, '', $p);
|
|
}
|
|
foreach (glob(base_path('plugins/_bundled/*/database/migrations'), GLOB_ONLYDIR) as $p) {
|
|
$paths[] = str_replace(base_path().DIRECTORY_SEPARATOR, '', $p);
|
|
}
|
|
|
|
return [
|
|
'--drop-views' => $this->shouldDropViews(),
|
|
'--drop-types' => $this->shouldDropTypes(),
|
|
'--seed' => $this->shouldSeed(),
|
|
'--seeder' => $this->seeder(),
|
|
'--path' => $paths,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* HookManager static state 스냅샷 — tearDown 에서 복원하여 테스트 간 훅 격리 보장.
|
|
*
|
|
* @var array{hooks: array, filters: array, dispatching: array}|null
|
|
*/
|
|
private ?array $hookSnapshot = null;
|
|
|
|
/**
|
|
* 테스트 환경 설정
|
|
*/
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
// 이커머스 모듈 오토로드 등록
|
|
$this->registerModuleAutoload();
|
|
|
|
// 플러그인 오토로드 등록
|
|
$this->registerPluginAutoload();
|
|
|
|
// 이커머스 모듈 ServiceProvider 등록 (Repository 바인딩)
|
|
$this->app->register(\Modules\Sirsoft\Ecommerce\Providers\EcommerceServiceProvider::class);
|
|
|
|
// 모듈 라우트를 수동으로 등록
|
|
$this->registerModuleRoutes();
|
|
|
|
// 플러그인 라우트를 수동으로 등록
|
|
$this->registerPluginRoutes();
|
|
|
|
// SettingsServiceProvider 가 storage/app/settings/general.json 의 site_url 로
|
|
// app.url 을 override 하면 Laravel 의 assertRedirect (APP_URL 기반) 와 mismatch.
|
|
// 테스트 환경에서는 APP_URL 그대로 사용하도록 명시 리셋.
|
|
\Illuminate\Support\Facades\Config::set('app.url', env('APP_URL', 'http://localhost'));
|
|
|
|
// HookManager 상태 스냅샷 (tearDown 에서 복원)
|
|
$this->snapshotHookManager();
|
|
}
|
|
|
|
/**
|
|
* tearDown 에 HookManager 상태 복원.
|
|
*/
|
|
protected function tearDown(): void
|
|
{
|
|
$this->restoreHookManager();
|
|
|
|
parent::tearDown();
|
|
}
|
|
|
|
/**
|
|
* HookManager static $hooks / $filters / $dispatching 를 스냅샷.
|
|
*/
|
|
private function snapshotHookManager(): void
|
|
{
|
|
$ref = new \ReflectionClass(\App\Extension\HookManager::class);
|
|
$this->hookSnapshot = [
|
|
'hooks' => $ref->getProperty('hooks')->getValue(),
|
|
'filters' => $ref->getProperty('filters')->getValue(),
|
|
'dispatching' => $ref->getProperty('dispatching')->getValue(),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 스냅샷 시점으로 HookManager 복원.
|
|
*/
|
|
private function restoreHookManager(): void
|
|
{
|
|
if ($this->hookSnapshot === null) {
|
|
return;
|
|
}
|
|
|
|
$ref = new \ReflectionClass(\App\Extension\HookManager::class);
|
|
$ref->getProperty('hooks')->setValue(null, $this->hookSnapshot['hooks']);
|
|
$ref->getProperty('filters')->setValue(null, $this->hookSnapshot['filters']);
|
|
$ref->getProperty('dispatching')->setValue(null, $this->hookSnapshot['dispatching']);
|
|
|
|
$this->hookSnapshot = null;
|
|
}
|
|
|
|
/**
|
|
* 이커머스 모듈 오토로드를 등록합니다.
|
|
*/
|
|
protected function registerModuleAutoload(): void
|
|
{
|
|
$moduleBasePath = base_path('modules/sirsoft-ecommerce/src/');
|
|
|
|
spl_autoload_register(function ($class) use ($moduleBasePath) {
|
|
$prefix = 'Modules\\Sirsoft\\Ecommerce\\';
|
|
$len = strlen($prefix);
|
|
|
|
if (strncmp($prefix, $class, $len) !== 0) {
|
|
return;
|
|
}
|
|
|
|
$relativeClass = substr($class, $len);
|
|
$file = $moduleBasePath . str_replace('\\', '/', $relativeClass) . '.php';
|
|
|
|
if (file_exists($file)) {
|
|
require $file;
|
|
}
|
|
});
|
|
|
|
// composer.json files 오토로드 (헬퍼 함수 등록)
|
|
$helpersFile = $moduleBasePath . 'Helpers/helpers.php';
|
|
if (file_exists($helpersFile)) {
|
|
require_once $helpersFile;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 플러그인 오토로드를 등록합니다.
|
|
*/
|
|
protected function registerPluginAutoload(): void
|
|
{
|
|
// 활성 디렉토리(plugins/sirsoft-tosspayments)가 아닌 자기 자신 기준 경로 —
|
|
// _bundled 에서 직접 실행할 때도 소스를 찾도록 한다.
|
|
$pluginBasePath = dirname(__DIR__) . '/src/';
|
|
|
|
spl_autoload_register(function ($class) use ($pluginBasePath) {
|
|
$prefix = 'Plugins\\Sirsoft\\Tosspayments\\';
|
|
$len = strlen($prefix);
|
|
|
|
if (strncmp($prefix, $class, $len) !== 0) {
|
|
return;
|
|
}
|
|
|
|
$relativeClass = substr($class, $len);
|
|
$file = $pluginBasePath . str_replace('\\', '/', $relativeClass) . '.php';
|
|
|
|
if (file_exists($file)) {
|
|
require $file;
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 모듈 라우트를 등록합니다.
|
|
*/
|
|
protected function registerModuleRoutes(): void
|
|
{
|
|
$apiRoutesFile = base_path('modules/sirsoft-ecommerce/src/routes/api.php');
|
|
|
|
if (file_exists($apiRoutesFile)) {
|
|
\Illuminate\Support\Facades\Route::prefix('api/modules/sirsoft-ecommerce')
|
|
->name('api.modules.sirsoft-ecommerce.')
|
|
->middleware('api')
|
|
->group($apiRoutesFile);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 플러그인 라우트를 등록합니다.
|
|
*/
|
|
protected function registerPluginRoutes(): void
|
|
{
|
|
$webRoutesFile = dirname(__DIR__) . '/src/routes/web.php';
|
|
|
|
if (file_exists($webRoutesFile)) {
|
|
\Illuminate\Support\Facades\Route::prefix('plugins/sirsoft-tosspayments')
|
|
->name('plugins.sirsoft-tosspayments.')
|
|
->middleware('web')
|
|
->group($webRoutesFile);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 관리자 사용자를 생성합니다.
|
|
*
|
|
* @param array $permissions 추가 권한 목록
|
|
* @return \App\Models\User
|
|
*/
|
|
protected function createAdminUser(array $permissions = []): \App\Models\User
|
|
{
|
|
$user = \App\Models\User::factory()->create();
|
|
|
|
$uniqueRoleIdentifier = 'admin-test-' . $user->id . '-' . time();
|
|
$userRole = \App\Models\Role::create([
|
|
'identifier' => $uniqueRoleIdentifier,
|
|
'name' => ['ko' => '테스트 관리자', 'en' => 'Test Admin'],
|
|
]);
|
|
$user->roles()->attach($userRole->id);
|
|
|
|
$adminAccessPermission = \App\Models\Permission::firstOrCreate(
|
|
['identifier' => 'admin.access'],
|
|
[
|
|
'name' => ['ko' => '관리자 접근', 'en' => 'Admin Access'],
|
|
'type' => \App\Enums\PermissionType::Admin,
|
|
]
|
|
);
|
|
$userRole->permissions()->attach($adminAccessPermission->id);
|
|
|
|
if (! empty($permissions)) {
|
|
foreach ($permissions as $permissionIdentifier) {
|
|
$permission = \App\Models\Permission::firstOrCreate(
|
|
['identifier' => $permissionIdentifier],
|
|
[
|
|
'name' => ['ko' => $permissionIdentifier, 'en' => $permissionIdentifier],
|
|
'type' => 'admin',
|
|
]
|
|
);
|
|
$userRole->permissions()->syncWithoutDetaching([$permission->id]);
|
|
}
|
|
}
|
|
|
|
return $user;
|
|
}
|
|
|
|
/**
|
|
* 일반 사용자를 생성합니다.
|
|
*
|
|
* @return \App\Models\User
|
|
*/
|
|
protected function createUser(): \App\Models\User
|
|
{
|
|
$userRole = \App\Models\Role::where('identifier', 'user')->first();
|
|
$user = \App\Models\User::factory()->create();
|
|
|
|
if ($userRole) {
|
|
$user->roles()->attach($userRole->id);
|
|
}
|
|
|
|
return $user;
|
|
}
|
|
}
|