Files
portal/app/tests/Feature/Supplier/OnlineDeferWindowTest.php
T

95 lines
4.5 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
use App\Jobs\SyncSupplierProjectJob;
use App\Models\Project;
use App\Models\Tenant;
use App\Services\Supplier\Channel\SupplierProjectChannel;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Tests\Concerns\SharesSupplierPdo;
uses(DatabaseTransactions::class, SharesSupplierPdo::class);
/**
* Task 4.1 — онлайн-режим в окне 18:00→00:00 МСК откладывает отправку поставщику.
*
* Поставщик фиксирует заказ слепком в 21:00. Если онлайн-правка уйдёт ему в 19:00,
* она перезапишет уже зафиксированное состояние (наш слепок снят в 18:02). Поэтому
* в окне правка кладётся в supplier_deferred_sync, а FlushDeferredOnlineSyncJob (00:05)
* досылает её вне окна. Вне окна онлайн работает как раньше (немедленно).
*/
beforeEach(function (): void {
DB::table('system_settings')->updateOrInsert(
['key' => 'supplier_export_mode'],
['value' => 'online'],
);
});
afterEach(fn () => Carbon::setTestNow());
it('в окне 18:00→00:00 онлайн не шлёт поставщику, а откладывает', function (): void {
Carbon::setTestNow(Carbon::parse('2026-06-25 19:00:00', 'Europe/Moscow'));
$tenant = Tenant::factory()->create();
$project = Project::factory()->for($tenant)->create([
'signal_type' => 'site',
'signal_identifier' => 'okna.ru',
'is_active' => true,
'daily_limit_target' => 10,
]);
// Канал НЕ должен дёргаться — defer происходит до любых обращений к поставщику.
$channel = Mockery::mock(SupplierProjectChannel::class);
$channel->shouldNotReceive('createProject');
$channel->shouldNotReceive('updateProject');
app()->instance(SupplierProjectChannel::class, $channel);
(new SyncSupplierProjectJob($project->id))->handle(app(SupplierProjectChannel::class));
expect(DB::table('supplier_deferred_sync')->where('project_id', $project->id)->exists())->toBeTrue();
});
it('повторная правка в окне не плодит дублей (project_id PK, ON CONFLICT DO NOTHING)', function (): void {
Carbon::setTestNow(Carbon::parse('2026-06-25 19:30:00', 'Europe/Moscow'));
$tenant = Tenant::factory()->create();
$project = Project::factory()->for($tenant)->create([
'signal_type' => 'site', 'signal_identifier' => 'okna.ru', 'is_active' => true, 'daily_limit_target' => 10,
]);
$channel = Mockery::mock(SupplierProjectChannel::class);
app()->instance(SupplierProjectChannel::class, $channel);
(new SyncSupplierProjectJob($project->id))->handle(app(SupplierProjectChannel::class));
(new SyncSupplierProjectJob($project->id))->handle(app(SupplierProjectChannel::class));
expect(DB::table('supplier_deferred_sync')->where('project_id', $project->id)->count())->toBe(1);
});
it('вне окна (до 18:00) онлайн НЕ откладывает — идёт обычным путём', function (): void {
Carbon::setTestNow(Carbon::parse('2026-06-25 12:00:00', 'Europe/Moscow'));
$tenant = Tenant::factory()->create();
$project = Project::factory()->for($tenant)->create([
'signal_type' => 'site', 'signal_identifier' => 'okna.ru', 'is_active' => true, 'daily_limit_target' => 10,
]);
// Вне окна defer-ветка не срабатывает: канал ПОЛУЧИТ обращения (online sync идёт).
// Не мокаем портал целиком — достаточно проверить, что в defer-очередь НЕ попало.
$channel = Mockery::mock(SupplierProjectChannel::class);
$channel->shouldReceive('updateProject')->andReturnNull();
$channel->shouldReceive('createProject')->andReturn(1);
app()->instance(SupplierProjectChannel::class, $channel);
try {
(new SyncSupplierProjectJob($project->id))->handle(app(SupplierProjectChannel::class));
} catch (\Throwable) {
// Онлайн-путь может бросить retry при неполном портал-ответе — нам важна только defer-очередь.
}
expect(DB::table('supplier_deferred_sync')->where('project_id', $project->id)->exists())->toBeFalse();
});