put('supplier:session', [ 'phpsessid' => 'sess', 'csrf' => 'csrf', 'refreshed_at' => now()->toIso8601String(), ], now()->addHours(6)); config(['services.supplier.portal_url' => 'https://crm.bp-gr.ru']); }); afterEach(function (): void { Cache::store('redis')->forget('supplier:session'); Carbon::setTestNow(); }); test('phase A re-activates supplier_project when active liderra project still references it', function (): void { $tenant = Tenant::factory()->create(); $sp = SupplierProject::factory()->create([ 'platform' => 'B1', 'signal_type' => 'site', 'unique_key' => 'reactivate.example.com', 'inactive_since' => now()->subDays(30), ]); Project::factory()->create([ 'tenant_id' => $tenant->id, 'is_active' => true, 'signal_type' => 'site', 'signal_identifier' => 'reactivate.example.com', 'supplier_b1_project_id' => $sp->id, ]); (new CleanupInactiveSupplierProjectsJob)->handle(); expect($sp->fresh()->inactive_since)->toBeNull(); }); test('phase B marks inactive_since=NOW for newly orphaned supplier_project', function (): void { $sp = SupplierProject::factory()->create([ 'platform' => 'B1', 'signal_type' => 'site', 'unique_key' => 'orphan.example.com', 'inactive_since' => null, ]); Carbon::setTestNow(Carbon::parse('2026-05-12 02:00:00')); (new CleanupInactiveSupplierProjectsJob)->handle(); expect($sp->fresh()->inactive_since)->not->toBeNull(); }); test('phase C deletes supplier_project after 180 days inactive and writes audit row', function (): void { $sp = SupplierProject::factory()->create([ 'platform' => 'B1', 'signal_type' => 'site', 'unique_key' => 'old.example.com', 'supplier_external_id' => '999', 'inactive_since' => now()->subDays(181), ]); Http::fake([ 'crm.bp-gr.ru/admin/rt-project-delete' => Http::response('', 200), ]); (new CleanupInactiveSupplierProjectsJob)->handle(); expect(SupplierProject::on('pgsql_supplier')->find($sp->id))->toBeNull(); // FK ON DELETE SET NULL зануляет supplier_project_id у лога после delete'а // supplier_project — трассировка через request_payload (JSONB snapshot). expect( SupplierSyncLog::on('pgsql_supplier') ->where('action', 'delete') ->where('http_status', 200) ->whereRaw("request_payload->>'supplier_project_id' = ?", [(string) $sp->id]) ->exists() )->toBeTrue(); }); test('phase A runs before phase C (safety ordering): returned-active is reactivated, not deleted', function (): void { $tenant = Tenant::factory()->create(); $sp = SupplierProject::factory()->create([ 'platform' => 'B1', 'signal_type' => 'site', 'unique_key' => 'edge.example.com', 'supplier_external_id' => '888', 'inactive_since' => now()->subDays(185), ]); Project::factory()->create([ 'tenant_id' => $tenant->id, 'is_active' => true, 'signal_type' => 'site', 'signal_identifier' => 'edge.example.com', 'supplier_b1_project_id' => $sp->id, ]); Http::fake(); (new CleanupInactiveSupplierProjectsJob)->handle(); expect($sp->fresh()?->inactive_since)->toBeNull(); expect(SupplierProject::on('pgsql_supplier')->find($sp->id))->not->toBeNull(); Http::assertNothingSent(); }); test('handles 404 from supplier as already-deleted: local delete + audit row with 404 status', function (): void { $sp = SupplierProject::factory()->create([ 'platform' => 'B1', 'signal_type' => 'site', 'unique_key' => 'ghost.example.com', 'supplier_external_id' => '777', 'inactive_since' => now()->subDays(181), ]); Http::fake([ 'crm.bp-gr.ru/admin/rt-project-delete' => Http::response('not found', 404), ]); (new CleanupInactiveSupplierProjectsJob)->handle(); expect(SupplierProject::on('pgsql_supplier')->find($sp->id))->toBeNull(); expect( SupplierSyncLog::on('pgsql_supplier') ->where('action', 'delete') ->where('http_status', 404) ->whereRaw("request_payload->>'supplier_project_id' = ?", [(string) $sp->id]) ->exists() )->toBeTrue(); }); test('does not delete supplier_project marked inactive less than 180 days ago', function (): void { $sp = SupplierProject::factory()->create([ 'platform' => 'B1', 'signal_type' => 'site', 'unique_key' => 'recent.example.com', 'supplier_external_id' => '555', 'inactive_since' => now()->subDays(100), ]); Http::fake(); (new CleanupInactiveSupplierProjectsJob)->handle(); expect(SupplierProject::on('pgsql_supplier')->find($sp->id))->not->toBeNull(); Http::assertNothingSent(); });