rendererMock = Mockery::mock(SeoRendererInterface::class); $this->cacheMock = Mockery::mock(SeoCacheManagerInterface::class); $this->regenerator = new SeoCacheRegenerator( $this->rendererMock, $this->cacheMock, ); } protected function tearDown(): void { Mockery::close(); parent::tearDown(); } // ─── 기본 재생성 ────────────────────────────────────── /** * 렌더링 성공 시 캐시에 저장되는지 확인 */ public function test_render_and_cache_stores_html_on_success(): void { config(['app.locale' => 'ko']); config(['app.supported_locales' => ['ko']]); $this->rendererMock->shouldReceive('render') ->once() ->with(Mockery::on(function (Request $request) { return $request->getPathInfo() === '/shop/products/1'; })) ->andReturnUsing(function (Request $request) { $request->attributes->set('seo_layout_name', 'shop/show'); return 'Product 1'; }); $this->cacheMock->shouldReceive('putWithLayout') ->once() ->with('/shop/products/1', 'ko', 'Product 1', 'shop/show'); Log::shouldReceive('debug')->atLeast()->once(); $result = $this->regenerator->renderAndCache('/shop/products/1'); $this->assertTrue($result); } /** * 렌더링 결과가 null이면 캐시 저장을 건너뛰는지 확인 */ public function test_render_and_cache_skips_when_render_returns_null(): void { config(['app.locale' => 'ko']); config(['app.supported_locales' => ['ko']]); $this->rendererMock->shouldReceive('render') ->once() ->andReturn(null); $this->cacheMock->shouldNotReceive('putWithLayout'); $result = $this->regenerator->renderAndCache('/nonexistent'); $this->assertFalse($result); } // ─── 다국어 재생성 ────────────────────────────────────── /** * 지원 로케일별로 각각 렌더링 + 캐시 저장이 수행되는지 확인 */ public function test_render_and_cache_iterates_supported_locales(): void { config(['app.locale' => 'ko']); config(['app.supported_locales' => ['ko', 'en']]); $this->rendererMock->shouldReceive('render') ->twice() ->andReturnUsing(function (Request $request) { $locale = app()->getLocale(); $request->attributes->set('seo_layout_name', 'shop/show'); return "Product ({$locale})"; }); $this->cacheMock->shouldReceive('putWithLayout') ->once() ->with('/shop/products/1', 'ko', Mockery::type('string'), 'shop/show'); $this->cacheMock->shouldReceive('putWithLayout') ->once() ->with('/shop/products/1', 'en', Mockery::type('string'), 'shop/show'); Log::shouldReceive('debug')->atLeast()->once(); $result = $this->regenerator->renderAndCache('/shop/products/1'); $this->assertTrue($result); } // ─── 예외 처리 ────────────────────────────────────── /** * 렌더링 중 예외 발생 시 graceful하게 처리되는지 확인 */ public function test_render_and_cache_handles_exceptions_gracefully(): void { config(['app.locale' => 'ko']); config(['app.supported_locales' => ['ko']]); $this->rendererMock->shouldReceive('render') ->once() ->andThrow(new \RuntimeException('Renderer failed')); $this->cacheMock->shouldNotReceive('putWithLayout'); Log::shouldReceive('warning') ->once() ->with('[SEO] Cache regeneration failed', Mockery::on(function ($context) { return $context['url'] === '/test' && $context['error'] === 'Renderer failed'; })); $result = $this->regenerator->renderAndCache('/test'); $this->assertFalse($result); } /** * 일부 로케일만 실패해도 나머지는 정상 처리되는지 확인 */ public function test_render_and_cache_continues_on_partial_locale_failure(): void { config(['app.locale' => 'ko']); config(['app.supported_locales' => ['ko', 'en']]); $callCount = 0; $this->rendererMock->shouldReceive('render') ->twice() ->andReturnUsing(function (Request $request) use (&$callCount) { $callCount++; if ($callCount === 1) { throw new \RuntimeException('ko render failed'); } $request->attributes->set('seo_layout_name', 'shop/show'); return 'EN'; }); $this->cacheMock->shouldReceive('putWithLayout') ->once() ->with('/shop/products/1', 'en', 'EN', 'shop/show'); Log::shouldReceive('warning')->once(); Log::shouldReceive('debug')->atLeast()->once(); $result = $this->regenerator->renderAndCache('/shop/products/1'); $this->assertTrue($result); } /** * 로케일이 렌더링 후 원래 값으로 복원되는지 확인 */ public function test_render_and_cache_restores_locale(): void { config(['app.locale' => 'ko']); config(['app.supported_locales' => ['en']]); app()->setLocale('ko'); $this->rendererMock->shouldReceive('render') ->once() ->andReturnUsing(function (Request $request) { $request->attributes->set('seo_layout_name', 'test'); return 'EN'; }); $this->cacheMock->shouldReceive('putWithLayout')->once(); Log::shouldReceive('debug')->atLeast()->once(); $this->regenerator->renderAndCache('/test'); $this->assertEquals('ko', app()->getLocale()); } }