오토로드 캐시(psr4)가 공집합으로 덮어써져 모든 모듈/플러그인 컨트롤러가 해석 불가능해지고 확장 화면 전체가 500 이 됐다. 이 매핑은 "DB 설치 목록 × 디스크 활성 디렉토리" 교집합이라 DB 쪽만 일시적으로 비어도(테이블 부재· 설치 중간 실패·testing DB) 정상 매핑이 통째로 소실된다. 코어 라우트는 200 인데 확장 라우트만 전부 500 인 비대칭이 이 결함의 진단 단서다. - 디스크에 확장 디렉토리가 남아 있는데 매핑이 0건이면 기록하지 않고 보존. 정상적인 0건(신규 설치·마지막 확장 삭제)은 디스크도 함께 비어 통과한다 - generateAutoloadFile 에 testing 실 캐시 경로 쓰기 차단. updateComposerAutoload 에만 있던 가드를 생성기 자체로 옮겨 어느 경로로 들어와도 막히게 했다 - extension:update-autoload 가 훅 캐시 재생성 전에 런타임 오토로드를 재등록. 미재등록 시 깨진 매핑으로 훅 캐시가 modules/plugins 0건으로 생성되어 확장 훅이 조용히 사라지고, 복구에 커맨드 2회 실행이 필요했다 소스맵 서빙은 에서 차단했으나 이번 브랜치에서 화이트리스트에 재추가되어 운영에서 원본 코드 전문(sourcesContent)이 노출될 수 있었다. 이 화이트리스트가 확장 에셋 서빙의 유일한 방어선이다. dev 빌드의 sourceMappingURL 404 를 피하면서 운영을 막도록 local 환경에서만 허용하도록 분기하고, 산출물을 production 으로 재빌드해 docroot 에 남아 있던 소스맵을 제거했다. --production 빌드는 소스맵을 생성하지 않을 뿐 기존 파일을 지우지 않는다. CoreUpdateCommandSpawnFailureTest 는 in-process fallback 이 실제 업그레이드 스텝을 돌려 코어 역할·권한·메뉴를 커밋하는데 RefreshDatabase 가 없어, 이후 모든 테스트 클래스가 중복 키로 깨졌다(단독 실행 시 미재현).
688 lines
26 KiB
PHP
688 lines
26 KiB
PHP
<?php
|
|
|
|
namespace Tests\Unit\Extension;
|
|
|
|
use App\Enums\ExtensionStatus;
|
|
use App\Enums\LayoutSourceType;
|
|
use App\Extension\TemplateManager;
|
|
use App\Extension\Traits\ClearsTemplateCaches;
|
|
use App\Models\Template;
|
|
use App\Models\TemplateLayout;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\File;
|
|
use Tests\TestCase;
|
|
|
|
class TemplateManagerOverrideTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
private TemplateManager $templateManager;
|
|
|
|
private string $testTemplatePath;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
// 서비스 컨테이너에서 TemplateManager 인스턴스 가져오기
|
|
$this->templateManager = app(TemplateManager::class);
|
|
|
|
// 테스트용 템플릿 경로
|
|
$this->testTemplatePath = base_path('templates/test-admin');
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
// 테스트 템플릿 디렉토리 정리
|
|
if (File::exists($this->testTemplatePath)) {
|
|
File::deleteDirectory($this->testTemplatePath);
|
|
}
|
|
|
|
parent::tearDown();
|
|
}
|
|
|
|
/**
|
|
* 테스트용 템플릿 구조 생성
|
|
*/
|
|
private function createTestTemplateStructure(): void
|
|
{
|
|
// 기본 디렉토리 생성
|
|
File::makeDirectory($this->testTemplatePath.'/layouts/overrides/sirsoft-sample', 0755, true);
|
|
|
|
// template.json 생성
|
|
$templateJson = [
|
|
'identifier' => 'test-admin',
|
|
'vendor' => 'test',
|
|
'name' => ['ko' => '테스트 관리자', 'en' => 'Test Admin'],
|
|
'version' => '1.0.0',
|
|
'type' => 'admin',
|
|
'description' => ['ko' => '테스트용 템플릿', 'en' => 'Test template'],
|
|
];
|
|
File::put($this->testTemplatePath.'/template.json', json_encode($templateJson, JSON_PRETTY_PRINT));
|
|
|
|
// 기본 레이아웃 생성
|
|
$baseLayout = [
|
|
'layout_name' => '_admin_base',
|
|
'meta' => ['title' => 'Admin Base'],
|
|
'components' => [['component' => 'Container', 'children' => []]],
|
|
];
|
|
File::put($this->testTemplatePath.'/layouts/_admin_base.json', json_encode($baseLayout, JSON_PRETTY_PRINT));
|
|
|
|
// 오버라이드 레이아웃 생성
|
|
$overrideLayout = [
|
|
'layout_name' => 'sirsoft-sample_admin_index',
|
|
'extends' => '_admin_base',
|
|
'slots' => [
|
|
'content' => [
|
|
['component' => 'CustomDataTable', 'props' => ['theme' => 'dark']],
|
|
],
|
|
],
|
|
];
|
|
File::put(
|
|
$this->testTemplatePath.'/layouts/overrides/sirsoft-sample/admin_index.json',
|
|
json_encode($overrideLayout, JSON_PRETTY_PRINT)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 오버라이드 레이아웃 등록 테스트
|
|
*/
|
|
public function test_register_layout_overrides(): void
|
|
{
|
|
$this->createTestTemplateStructure();
|
|
|
|
// 템플릿 로드
|
|
$this->templateManager->loadTemplates();
|
|
|
|
// 템플릿 레코드 생성
|
|
$template = Template::create([
|
|
'identifier' => 'test-admin',
|
|
'vendor' => 'test',
|
|
'name' => ['ko' => '테스트 관리자', 'en' => 'Test Admin'],
|
|
'version' => '1.0.0',
|
|
'type' => 'admin',
|
|
'status' => ExtensionStatus::Inactive->value,
|
|
'description' => ['ko' => '테스트용', 'en' => 'For testing'],
|
|
]);
|
|
|
|
// 리플렉션을 통해 protected 메서드 호출
|
|
$reflection = new \ReflectionClass($this->templateManager);
|
|
$method = $reflection->getMethod('registerLayoutOverrides');
|
|
$method->setAccessible(true);
|
|
$method->invoke($this->templateManager, 'test-admin', $template->id);
|
|
|
|
// 오버라이드 레이아웃이 등록되었는지 확인
|
|
$override = TemplateLayout::where('template_id', $template->id)
|
|
->where('name', 'sirsoft-sample_admin_index')
|
|
->first();
|
|
|
|
$this->assertNotNull($override);
|
|
$this->assertEquals(LayoutSourceType::Template, $override->source_type);
|
|
$this->assertEquals('test-admin', $override->source_identifier);
|
|
$this->assertEquals('_admin_base', $override->extends);
|
|
}
|
|
|
|
/**
|
|
* 오버라이드 레이아웃 content 추출 테스트
|
|
*/
|
|
public function test_extract_layout_content(): void
|
|
{
|
|
$layoutData = [
|
|
'layout_name' => 'test_layout',
|
|
'extends' => '_base',
|
|
'slots' => ['content' => [['component' => 'Test']]],
|
|
'meta' => ['title' => 'Test'],
|
|
'data_sources' => [['id' => 'ds1', 'endpoint' => '/api/test']],
|
|
'version' => '1.0',
|
|
];
|
|
|
|
$reflection = new \ReflectionClass($this->templateManager);
|
|
$method = $reflection->getMethod('extractLayoutContent');
|
|
$method->setAccessible(true);
|
|
|
|
$content = $method->invoke($this->templateManager, $layoutData);
|
|
|
|
$this->assertArrayHasKey('slots', $content);
|
|
$this->assertArrayHasKey('meta', $content);
|
|
$this->assertArrayHasKey('data_sources', $content);
|
|
$this->assertArrayHasKey('version', $content);
|
|
$this->assertArrayNotHasKey('layout_name', $content);
|
|
$this->assertArrayNotHasKey('extends', $content);
|
|
}
|
|
|
|
/**
|
|
* 오버라이드 레이아웃 이름 생성 테스트
|
|
*/
|
|
public function test_generate_override_layout_name(): void
|
|
{
|
|
$basePath = $this->testTemplatePath.'/layouts/overrides/sirsoft-sample';
|
|
$filePath = $basePath.'/admin_index.json';
|
|
|
|
// Windows/Unix 경로 정규화
|
|
$normalizedBasePath = str_replace('/', DIRECTORY_SEPARATOR, $basePath);
|
|
$normalizedFilePath = str_replace('/', DIRECTORY_SEPARATOR, $filePath);
|
|
|
|
$reflection = new \ReflectionClass($this->templateManager);
|
|
$method = $reflection->getMethod('generateLayoutNameFromPath');
|
|
$method->setAccessible(true);
|
|
|
|
$layoutName = $method->invoke($this->templateManager, $normalizedBasePath, $normalizedFilePath, 'sirsoft-sample');
|
|
|
|
$this->assertEquals('sirsoft-sample_admin_index', $layoutName);
|
|
}
|
|
|
|
/**
|
|
* 중첩된 경로의 오버라이드 레이아웃 이름 생성 테스트
|
|
*/
|
|
public function test_generate_override_layout_name_with_nested_path(): void
|
|
{
|
|
$basePath = $this->testTemplatePath.'/layouts/overrides/sirsoft-sample';
|
|
File::makeDirectory($basePath.'/products', 0755, true);
|
|
|
|
$filePath = $basePath.'/products/list.json';
|
|
|
|
// Windows/Unix 경로 정규화
|
|
$normalizedBasePath = str_replace('/', DIRECTORY_SEPARATOR, $basePath);
|
|
$normalizedFilePath = str_replace('/', DIRECTORY_SEPARATOR, $filePath);
|
|
|
|
$reflection = new \ReflectionClass($this->templateManager);
|
|
$method = $reflection->getMethod('generateLayoutNameFromPath');
|
|
$method->setAccessible(true);
|
|
|
|
$layoutName = $method->invoke($this->templateManager, $normalizedBasePath, $normalizedFilePath, 'sirsoft-sample');
|
|
|
|
$this->assertEquals('sirsoft-sample_products_list', $layoutName);
|
|
}
|
|
|
|
/**
|
|
* 오버라이드 파일 스캔 테스트
|
|
*/
|
|
public function test_scan_override_layout_files(): void
|
|
{
|
|
// 디렉토리 구조 생성
|
|
$overridesPath = $this->testTemplatePath.'/layouts/overrides/sirsoft-sample';
|
|
File::makeDirectory($overridesPath.'/nested', 0755, true);
|
|
|
|
$testLayout = ['slots' => []];
|
|
File::put($overridesPath.'/index.json', json_encode($testLayout));
|
|
File::put($overridesPath.'/edit.json', json_encode($testLayout));
|
|
File::put($overridesPath.'/nested/detail.json', json_encode($testLayout));
|
|
|
|
$reflection = new \ReflectionClass($this->templateManager);
|
|
$method = $reflection->getMethod('scanLayoutFilesRecursively');
|
|
$method->setAccessible(true);
|
|
|
|
$files = $method->invoke($this->templateManager, $overridesPath);
|
|
|
|
$this->assertCount(3, $files);
|
|
}
|
|
|
|
/**
|
|
* 오버라이드 캐시 무효화 테스트
|
|
*/
|
|
public function test_invalidate_override_layout_caches(): void
|
|
{
|
|
// 템플릿 생성
|
|
$template = Template::create([
|
|
'identifier' => 'cache-test-template',
|
|
'vendor' => 'test',
|
|
'name' => ['ko' => '캐시 테스트', 'en' => 'Cache Test'],
|
|
'version' => '1.0.0',
|
|
'type' => 'admin',
|
|
'status' => ExtensionStatus::Active->value,
|
|
'description' => ['ko' => '테스트용', 'en' => 'For testing'],
|
|
]);
|
|
|
|
// 오버라이드 레이아웃 생성
|
|
$override = TemplateLayout::create([
|
|
'template_id' => $template->id,
|
|
'name' => 'module_layout_override',
|
|
'content' => ['slots' => []],
|
|
'source_type' => LayoutSourceType::Template,
|
|
'source_identifier' => 'cache-test-template',
|
|
]);
|
|
|
|
// 캐시 설정
|
|
$cacheKey = "g7:core:template.{$template->id}.layout.{$override->name}";
|
|
Cache::put($cacheKey, $override->content, 3600);
|
|
$this->assertNotNull(Cache::get($cacheKey));
|
|
|
|
// 캐시 무효화
|
|
$reflection = new \ReflectionClass($this->templateManager);
|
|
$method = $reflection->getMethod('invalidateOverrideLayoutCaches');
|
|
$method->setAccessible(true);
|
|
$method->invoke($this->templateManager, $template->id);
|
|
|
|
// 캐시 삭제 확인
|
|
$this->assertNull(Cache::get($cacheKey));
|
|
}
|
|
|
|
/**
|
|
* source_type이 template으로 설정되는지 테스트
|
|
*/
|
|
public function test_override_source_type_is_template(): void
|
|
{
|
|
$template = Template::create([
|
|
'identifier' => 'source-type-test',
|
|
'vendor' => 'test',
|
|
'name' => ['ko' => '소스타입 테스트', 'en' => 'Source Type Test'],
|
|
'version' => '1.0.0',
|
|
'type' => 'admin',
|
|
'status' => ExtensionStatus::Active->value,
|
|
'description' => ['ko' => '테스트용', 'en' => 'For testing'],
|
|
]);
|
|
|
|
// 오버라이드 레이아웃 생성
|
|
$override = TemplateLayout::create([
|
|
'template_id' => $template->id,
|
|
'name' => 'sirsoft-sample_admin_index',
|
|
'content' => ['slots' => []],
|
|
'source_type' => LayoutSourceType::Template,
|
|
'source_identifier' => 'source-type-test',
|
|
]);
|
|
|
|
$this->assertEquals(LayoutSourceType::Template, $override->source_type);
|
|
$this->assertNotNull($override->source_identifier);
|
|
}
|
|
|
|
/**
|
|
* 여러 모듈의 오버라이드 등록 테스트
|
|
*/
|
|
public function test_register_overrides_for_multiple_modules(): void
|
|
{
|
|
// 두 모듈의 오버라이드 디렉토리 생성
|
|
$module1Path = $this->testTemplatePath.'/layouts/overrides/sirsoft-module1';
|
|
$module2Path = $this->testTemplatePath.'/layouts/overrides/sirsoft-module2';
|
|
File::makeDirectory($module1Path, 0755, true);
|
|
File::makeDirectory($module2Path, 0755, true);
|
|
|
|
// template.json 생성
|
|
$templateJson = [
|
|
'identifier' => 'test-admin',
|
|
'vendor' => 'test',
|
|
'name' => ['ko' => '테스트', 'en' => 'Test'],
|
|
'version' => '1.0.0',
|
|
'type' => 'admin',
|
|
];
|
|
File::put($this->testTemplatePath.'/template.json', json_encode($templateJson));
|
|
|
|
// 각 모듈의 오버라이드 레이아웃 생성
|
|
$layout1 = ['layout_name' => 'sirsoft-module1_admin_index', 'slots' => []];
|
|
$layout2 = ['layout_name' => 'sirsoft-module2_admin_index', 'slots' => []];
|
|
File::put($module1Path.'/admin_index.json', json_encode($layout1));
|
|
File::put($module2Path.'/admin_index.json', json_encode($layout2));
|
|
|
|
// 템플릿 로드
|
|
$this->templateManager->loadTemplates();
|
|
|
|
// 템플릿 레코드 생성
|
|
$template = Template::create([
|
|
'identifier' => 'test-admin',
|
|
'vendor' => 'test',
|
|
'name' => ['ko' => '테스트', 'en' => 'Test'],
|
|
'version' => '1.0.0',
|
|
'type' => 'admin',
|
|
'status' => ExtensionStatus::Inactive->value,
|
|
'description' => ['ko' => '', 'en' => ''],
|
|
]);
|
|
|
|
// 오버라이드 등록
|
|
$reflection = new \ReflectionClass($this->templateManager);
|
|
$method = $reflection->getMethod('registerLayoutOverrides');
|
|
$method->setAccessible(true);
|
|
$method->invoke($this->templateManager, 'test-admin', $template->id);
|
|
|
|
// 두 모듈의 오버라이드가 모두 등록되었는지 확인
|
|
$overrides = TemplateLayout::where('template_id', $template->id)
|
|
->where('source_type', LayoutSourceType::Template)
|
|
->whereNotNull('source_identifier')
|
|
->get();
|
|
|
|
$this->assertCount(2, $overrides);
|
|
}
|
|
|
|
/**
|
|
* updateOrCreate로 중복 오버라이드 방지 테스트
|
|
*/
|
|
public function test_prevent_duplicate_override_registration(): void
|
|
{
|
|
$template = Template::create([
|
|
'identifier' => 'duplicate-test',
|
|
'vendor' => 'test',
|
|
'name' => ['ko' => '중복테스트', 'en' => 'Duplicate Test'],
|
|
'version' => '1.0.0',
|
|
'type' => 'admin',
|
|
'status' => ExtensionStatus::Active->value,
|
|
'description' => ['ko' => '테스트용', 'en' => 'For testing'],
|
|
]);
|
|
|
|
$layoutName = 'sirsoft-sample_admin_index';
|
|
|
|
// 첫 번째 등록
|
|
TemplateLayout::updateOrCreate(
|
|
[
|
|
'template_id' => $template->id,
|
|
'name' => $layoutName,
|
|
],
|
|
[
|
|
'content' => ['version' => '1'],
|
|
'source_type' => LayoutSourceType::Template,
|
|
'source_identifier' => 'duplicate-test',
|
|
]
|
|
);
|
|
|
|
// 두 번째 등록 (업데이트)
|
|
TemplateLayout::updateOrCreate(
|
|
[
|
|
'template_id' => $template->id,
|
|
'name' => $layoutName,
|
|
],
|
|
[
|
|
'content' => ['version' => '2'],
|
|
'source_type' => LayoutSourceType::Template,
|
|
'source_identifier' => 'duplicate-test',
|
|
]
|
|
);
|
|
|
|
// 레이아웃이 1개만 존재
|
|
$count = TemplateLayout::where('template_id', $template->id)
|
|
->where('name', $layoutName)
|
|
->count();
|
|
|
|
$this->assertEquals(1, $count);
|
|
|
|
// 내용이 업데이트됨
|
|
$layout = TemplateLayout::where('template_id', $template->id)
|
|
->where('name', $layoutName)
|
|
->first();
|
|
|
|
$this->assertEquals('2', $layout->content['version']);
|
|
}
|
|
|
|
/**
|
|
* 오버라이드 디렉토리가 없는 경우 테스트
|
|
*/
|
|
public function test_handles_missing_overrides_directory(): void
|
|
{
|
|
// 기본 템플릿 구조만 생성 (overrides 없음)
|
|
File::makeDirectory($this->testTemplatePath.'/layouts', 0755, true);
|
|
|
|
$templateJson = [
|
|
'identifier' => 'test-admin',
|
|
'vendor' => 'test',
|
|
'name' => ['ko' => '테스트', 'en' => 'Test'],
|
|
'version' => '1.0.0',
|
|
'type' => 'admin',
|
|
];
|
|
File::put($this->testTemplatePath.'/template.json', json_encode($templateJson));
|
|
|
|
$this->templateManager->loadTemplates();
|
|
|
|
$template = Template::create([
|
|
'identifier' => 'test-admin',
|
|
'vendor' => 'test',
|
|
'name' => ['ko' => '테스트', 'en' => 'Test'],
|
|
'version' => '1.0.0',
|
|
'type' => 'admin',
|
|
'status' => ExtensionStatus::Inactive->value,
|
|
'description' => ['ko' => '', 'en' => ''],
|
|
]);
|
|
|
|
// 예외 없이 실행되어야 함
|
|
$reflection = new \ReflectionClass($this->templateManager);
|
|
$method = $reflection->getMethod('registerLayoutOverrides');
|
|
$method->setAccessible(true);
|
|
$method->invoke($this->templateManager, 'test-admin', $template->id);
|
|
|
|
// 오버라이드가 등록되지 않음
|
|
$overrides = TemplateLayout::where('template_id', $template->id)
|
|
->where('source_type', LayoutSourceType::Template)
|
|
->whereNotNull('source_identifier')
|
|
->count();
|
|
|
|
$this->assertEquals(0, $overrides);
|
|
}
|
|
|
|
/**
|
|
* incrementExtensionCacheVersion 메서드가 캐시 버전을 증가시키는지 테스트
|
|
*/
|
|
public function test_increment_extension_cache_version(): void
|
|
{
|
|
// 초기 캐시 버전 설정
|
|
Cache::put('g7:core:ext.cache_version', 1000);
|
|
|
|
$reflection = new \ReflectionClass($this->templateManager);
|
|
$method = $reflection->getMethod('incrementExtensionCacheVersion');
|
|
$method->setAccessible(true);
|
|
|
|
$beforeTime = time();
|
|
$method->invoke($this->templateManager);
|
|
$afterTime = time();
|
|
|
|
$newVersion = Cache::get('g7:core:ext.cache_version');
|
|
$this->assertGreaterThanOrEqual($beforeTime, $newVersion);
|
|
$this->assertLessThanOrEqual($afterTime, $newVersion);
|
|
}
|
|
|
|
/**
|
|
* getExtensionCacheVersion 정적 메서드 테스트
|
|
*/
|
|
public function test_get_extension_cache_version(): void
|
|
{
|
|
$expectedVersion = 1735000000;
|
|
Cache::put('g7:core:ext.cache_version', $expectedVersion);
|
|
|
|
// ClearsTemplateCaches trait의 정적 메서드 호출
|
|
$version = ClearsTemplateCaches::getExtensionCacheVersion();
|
|
|
|
$this->assertEquals($expectedVersion, $version);
|
|
}
|
|
|
|
/**
|
|
* 캐시 버전 키가 없으면 유효한 새 버전을 생성·저장하는지 테스트
|
|
*
|
|
* 0 을 반환하면 버전 기반 캐시 무효화가 통째로 붕괴한다(모든 URL 이 `?v=0`
|
|
* 으로 고정되어 갱신이 전파되지 않음). 키 부재는 캐시 플러시·TTL 만료로
|
|
* 언제든 발생하므로, 그 자리에서 새 버전을 만들어 저장한다.
|
|
*/
|
|
public function test_get_extension_cache_version_regenerates_when_not_set(): void
|
|
{
|
|
Cache::forget('g7:core:ext.cache_version');
|
|
|
|
$beforeTime = time();
|
|
$version = ClearsTemplateCaches::getExtensionCacheVersion();
|
|
$afterTime = time();
|
|
|
|
$this->assertGreaterThan(0, $version, '키 부재 시 0 을 반환하면 캐시 무효화가 붕괴합니다.');
|
|
$this->assertGreaterThanOrEqual($beforeTime, $version);
|
|
$this->assertLessThanOrEqual($afterTime, $version);
|
|
|
|
$this->assertEquals(
|
|
$version,
|
|
Cache::get('g7:core:ext.cache_version'),
|
|
'생성한 버전은 캐시에 저장되어 다음 조회에서 재사용되어야 합니다.'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* layout_name 변경 시 이전 이름의 고아 레코드가 삭제되는지 테스트
|
|
*/
|
|
public function test_cleanup_orphan_override_when_layout_name_changes(): void
|
|
{
|
|
$this->createTestTemplateStructure();
|
|
$this->templateManager->loadTemplates();
|
|
|
|
$template = Template::create([
|
|
'identifier' => 'test-admin',
|
|
'vendor' => 'test',
|
|
'name' => ['ko' => '테스트 관리자', 'en' => 'Test Admin'],
|
|
'version' => '1.0.0',
|
|
'type' => 'admin',
|
|
'status' => ExtensionStatus::Inactive->value,
|
|
'description' => ['ko' => '테스트용', 'en' => 'For testing'],
|
|
]);
|
|
|
|
// 이전 이름으로 오버라이드 레이아웃이 DB에 이미 등록된 상태 시뮬레이션
|
|
TemplateLayout::create([
|
|
'template_id' => $template->id,
|
|
'name' => 'sirsoft-sample_old_name',
|
|
'content' => ['slots' => []],
|
|
'source_type' => LayoutSourceType::Template,
|
|
'source_identifier' => 'test-admin',
|
|
]);
|
|
|
|
// registerLayoutOverrides 실행 (파일에는 sirsoft-sample_admin_index만 존재)
|
|
$reflection = new \ReflectionClass($this->templateManager);
|
|
$method = $reflection->getMethod('registerLayoutOverrides');
|
|
$method->setAccessible(true);
|
|
$method->invoke($this->templateManager, 'test-admin', $template->id);
|
|
|
|
// 새 이름의 오버라이드가 등록되어야 함
|
|
$newOverride = TemplateLayout::where('template_id', $template->id)
|
|
->where('name', 'sirsoft-sample_admin_index')
|
|
->first();
|
|
$this->assertNotNull($newOverride);
|
|
|
|
// 이전 이름의 고아 레코드가 삭제되어야 함
|
|
$oldOverride = TemplateLayout::where('template_id', $template->id)
|
|
->where('name', 'sirsoft-sample_old_name')
|
|
->first();
|
|
$this->assertNull($oldOverride);
|
|
}
|
|
|
|
/**
|
|
* 다른 템플릿의 오버라이드는 정리하지 않는지 테스트
|
|
*/
|
|
public function test_cleanup_does_not_affect_other_template_overrides(): void
|
|
{
|
|
$this->createTestTemplateStructure();
|
|
$this->templateManager->loadTemplates();
|
|
|
|
$template = Template::create([
|
|
'identifier' => 'test-admin',
|
|
'vendor' => 'test',
|
|
'name' => ['ko' => '테스트 관리자', 'en' => 'Test Admin'],
|
|
'version' => '1.0.0',
|
|
'type' => 'admin',
|
|
'status' => ExtensionStatus::Inactive->value,
|
|
'description' => ['ko' => '테스트용', 'en' => 'For testing'],
|
|
]);
|
|
|
|
// 다른 source_identifier를 가진 오버라이드 (다른 템플릿에서 등록)
|
|
TemplateLayout::create([
|
|
'template_id' => $template->id,
|
|
'name' => 'sirsoft-sample_other_layout',
|
|
'content' => ['slots' => []],
|
|
'source_type' => LayoutSourceType::Template,
|
|
'source_identifier' => 'other-template',
|
|
]);
|
|
|
|
// registerLayoutOverrides 실행
|
|
$reflection = new \ReflectionClass($this->templateManager);
|
|
$method = $reflection->getMethod('registerLayoutOverrides');
|
|
$method->setAccessible(true);
|
|
$method->invoke($this->templateManager, 'test-admin', $template->id);
|
|
|
|
// 다른 템플릿의 오버라이드는 삭제되지 않아야 함
|
|
$otherOverride = TemplateLayout::where('template_id', $template->id)
|
|
->where('name', 'sirsoft-sample_other_layout')
|
|
->where('source_identifier', 'other-template')
|
|
->first();
|
|
$this->assertNotNull($otherOverride);
|
|
}
|
|
|
|
/**
|
|
* overrides 디렉토리 삭제 시 기존 override가 모두 정리되는지 테스트
|
|
*/
|
|
public function test_cleanup_all_overrides_when_directory_removed(): void
|
|
{
|
|
$template = Template::create([
|
|
'identifier' => 'test-admin',
|
|
'vendor' => 'test',
|
|
'name' => ['ko' => '테스트 관리자', 'en' => 'Test Admin'],
|
|
'version' => '1.0.0',
|
|
'type' => 'admin',
|
|
'status' => ExtensionStatus::Inactive->value,
|
|
'description' => ['ko' => '테스트용', 'en' => 'For testing'],
|
|
]);
|
|
|
|
// 기존 오버라이드 레이아웃이 DB에 존재
|
|
TemplateLayout::create([
|
|
'template_id' => $template->id,
|
|
'name' => 'sirsoft-sample_admin_index',
|
|
'content' => ['slots' => []],
|
|
'source_type' => LayoutSourceType::Template,
|
|
'source_identifier' => 'test-admin',
|
|
]);
|
|
|
|
TemplateLayout::create([
|
|
'template_id' => $template->id,
|
|
'name' => 'sirsoft-sample_admin_edit',
|
|
'content' => ['slots' => []],
|
|
'source_type' => LayoutSourceType::Template,
|
|
'source_identifier' => 'test-admin',
|
|
]);
|
|
|
|
// overrides 디렉토리가 없는 상태에서 registerLayoutOverrides 실행
|
|
// (templates/test-admin/layouts/overrides가 존재하지 않음)
|
|
$reflection = new \ReflectionClass($this->templateManager);
|
|
$method = $reflection->getMethod('registerLayoutOverrides');
|
|
$method->setAccessible(true);
|
|
$method->invoke($this->templateManager, 'test-admin', $template->id);
|
|
|
|
// 모든 오버라이드가 삭제되어야 함
|
|
$overrides = TemplateLayout::where('template_id', $template->id)
|
|
->where('source_type', LayoutSourceType::Template)
|
|
->where('source_identifier', 'test-admin')
|
|
->count();
|
|
$this->assertEquals(0, $overrides);
|
|
}
|
|
|
|
/**
|
|
* 고아 레코드 정리 시 관련 캐시도 삭제되는지 테스트
|
|
*/
|
|
public function test_cleanup_orphan_overrides_also_clears_cache(): void
|
|
{
|
|
$template = Template::create([
|
|
'identifier' => 'test-admin',
|
|
'vendor' => 'test',
|
|
'name' => ['ko' => '테스트 관리자', 'en' => 'Test Admin'],
|
|
'version' => '1.0.0',
|
|
'type' => 'admin',
|
|
'status' => ExtensionStatus::Inactive->value,
|
|
'description' => ['ko' => '테스트용', 'en' => 'For testing'],
|
|
]);
|
|
|
|
$orphanName = 'sirsoft-sample_orphan_layout';
|
|
|
|
// 고아 레이아웃 DB에 생성
|
|
TemplateLayout::create([
|
|
'template_id' => $template->id,
|
|
'name' => $orphanName,
|
|
'content' => ['slots' => []],
|
|
'source_type' => LayoutSourceType::Template,
|
|
'source_identifier' => 'test-admin',
|
|
]);
|
|
|
|
// 캐시 설정
|
|
$cacheKey = "g7:core:template.{$template->id}.layout.{$orphanName}";
|
|
Cache::put($cacheKey, ['cached' => true], 3600);
|
|
$this->assertNotNull(Cache::get($cacheKey));
|
|
|
|
// cleanupOrphanOverrideLayouts 실행 (등록된 이름 목록은 비어있음)
|
|
$reflection = new \ReflectionClass($this->templateManager);
|
|
$method = $reflection->getMethod('cleanupOrphanOverrideLayouts');
|
|
$method->setAccessible(true);
|
|
$method->invoke($this->templateManager, $template->id, 'test-admin', []);
|
|
|
|
// 캐시도 삭제되어야 함
|
|
$this->assertNull(Cache::get($cacheKey));
|
|
|
|
// DB 레코드도 삭제되어야 함
|
|
$orphan = TemplateLayout::where('template_id', $template->id)
|
|
->where('name', $orphanName)
|
|
->first();
|
|
$this->assertNull($orphan);
|
|
}
|
|
}
|