Files
breadcrumb-the-shire/modules/helpdesk/tests/Module/Helpdesk/Service/CustomerEngagementServiceTest.php

184 lines
6.7 KiB
PHP
Raw Normal View History

<?php
namespace MintyPHP\Tests\Module\Helpdesk\Service;
use MintyPHP\Module\Helpdesk\Service\CustomerEngagementService;
use PHPUnit\Framework\TestCase;
class CustomerEngagementServiceTest extends TestCase
{
// Fixed "now" so tests are deterministic: 2026-06-25 12:00:00 UTC.
private const NOW_TS = 1782475200;
/**
* @return array<string, mixed>
*/
private static function ticket(
string $no,
string $customerNo,
string $customerName,
int $createdDaysAgo,
?int $lastActivityDaysAgo = null
): array {
$createdTs = self::NOW_TS - ($createdDaysAgo * 86400);
$activityTs = $lastActivityDaysAgo !== null
? self::NOW_TS - ($lastActivityDaysAgo * 86400)
: $createdTs;
return [
'No' => $no,
'Customer_No' => $customerNo,
'Cust_Name' => $customerName,
'Created_On' => gmdate('Y-m-d\TH:i:s\Z', $createdTs),
'Last_Activity_Date' => gmdate('Y-m-d\TH:i:s\Z', $activityTs),
'Ticket_State' => 'Offen',
];
}
public function testEmptyTicketsReturnsEmptyResult(): void
{
$result = CustomerEngagementService::buildEngagement([], 90, 60, self::NOW_TS);
$this->assertSame(0, $result['summary']['total_customers']);
$this->assertSame(0, $result['summary']['active']);
$this->assertSame(0, $result['summary']['silent']);
$this->assertSame([], $result['silent']);
$this->assertSame([], $result['top_communication']);
}
public function testSkipsTicketsWithoutCustomerNo(): void
{
$tickets = [
self::ticket('T1', '', 'No Customer', 5),
self::ticket('T2', '10610', 'Valid', 5),
];
$result = CustomerEngagementService::buildEngagement($tickets, 90, 60, self::NOW_TS);
$this->assertSame(1, $result['summary']['total_customers']);
}
public function testAggregatesTicketCountsPerCustomer(): void
{
$tickets = [
self::ticket('T1', '10610', 'Kunde A', 5),
self::ticket('T2', '10610', 'Kunde A', 10),
self::ticket('T3', '10620', 'Kunde B', 3),
];
$result = CustomerEngagementService::buildEngagement($tickets, 90, 60, self::NOW_TS);
$this->assertSame(2, $result['summary']['total_customers']);
$top = $result['top_communication'];
$byCustomer = [];
foreach ($top as $row) {
$byCustomer[$row['customer_no']] = $row;
}
$this->assertSame(2, $byCustomer['10610']['ticket_count_period']);
$this->assertSame(1, $byCustomer['10620']['ticket_count_period']);
}
public function testActiveCustomerWithinThreshold(): void
{
// Last contact 10 days ago, threshold 60 => active.
$tickets = [self::ticket('T1', '10610', 'Kunde A', 10)];
$result = CustomerEngagementService::buildEngagement($tickets, 90, 60, self::NOW_TS);
$this->assertSame(1, $result['summary']['active']);
$this->assertSame(0, $result['summary']['silent']);
$this->assertSame([], $result['silent']);
}
public function testSilentCustomerBeyondThreshold(): void
{
// Last contact 90 days ago, threshold 60 => silent.
$tickets = [self::ticket('T1', '10610', 'Kunde A', 90)];
$result = CustomerEngagementService::buildEngagement($tickets, 365, 60, self::NOW_TS);
$this->assertSame(0, $result['summary']['active']);
$this->assertSame(1, $result['summary']['silent']);
$this->assertCount(1, $result['silent']);
$this->assertSame('10610', $result['silent'][0]['customer_no']);
$this->assertSame('silent', $result['silent'][0]['status']);
$this->assertSame(90, $result['silent'][0]['days_since_contact']);
}
public function testLastContactUsesMostRecentActivity(): void
{
// Two tickets: one created 100 days ago but updated 5 days ago, one created 50 days ago.
// Most recent activity is 5 days ago => active.
$tickets = [
self::ticket('T1', '10610', 'Kunde A', 100, 5),
self::ticket('T2', '10610', 'Kunde A', 50, 50),
];
$result = CustomerEngagementService::buildEngagement($tickets, 365, 60, self::NOW_TS);
$this->assertSame(1, $result['summary']['active']);
$row = $result['top_communication'][0];
$this->assertSame(5, $row['days_since_contact']);
}
public function testSilentListSortedByLongestSilenceFirst(): void
{
$tickets = [
self::ticket('T1', '10610', 'Recently Silent', 70),
self::ticket('T2', '10620', 'Long Silent', 200),
self::ticket('T3', '10630', 'Mid Silent', 120),
];
$result = CustomerEngagementService::buildEngagement($tickets, 365, 60, self::NOW_TS);
$order = array_column($result['silent'], 'customer_no');
$this->assertSame(['10620', '10630', '10610'], $order);
}
public function testTopCommunicationExcludesCustomersWithNoPeriodActivity(): void
{
// Ticket older than the 30-day period => not counted in top list.
$tickets = [
self::ticket('T1', '10610', 'Active', 5),
self::ticket('T2', '10620', 'Old', 100),
];
$result = CustomerEngagementService::buildEngagement($tickets, 30, 60, self::NOW_TS);
$tops = array_column($result['top_communication'], 'customer_no');
$this->assertContains('10610', $tops);
$this->assertNotContains('10620', $tops);
}
public function testIgnoresUninitializedBcDates(): void
{
// BC sentinel date 0001-01-01 must be treated as "no contact".
$tickets = [[
'No' => 'T1',
'Customer_No' => '10610',
'Cust_Name' => 'Kunde A',
'Created_On' => '0001-01-01T00:00:00Z',
'Last_Activity_Date' => '0001-01-01T00:00:00Z',
'Ticket_State' => 'Offen',
]];
$result = CustomerEngagementService::buildEngagement($tickets, 90, 60, self::NOW_TS);
$this->assertSame(1, $result['summary']['no_contact_ever']);
$this->assertNull($result['silent'][0]['last_contact_ts']);
$this->assertSame('no_contact_ever', $result['silent'][0]['status']);
}
public function testMetaReflectsInputs(): void
{
$result = CustomerEngagementService::buildEngagement([], 180, 45, self::NOW_TS, true);
$this->assertSame(180, $result['meta']['period_days']);
$this->assertSame(45, $result['meta']['inactivity_days']);
$this->assertTrue($result['meta']['truncated']);
$this->assertSame('medium', $result['meta']['confidence']);
$this->assertSame(self::NOW_TS, $result['meta']['generated_ts']);
}
}