feat(core,ecommerce,ckeditor5,board,admin_basic): 공개 자산 직접 URL(CDN) 서빙 옵트인
StorageInterface::url 의 public 하드코딩을 완화하고 core.storage.filter_url 훅을 신설해, 완전 공개 자산(상품·카테고리·리뷰·에디터 이미지)의 직접 URL 서빙을 옵트인으로 열었다. 미설정 시 기존 스트리밍 100% 보존. 디스크 결정은 확장 개별 설정 > 코어 전역 > 미설정 순이며, 서빙·삭제·이동은 행에 기록된 disk 를 따라 전환 전후 자산이 혼재해도 각자 실제 저장 위치를 향한다. 고아 disk(공급 플러그인 비활성화)는 스트리밍으로 폴백해 500 이 되지 않는다. 공개 이슈 (@lyg-kaban 건의) 대응.
This commit is contained in:
@@ -2,11 +2,14 @@
|
||||
|
||||
namespace Tests\Unit\Extension\Storage;
|
||||
|
||||
use App\Extension\HookManager;
|
||||
use App\Extension\Storage\ModuleStorageDriver;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Tests\TestCase;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* ModuleStorageDriver 단위 테스트
|
||||
@@ -32,6 +35,7 @@ class ModuleStorageDriverTest extends TestCase
|
||||
{
|
||||
// 테스트 데이터 정리
|
||||
Storage::disk('modules')->deleteDirectory('test-module');
|
||||
HookManager::resetAll();
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
@@ -189,6 +193,74 @@ class ModuleStorageDriverTest extends TestCase
|
||||
Storage::disk('public')->deleteDirectory('test-module');
|
||||
}
|
||||
|
||||
/**
|
||||
* url 이 설정된 디스크(S3+CDN 등)면 모듈 드라이버도 직접 URL 을 돌려준다.
|
||||
*
|
||||
* @effects url_returned_for_public_and_url_configured_disk
|
||||
*/
|
||||
#[Test]
|
||||
public function it_returns_direct_url_for_url_configured_disk(): void
|
||||
{
|
||||
Config::set('filesystems.disks.fake_cdn', [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('framework/testing/disks/fake_cdn'),
|
||||
'url' => 'https://cdn.test/assets',
|
||||
]);
|
||||
|
||||
$cdnDriver = new ModuleStorageDriver('test-module', 'fake_cdn');
|
||||
|
||||
$this->assertSame(
|
||||
'https://cdn.test/assets/test-module/images/test.jpg',
|
||||
$cdnDriver->url('images', 'test.jpg')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* url 값이 빈 문자열/공백이면 직접 URL 로 보지 않는다 (AWS_URL 미설정 방어).
|
||||
*
|
||||
* @effects blank_url_config_defended
|
||||
*/
|
||||
#[Test]
|
||||
public function it_returns_null_when_disk_url_is_blank(): void
|
||||
{
|
||||
Config::set('filesystems.disks.fake_cdn', [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('framework/testing/disks/fake_cdn'),
|
||||
'url' => ' ',
|
||||
]);
|
||||
|
||||
$this->assertNull((new ModuleStorageDriver('test-module', 'fake_cdn'))->url('images', 'test.jpg'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 모듈 드라이버의 `core.storage.filter_url` 컨텍스트는 scope=module +
|
||||
* 확장 식별자를 싣는다 (코어/플러그인과 구분되는 축).
|
||||
*
|
||||
* @effects filter_url_hook_always_fires_with_six_context_keys
|
||||
*/
|
||||
#[Test]
|
||||
public function it_passes_module_scope_and_identifier_to_url_filter_hook(): void
|
||||
{
|
||||
$captured = null;
|
||||
|
||||
HookManager::addFilter('core.storage.filter_url', function ($url, $context) use (&$captured) {
|
||||
$captured = $context;
|
||||
|
||||
return $url;
|
||||
});
|
||||
|
||||
(new ModuleStorageDriver('test-module', 'modules'))->url('images', 'sub/a.jpg');
|
||||
|
||||
$this->assertSame([
|
||||
'scope' => 'module',
|
||||
'identifier' => 'test-module',
|
||||
'disk' => 'modules',
|
||||
'category' => 'images',
|
||||
'path' => 'sub/a.jpg',
|
||||
'full_path' => 'test-module/images/sub/a.jpg',
|
||||
], $captured);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function it_uses_correct_path_pattern(): void
|
||||
{
|
||||
@@ -218,7 +290,7 @@ class ModuleStorageDriverTest extends TestCase
|
||||
]);
|
||||
|
||||
// Then: StreamedResponse 반환
|
||||
$this->assertInstanceOf(\Symfony\Component\HttpFoundation\StreamedResponse::class, $response);
|
||||
$this->assertInstanceOf(StreamedResponse::class, $response);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
|
||||
Reference in New Issue
Block a user