Files
portal/app/tests/Feature/Jobs/RouteSupplierLeadJobSnapshotTest.php
T
Дмитрий 88ace4e3d9
Accessibility (Pa11y live) / a11y (push) Has been cancelled
test: дозакрытие оздоровления — protekateli pd-аудита, видимость supplier, новый флоу регистрации
Снижение остатка 19 to 5. Всё тест-сторона:
- PdErasureServiceTest + AdminPdSubjectRequestsControllerTest: SharesSupplierPdo —
  перестали коммитить pd_processing_log через pgsql_supplier, что ломало
  глобальный audit:verify-chains (6 падений) и амплифицировало PhoneRegionSmoke.
- ReportFileDeletePdLogTest: SharesSupplierPdo — cron reports:cleanup-expired
  теперь видит незакоммиченные job'ы теста.
- AdminSuppliersControllerTest: устойчивый ассерт (с фазы 3 в suppliers есть direct).
- AuthLogCoverageTest/AuthFlowIntegrationTest: новый флоу самозаписи G1/SP1 —
  register_success пишется после confirm-email; добавлен шаг подтверждения.
- ImpersonationTest end: verify (G7-B) ставит маркер impersonation → admin-зона
  закрыта by design; помечаем токен used напрямую вместо session-takeover.
- CleanupInactiveSupplierProjectsJobTest: phase A читает pivot project_supplier_links —
  добавлена привязка linkProjectToSupplier (раньше был только legacy FK).
- Pint-нормализация uses() FQN to import в ранее тронутых файлах.

Остаток 5 (НЕ слепой патч): webhook B-префикс ×2 (решение владельца), advisory-lock
audit-цепочки (возможный дрейф схемы, флажок), SupplierConnection WARN#2 (cap-3,
поведенческое), SupplierPortalClientTest (пре-существующий, не от этих правок).

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

143 lines
5.5 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\RouteSupplierLeadJob;
use App\Models\Project;
use App\Models\SupplierLead;
use App\Models\SupplierProject;
use App\Models\Tenant;
use App\Services\Billing\LedgerService;
use App\Services\LeadDistributor;
use App\Services\LeadRouter;
use App\Services\MonthlyPartitionManager;
use App\Services\NotificationService;
use App\Services\RegionTagResolver;
use App\Services\SupplierProjects\SupplierProjectResolver;
use Carbon\Carbon;
use Database\Seeders\PricingTierSeeder;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Tests\Concerns\SharesSupplierPdo;
uses(DatabaseTransactions::class);
uses(SharesSupplierPdo::class);
beforeEach(function (): void {
$this->seed(PricingTierSeeder::class);
DB::statement("SELECT set_config('app.current_tenant_id', '0', true)");
// Тесты фиксируют Carbon на 2026-05-28; доставка пишет в несколько
// партиционированных таблиц (deals/balance_transactions/activity_log/
// pd_processing_log/tenant_operations_log) → гарантируем партиции мая для всех.
$mpm = app(MonthlyPartitionManager::class);
foreach (array_keys(MonthlyPartitionManager::PARTITIONED_TABLES) as $table) {
$mpm->ensureRange($table, Carbon::parse('2026-05-01'), Carbon::parse('2026-05-31'));
}
});
function runSnapshotRouteJob(int $supplierLeadId): void
{
(new RouteSupplierLeadJob($supplierLeadId))->handle(
app(LeadRouter::class),
app(SupplierProjectResolver::class),
app(NotificationService::class),
app(LedgerService::class),
app(LeadDistributor::class),
app(RegionTagResolver::class),
);
}
it('uses snapshot daily_limit, not live daily_limit_target (R-04/R-06)', function (): void {
Carbon::setTestNow('2026-05-28 12:00:00', 'Europe/Moscow');
$tenant = Tenant::factory()->create(['balance_rub' => '500.00', 'frozen_by_balance_at' => null]);
$project = Project::factory()->for($tenant)->create([
'is_active' => true,
'delivery_days_mask' => 127,
'daily_limit_target' => 100, // live limit big
'delivered_today' => 4,
'delivered_in_month' => 0,
]);
$sp = SupplierProject::factory()->create(['signal_type' => 'site']);
linkProjectToSupplier($project, $sp);
// Snapshot имеет МАЛЕНЬКИЙ daily_limit=5 — после доставки 1 deal'a должно стать 5.
createRoutingSnapshotFromProject(
$project,
date: '2026-05-28',
signalType: 'site',
signalIdentifier: $sp->unique_key,
dailyLimit: 5,
);
$lead = SupplierLead::factory()->create([
'supplier_project_id' => $sp->id,
'raw_payload' => ['project' => $sp->platform.'_'.$sp->unique_key, 'phones' => ['79161234567']],
'phone' => '79161234567',
'vid' => 1001,
]);
runSnapshotRouteJob($lead->id);
expect(DB::table('deals')->where('tenant_id', $tenant->id)->count())->toBe(1);
// delivered_today инкрементнут на live, delivered_count на snapshot.
expect((int) DB::table('projects')->where('id', $project->id)->value('delivered_today'))->toBe(5);
$snap = DB::table('project_routing_snapshots')
->where('snapshot_date', '2026-05-28')
->where('project_id', $project->id)
->first();
expect((int) $snap->delivered_count)->toBe(1);
Carbon::setTestNow();
});
it('rejects lead when is_active becomes false under lock (R-09)', function (): void {
Carbon::setTestNow('2026-05-28 12:00:00', 'Europe/Moscow');
$tenant = Tenant::factory()->create(['balance_rub' => '500.00', 'frozen_by_balance_at' => null]);
// Live state: is_active=true для snapshot:backfill, но потом клиент нажмёт «пауза»
// (между matchEligibleProjects и handle). Имитируем это inline UPDATE'ом.
$project = Project::factory()->for($tenant)->create([
'is_active' => true,
'delivery_days_mask' => 127,
'daily_limit_target' => 10,
'delivered_today' => 0,
'delivered_in_month' => 0,
]);
$sp = SupplierProject::factory()->create(['signal_type' => 'site']);
linkProjectToSupplier($project, $sp);
// Snapshot НЕ paused (fixed до момента pause'а).
createRoutingSnapshotFromProject(
$project,
date: '2026-05-28',
signalType: 'site',
signalIdentifier: $sp->unique_key,
dailyLimit: 10,
);
// ↓ Имитация: клиент paused проект в окне между matchEligible и handle.
DB::table('projects')->where('id', $project->id)->update(['is_active' => false]);
$lead = SupplierLead::factory()->create([
'supplier_project_id' => $sp->id,
'raw_payload' => ['project' => $sp->platform.'_'.$sp->unique_key, 'phones' => ['79161234567']],
'phone' => '79161234567',
'vid' => 1002,
]);
runSnapshotRouteJob($lead->id);
// Snapshot говорит «доставлять», но live is_active=false под lock'ом — НЕ доставляем.
expect(DB::table('deals')->where('tenant_id', $tenant->id)->count())->toBe(0);
expect((int) DB::table('projects')->where('id', $project->id)->value('delivered_today'))->toBe(0);
$snap = DB::table('project_routing_snapshots')
->where('snapshot_date', '2026-05-28')
->where('project_id', $project->id)
->first();
expect((int) $snap->delivered_count)->toBe(0);
Carbon::setTestNow();
});