- 버전 이력 변경량(+N/-N 줄) 계산이 변경 영역 (줄 수)² 크기의 LCS 표를 만들어 큰 공통 레이아웃의 첫 편집기 저장이 PHP 기본 메모리 한도(128M) 서버에서 500 으로 끝났다. 두 행 DP 로 길이만 구하도록 바꿔 메모리가 줄 수에 비례하고 표시 숫자는 종전과 같다(참조 구현 동치·메모리 상한 회귀 테스트) - 확장 편집 모드 저장이 overlay 확장의 injections 를 비우던 결함: 서버가 주입 노드 출처 메타에 injection 순번을 싣고, 편집기는 그 순번(없으면 원본 노드 id) 으로 되돌리며, 되돌릴 수 없는 노드가 있으면 저장하지 않고 안내한다 - 서빙 캐시 키를 서버 현재 확장 캐시 버전으로만 조립해 저장·복원 두 번째부터 재로드·「최신 불러오기」가 옛 내용을 받던 결함 수정(요청 v 는 HTTP 캐시 우회용) - 409 충돌 안내가 errors 아래의 버전을 읽지 못해 「최신 버전: -1」 로 표시되던 결함을 세 저장 경로 공용 판독으로 수정 - 버전 저장 시 저장자를 기록하고, 복원 시에도 잠금 번호를 올린다 - 회귀 테스트(PHPUnit·Vitest), 트러블슈팅 사례 32~34, 규정·API 문서, ja 언어팩, 편집기 번들 재빌드, 이력 문서 동반
126 lines
5.4 KiB
PHP
126 lines
5.4 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Template;
|
|
|
|
use App\Contracts\Repositories\LayoutRepositoryInterface;
|
|
use App\Enums\ExtensionStatus;
|
|
use App\Extension\Cache\CoreCacheDriver;
|
|
use App\Extension\Traits\InvalidatesLayoutCache;
|
|
use App\Models\Template;
|
|
use App\Models\TemplateLayout;
|
|
use App\Services\ExtensionStaticCacheService;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* 레이아웃 편집기(`with_source_meta=1`) 캐시 무효화 회귀
|
|
*
|
|
* 배경: `PublicLayoutController::serve()` 는 일반 응답과 레이아웃 편집기 응답
|
|
* (`with_source_meta=1`)을 **별도 캐시 키**(`.meta` 접미사)로 저장한다. 템플릿/
|
|
* 레이아웃 상태 변화(refresh-layout / activate / deactivate / uninstall 등) 시
|
|
* 공통으로 거치는 `InvalidatesLayoutCache::forgetLayoutCacheKeys()` 가 일반 키만
|
|
* 삭제하고 `.meta` 키를 누락하면, 사용자 페이지는 갱신되지만 **편집기 캔버스만
|
|
* stale 캐시**(옛 레이아웃)를 받는다( 재현 — 사용자는 복원, 편집기는
|
|
* 미복원). 레이아웃 저장 경로(`LayoutService::clearPublicServingCache`)는 이미 두
|
|
* 키를 지우므로, 본 테스트는 상태 변화 공통 지점이 두 키를 모두 무효화함을 잠근다.
|
|
*/
|
|
class LayoutEditorCacheInvalidationTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
/** trait 를 노출하는 익명 테스트 더블 */
|
|
private function makeInvalidator(): object
|
|
{
|
|
return new class
|
|
{
|
|
use InvalidatesLayoutCache;
|
|
|
|
public LayoutRepositoryInterface $layoutRepository;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->layoutRepository = app(LayoutRepositoryInterface::class);
|
|
}
|
|
|
|
/** protected 메서드 노출 */
|
|
public function invalidate(int $templateId, string $identifier): void
|
|
{
|
|
$this->invalidateTemplateLayoutCache($templateId, $identifier);
|
|
}
|
|
};
|
|
}
|
|
|
|
private function coreCache(): CoreCacheDriver
|
|
{
|
|
return new CoreCacheDriver(config('cache.default', 'array'));
|
|
}
|
|
|
|
#[Test]
|
|
public function 레이아웃_캐시_무효화는_일반키와_편집기meta키를_모두_삭제한다(): void
|
|
{
|
|
$template = Template::factory()->create([
|
|
'identifier' => 'sirsoft-basic',
|
|
'type' => 'user',
|
|
'status' => ExtensionStatus::Active->value,
|
|
]);
|
|
TemplateLayout::create([
|
|
'template_id' => $template->id,
|
|
'name' => 'home',
|
|
'content' => ['meta' => ['title' => 'Home'], 'components' => []],
|
|
]);
|
|
|
|
$cache = $this->coreCache();
|
|
// 트레이트 게터와 같은 근거 — 원시 키 읽기는 키 부재 시 0 을 돌려주어 실제 무효화 키(`.v{time}`)와
|
|
// 어긋난 `.v0` 키를 만든다(트레이트는 부재 시 재생성). 무효화 대상 키는 게터가 SSoT 다.
|
|
$version = ExtensionStaticCacheService::getExtensionCacheVersion();
|
|
|
|
// PublicLayoutController::serve() 가 쓰는 두 캐시 키 — 일반 + 편집기(.meta)
|
|
$generalKey = "layout.sirsoft-basic.home.v{$version}";
|
|
$metaKey = "layout.sirsoft-basic.home.v{$version}.meta";
|
|
$cache->put($generalKey, ['cached' => 'general'], 3600);
|
|
$cache->put($metaKey, ['cached' => 'meta'], 3600);
|
|
|
|
// 사전 조건: 두 키 모두 캐시에 존재
|
|
$this->assertNotNull($cache->get($generalKey), '사전: 일반 키 캐시 존재');
|
|
$this->assertNotNull($cache->get($metaKey), '사전: 편집기 .meta 키 캐시 존재');
|
|
|
|
// 상태 변화 공통 지점 — 레이아웃 캐시 무효화
|
|
$this->makeInvalidator()->invalidate($template->id, 'sirsoft-basic');
|
|
|
|
// 두 키 모두 무효화되어야 함 (.meta 누락이면 편집기 stale 회귀)
|
|
$this->assertNull($cache->get($generalKey), '일반 키가 무효화됨');
|
|
$this->assertNull($cache->get($metaKey), '편집기.meta 키도 무효화됨');
|
|
}
|
|
|
|
#[Test]
|
|
public function 템플릿_레이아웃_refresh_후_편집기meta_캐시가_stale로_남지_않는다(): void
|
|
{
|
|
$template = Template::factory()->create([
|
|
'identifier' => 'sirsoft-basic',
|
|
'type' => 'user',
|
|
'status' => ExtensionStatus::Active->value,
|
|
]);
|
|
TemplateLayout::create([
|
|
'template_id' => $template->id,
|
|
'name' => 'home',
|
|
'content' => ['meta' => ['title' => 'Home'], 'components' => []],
|
|
]);
|
|
|
|
$cache = $this->coreCache();
|
|
// 트레이트 게터와 같은 근거 — 원시 키 읽기는 키 부재 시 0 을 돌려주어 실제 무효화 키(`.v{time}`)와
|
|
// 어긋난 `.v0` 키를 만든다(트레이트는 부재 시 재생성). 무효화 대상 키는 게터가 SSoT 다.
|
|
$version = ExtensionStaticCacheService::getExtensionCacheVersion();
|
|
$metaKey = "layout.sirsoft-basic.home.v{$version}.meta";
|
|
$cache->put($metaKey, ['stale' => 'editor-old-content'], 3600);
|
|
|
|
// 상태 변화 공통 지점 호출 → 편집기 .meta 키가 stale 로 남으면 실패
|
|
$this->makeInvalidator()->invalidate($template->id, 'sirsoft-basic');
|
|
|
|
$this->assertNull(
|
|
$cache->get($metaKey),
|
|
'refresh/상태변화 후 편집기 .meta 캐시가 stale 로 잔존하면 편집기 캔버스가 옛 레이아웃을 표시한다',
|
|
);
|
|
}
|
|
}
|