StorageInterface::url 의 public 하드코딩을 완화하고 core.storage.filter_url 훅을 신설해, 완전 공개 자산(상품·카테고리·리뷰·에디터 이미지)의 직접 URL 서빙을 옵트인으로 열었다. 미설정 시 기존 스트리밍 100% 보존. 디스크 결정은 확장 개별 설정 > 코어 전역 > 미설정 순이며, 서빙·삭제·이동은 행에 기록된 disk 를 따라 전환 전후 자산이 혼재해도 각자 실제 저장 위치를 향한다. 고아 disk(공급 플러그인 비활성화)는 스트리밍으로 폴백해 500 이 되지 않는다. 공개 이슈 (@lyg-kaban 건의) 대응.
153 lines
5.3 KiB
PHP
153 lines
5.3 KiB
PHP
<?php
|
|
|
|
namespace Modules\Sirsoft\Ecommerce\Tests\Unit\Models;
|
|
|
|
use App\Extension\HookManager;
|
|
use Illuminate\Support\Facades\Config;
|
|
use Modules\Sirsoft\Ecommerce\Models\CategoryImage;
|
|
use Modules\Sirsoft\Ecommerce\Models\ProductImage;
|
|
use Modules\Sirsoft\Ecommerce\Models\ProductReviewImage;
|
|
use Modules\Sirsoft\Ecommerce\Support\AssetStorage;
|
|
use Modules\Sirsoft\Ecommerce\Tests\ModuleTestCase;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
|
|
/**
|
|
* HasDirectAssetUrl(download_url 직접 URL/폴백) 단위 테스트
|
|
*
|
|
* 행 disk 가 직접 URL 지원 디스크(fake_cdn)면 CDN URL, 아니면 기존 API 경로 폴백을
|
|
* 3개 이미지 모델(상품/카테고리/리뷰)에서 검증합니다. 혼재 운용(구 로컬 행 + 신규
|
|
* 원격 행)과 훅 차단 폴백도 함께 고정합니다.
|
|
*/
|
|
class HasDirectAssetUrlTest extends ModuleTestCase
|
|
{
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
Config::set('filesystems.disks.fake_cdn', [
|
|
'driver' => 'local',
|
|
'root' => storage_path('framework/testing/disks/fake_cdn'),
|
|
'url' => 'https://cdn.test/assets',
|
|
]);
|
|
AssetStorage::flush();
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
AssetStorage::flush();
|
|
HookManager::resetAll();
|
|
|
|
parent::tearDown();
|
|
}
|
|
|
|
#[Test]
|
|
public function product_image_falls_back_to_api_url_for_local_disk_row(): void
|
|
{
|
|
$image = new ProductImage([
|
|
'hash' => 'aaaabbbbcccc',
|
|
'disk' => 'modules',
|
|
'path' => 'products/P001/a.jpg',
|
|
]);
|
|
|
|
$this->assertSame(
|
|
'/api/modules/sirsoft-ecommerce/product-image/aaaabbbbcccc',
|
|
$image->download_url
|
|
);
|
|
}
|
|
|
|
#[Test]
|
|
public function product_image_returns_direct_url_for_cdn_disk_row(): void
|
|
{
|
|
$image = new ProductImage([
|
|
'hash' => 'aaaabbbbcccc',
|
|
'disk' => 'fake_cdn',
|
|
'path' => 'products/P001/a.jpg',
|
|
]);
|
|
|
|
$this->assertSame(
|
|
'https://cdn.test/assets/sirsoft-ecommerce/images/products/P001/a.jpg',
|
|
$image->download_url
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @scenario consumer=category, disk_setting=public, e2e=ecommerce_settings_field, hook=supply, override=module_override, row_state=new_remote_row
|
|
*
|
|
* @effects download_url_falls_back_to_api_path_when_direct_unavailable
|
|
*/
|
|
#[Test]
|
|
public function category_and_review_images_resolve_direct_url_and_fallback(): void
|
|
{
|
|
$cdnCategory = new CategoryImage([
|
|
'hash' => 'ccc111222333',
|
|
'disk' => 'fake_cdn',
|
|
'path' => 'category/2026/01/01/c.jpg',
|
|
]);
|
|
$localReview = new ProductReviewImage([
|
|
'hash' => 'rrr111222333',
|
|
'disk' => 'modules',
|
|
'path' => 'reviews/1/r.jpg',
|
|
]);
|
|
|
|
$this->assertSame(
|
|
'https://cdn.test/assets/sirsoft-ecommerce/images/category/2026/01/01/c.jpg',
|
|
$cdnCategory->download_url
|
|
);
|
|
$this->assertSame(
|
|
'/api/modules/sirsoft-ecommerce/review-image/rrr111222333',
|
|
$localReview->download_url
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @effects mixed_rows_resolve_per_row_disk
|
|
*/
|
|
#[Test]
|
|
public function mixed_rows_resolve_urls_per_row_disk(): void
|
|
{
|
|
// 혼재 운용 — 디스크 전환 이전 로컬 행과 이후 원격 행이 각자 disk 기준으로 해석
|
|
$localRow = new ProductImage(['hash' => 'llllllllllll', 'disk' => 'modules', 'path' => 'products/P001/old.jpg']);
|
|
$remoteRow = new ProductImage(['hash' => 'rrrrrrrrrrrr', 'disk' => 'fake_cdn', 'path' => 'products/P001/new.jpg']);
|
|
|
|
$this->assertStringStartsWith('/api/modules/sirsoft-ecommerce/product-image/', $localRow->download_url);
|
|
$this->assertStringStartsWith('https://cdn.test/assets/', $remoteRow->download_url);
|
|
}
|
|
|
|
/**
|
|
* @scenario consumer=review, disk_setting=s3_without_url, e2e=ecommerce_settings_field, hook=unregistered, override=follow_core, row_state=new_remote_row
|
|
*
|
|
* @effects download_url_falls_back_to_api_path_when_direct_unavailable
|
|
*/
|
|
#[Test]
|
|
public function blank_disk_or_path_falls_back_to_api_url(): void
|
|
{
|
|
$noDisk = new ProductImage(['hash' => 'aaaabbbbcccc', 'disk' => '', 'path' => 'products/P001/a.jpg']);
|
|
$noPath = new ProductImage(['hash' => 'aaaabbbbcccc', 'disk' => 'fake_cdn', 'path' => '']);
|
|
|
|
$this->assertSame('/api/modules/sirsoft-ecommerce/product-image/aaaabbbbcccc', $noDisk->download_url);
|
|
$this->assertSame('/api/modules/sirsoft-ecommerce/product-image/aaaabbbbcccc', $noPath->download_url);
|
|
}
|
|
|
|
/**
|
|
* @scenario consumer=category, disk_setting=none, e2e=ecommerce_settings_field, hook=block, override=follow_core, row_state=legacy_local_row
|
|
*
|
|
* @effects download_url_falls_back_to_api_path_when_direct_unavailable
|
|
*/
|
|
#[Test]
|
|
public function hook_block_falls_back_to_api_url(): void
|
|
{
|
|
HookManager::addFilter('core.storage.filter_url', fn ($url) => '');
|
|
|
|
$image = new ProductImage([
|
|
'hash' => 'aaaabbbbcccc',
|
|
'disk' => 'fake_cdn',
|
|
'path' => 'products/P001/a.jpg',
|
|
]);
|
|
|
|
$this->assertSame(
|
|
'/api/modules/sirsoft-ecommerce/product-image/aaaabbbbcccc',
|
|
$image->download_url
|
|
);
|
|
}
|
|
}
|