Files
WMS/Modules/Helpdesk/Tests/Feature/RedmineInspiredHelpdeskFeatureTest.php
T
2026-06-23 13:39:02 +01:00

137 lines
5.7 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);
}
}