95 lines
4.5 KiB
PHP
95 lines
4.5 KiB
PHP
|
|
<?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();
|
|||
|
|
});
|