create(); $user->roles()->attach(Role::firstOrCreate(['name' => $role->value])); return $user; } function completedReportedReservation(User $requester): Reservation { $asset = SnipeAsset::factory()->create([ 'asset_tag' => 'REPORT-001', 'name' => 'Câmara de relatório', 'category_name' => 'Fotografia', ]); $reservation = Reservation::create([ 'reference' => 'REQ-REPORT-001', 'requester_id' => $requester->id, 'purpose' => 'Produção de conteúdos para relatório.', 'usage_location' => 'Estúdio', 'starts_at' => now()->subDays(2), 'ends_at' => now()->subDays(2)->addHours(3), 'status' => ReservationStatus::Completed, ]); $reservation->items()->create([ 'snipe_asset_id' => $asset->id, 'starts_at' => $reservation->starts_at, 'ends_at' => $reservation->ends_at, 'buffer_starts_at' => $reservation->starts_at, 'buffer_ends_at' => $reservation->ends_at, 'status' => ReservationItemStatus::Returned, 'delivered_at' => $reservation->starts_at, 'returned_at' => $reservation->ends_at, ]); return $reservation; } it('apresenta o calendário global apenas a perfis autorizados', function () { $requester = phaseSixUser(RoleName::Requester); $reservation = completedReportedReservation($requester); $month = $reservation->starts_at->format('Y-m'); $this->actingAs($requester)->get("/calendario-global?month={$month}")->assertForbidden(); $this->actingAs(phaseSixUser(RoleName::Approver)) ->get("/calendario-global?month={$month}") ->assertOk() ->assertInertia(fn (Assert $page) => $page ->component('reservations/calendar') ->where('scope', 'global') ->has('reservations', 1) ->where('reservations.0.reference', 'REQ-REPORT-001') ->where('reservations.0.requester.id', $requester->id)); }); it('calcula indicadores e exporta detalhe em CSV', function () { $requester = User::factory()->create([ 'name' => '=HYPERLINK("https://invalid.test")', 'department' => 'Comunicação', ]); completedReportedReservation($requester); $auditor = phaseSixUser(RoleName::Auditor); $query = http_build_query([ 'date_from' => now()->subDays(7)->toDateString(), 'date_to' => now()->toDateString(), ]); $this->actingAs($auditor) ->get("/relatorios?{$query}") ->assertOk() ->assertInertia(fn (Assert $page) => $page ->component('reports/index') ->where('summary.total', 1) ->where('summary.completed', 1) ->where('summary.utilizationHours', 3) ->where('byCategory.Fotografia', 1) ->where('byDepartment.Comunicação', 1)); $response = $this->actingAs($auditor)->get("/relatorios/exportar?{$query}"); $response->assertOk()->assertDownload(); expect($response->streamedContent())->toContain( 'REQ-REPORT-001', 'REPORT-001', 'Comunicação', "'=HYPERLINK", ); }); it('disponibiliza auditoria imutável a auditores e administradores', function () { $actor = User::factory()->create(); AuditLog::create([ 'actor_id' => $actor->id, 'event' => 'reservation.test_audit', 'metadata' => ['reference' => 'REQ-AUDIT-001'], ]); $this->actingAs(User::factory()->create())->get('/auditoria')->assertForbidden(); $this->actingAs(phaseSixUser(RoleName::Auditor)) ->get('/auditoria?event=test_audit') ->assertOk() ->assertInertia(fn (Assert $page) => $page ->component('audit/index') ->has('logs.data', 1) ->where('logs.data.0.event', 'reservation.test_audit') ->where('logs.data.0.actor.name', $actor->name)); });