'ko']); } protected function tearDown(): void { HookManager::clearFilter(self::FILTER_HOOK); parent::tearDown(); } /** * @param array $attributes 덮어쓸 속성 * @return Page 생성된 페이지 */ private function createPage(array $attributes = []): Page { return Page::factory()->create($attributes); } /** * @scenario image_source=content_internal_only, locale_content=default_locale * * @effects content_image_fills_page_og */ #[Test] public function saving_extracts_first_internal_image(): void { $page = $this->createPage([ 'content' => [ 'ko' => '
본문
',
'en' => '
',
],
]);
$this->assertSame('/storage/pages/ko-first.jpg', $page->fresh()->content_thumbnail_url);
}
/**
* @scenario image_source=content_internal_only, locale_content=other_locale_only
*
* @effects other_locale_image_used_when_default_has_none
*/
#[Test]
public function falls_back_to_other_locale(): void
{
$page = $this->createPage([
'content' => [
'ko' => '이미지 없음
', 'en' => '
',
],
]);
$this->assertSame('/storage/pages/en-only.jpg', $page->fresh()->content_thumbnail_url);
}
/**
* @scenario image_source=content_external_only, locale_content=default_locale
*
* @effects external_only_content_yields_null
*/
#[Test]
public function external_only_content_yields_null(): void
{
$page = $this->createPage([
'content' => ['ko' => '
'],
]);
$this->assertNull($page->fresh()->content_thumbnail_url);
}
/**
* @scenario image_source=none, locale_content=default_locale
*
* @effects external_only_content_yields_null
*/
#[Test]
public function text_mode_and_no_image_yield_null(): void
{
$noImage = $this->createPage(['content' => ['ko' => '이미지 없음
']]); $textMode = $this->createPage([ 'content_mode' => 'text', 'content' => ['ko' => '텍스트
마크업'],
]);
$this->assertNull($noImage->fresh()->content_thumbnail_url);
$this->assertNull($textMode->fresh()->content_thumbnail_url, 'text 모드는 캐시하지 않아야 합니다.');
}
/**
* @effects recompute_on_content_change
*/
#[Test]
public function content_change_recomputes_and_unrelated_update_does_not(): void
{
$page = $this->createPage([
'content' => ['ko' => '
'],
]);
$page->update(['content' => ['ko' => '
']]);
$this->assertSame('/storage/pages/new.jpg', $page->fresh()->content_thumbnail_url);
DB::table('pages')->where('id', $page->id)
->update(['content_thumbnail_url' => '/storage/pages/manual.jpg']);
$page->fresh()->update(['published' => true, 'published_at' => now()]);
$this->assertSame(
'/storage/pages/manual.jpg',
$page->fresh()->content_thumbnail_url,
'content 가 dirty 가 아니면 재계산하지 않아야 합니다.'
);
}
/**
* @effects filter_hook_can_override_or_block
*/
#[Test]
public function filter_hook_can_override_or_block(): void
{
HookManager::addFilter(self::FILTER_HOOK, fn ($value) => null);
$blocked = $this->createPage([
'content' => ['ko' => '
'],
]);
$this->assertNull($blocked->fresh()->content_thumbnail_url);
}
/**
* 버전 롤백 경로(Repository 직접 update)에서도 캐시가 재계산되어야 합니다.
*
* @effects version_restore_recomputes_cache
*/
#[Test]
public function model_level_update_recomputes_cache(): void
{
$page = $this->createPage([
'content' => ['ko' => '
'],
]);
// restoreVersion 은 Repository 를 직접 호출한다 — 모델 update 경로와 동일한
// saving 이벤트를 타므로 모델 레벨 update 로 등가 검증한다
Page::findOrFail($page->id)->update([
'content' => ['ko' => '
'],
]);
$this->assertSame('/storage/pages/v2-restored.jpg', $page->fresh()->content_thumbnail_url);
}
}