271 lines
8.8 KiB
PHP
271 lines
8.8 KiB
PHP
<?php
|
|
|
|
namespace Tests\Unit\Extension\Storage;
|
|
|
|
use App\Extension\Storage\ModuleStorageDriver;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Tests\TestCase;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
|
|
/**
|
|
* ModuleStorageDriver 단위 테스트
|
|
*/
|
|
class ModuleStorageDriverTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
private ModuleStorageDriver $driver;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
// 테스트용 스토리지 드라이버 생성
|
|
$this->driver = new ModuleStorageDriver('test-module', 'modules');
|
|
|
|
// 테스트 디렉토리 초기화
|
|
Storage::disk('modules')->deleteDirectory('test-module');
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
// 테스트 데이터 정리
|
|
Storage::disk('modules')->deleteDirectory('test-module');
|
|
|
|
parent::tearDown();
|
|
}
|
|
|
|
#[Test]
|
|
public function it_can_put_and_get_file(): void
|
|
{
|
|
// Given: 파일 내용
|
|
$content = 'test content';
|
|
|
|
// When: 파일 저장
|
|
$result = $this->driver->put('settings', 'config.json', $content);
|
|
|
|
// Then: 저장 성공
|
|
$this->assertTrue($result);
|
|
|
|
// And: 파일 읽기 성공
|
|
$retrieved = $this->driver->get('settings', 'config.json');
|
|
$this->assertEquals($content, $retrieved);
|
|
}
|
|
|
|
#[Test]
|
|
public function it_returns_null_for_non_existent_file(): void
|
|
{
|
|
// When: 존재하지 않는 파일 읽기
|
|
$result = $this->driver->get('settings', 'non-existent.json');
|
|
|
|
// Then: null 반환
|
|
$this->assertNull($result);
|
|
}
|
|
|
|
#[Test]
|
|
public function it_can_check_file_existence(): void
|
|
{
|
|
// Given: 파일 저장
|
|
$this->driver->put('settings', 'config.json', 'test');
|
|
|
|
// When & Then: 파일 존재 확인
|
|
$this->assertTrue($this->driver->exists('settings', 'config.json'));
|
|
$this->assertFalse($this->driver->exists('settings', 'non-existent.json'));
|
|
}
|
|
|
|
#[Test]
|
|
public function it_can_delete_file(): void
|
|
{
|
|
// Given: 파일 저장
|
|
$this->driver->put('settings', 'config.json', 'test');
|
|
|
|
// When: 파일 삭제
|
|
$result = $this->driver->delete('settings', 'config.json');
|
|
|
|
// Then: 삭제 성공
|
|
$this->assertTrue($result);
|
|
$this->assertFalse($this->driver->exists('settings', 'config.json'));
|
|
}
|
|
|
|
#[Test]
|
|
public function it_can_list_files_in_category(): void
|
|
{
|
|
// Given: 여러 파일 저장
|
|
$this->driver->put('attachments', 'file1.txt', 'content1');
|
|
$this->driver->put('attachments', 'file2.txt', 'content2');
|
|
$this->driver->put('attachments', 'subdir/file3.txt', 'content3');
|
|
|
|
// When: 파일 목록 조회
|
|
$files = $this->driver->files('attachments');
|
|
|
|
// Then: 파일 목록 확인 (재귀적으로 모든 파일 포함)
|
|
$this->assertIsArray($files);
|
|
$this->assertNotEmpty($files);
|
|
}
|
|
|
|
#[Test]
|
|
public function it_can_delete_directory(): void
|
|
{
|
|
// Given: 디렉토리와 파일 생성
|
|
$this->driver->put('cache', 'dir1/file1.txt', 'content1');
|
|
$this->driver->put('cache', 'dir1/file2.txt', 'content2');
|
|
|
|
// When: 디렉토리 삭제
|
|
$result = $this->driver->deleteDirectory('cache', 'dir1');
|
|
|
|
// Then: 삭제 성공
|
|
$this->assertTrue($result);
|
|
$this->assertFalse($this->driver->exists('cache', 'dir1/file1.txt'));
|
|
}
|
|
|
|
#[Test]
|
|
public function it_can_delete_all_files_in_category(): void
|
|
{
|
|
// Given: 여러 파일 저장
|
|
$this->driver->put('temp', 'file1.txt', 'content1');
|
|
$this->driver->put('temp', 'file2.txt', 'content2');
|
|
$this->driver->put('temp', 'subdir/file3.txt', 'content3');
|
|
|
|
// When: 카테고리 전체 삭제
|
|
$result = $this->driver->deleteAll('temp');
|
|
|
|
// Then: 삭제 성공
|
|
$this->assertTrue($result);
|
|
$this->assertFalse($this->driver->exists('temp', 'file1.txt'));
|
|
$this->assertFalse($this->driver->exists('temp', 'file2.txt'));
|
|
$this->assertFalse($this->driver->exists('temp', 'subdir/file3.txt'));
|
|
}
|
|
|
|
#[Test]
|
|
public function it_returns_correct_base_path(): void
|
|
{
|
|
// When: 기본 경로 조회
|
|
$basePath = $this->driver->getBasePath('settings');
|
|
|
|
// Then: 올바른 경로 반환
|
|
$expectedPath = Storage::disk('modules')->path('test-module/settings');
|
|
$this->assertEquals($expectedPath, $basePath);
|
|
}
|
|
|
|
#[Test]
|
|
public function it_returns_correct_disk_name(): void
|
|
{
|
|
// When: 디스크 이름 조회
|
|
$disk = $this->driver->getDisk();
|
|
|
|
// Then: 'modules' 반환
|
|
$this->assertEquals('modules', $disk);
|
|
}
|
|
|
|
#[Test]
|
|
public function it_returns_null_for_url_on_private_disk(): void
|
|
{
|
|
// Given: local disk (private)
|
|
$this->driver->put('images', 'test.jpg', 'image content');
|
|
|
|
// When: URL 조회
|
|
$url = $this->driver->url('images', 'test.jpg');
|
|
|
|
// Then: null 반환 (private disk)
|
|
$this->assertNull($url);
|
|
}
|
|
|
|
#[Test]
|
|
public function it_returns_url_for_public_disk(): void
|
|
{
|
|
// Given: public disk 사용
|
|
$publicDriver = new ModuleStorageDriver('test-module', 'public');
|
|
$publicDriver->put('images', 'test.jpg', 'image content');
|
|
|
|
// When: URL 조회
|
|
$url = $publicDriver->url('images', 'test.jpg');
|
|
|
|
// Then: URL 반환
|
|
$this->assertNotNull($url);
|
|
$this->assertStringContainsString('test-module/images/test.jpg', $url);
|
|
|
|
// Cleanup
|
|
Storage::disk('public')->deleteDirectory('test-module');
|
|
}
|
|
|
|
#[Test]
|
|
public function it_uses_correct_path_pattern(): void
|
|
{
|
|
// Given: 파일 저장
|
|
$this->driver->put('attachments', 'board/2024/01/file.txt', 'content');
|
|
|
|
// When: 파일 존재 확인
|
|
$exists = $this->driver->exists('attachments', 'board/2024/01/file.txt');
|
|
|
|
// Then: 올바른 경로로 저장됨
|
|
$this->assertTrue($exists);
|
|
|
|
// And: Storage Facade로도 확인 가능
|
|
$storagePath = 'test-module/attachments/board/2024/01/file.txt';
|
|
$this->assertTrue(Storage::disk('modules')->exists($storagePath));
|
|
}
|
|
|
|
#[Test]
|
|
public function it_returns_streamed_response_for_existing_file(): void
|
|
{
|
|
// Given: 파일 저장
|
|
$this->driver->put('images', 'test.jpg', 'image content');
|
|
|
|
// When: StreamedResponse 생성
|
|
$response = $this->driver->response('images', 'test.jpg', 'download.jpg', [
|
|
'Content-Type' => 'image/jpeg',
|
|
]);
|
|
|
|
// Then: StreamedResponse 반환
|
|
$this->assertInstanceOf(\Symfony\Component\HttpFoundation\StreamedResponse::class, $response);
|
|
}
|
|
|
|
#[Test]
|
|
public function it_returns_null_for_response_on_non_existent_file(): void
|
|
{
|
|
// When: 존재하지 않는 파일로 StreamedResponse 생성 시도
|
|
$response = $this->driver->response('images', 'non-existent.jpg', 'download.jpg');
|
|
|
|
// Then: null 반환
|
|
$this->assertNull($response);
|
|
}
|
|
|
|
// ========================================================================
|
|
// 트랙 2-A — `.preserve-ownership` 마커 자동 작성 (코어 7.0.0-beta.4+)
|
|
//
|
|
// ModuleStorageDriver 가 첫 디렉토리 생성 시 마커 파일을 자동으로 작성하여
|
|
// 미래 코어 update 의 chownRecursive 가 storage/app/modules/{id}/ 트리를
|
|
// 자동 skip 하도록 함. 시드 시점 owner/perms 영구 보존.
|
|
// ========================================================================
|
|
|
|
#[Test]
|
|
public function it_writes_preservation_marker_on_first_put(): void
|
|
{
|
|
// When: 첫 파일 저장 (디렉토리 자동 생성)
|
|
$this->driver->put('images', 'product/img.jpg', 'image-data');
|
|
|
|
// Then: 모듈 루트에 .preserve-ownership 마커 자동 생성
|
|
$markerPath = 'test-module/.preserve-ownership';
|
|
$this->assertTrue(
|
|
Storage::disk('modules')->exists($markerPath),
|
|
'ModuleStorageDriver 가 첫 사용 시 .preserve-ownership 마커를 자동 작성해야 함 '
|
|
.'(미래 chownRecursive 가드 트리거)',
|
|
);
|
|
}
|
|
|
|
#[Test]
|
|
public function it_does_not_overwrite_existing_marker(): void
|
|
{
|
|
// Given: 마커 사전 작성 (운영자 커스텀 내용)
|
|
Storage::disk('modules')->put('test-module/.preserve-ownership', 'custom marker');
|
|
|
|
// When: 추가 파일 저장
|
|
$this->driver->put('images', 'test.jpg', 'data');
|
|
|
|
// Then: 기존 마커 내용 보존 (멱등)
|
|
$content = Storage::disk('modules')->get('test-module/.preserve-ownership');
|
|
$this->assertSame('custom marker', $content, '기존 마커 내용 무변경 (멱등)');
|
|
}
|
|
}
|