133 lines
4.6 KiB
PHP
133 lines
4.6 KiB
PHP
|
|
<?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);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Helper: вставка snapshot за tomorrow MSK.
|
||
|
|
*/
|
||
|
|
function insertTomorrowSnapshot(
|
||
|
|
Project $project,
|
||
|
|
string $signalType = 'call',
|
||
|
|
?string $signalIdentifier = '79161234567',
|
||
|
|
int $dailyLimit = 10,
|
||
|
|
int $deliveryDaysMask = 127,
|
||
|
|
string $regions = '{}',
|
||
|
|
): 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' => $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();
|
||
|
|
});
|