CRM/backend/tests/Feature/AgentMobileTest.php

251 خطوط
8.7 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\Contact;
use App\Models\ContactPhone;
use App\Models\ContactRelation;
use App\Models\FollowUp;
use App\Models\Lead;
use App\Models\LeadStatus;
use App\Models\PipelineStage;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Spatie\Permission\Models\Role;
use Tests\TestCase;
class AgentMobileTest extends TestCase
{
use RefreshDatabase;
public function test_agent_can_open_mobile_dashboard_and_queue(): void
{
$agent = $this->makeUserWithRole('agent');
$lead = $this->makeLead($agent, [
'priority' => 9,
'next_follow_up_at' => now()->subHour(),
]);
$this->makePrimaryContact($lead, '09120001122');
FollowUp::create([
'lead_id' => $lead->id,
'user_id' => $agent->id,
'scheduled_at' => now()->subHour(),
'status' => 'pending',
]);
$response = $this->actingAs($agent)
->getJson('/api/agent/mobile/dashboard')
->assertOk()
->assertJsonPath('data.stats.leads_needing_action', 1)
->assertJsonPath('data.suggested_call.id', $lead->id);
$this->assertStringContainsString('no-store', $response->headers->get('Cache-Control'));
$this->actingAs($agent)
->getJson('/api/agent/mobile/call-queue')
->assertOk()
->assertJsonPath('data.0.id', $lead->id)
->assertJsonMissing(['phone' => '09120001122']);
}
public function test_mobile_leads_are_scoped_to_logged_in_agent(): void
{
$agent = $this->makeUserWithRole('agent');
$otherAgent = $this->makeUserWithRole('agent');
$ownLead = $this->makeLead($agent, ['first_name' => 'Own']);
$otherLead = $this->makeLead($otherAgent, ['first_name' => 'Other']);
$this->actingAs($agent)
->getJson('/api/agent/mobile/leads')
->assertOk()
->assertJsonFragment(['id' => $ownLead->id])
->assertJsonMissing(['id' => $otherLead->id]);
$this->actingAs($agent)
->getJson("/api/agent/mobile/leads/{$otherLead->id}")
->assertForbidden();
}
public function test_non_agent_cannot_access_mobile_agent_endpoints(): void
{
$supervisor = $this->makeUserWithRole('supervisor');
$this->actingAs($supervisor)
->getJson('/api/agent/mobile/dashboard')
->assertForbidden();
}
public function test_mobile_lead_detail_does_not_expose_phone_numbers(): void
{
$agent = $this->makeUserWithRole('agent');
$lead = $this->makeLead($agent, [
'phone' => '09123334455',
'phone_secondary' => '02199887766',
]);
$this->makePrimaryContact($lead, '09123334455');
$response = $this->actingAs($agent)
->getJson("/api/agent/mobile/leads/{$lead->id}")
->assertOk();
$content = $response->getContent();
$this->assertStringNotContainsString('09123334455', $content);
$this->assertStringNotContainsString('02199887766', $content);
}
public function test_mobile_start_call_uses_secure_endpoint_without_exposing_phone(): void
{
$agent = $this->makeUserWithRole('agent');
$lead = $this->makeLead($agent);
$contact = $this->makePrimaryContact($lead, '09124445566');
$phone = $contact->phones()->first();
$response = $this->actingAs($agent)
->postJson('/api/agent/mobile/calls', [
'lead_id' => $lead->id,
'contact_phone_id' => $phone->id,
])
->assertCreated()
->assertJsonPath('data.contact_phone_id', $phone->id);
$this->assertStringNotContainsString('09124445566', $response->getContent());
$response->assertJsonMissingPath('data.recording_url');
$response->assertJsonMissingPath('data.provider_call_id');
}
public function test_ami_call_requires_agent_extension(): void
{
Setting::updateOrCreate(['key' => 'voip_provider'], ['value' => 'ami', 'group' => 'voip', 'type' => 'string']);
$agent = $this->makeUserWithRole('agent');
$lead = $this->makeLead($agent);
$contact = $this->makePrimaryContact($lead, '09124445566');
$phone = $contact->phones()->first();
$this->actingAs($agent)
->postJson('/api/agent/mobile/calls', [
'lead_id' => $lead->id,
'contact_phone_id' => $phone->id,
])
->assertUnprocessable()
->assertJsonPath('message', 'شماره داخلی برای این کاربر ثبت نشده است.');
$this->assertDatabaseMissing('calls', [
'lead_id' => $lead->id,
'user_id' => $agent->id,
]);
}
public function test_mobile_call_result_creates_follow_up_new_contact_and_primary_change(): void
{
$agent = $this->makeUserWithRole('agent');
$lead = $this->makeLead($agent);
$contact = $this->makePrimaryContact($lead, '09127778899');
$phone = $contact->phones()->first();
$callId = $this->actingAs($agent)
->postJson('/api/agent/mobile/calls', [
'lead_id' => $lead->id,
'contact_phone_id' => $phone->id,
])
->json('data.call_id');
$followUpAt = now()->addDay()->setSecond(0)->toIso8601String();
$this->actingAs($agent)
->postJson('/api/agent/mobile/call-results', [
'call_id' => $callId,
'result' => 'معرفی شماره جدید',
'notes' => 'با مخاطب جدید هماهنگ شود',
'referral' => [
'name' => 'خانم محمدی',
'phone' => '09121110000',
'role' => 'مدیر خرید',
'relation_description' => 'همکار تصمیم‌گیرنده',
'make_primary' => true,
'next_follow_up_at' => $followUpAt,
],
])
->assertOk()
->assertJsonPath('message', 'نتیجه تماس ثبت شد');
$this->assertDatabaseHas('calls', [
'id' => $callId,
'result' => 'معرفی شماره جدید',
'user_id' => $agent->id,
]);
$this->assertDatabaseHas('follow_ups', [
'lead_id' => $lead->id,
'user_id' => $agent->id,
'call_id' => $callId,
'status' => 'pending',
]);
$this->assertDatabaseHas('contacts', [
'lead_id' => $lead->id,
'name' => 'خانم محمدی',
'is_primary' => true,
]);
$this->assertDatabaseHas('contact_phones', [
'phone' => '09121110000',
'status' => 'active',
]);
$this->assertSame(1, ContactRelation::where('lead_id', $lead->id)->where('from_contact_id', $contact->id)->count());
$this->assertFalse($contact->fresh()->is_primary);
}
private function makeUserWithRole(string $roleName): User
{
$role = Role::firstOrCreate(['name' => $roleName, 'guard_name' => 'web']);
$user = User::factory()->create(['is_active' => true]);
$user->assignRole($role);
return $user;
}
private function makeLead(User $agent, array $attributes = []): Lead
{
$status = LeadStatus::firstOrCreate(
['name' => 'در جریان'],
['color' => '#2563EB', 'sort_order' => 0, 'is_default' => true]
);
$stage = PipelineStage::firstOrCreate(
['name' => 'در انتظار تماس'],
['color' => '#6366F1', 'sort_order' => 1, 'is_default' => false]
);
return Lead::create(array_merge([
'company' => 'Acme',
'first_name' => 'Ali',
'last_name' => 'Karimi',
'phone' => '02111112222',
'assigned_to' => $agent->id,
'is_unassigned' => false,
'lead_status_id' => $status->id,
'pipeline_stage_id' => $stage->id,
], $attributes));
}
private function makePrimaryContact(Lead $lead, string $phone): Contact
{
$contact = Contact::create([
'lead_id' => $lead->id,
'name' => 'آقای رضایی',
'role' => 'مسئول خرید',
'status' => 'active',
'is_primary' => true,
'primary_reason' => 'مخاطب اصلی',
'created_by' => $lead->assigned_to,
]);
ContactPhone::create([
'contact_id' => $contact->id,
'phone' => $phone,
'type' => 'mobile',
'status' => 'active',
]);
return $contact;
}
}