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; } }