Files

153 lines
5.6 KiB
PHP

<?php
declare(strict_types=1);
require __DIR__ . '/validation.php';
use Box\Mod\Support\Entity\Helpdesk;
use Box\Mod\Support\Entity\SupportTicket;
use Box\Mod\Support\Entity\SupportTicketMessage;
$ticket = new SupportTicket();
(new ReflectionProperty($ticket, 'id'))->setValue($ticket, 4);
$ticket->setClientId(7);
$ticket->setStatus('on_hold');
$helpdesk = new Helpdesk();
$helpdesk->setCanReopen(true);
$ticket->setSupportHelpdesk($helpdesk);
$support = new class($ticket) extends \Box\Mod\Support\Service {
public function __construct(private SupportTicket $ticket) {}
public function setDi(\Pimple\Container $di): void { $this->di = $di; }
public function getTicketById(int $id): SupportTicket
{
if ($id !== 4) {
throw new \FOSSBilling\InformationException('Ticket not found');
}
return $this->ticket;
}
};
$em = new class {
public array $messages = [];
public function persist($message): void
{
if (!$message instanceof SupportTicketMessage) {
throw new RuntimeException('Unexpected entity');
}
(new ReflectionProperty($message, 'id'))->setValue($message, count($this->messages) + 10);
$this->messages[] = $message;
}
public function flush(): void {}
};
$di = new \Pimple\Container();
$lookup = new class {
public array $ids = [7];
public function fetchFirstColumn($sql, $params) { return $this->ids; }
};
$di['dbal'] = $lookup;
$di['mailgateway_notification_cooldown'] = new class {
public function run($sender, $queue): bool { return $queue() !== false; }
};
$di['db'] = new class($client) {
public function __construct(public $client) {}
public function getExistingModelById($type, $id) { return $this->client; }
};
$di['em'] = $em;
$di['request'] = new \Symfony\Component\HttpFoundation\Request();
$di['events_manager'] = new class {
public array $events = [];
public function fire($data): void { $this->events[] = $data; }
};
$di['logger'] = new class {
public function info(...$args): void {}
public function warning(...$args): void {}
};
$email = new class {
public array $notifications = [];
public bool $fail = false;
public function sendTemplate($data): void
{
if ($this->fail) {
throw new RuntimeException('Simulated mail failure');
}
$this->notifications[] = $data;
}
};
$di['mod_service'] = $di->protect(fn ($module) => $module === 'email' ? $email : $support);
$support->setDi($di);
$service->setDi($di);
$reply = ['ticket_id' => '4', 'sender_email' => 'customer@example.com', 'content' => ' Customer reply '];
$result = $service->reply($reply);
if ($result !== ['ticket_id' => 4, 'message_id' => 10]
|| $ticket->getStatus() !== 'open' || $em->messages[0]->getClientId() !== 7
|| $em->messages[0]->getAdminId() !== null || $em->messages[0]->getContent() !== 'Customer reply') {
throw new RuntimeException('Customer reply attribution or status incorrect');
}
foreach ([[], [8], [7, 8]] as $ids) {
$lookup->ids = $ids;
try {
$service->reply($reply);
throw new RuntimeException('Wrong or ambiguous sender accepted');
} catch (\FOSSBilling\InformationException) {}
}
$lookup->ids = [7];
foreach ([0, -1, '4x', [], true, null, 4.5, 999] as $id) {
try {
$service->reply(array_replace($reply, ['ticket_id' => $id]));
throw new RuntimeException('Invalid or missing ticket accepted');
} catch (\FOSSBilling\InformationException) {}
}
$ticket->setStatus('closed');
$helpdesk->setCanReopen(false);
try {
$service->reply($reply);
throw new RuntimeException('Reopen restriction bypassed');
} catch (\FOSSBilling\InformationException) {}
$helpdesk->setCanReopen(true);
$service->reply($reply);
// A guest ticket retains its guest owner even if that address later registers.
$ticket->setClientId(null);
$ticket->setAccessHash('private-hash');
$ticket->setAuthorEmail('guest@example.com');
$ticket->setStatus('on_hold');
$reply['sender_email'] = 'GUEST@example.com';
$service->reply($reply);
$message = $em->messages[2];
if ($message->getClientId() !== null || $message->getAdminId() !== null || $ticket->getStatus() !== 'open') {
throw new RuntimeException('Guest reply attribution or status incorrect');
}
$reply['sender_email'] = 'other@example.com';
try {
$service->reply($reply);
throw new RuntimeException('Wrong guest accepted');
} catch (\FOSSBilling\InformationException) {}
if (count($em->messages) !== 3) {
throw new RuntimeException('Rejected reply created a message');
}
if ($email->notifications !== []) {
throw new RuntimeException('Direct reply processing attempted a notification');
}
$service->notifySenderMismatch('other@example.com', 4);
foreach ($email->notifications as $notification) {
if ($notification !== [
'to' => $notification['sender_email'],
'code' => 'mailgateway_sender_mismatch',
'sender_email' => $notification['sender_email'],
'ticket_id' => 4,
]) {
throw new RuntimeException('Mismatch notification contains incorrect or private ticket data');
}
}
$email->fail = true;
$service->notifySenderMismatch('other@example.com', 4);
try {
$service->reply($reply);
throw new RuntimeException('Mail failure allowed a mismatched reply');
} catch (\FOSSBilling\InformationException) {}
if (count($em->messages) !== 3) {
throw new RuntimeException('Mail failure created a message');
}
echo "Passed customer and guest reply attribution, sender ownership, ambiguity, ticket validation, and reopen policy using Support ticketReply.\n";
echo "Passed mismatch notification recipient, private data exclusion, and rejection despite mail failure.\n";