137 lines
5.6 KiB
PHP
137 lines
5.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Modules\Helpdesk\Tests\Feature;
|
|
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Livewire\Livewire;
|
|
use Modules\Helpdesk\Livewire\TicketShow;
|
|
use Modules\Helpdesk\Models\AutomationRule;
|
|
use Modules\Helpdesk\Models\CannedResponse;
|
|
use Modules\Helpdesk\Models\ChecklistItem;
|
|
use Modules\Helpdesk\Models\ChecklistTemplate;
|
|
use Modules\Helpdesk\Models\Product;
|
|
use Modules\Helpdesk\Models\SlaPolicy;
|
|
use Modules\Helpdesk\Models\TicketSlaEvent;
|
|
use Modules\Helpdesk\Services\ChecklistService;
|
|
use Modules\Helpdesk\Services\SlaService;
|
|
use Modules\Helpdesk\Services\TicketService;
|
|
use Modules\Helpdesk\Tests\Concerns\BuildsHelpdeskFixtures;
|
|
use Tests\TestCase;
|
|
|
|
final class RedmineInspiredHelpdeskFeatureTest extends TestCase
|
|
{
|
|
use BuildsHelpdeskFixtures;
|
|
use RefreshDatabase;
|
|
|
|
public function test_ticket_creation_applies_automation_sla_checklist_and_product_linkage(): void
|
|
{
|
|
$fixtures = $this->seedLookupData();
|
|
$user = $this->user(['name' => 'Mark Requester']);
|
|
$product = Product::query()->create(['name' => 'WMS Platform', 'slug' => 'wms-platform', 'type' => 'service', 'is_active' => true]);
|
|
|
|
SlaPolicy::query()->create([
|
|
'name' => 'High Priority SLA',
|
|
'slug' => 'high-priority-sla',
|
|
'priority_id' => $fixtures['high']->id,
|
|
'first_response_minutes' => 30,
|
|
'resolution_minutes' => 240,
|
|
'is_default' => true,
|
|
'is_active' => true,
|
|
]);
|
|
|
|
AutomationRule::query()->create([
|
|
'name' => 'Printer escalation',
|
|
'slug' => 'printer-escalation',
|
|
'match_subject' => 'printer',
|
|
'set_priority_id' => $fixtures['high']->id,
|
|
'set_team_id' => $fixtures['team']->id,
|
|
'is_active' => true,
|
|
'sort_order' => 1,
|
|
]);
|
|
|
|
ChecklistTemplate::query()->create([
|
|
'name' => 'Bug checklist',
|
|
'slug' => 'bug-checklist',
|
|
'category_id' => $fixtures['category']->id,
|
|
'items' => ['Confirm reproduction', 'Record workaround'],
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$ticket = app(TicketService::class)->create($user, [
|
|
'subject' => 'Printer labels are failing',
|
|
'body' => '<p>Printer labels fail for every order.</p>',
|
|
'priority_id' => $fixtures['normal']->id,
|
|
'category_id' => $fixtures['category']->id,
|
|
'project_id' => $fixtures['project']->id,
|
|
'product_id' => $product->id,
|
|
]);
|
|
|
|
$ticket = $ticket->fresh(['checklistItems']);
|
|
|
|
self::assertSame($fixtures['high']->id, $ticket->priority_id);
|
|
self::assertSame($fixtures['team']->id, $ticket->team_id);
|
|
self::assertSame($product->id, $ticket->product_id);
|
|
self::assertNotNull($ticket->sla_policy_id);
|
|
self::assertNotNull($ticket->sla_first_response_due_at);
|
|
self::assertCount(2, $ticket->checklistItems);
|
|
$this->assertDatabaseHas('helpdesk_ticket_sla_events', ['ticket_id' => $ticket->id, 'event' => 'sla.policy_applied']);
|
|
}
|
|
|
|
public function test_canned_response_macros_insert_into_ticket_reply_editor(): void
|
|
{
|
|
$fixtures = $this->seedLookupData();
|
|
$agent = $this->agent(['name' => 'Agent Smith']);
|
|
$ticket = $this->ticket(['owner' => $this->user(['name' => 'Customer Jane']), 'agent' => $agent, 'status_id' => $fixtures['open']->id, 'priority_id' => $fixtures['normal']->id]);
|
|
$response = CannedResponse::query()->create([
|
|
'name' => 'Acknowledgement',
|
|
'slug' => 'acknowledgement',
|
|
'body' => 'Hello {{ requester.name }}, ticket {{ ticket.reference }} is being reviewed by {{ agent.name }}.',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
Livewire::actingAs($agent)
|
|
->test(TicketShow::class, ['ticket' => $ticket])
|
|
->set('canned_response_id', $response->id)
|
|
->call('applyCannedResponse')
|
|
->assertSet('commentBody', 'Hello Customer Jane, ticket '.$ticket->reference.' is being reviewed by Agent Smith.');
|
|
}
|
|
|
|
public function test_sla_service_records_first_response_and_resolution_breaches(): void
|
|
{
|
|
$fixtures = $this->seedLookupData();
|
|
$agent = $this->agent();
|
|
$ticket = $this->ticket(['status_id' => $fixtures['open']->id, 'priority_id' => $fixtures['high']->id]);
|
|
|
|
$ticket->forceFill([
|
|
'sla_first_response_due_at' => now()->subMinute(),
|
|
'sla_resolution_due_at' => now()->subMinute(),
|
|
])->save();
|
|
|
|
app(SlaService::class)->recordPublicResponse($ticket->fresh(), $agent);
|
|
app(SlaService::class)->recordClosure($ticket->fresh());
|
|
|
|
$ticket = $ticket->fresh();
|
|
|
|
self::assertNotNull($ticket->first_response_at);
|
|
self::assertNotNull($ticket->sla_first_response_breached_at);
|
|
self::assertNotNull($ticket->sla_resolution_breached_at);
|
|
self::assertGreaterThanOrEqual(2, TicketSlaEvent::query()->where('ticket_id', $ticket->id)->count());
|
|
}
|
|
|
|
public function test_checklist_items_can_be_toggled_by_agents(): void
|
|
{
|
|
$fixtures = $this->seedLookupData();
|
|
$agent = $this->agent();
|
|
$ticket = $this->ticket(['agent' => $agent, 'status_id' => $fixtures['open']->id, 'priority_id' => $fixtures['normal']->id]);
|
|
$item = ChecklistItem::query()->create(['ticket_id' => $ticket->id, 'title' => 'Confirm reproduction', 'sort_order' => 10]);
|
|
|
|
$updated = app(ChecklistService::class)->toggle($item, $agent);
|
|
|
|
self::assertTrue($updated->is_completed);
|
|
self::assertSame($agent->id, $updated->completed_by);
|
|
self::assertNotNull($updated->completed_at);
|
|
}
|
|
}
|