Files
portal/app/tests/Support/Imitation/ImitationTestCase.php
T
Дмитрий 2eaa78f95b
Accessibility (Pa11y live) / a11y (push) Has been cancelled
SAST — Semgrep / Semgrep SAST scan (push) Has been cancelled
fix(stan): Larastan-долг G1/G6 = 0 ошибок (реальные баги — починены, не спрятаны)
Продуктовый код (фиксы, не baseline): TenantRequisites+SupplierLead — явные @property (ide-helper:models пропускал модели); DealsController V1 — лишний ?-> на non-null received_at; ScrubPii — guard instanceof Monolog. Тест-код: ImitationTestCase @param int; findByInn return-type. Baseline перегенерён — в нём ТОЛЬКО ложноположительные (Pest TestCall + защитный ?-> на nullable first() в debug-строках ScenarioBC), 0 продуктовых подавлений (проверено диффом). composer stan: 0.

NB: столбцы lead-region (dadata_qc/phone_operator/region_source/resolved_subject_code) есть в БД, но отсутствуют в db/schema.sql — отдельный дрейф схемы.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 12:40:00 +03:00

120 lines
4.6 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Support\Imitation;
use App\Support\RussianRegions;
use Database\Seeders\PricingTierSeeder;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Tests\Concerns\SharesSupplierPdo;
use Tests\TestCase;
/**
* Base test case for Phase 1 imitation harness tests.
*
* Seeds reference data (pricing_tiers, suppliers) once per test within a
* database transaction so every test starts from a clean, known state.
*
* Usage:
* Extend this class (PHPUnit-style) or include the trait equivalents
* in Pest tests. Pest tests should prefer:
*
* uses(DatabaseTransactions::class, SharesSupplierPdo::class);
* beforeEach(fn () => (new ImitationTestCase())->seedReferenceData());
*
* Schema dependencies (exact columns verified against db/schema.sql):
* pricing_tiers: id, tier_no (1..7), leads_in_tier, price_per_lead_kopecks,
* is_active, effective_from, created_at, updated_at
* suppliers: id, code, name, accepts_types (varchar[]), cost_rub,
* channel, quality_score, is_active, sort_order, created_at
*
* Note: suppliers (b1/b2/b3/direct) are seeded via the initial schema
* migration and delta-migrations — they are expected to already exist in
* the test database. This case does NOT re-seed suppliers; it only verifies
* that at least one supplier row with code='b1' is present and seeds
* pricing_tiers via PricingTierSeeder.
*
* phone_ranges are NOT seeded globally. Tests that exercise the region-
* resolution cascade (Россвязь lookup) should call seedPhoneRange() directly
* for the specific range their scenario requires.
*
* Task 0.5 — Phase 1 Portal Client Imitation Harness.
* Spec: docs/superpowers/specs/2026-06-03-portal-client-imitation-phase1-design.md
*/
abstract class ImitationTestCase extends TestCase
{
use DatabaseTransactions;
use SharesSupplierPdo;
protected function setUp(): void
{
parent::setUp();
$this->seedReferenceData();
}
/**
* Seed shared reference data required by all imitation tests.
*
* Called automatically from setUp(). Safe to call multiple times within a
* transaction (PricingTierSeeder uses updateOrCreate; supplier check is
* read-only).
*/
public function seedReferenceData(): void
{
// Pricing tiers — required by LedgerService::chargeForDelivery.
// PricingTierSeeder uses updateOrCreate so it is safe to call within
// a DatabaseTransactions-wrapped test.
$this->seed(PricingTierSeeder::class);
// Tenant context: global bypass to allow cross-tenant reads during seeding.
DB::statement("SELECT set_config('app.current_tenant_id', '0', true)");
}
/**
* Seed a single phone range for Россвязь prefix lookup tests.
*
* Only call this when your specific test scenario exercises the Россвязь
* branch of LeadRegionResolver (e.g. DaData degradation tests).
*
* @param int $defCode DEF-code prefix (e.g. 999).
* @param int $from Lower bound of number range (e.g. 0).
* @param int $to Upper bound of number range (e.g. 99999).
* @param int $subjectCode Subject code (1..89, порядковый, НЕ ГИБДД).
* Use App\Support\RussianRegions::nameToCode() for lookup.
*/
protected function seedPhoneRange(
int $defCode,
int $from,
int $to,
int $subjectCode,
): void {
// Anchor phone_ranges_imports row first — phone_ranges.import_id is a
// required FK (migration 2026_05_31_100000). F1 fix: the previous version
// used non-existent columns (range_from/range_to/region_name) and omitted
// import_id, so every Россвязь-branch test that called it failed at runtime.
$importId = DB::table('phone_ranges_imports')->insertGetId([
'imported_at' => now(),
'source_url' => 'test://rossvyaz',
'rows_inserted' => 1,
'rows_updated' => 0,
'checksum_sha256' => str_repeat('0', 64),
'status' => 'completed',
'completed_at' => now(),
]);
DB::table('phone_ranges')->insert([
'def_code' => $defCode,
'from_num' => $from,
'to_num' => $to,
'operator' => 'test-operator',
'region' => RussianRegions::CODE_TO_NAME[$subjectCode] ?? 'test-region',
'region_normalized' => null,
'subject_code' => $subjectCode,
'imported_at' => now(),
'import_id' => $importId,
]);
}
}