CaiWuXiTong/tests/Feature/Console/SyncBusinessFilesToQiniuTest.php
WuYongJie da9c3cbf88 fix: 容忍七牛对象删除传播延迟 (#42)
对象删除后进行有限重试确认,持续存在仍失败回滚,并覆盖最终一致性边界测试。
2026-08-13 12:35:06 +08:00

106 lines
3.3 KiB
PHP

<?php
namespace Tests\Feature\Console;
use App\Console\Commands\SyncBusinessFilesToQiniu;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use Mockery;
use Tests\TestCase;
class SyncBusinessFilesToQiniuTest extends TestCase
{
private string $markerPath;
protected function setUp(): void
{
parent::setUp();
$this->markerPath = storage_path('app/.business-storage-qiniu-synced');
File::delete($this->markerPath);
}
protected function tearDown(): void
{
File::delete($this->markerPath);
parent::tearDown();
}
public function test_it_copies_retained_local_files_without_deleting_them(): void
{
config([
'business_storage.driver' => 'qiniu',
'business_storage.qiniu.access_key' => 'test-access-key',
'business_storage.qiniu.secret_key' => 'test-secret-key',
]);
Http::fake([
'https://caiwu.jiansun.vip/*' => Http::sequence()
->push('', 403)
->push('', 200),
]);
Storage::fake('business_uploads_local');
Storage::fake('business_uploads_qiniu');
Storage::disk('business_uploads_local')->put('2026/07/document.pdf', 'financial-document');
$this->artisan('storage:business-files:sync', ['--require-qiniu' => true])
->assertSuccessful();
Storage::disk('business_uploads_local')->assertExists('2026/07/document.pdf');
Storage::disk('business_uploads_qiniu')->assertExists('2026/07/document.pdf');
$this->assertSame(
'financial-document',
Storage::disk('business_uploads_qiniu')->get('2026/07/document.pdf')
);
$this->assertFileExists($this->markerPath);
}
public function test_it_fails_when_production_requires_qiniu_but_the_driver_is_local(): void
{
config(['business_storage.driver' => 'local']);
$this->artisan('storage:business-files:sync', ['--require-qiniu' => true])
->assertFailed();
}
public function test_it_retries_probe_cleanup_until_qiniu_reports_the_object_missing(): void
{
$this->assertSame(
[500_000, 500_000],
$this->invokeProbeCleanup([true, true, false])
);
}
public function test_it_fails_after_bounded_probe_cleanup_retries_when_qiniu_keeps_reporting_the_object(): void
{
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('Business storage probe cleanup failed.');
$this->invokeProbeCleanup(array_fill(0, 10, true));
}
private function invokeProbeCleanup(array $existsResults): array
{
$waits = [];
$disk = Mockery::mock(Filesystem::class);
$disk->shouldReceive('exists')
->times(count($existsResults))
->andReturn(...$existsResults);
$method = new \ReflectionMethod(SyncBusinessFilesToQiniu::class, 'confirmProbeCleanup');
$method->setAccessible(true);
$method->invoke(
app(SyncBusinessFilesToQiniu::class),
$disk,
'probe-path',
static function (int $microseconds) use (&$waits): void {
$waits[] = $microseconds;
}
);
return $waits;
}
}