false]); $synchronizer = Mockery::mock(ThirdPartyWarehouseStorageSynchronizer::class); $synchronizer->shouldNotReceive('sync'); $this->app->instance(ThirdPartyWarehouseStorageSynchronizer::class, $synchronizer); $this->artisan('business-mirror:sync-third-party-warehouse-storage') ->assertSuccessful(); } public function test_explicit_full_sync_and_batch_limit_are_forwarded(): void { config(['upstream_mirrors.refresh_enabled' => false]); $synchronizer = Mockery::mock(ThirdPartyWarehouseStorageSynchronizer::class); $synchronizer->shouldReceive('sync')->once()->with(true, 7)->andReturn([ 'status' => 'running', 'mode' => 'full', 'phase' => 'headers', 'batches' => 7, 'headers' => 10, 'items' => 20, 'inventory' => 0, ]); $this->app->instance(ThirdPartyWarehouseStorageSynchronizer::class, $synchronizer); $this->artisan('business-mirror:sync-third-party-warehouse-storage', [ '--full' => true, '--max-batches' => 7, ])->expectsOutputToContain('全量校准:进行中') ->assertSuccessful(); } public function test_uninitialized_incremental_sync_prints_the_full_sync_instruction(): void { config(['upstream_mirrors.refresh_enabled' => true]); $synchronizer = Mockery::mock(ThirdPartyWarehouseStorageSynchronizer::class); $synchronizer->shouldReceive('sync')->once()->with(false, null)->andReturn([ 'status' => 'requires_full_sync', 'mode' => 'incremental', 'phase' => 'headers', 'batches' => 0, 'headers' => 0, 'items' => 0, 'inventory' => 0, ]); $this->app->instance(ThirdPartyWarehouseStorageSynchronizer::class, $synchronizer); $this->artisan('business-mirror:sync-third-party-warehouse-storage') ->expectsOutputToContain('显式使用 --full') ->assertSuccessful(); } public function test_concurrent_sync_is_skipped_without_reporting_a_failure(): void { config(['upstream_mirrors.refresh_enabled' => true]); $synchronizer = Mockery::mock(ThirdPartyWarehouseStorageSynchronizer::class); $synchronizer->shouldReceive('sync')->once()->with(false, null)->andReturn([ 'status' => 'already_running', 'mode' => 'incremental', 'phase' => 'locked', 'batches' => 0, 'headers' => 0, 'items' => 0, 'inventory' => 0, ]); $this->app->instance(ThirdPartyWarehouseStorageSynchronizer::class, $synchronizer); $this->artisan('business-mirror:sync-third-party-warehouse-storage') ->expectsOutputToContain('已有同步任务运行') ->assertSuccessful(); } public function test_sync_failure_reports_that_the_previous_complete_data_remains_available(): void { config(['upstream_mirrors.refresh_enabled' => true]); $synchronizer = Mockery::mock(ThirdPartyWarehouseStorageSynchronizer::class); $synchronizer->shouldReceive('sync')->once()->andThrow(new RuntimeException('offline')); $this->app->instance(ThirdPartyWarehouseStorageSynchronizer::class, $synchronizer); $this->artisan('business-mirror:sync-third-party-warehouse-storage') ->expectsOutputToContain('页面继续读取上一版完整数据') ->assertFailed(); } }