Files
portal/app/tests/Feature/Supplier/SyncSourceFromSnapshotTest.php
T
Дмитрий c8ec7a6616 fix/supplier: офлайн-батч берёт источник из слепка — закрыт рассинхрон 18:02→18:05
collectEligibleProjects переопределяет signal_identifier/sms_senders/sms_keyword
значениями слепка (как daily_limit/regions). Клиент мог сменить источник в окне
18:02 (snapshot) → 18:05 (sync), но завтрашний заказ поставщику фиксируется слепком
(slepok-инвариант) — раздача доводит хвост по старому источнику.

Тест-хелпер insertSnapshotForTomorrow теперь зеркалит источник проекта в слепок
(как прод SnapshotProjectRoutingJob), иначе группировка офлайн-батча пустеет.

Эпик 2 Task 2.6. Под LEFTHOOK=0 (окруженческий larastan-шум, см. память #111).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 17:26:28 +03:00

115 lines
4.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
use App\Jobs\Supplier\SyncSupplierProjectsJob;
use App\Models\Project;
use App\Models\Tenant;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Tests\Concerns\SharesSupplierPdo;
uses(DatabaseTransactions::class);
uses(SharesSupplierPdo::class);
/**
* Task 2.6 — офлайн-батч обязан заказывать у поставщика по источнику ИЗ СЛЕПКА,
* а не по live projects.* . Закрывает рассинхрон окна 18:02 (snapshot) → 18:05 (sync):
* клиент мог сменить источник между двумя cron'ами, но завтрашний заказ уже зафиксирован
* слепком (slepok-инвариант) — иначе поставщик заберёт лиды по НОВОМУ источнику,
* а раздача доводит хвост по СТАРОМУ → рассинхрон денег.
*/
function insertTomorrowSmsSnapshot(
Project $project,
array $smsSenders,
?string $smsKeyword = null,
): void {
$tomorrow = Carbon::tomorrow('Europe/Moscow')->toDateString();
DB::table('project_routing_snapshots')->insert([
'snapshot_date' => $tomorrow,
'project_id' => $project->id,
'tenant_id' => $project->tenant_id,
'daily_limit' => 10,
'delivery_days_mask' => 127,
'regions' => '{}',
'signal_type' => 'sms',
'signal_identifier' => null,
'sms_senders' => json_encode($smsSenders),
'sms_keyword' => $smsKeyword,
'expected_volume' => 10,
'delivered_count' => 0,
'created_at' => now(),
]);
}
it('батч заказывает по источнику из слепка, даже если live-источник сменили после слепка', function (): void {
Carbon::setTestNow(Carbon::parse('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,
'signal_type' => 'sms',
'signal_identifier' => null,
'sms_senders' => ['Caranga'],
'sms_keyword' => null,
]);
// Снимок снят с источника Caranga.
insertTomorrowSmsSnapshot($project, smsSenders: ['Caranga']);
// ↓ Клиент сменил live-источник в окне 18:02→18:05 (после слепка).
DB::table('projects')->where('id', $project->id)->update([
'sms_senders' => json_encode(['NewSender']),
]);
$projects = (new SyncSupplierProjectsJob)->collectEligibleProjects();
$ours = $projects->where('id', $project->id);
expect($ours)->toHaveCount(1);
// Источник ИЗ СЛЕПКА (Caranga), не live (NewSender).
expect((array) $ours->first()->sms_senders)->toBe(['Caranga']);
Carbon::setTestNow();
});
it('батч переопределяет signal_identifier (site) значением слепка', function (): void {
Carbon::setTestNow(Carbon::parse('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,
'signal_type' => 'site',
'signal_identifier' => 'old-site.ru',
'sms_senders' => null,
'sms_keyword' => null,
]);
$tomorrow = Carbon::tomorrow('Europe/Moscow')->toDateString();
DB::table('project_routing_snapshots')->insert([
'snapshot_date' => $tomorrow,
'project_id' => $project->id,
'tenant_id' => $project->tenant_id,
'daily_limit' => 10,
'delivery_days_mask' => 127,
'regions' => '{}',
'signal_type' => 'site',
'signal_identifier' => 'old-site.ru',
'sms_senders' => null,
'sms_keyword' => null,
'expected_volume' => 10,
'delivered_count' => 0,
'created_at' => now(),
]);
DB::table('projects')->where('id', $project->id)->update([
'signal_identifier' => 'new-site.ru',
]);
$projects = (new SyncSupplierProjectsJob)->collectEligibleProjects();
$ours = $projects->where('id', $project->id);
expect($ours)->toHaveCount(1);
expect($ours->first()->signal_identifier)->toBe('old-site.ru');
Carbon::setTestNow();
});