e07d025efd
- PricingTierResolver: pure-function, ищет tier для N-го лида (1-based). 100→tier1, 101→tier2, 6000→tier6 (cumul.sum 1-6), 6001+→tier7 (NULL=unlimited). RuntimeException на пустой коллекции. - PricingTierRepository::activeAt(Carbon): DB-обёртка, MAX(effective_from) <= $at per tier_no (учёт «новая сетка перекрывает старую»), is_active=true. - 7 unit-тестов (in-memory, без БД) + 4 integration-теста (DatabaseTransactions с baseline-cleanup для seed-7-tiers из PricingTierSeeder Task 1). - phpstan-baseline.neon: +3 entry (Pest TestCall::\$resolver/\$tiers/\$repo) — следуем project-convention (см. SupplierResolverTest идентичные baseline-entries). Spec: docs/superpowers/specs/2026-05-11-plan4-billing-csv-admin-design.md §3.1 Plan: docs/superpowers/plans/2026-05-11-plan4-billing-csv-admin.md Task 2 Tests: 633 passed / 3 skipped / 0 failed (+11 new); pint clean; stan 0 errors above baseline.
52 lines
2.0 KiB
PHP
52 lines
2.0 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
use App\Models\PricingTier;
|
||
use App\Repositories\PricingTierRepository;
|
||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||
use Illuminate\Support\Carbon;
|
||
|
||
uses(DatabaseTransactions::class);
|
||
|
||
beforeEach(function () {
|
||
$this->repo = new PricingTierRepository;
|
||
|
||
// Clean baseline — удаляем seed-7-tiers, чтобы тесты могли создать свои контексты
|
||
// (PricingTierSeeder seed'ит 7 ступеней с effective_from='1970-01-01' через DatabaseSeeder).
|
||
PricingTier::query()->delete();
|
||
});
|
||
|
||
it('returns active tiers ordered by tier_no', function () {
|
||
PricingTier::factory()->create(['tier_no' => 2, 'effective_from' => '2024-01-01', 'is_active' => true]);
|
||
PricingTier::factory()->create(['tier_no' => 1, 'effective_from' => '2024-01-01', 'is_active' => true]);
|
||
|
||
$tiers = $this->repo->activeAt(Carbon::parse('2024-06-01'));
|
||
|
||
expect($tiers->pluck('tier_no')->all())->toBe([1, 2]);
|
||
});
|
||
|
||
it('returns max effective_from <= today (newer overrides older)', function () {
|
||
PricingTier::factory()->create(['tier_no' => 1, 'effective_from' => '2024-01-01', 'price_per_lead_kopecks' => 50000]);
|
||
PricingTier::factory()->create(['tier_no' => 1, 'effective_from' => '2024-06-01', 'price_per_lead_kopecks' => 30000]);
|
||
|
||
$tiers = $this->repo->activeAt(Carbon::parse('2024-07-01'));
|
||
|
||
expect($tiers->first()->price_per_lead_kopecks)->toBe(30000);
|
||
});
|
||
|
||
it('ignores future effective_from', function () {
|
||
PricingTier::factory()->create(['tier_no' => 1, 'effective_from' => '2024-01-01', 'price_per_lead_kopecks' => 50000]);
|
||
PricingTier::factory()->create(['tier_no' => 1, 'effective_from' => '2099-01-01', 'price_per_lead_kopecks' => 99999]);
|
||
|
||
$tiers = $this->repo->activeAt(Carbon::parse('2024-06-01'));
|
||
|
||
expect($tiers->first()->price_per_lead_kopecks)->toBe(50000);
|
||
});
|
||
|
||
it('returns empty collection when no active tiers exist', function () {
|
||
$tiers = $this->repo->activeAt(Carbon::parse('2024-06-01'));
|
||
|
||
expect($tiers)->toBeEmpty();
|
||
});
|