toDateString(); DB::table('project_routing_snapshots')->insert([ 'snapshot_date' => $tomorrow, 'project_id' => $project->id, 'tenant_id' => $project->tenant_id, 'daily_limit' => $dailyLimit, 'delivery_days_mask' => $deliveryDaysMask, 'regions' => $regions, 'signal_type' => $signalType, 'signal_identifier' => $signalIdentifier, 'sms_senders' => null, 'sms_keyword' => null, 'expected_volume' => $dailyLimit, 'delivered_count' => 0, 'created_at' => now(), ]); } it('reads from snapshot for tomorrow, picks up live-paused project (race 18:02→18:05)', function (): void { Carbon::setTestNow('2026-05-27 18:04:00', 'Europe/Moscow'); $tenant = Tenant::factory()->create(['frozen_by_balance_at' => null]); // ↓ Клиент paus'нул проект между 18:02 (snapshot) и 18:05 (sync). $project = Project::factory()->for($tenant)->create([ 'is_active' => false, // live state — paused 'daily_limit_target' => 10, 'delivery_days_mask' => 127, ]); insertTomorrowSnapshot($project); $projects = (new SyncSupplierProjectsJob)->collectEligibleProjects(); // Изолируем от leftover dev data в pgsql_supplier (SharesSupplierPdo шарит PDO, // но не транзакции — данные между тестами остаются). $ours = $projects->where('id', $project->id); expect($ours)->toHaveCount(1); Carbon::setTestNow(); }); it('skips project that has NO snapshot for tomorrow (live is_active=true ignored)', function (): void { Carbon::setTestNow('2026-05-27 18:04:00', 'Europe/Moscow'); $tenant = Tenant::factory()->create(['frozen_by_balance_at' => null]); $project = Project::factory()->for($tenant)->create([ 'is_active' => true, // live state — active 'daily_limit_target' => 10, 'delivery_days_mask' => 127, ]); // НЕТ snapshot за tomorrow — sync должен пропустить. $projects = (new SyncSupplierProjectsJob)->collectEligibleProjects(); $ours = $projects->where('id', $project->id); expect($ours)->toHaveCount(0); Carbon::setTestNow(); }); it('overrides daily_limit_target / regions / delivery_days_mask with snapshot values', function (): void { Carbon::setTestNow('2026-05-27 18:04:00', 'Europe/Moscow'); $tenant = Tenant::factory()->create(['frozen_by_balance_at' => null]); $project = Project::factory()->for($tenant)->create([ 'is_active' => true, 'daily_limit_target' => 100, // live = 100 'delivery_days_mask' => 127, // live = mon-sun ]); // Snapshot имеет другой лимит / маску. insertTomorrowSnapshot( $project, dailyLimit: 7, // snapshot = 7 deliveryDaysMask: 31, // mon-fri only regions: '{77}', // только Москва ); $projects = (new SyncSupplierProjectsJob)->collectEligibleProjects(); $ours = $projects->where('id', $project->id); expect($ours)->toHaveCount(1); $p = $ours->first(); expect((int) $p->daily_limit_target)->toBe(7); expect((int) $p->delivery_days_mask)->toBe(31); expect((array) $p->regions)->toBe([77]); Carbon::setTestNow(); }); it('skips frozen tenants regardless of snapshot presence', function (): void { Carbon::setTestNow('2026-05-27 18:04:00', 'Europe/Moscow'); $tenant = Tenant::factory()->create(['frozen_by_balance_at' => now()->subDay()]); $project = Project::factory()->for($tenant)->create([ 'is_active' => true, 'daily_limit_target' => 10, 'delivery_days_mask' => 127, ]); insertTomorrowSnapshot($project); $projects = (new SyncSupplierProjectsJob)->collectEligibleProjects(); $ours = $projects->where('id', $project->id); expect($ours)->toHaveCount(0); // frozen tenant — sync должен пропустить Carbon::setTestNow(); });