create([ 'tier_no' => 1, 'leads_in_tier' => null, 'price_per_lead_kopecks' => 5000, // 50₽/лид 'is_active' => true, 'effective_from' => now(), ]); // Баланс заведомо большой — преflight всегда проходит, тест про нормализацию. $this->tenant = Tenant::factory()->withRequisites()->create(['balance_rub' => '100000.00']); $this->user = User::factory()->create(['tenant_id' => $this->tenant->id]); }); function createCallProject(string $rawPhone, string $name = 'Звонок'): TestResponse { return test()->actingAs(test()->user)->postJson('/api/projects', [ 'name' => $name, 'signal_type' => 'call', 'signal_identifier' => $rawPhone, 'daily_limit_target' => 5, 'regions' => [], 'delivery_days_mask' => 127, ]); } it('normalizes +7 (916) 123-45-67 to 79161234567 on create', function () { createCallProject('+7 (916) 123-45-67')->assertCreated(); expect(Project::where('tenant_id', $this->tenant->id)->value('signal_identifier')) ->toBe('79161234567'); }); it('normalizes leading 8 to 7 on create', function () { createCallProject('8 916 123 45 67')->assertCreated(); expect(Project::where('tenant_id', $this->tenant->id)->value('signal_identifier')) ->toBe('79161234567'); }); it('normalizes hyphenated 7-916-123-45-67 on create', function () { createCallProject('7-916-123-45-67')->assertCreated(); expect(Project::where('tenant_id', $this->tenant->id)->value('signal_identifier')) ->toBe('79161234567'); }); it('keeps already canonical 79161234567 unchanged (idempotent)', function () { createCallProject('79161234567')->assertCreated(); expect(Project::where('tenant_id', $this->tenant->id)->value('signal_identifier')) ->toBe('79161234567'); }); it('never stores a leading plus (routing matches without +)', function () { createCallProject('+79161234567')->assertCreated(); $stored = Project::where('tenant_id', $this->tenant->id)->value('signal_identifier'); expect($stored)->toBe('79161234567') ->and($stored)->not->toContain('+'); }); it('rejects real garbage with a helpful message naming the on-screen field', function () { $response = createCallProject('12345'); $response->assertStatus(422); $msg = $response->json('errors.signal_identifier.0'); expect($msg)->toContain('79161234567') // показывает пример формата ->and($msg)->not->toContain('Источник'); // не всплывает внутреннее имя поля }); it('does NOT normalize a site domain (only call is a phone)', function () { test()->actingAs($this->user)->postJson('/api/projects', [ 'name' => 'Сайт', 'signal_type' => 'site', 'signal_identifier' => 'my-site.ru', 'daily_limit_target' => 5, 'regions' => [], 'delivery_days_mask' => 127, ])->assertCreated(); expect(Project::where('tenant_id', $this->tenant->id)->value('signal_identifier')) ->toBe('my-site.ru'); }); it('normalizes phone on update of a call project', function () { $project = Project::factory()->for($this->tenant)->create([ 'signal_type' => 'call', 'signal_identifier' => '79990001122', 'is_active' => true, 'daily_limit_target' => 5, ]); test()->actingAs($this->user)->patchJson("/api/projects/{$project->id}", [ 'signal_identifier' => '+7 (916) 123-45-67', ])->assertOk(); expect($project->fresh()->signal_identifier)->toBe('79161234567'); });