CaiWuXiTong/tests/Feature/Console/SupplierManagementMirrorSyncCommandTest.php
苏木 817e6cbf17 fix: 避免供应商主档重复调度覆盖 (#838)
供应商主档同步只保留主机 cron 的全局上游锁入口,移除 Laravel 调度器重复执行,避免两个完整同步交叉删除镜像。
Co-authored-by: 苏木 <594326199@qq.com>
Co-committed-by: 苏木 <594326199@qq.com>
2026-11-03 11:14:09 +08:00

88 lines
4.3 KiB
PHP

<?php
namespace Tests\Feature\Console;
use Illuminate\Contracts\Console\Kernel;
use Illuminate\Console\Scheduling\Schedule;
use Mockery;
use Modules\EcommerceFinance\Services\BusinessMirrorDatasetRegistry;
use Modules\EcommerceFinance\Services\BusinessMirrorSynchronizer;
use RuntimeException;
use Tests\TestCase;
class SupplierManagementMirrorSyncCommandTest extends TestCase
{
private const DATASETS = [
'jiansun.dbo.js_supplier_View',
'jiansun.dbo.js_supplier_contract_View',
'jiansun.dbo.js_supplier_linkers',
'jiansun.dbo.js_supplier_payinfo',
];
public function test_daily_sync_finishes_all_four_datasets_in_order(): void
{
config(['upstream_mirrors.refresh_enabled' => true]);
$registry = Mockery::mock(BusinessMirrorDatasetRegistry::class);
$sync = Mockery::mock(BusinessMirrorSynchronizer::class);
foreach (self::DATASETS as $key) {
$registry->shouldReceive('scheduled')->once()->with($key)->andReturn(true);
$sync->shouldReceive('sync')->once()->with($key, true, null, 0)->ordered()
->andReturn(['status' => 'succeeded', 'batches' => 3, 'rows' => 1200]);
}
$this->app->instance(BusinessMirrorDatasetRegistry::class, $registry);
$this->app->instance(BusinessMirrorSynchronizer::class, $sync);
$this->artisan('business-mirror:sync-supplier-management')->assertSuccessful();
}
public function test_one_failure_does_not_prevent_other_supplier_data_from_refreshing(): void
{
config(['upstream_mirrors.refresh_enabled' => true]);
$registry = Mockery::mock(BusinessMirrorDatasetRegistry::class);
$sync = Mockery::mock(BusinessMirrorSynchronizer::class);
foreach (self::DATASETS as $index => $key) {
$registry->shouldReceive('scheduled')->once()->with($key)->andReturn(true);
$expectation = $sync->shouldReceive('sync')->once()->with($key, true, null, 0)->ordered();
if ($index === 0) $expectation->andThrow(new RuntimeException('upstream unavailable'));
else $expectation->andReturn(['status' => 'succeeded', 'batches' => 1, 'rows' => 5]);
}
$this->app->instance(BusinessMirrorDatasetRegistry::class, $registry);
$this->app->instance(BusinessMirrorSynchronizer::class, $sync);
$this->artisan('business-mirror:sync-supplier-management')
->expectsOutputToContain('同步失败')->assertExitCode(1);
}
public function test_local_refresh_disabled_never_reads_upstream(): void
{
config(['upstream_mirrors.refresh_enabled' => false]);
$registry = Mockery::mock(BusinessMirrorDatasetRegistry::class);
$registry->shouldNotReceive('scheduled');
$sync = Mockery::mock(BusinessMirrorSynchronizer::class);
$sync->shouldNotReceive('sync');
$this->app->instance(BusinessMirrorDatasetRegistry::class, $registry);
$this->app->instance(BusinessMirrorSynchronizer::class, $sync);
$this->artisan('business-mirror:sync-supplier-management')->assertSuccessful();
}
public function test_disabled_supplier_datasets_are_not_synced(): void
{
config(['upstream_mirrors.refresh_enabled' => true]);
$registry = Mockery::mock(BusinessMirrorDatasetRegistry::class);
$registry->shouldReceive('scheduled')->times(4)->andReturn(false);
$sync = Mockery::mock(BusinessMirrorSynchronizer::class);
$sync->shouldNotReceive('sync');
$this->app->instance(BusinessMirrorDatasetRegistry::class, $registry);
$this->app->instance(BusinessMirrorSynchronizer::class, $sync);
$this->artisan('business-mirror:sync-supplier-management')->assertSuccessful();
}
public function test_schedule_does_not_duplicate_the_host_cron_supplier_sync(): void
{
config(['app.installed' => true, 'app.schedule_time' => '09:00', 'app.timezone' => 'UTC']);
$schedule = new Schedule('UTC');
(new \ReflectionMethod(\App\Console\Kernel::class, 'schedule'))->invoke($this->app->make(Kernel::class), $schedule);
$events = collect($schedule->events())
->filter(fn ($event) => str_contains($event->command ?? '', 'business-mirror:sync-supplier-management'));
$this->assertCount(0, $events, '供应商主档只能由带全局上游锁的主机 cron 调度。');
}
}