mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-08-30 04:35:33 +08:00
test(storage): S3与WebDAV驱动解析及URL生成测试
This commit is contained in:
215
tests/Feature/StorageDriverTest.php
Normal file
215
tests/Feature/StorageDriverTest.php
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace tests\Feature;
|
||||||
|
|
||||||
|
use app\admin\model\SystemConfig;
|
||||||
|
use app\common\service\UploadService;
|
||||||
|
use app\common\test\TestCase;
|
||||||
|
use think\exception\ValidateException;
|
||||||
|
use think\facade\Cache;
|
||||||
|
use think\facade\Filesystem;
|
||||||
|
use think\filesystem\driver\S3;
|
||||||
|
use think\filesystem\driver\Webdav;
|
||||||
|
use think\File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储驱动测试:S3 与 WebDAV 磁盘解析、URL 拼接、上传白名单放行.
|
||||||
|
*
|
||||||
|
* 覆盖 storage-webdav-s3 任务的运行时行为:
|
||||||
|
* 1. Filesystem::disk('s3') / disk('webdav') 能按 sysconfig 正确解析驱动实例
|
||||||
|
* 2. 两个驱动的 url() 拼接(domain + path)
|
||||||
|
* 3. UploadService 对新类型 s3 / webdav 真正通过 in:{upload_allow_type} 白名单闸门
|
||||||
|
* (UploadServiceBase.php:40 的校验规则,非仅构造)
|
||||||
|
*
|
||||||
|
* 隔离策略:
|
||||||
|
* - 测试自播种 sysconfig(不依赖库中既有种子值),写入发生在 setUp 事务内,
|
||||||
|
* tearDown 的 Db::rollback 自动撤销
|
||||||
|
* - sysconfig 有 3600s 文件缓存(extend/base/helper.php sysconfig()),
|
||||||
|
* 播种后必须 Cache::tag('sysconfig')->clear() 才能读到新值;
|
||||||
|
* tearDown 里回滚后同样要清一次,防止测试期缓存值泄漏给同进程后续套件
|
||||||
|
*/
|
||||||
|
class StorageDriverTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* S3 七项配置(与后台表单 / T5 种子键名一致).
|
||||||
|
*/
|
||||||
|
private const S3_KV = [
|
||||||
|
's3_access_key' => 'testkey',
|
||||||
|
's3_secret_key' => 'testsecret',
|
||||||
|
's3_region' => 'us-east-1',
|
||||||
|
's3_bucket' => 'test-bucket',
|
||||||
|
's3_domain' => 'https://s3-test.example.com',
|
||||||
|
's3_endpoint' => '',
|
||||||
|
's3_path_style' => '0',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebDAV 五项配置.
|
||||||
|
*/
|
||||||
|
private const WEBDAV_KV = [
|
||||||
|
'webdav_url' => 'https://webdav-test.example.com/dav/',
|
||||||
|
'webdav_username' => 'u',
|
||||||
|
'webdav_password' => 'p',
|
||||||
|
'webdav_prefix' => '',
|
||||||
|
'webdav_domain' => 'https://webdav-test.example.com',
|
||||||
|
];
|
||||||
|
|
||||||
|
private const ALLOW_TYPE_ALL = 'local_public,alioss,qnoss,txcos,s3,webdav';
|
||||||
|
|
||||||
|
protected function tearDown(): void
|
||||||
|
{
|
||||||
|
// 先回滚事务(parent 内含 Db::rollback,不要手动重复)
|
||||||
|
parent::tearDown();
|
||||||
|
|
||||||
|
// 回滚后文件缓存仍持有测试期值,不清会泄漏给同进程后续套件与下次运行
|
||||||
|
Cache::tag('sysconfig')->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 播种 upload 组 sysconfig 并清缓存.
|
||||||
|
*
|
||||||
|
* 注:think-orm Model 没有 updateOrCreate(已验证 method_exists 为 false),
|
||||||
|
* 用等价的 find → save / create 实现同样的 upsert 语义。
|
||||||
|
*/
|
||||||
|
private function seedUploadConfigs(array $kv): void
|
||||||
|
{
|
||||||
|
foreach ($kv as $name => $value) {
|
||||||
|
$row = SystemConfig::where('group', 'upload')->where('name', $name)->find();
|
||||||
|
if ($row) {
|
||||||
|
$row->save(['value' => (string) $value, 'remark' => 'test', 'sort' => 0]);
|
||||||
|
} else {
|
||||||
|
SystemConfig::create([
|
||||||
|
'group' => 'upload',
|
||||||
|
'name' => $name,
|
||||||
|
'value' => (string) $value,
|
||||||
|
'remark' => 'test',
|
||||||
|
'sort' => 0,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sysconfig 有 3600s 缓存,不清会读到旧值
|
||||||
|
Cache::tag('sysconfig')->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用例 1:播种 s3_* 配置后 disk('s3') 解析为 S3 驱动实例.
|
||||||
|
*/
|
||||||
|
public function test_s3_disk_resolves(): void
|
||||||
|
{
|
||||||
|
$this->seedUploadConfigs(self::S3_KV + ['upload_allow_type' => self::ALLOW_TYPE_ALL]);
|
||||||
|
|
||||||
|
$disk = Filesystem::disk('s3');
|
||||||
|
|
||||||
|
$this->assertInstanceOf(S3::class, $disk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用例 2:播种 webdav_* 配置后 disk('webdav') 解析为 Webdav 驱动实例.
|
||||||
|
*/
|
||||||
|
public function test_webdav_disk_resolves(): void
|
||||||
|
{
|
||||||
|
$this->seedUploadConfigs(self::WEBDAV_KV);
|
||||||
|
|
||||||
|
$disk = Filesystem::disk('webdav');
|
||||||
|
|
||||||
|
$this->assertInstanceOf(Webdav::class, $disk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用例 3:S3 url() 拼接 = s3_domain + '/' + path.
|
||||||
|
*
|
||||||
|
* 本用例内重新播种全部七项:url() 实时读 sysconfig,且前一用例的种子
|
||||||
|
* 已随其 tearDown 回滚;--filter 单独跑本用例时磁盘首次构造也需要
|
||||||
|
* region / bucket 等配置。
|
||||||
|
*/
|
||||||
|
public function test_s3_url_concat(): void
|
||||||
|
{
|
||||||
|
$this->seedUploadConfigs(self::S3_KV);
|
||||||
|
|
||||||
|
$url = Filesystem::disk('s3')->url('upload/20260817/abc.png');
|
||||||
|
|
||||||
|
$this->assertSame('https://s3-test.example.com/upload/20260817/abc.png', $url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用例 4:WebDAV url() 拼接 = webdav_domain + '/' + path.
|
||||||
|
*
|
||||||
|
* 同理本用例内重新播种全部五项(url() 实时读 sysconfig + 单独运行时
|
||||||
|
* 构造 Client 需要 url/username/password)。
|
||||||
|
*/
|
||||||
|
public function test_webdav_url_concat(): void
|
||||||
|
{
|
||||||
|
$this->seedUploadConfigs(self::WEBDAV_KV);
|
||||||
|
|
||||||
|
$url = Filesystem::disk('webdav')->url('upload/20260817/abc.png');
|
||||||
|
|
||||||
|
$this->assertSame('https://webdav-test.example.com/upload/20260817/abc.png', $url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用例 5:UploadService 对 s3 / webdav 真正通过 in:{upload_allow_type} 白名单闸门.
|
||||||
|
*
|
||||||
|
* 走 validateException 全链(白名单 + fileExt + fileSize + PHP 注入检查),
|
||||||
|
* 而非仅构造 UploadService(只构造不校验是恒真断言,不可取)。
|
||||||
|
* webdav 构造只读 uploadType 属性不连磁盘,安全。
|
||||||
|
*/
|
||||||
|
public function test_upload_service_accepts_new_types(): void
|
||||||
|
{
|
||||||
|
$this->seedUploadConfigs([
|
||||||
|
'upload_type' => 's3',
|
||||||
|
'upload_allow_type' => self::ALLOW_TYPE_ALL,
|
||||||
|
'upload_allow_ext' => 'jpg,png',
|
||||||
|
'upload_allow_size' => '1024000',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$tmpPath = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'ulthon_t8_test.png';
|
||||||
|
file_put_contents($tmpPath, 'pngdata');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$file = new File($tmpPath);
|
||||||
|
|
||||||
|
$this->assertTrue(
|
||||||
|
(new UploadService('s3'))->validateException($file),
|
||||||
|
'upload_type=s3 应通过 in:{upload_allow_type} 白名单与文件校验'
|
||||||
|
);
|
||||||
|
$this->assertTrue(
|
||||||
|
(new UploadService('webdav'))->validateException($file),
|
||||||
|
'upload_type=webdav 应通过 in:{upload_allow_type} 白名单与文件校验'
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
@unlink($tmpPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附加用例(任务允许的可选白名单拒绝场景):白名单不含 s3 时闸门应拒绝.
|
||||||
|
*
|
||||||
|
* 锁死 in:{upload_allow_type} 的拒绝方向:防止有人把白名单校验改坏后
|
||||||
|
* 用例 5 仍绿。expectException 场景下 tearDown(回滚 + 清缓存)照常执行。
|
||||||
|
*/
|
||||||
|
public function test_upload_service_rejects_type_not_in_whitelist(): void
|
||||||
|
{
|
||||||
|
$this->seedUploadConfigs([
|
||||||
|
'upload_allow_type' => 'local_public,alioss,qnoss,txcos',
|
||||||
|
'upload_allow_ext' => 'jpg,png',
|
||||||
|
'upload_allow_size' => '1024000',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$tmpPath = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'ulthon_t8_reject.png';
|
||||||
|
file_put_contents($tmpPath, 'pngdata');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$file = new File($tmpPath);
|
||||||
|
|
||||||
|
$this->expectException(ValidateException::class);
|
||||||
|
$this->expectExceptionMessage('指定上传类型有误');
|
||||||
|
|
||||||
|
(new UploadService('s3'))->validateException($file);
|
||||||
|
} finally {
|
||||||
|
@unlink($tmpPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user