singleloginsystem/tests/Feature/TicketSystemTest.php
= cac5f4905b WIP: implement ticketing system with support for notifications and attachments
- Added database migrations for tickets, statuses, roles, users, and related associations.
- Introduced models and enums for ticket status, permissions, and notifications.
- Implemented `TicketService` for ticket management and notification handling.
- Created Livewire form and UI for raising tickets with file attachments and routing options.
- Added tests covering ticket creation, validation, notifications, and file uploads.
2026-05-25 13:04:43 +00:00

186 lines
5.7 KiB
PHP

<?php
declare(strict_types=1);
use App\Data\Ticket\CreateTicketRequest;
use App\Enums\TicketStatusEnum;
use App\Models\{ConnectedApp, Role, User};
use App\Notifications\TicketRaisedNotification;
use App\Services\TicketService;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\{Notification, Storage};
use Livewire\Livewire;
beforeEach(function (): void {
$this->seed(DatabaseSeeder::class);
});
test('guests cannot access the tickets page', function (): void {
$this->get(route('tickets.index'))
->assertRedirect(route('login'));
});
test('authenticated users with correct permissions can access the tickets page', function (): void {
$user = User::factory()->create();
$user->assignRole('user');
$this->actingAs($user);
$this->get(route('tickets.index'))
->assertOk()
->assertSee('Support Tickets');
});
test('submitting a support ticket creates the ticket and generates a unique ticket ID', function (): void {
$user = User::factory()->create();
$user->assignRole('user');
$this->actingAs($user);
$app = ConnectedApp::first();
$supportRole = Role::where('name', 'Support')->firstOrFail();
$dto = new CreateTicketRequest(
userId: $user->id,
connectedAppId: $app->id,
issueCategory: 'Billing Query',
description: 'Need assistance with billing renewal.',
notifiedRoleIds: [$supportRole->id],
notifiedUserIds: [],
attachment: null,
);
$service = app(TicketService::class);
$ticketData = $service->create($dto);
expect($ticketData->ticketId)->toStartWith('TKT-')
->and(mb_strlen($ticketData->ticketId))->toBe(12)
->and($ticketData->issueCategory)->toBe('Billing Query')
->and($ticketData->statusSlug)->toBe(TicketStatusEnum::Open->value)
->and($ticketData->notifiedRoleNames)->toContain('Support');
$this->assertDatabaseHas('tickets', [
'user_id' => $user->id,
'connected_app_id' => $app->id,
'issue_category' => 'Billing Query',
]);
$this->assertDatabaseHas('ticket_notified_roles', [
'ticket_id' => $ticketData->id,
'role_id' => $supportRole->id,
]);
});
test('submitting a ticket with category Others requires at least 20 characters for description', function (): void {
$user = User::factory()->create();
$user->assignRole('user');
$this->actingAs($user);
$supportRole = Role::where('name', 'Support')->firstOrFail();
// Test Validation Failure via Livewire component form object
Livewire::test('pages::tickets.index')
->set('form.issueCategory', 'Others')
->set('form.description', 'Short desc')
->set('form.routingType', 'role')
->set('form.notifiedRoleIds', [$supportRole->id])
->call('saveTicket')
->assertHasErrors(['form.description' => 'min']);
});
test('attaching a file for Others category uploads and stores the file', function (): void {
Storage::fake('public');
$user = User::factory()->create();
$user->assignRole('user');
$this->actingAs($user);
$supportRole = Role::where('name', 'Support')->firstOrFail();
$file = UploadedFile::fake()->create('screenshot.png', 500);
$dto = new CreateTicketRequest(
userId: $user->id,
connectedAppId: null,
issueCategory: 'Others',
description: 'A very detailed query containing more than twenty characters.',
notifiedRoleIds: [$supportRole->id],
notifiedUserIds: [],
attachment: $file,
);
$service = app(TicketService::class);
$ticketData = $service->create($dto);
$this->assertDatabaseHas('ticket_attachments', [
'file_name' => 'screenshot.png',
'mime_type' => 'image/png',
]);
expect(count($ticketData->attachments))->toBe(1);
Storage::disk('public')->assertExists($ticketData->attachments[0]['file_path']);
});
test('notification routing to a specific user triggers notification to that user', function (): void {
Notification::fake();
$user = User::factory()->create();
$user->assignRole('user');
$this->actingAs($user);
$adminUser = User::where('email', 'admin@example.com')->firstOrFail();
$dto = new CreateTicketRequest(
userId: $user->id,
connectedAppId: null,
issueCategory: 'Technical Issue',
description: 'SSO log-in error.',
notifiedRoleIds: [],
notifiedUserIds: [$adminUser->id],
attachment: null,
);
$service = app(TicketService::class);
$service->create($dto);
Notification::assertSentTo(
$adminUser,
TicketRaisedNotification::class
);
});
test('notification routing to multiple roles triggers notifications to all users assigned to those roles', function (): void {
Notification::fake();
$user = User::factory()->create();
$user->assignRole('user');
$this->actingAs($user);
$supportRole = Role::where('name', 'Support')->firstOrFail();
$managerRole = Role::where('name', 'Manager')->firstOrFail();
// Create a support user
$supportUser = User::factory()->create();
$supportUser->assignRole($supportRole);
// Create a manager user
$managerUser = User::factory()->create();
$managerUser->assignRole($managerRole);
$dto = new CreateTicketRequest(
userId: $user->id,
connectedAppId: null,
issueCategory: 'Service Extension',
description: 'Requesting 3 days trial extension.',
notifiedRoleIds: [$supportRole->id, $managerRole->id],
notifiedUserIds: [],
attachment: null,
);
$service = app(TicketService::class);
$service->create($dto);
Notification::assertSentTo(
[$supportUser, $managerUser],
TicketRaisedNotification::class
);
});