From e1a67fd9f3d1b0ed9c1edbafd48b71738503280b Mon Sep 17 00:00:00 2001 From: ncoronel Date: Tue, 21 Jul 2026 14:56:17 -0300 Subject: [PATCH] feat(ticket): add TicketController, TicketResource, and related routes with tests --- .../Ticket/Controllers/TicketController.php | 24 ++++ .../Ticket/Resources/TicketResource.php | 31 ++++++ app/Domains/Ticket/routes/api.php | 10 ++ routes/api.php | 1 + tests/Feature/Ticket/TicketControllerTest.php | 104 ++++++++++++++++++ 5 files changed, 170 insertions(+) create mode 100644 app/Domains/Ticket/Controllers/TicketController.php create mode 100644 app/Domains/Ticket/Resources/TicketResource.php create mode 100644 app/Domains/Ticket/routes/api.php create mode 100644 tests/Feature/Ticket/TicketControllerTest.php diff --git a/app/Domains/Ticket/Controllers/TicketController.php b/app/Domains/Ticket/Controllers/TicketController.php new file mode 100644 index 0000000..a7f9cfe --- /dev/null +++ b/app/Domains/Ticket/Controllers/TicketController.php @@ -0,0 +1,24 @@ +where('tenant_code', $tenant->codigo) + ->where('user_id', $request->user()->getKey()) + ->orderByDesc('id') + ->get(); + + return TicketResource::collection($tickets)->response(); + } +} diff --git a/app/Domains/Ticket/Resources/TicketResource.php b/app/Domains/Ticket/Resources/TicketResource.php new file mode 100644 index 0000000..cafac90 --- /dev/null +++ b/app/Domains/Ticket/Resources/TicketResource.php @@ -0,0 +1,31 @@ + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'tenant_code' => $this->tenant_code, + 'ticket' => $this->ticket, + 'name' => $this->name, + 'description' => $this->description, + 'source_catalog_item_id' => $this->source_catalog_item_id, + 'source_variant_id' => $this->source_variant_id, + 'starts_at' => $this->starts_at, + 'expires_at' => $this->expires_at, + 'used_at' => $this->used_at, + 'is_valid' => $this->is_valid, + 'is_expired' => $this->is_expired, + 'is_used' => $this->is_used, + ]; + } +} diff --git a/app/Domains/Ticket/routes/api.php b/app/Domains/Ticket/routes/api.php new file mode 100644 index 0000000..f1de10a --- /dev/null +++ b/app/Domains/Ticket/routes/api.php @@ -0,0 +1,10 @@ +middleware('auth:sanctum') + ->group(function (): void { + Route::get('tickets', [TicketController::class, 'index']); + }); diff --git a/routes/api.php b/routes/api.php index a4b3d0d..694d7ce 100644 --- a/routes/api.php +++ b/routes/api.php @@ -8,3 +8,4 @@ require __DIR__.'/../app/Domains/Purchase/routes/api.php'; require __DIR__.'/../app/Domains/Tenant/routes/api.php'; require __DIR__.'/../app/Domains/Integration/routes/api.php'; require __DIR__.'/../app/Domains/Menu/routes/api.php'; +require __DIR__.'/../app/Domains/Ticket/routes/api.php'; diff --git a/tests/Feature/Ticket/TicketControllerTest.php b/tests/Feature/Ticket/TicketControllerTest.php new file mode 100644 index 0000000..23ef1cb --- /dev/null +++ b/tests/Feature/Ticket/TicketControllerTest.php @@ -0,0 +1,104 @@ +createTenant('current'); + $user = User::factory()->create(); + $olderTicket = $this->createTicket($tenant, $user, 'Older ticket'); + $newerTicket = $this->createTicket($tenant, $user, 'Newer ticket'); + + $this->actingAs($user, 'sanctum') + ->getJson("/api/tenants/{$tenant->codigo}/tickets") + ->assertOk() + ->assertJsonCount(2, 'data') + ->assertJsonPath('data.0.id', $newerTicket->id) + ->assertJsonPath('data.0.name', 'Newer ticket') + ->assertJsonPath('data.0.is_valid', true) + ->assertJsonPath('data.0.is_expired', false) + ->assertJsonPath('data.0.is_used', false) + ->assertJsonPath('data.1.id', $olderTicket->id) + ->assertJsonMissingPath('meta') + ->assertJsonMissingPath('links'); + } + + public function test_it_does_not_include_tickets_from_other_users_or_tenants(): void + { + $tenant = $this->createTenant('current'); + $otherTenant = $this->createTenant('other'); + $user = User::factory()->create(); + $otherUser = User::factory()->create(); + $visibleTicket = $this->createTicket($tenant, $user, 'Visible'); + $this->createTicket($tenant, $otherUser, 'Other user'); + $this->createTicket($otherTenant, $user, 'Other tenant'); + + $this->actingAs($user, 'sanctum') + ->getJson("/api/tenants/{$tenant->codigo}/tickets") + ->assertOk() + ->assertJsonCount(1, 'data') + ->assertJsonPath('data.0.id', $visibleTicket->id); + } + + public function test_authentication_is_required_to_list_tickets(): void + { + $tenant = $this->createTenant('current'); + + $this->getJson("/api/tenants/{$tenant->codigo}/tickets") + ->assertUnauthorized(); + } + + private function createTicket(Tenant $tenant, User $user, string $name): Ticket + { + return Ticket::query()->create([ + 'tenant_code' => $tenant->codigo, + 'ticket' => (string) Str::uuid(), + 'name' => $name, + 'description' => "Description for {$name}", + 'user_id' => $user->id, + ]); + } + + private function createTenant(string $code): Tenant + { + $headerLogo = $this->createAttachment("{$code}-header"); + $footerLogo = $this->createAttachment("{$code}-footer"); + + return Tenant::query()->create([ + 'codigo' => $code, + 'nombre' => ucfirst($code), + 'dominio' => "{$code}.local", + 'primary_color' => '#000000', + 'secondary_color' => '#000000', + 'danger_color' => '#000000', + 'success_color' => '#000000', + 'header_bg_color' => '#000000', + 'footer_bg_color' => '#000000', + 'header_logo_id' => $headerLogo->id, + 'footer_logo_id' => $footerLogo->id, + ]); + } + + private function createAttachment(string $name): Attachment + { + return Attachment::query()->create([ + 'path' => "test/{$name}.png", + 'filename' => "{$name}.png", + 'type' => AttachmentType::Image, + 'mime_type' => 'image/png', + ]); + } +}