create(); $role = Role::firstOrCreate(['name' => RoleName::Administrator->value]); $administrator->roles()->attach($role); return $administrator; } /** @return array */ function policyValues(array $overrides = []): array { return array_replace([ 'reservation.min_notice_hours' => 0, 'reservation.max_duration_hours' => 168, 'reservation.turnaround_minutes' => 0, 'reservation.approval_deadline_hours' => 48, 'reservation.max_assets' => 10, ], $overrides); } it('permite ao administrador alterar políticas com auditoria', function () { $administrator = policyAdministrator(); $values = policyValues([ 'reservation.min_notice_hours' => 24, 'reservation.turnaround_minutes' => 30, ]); $this->actingAs($administrator) ->put('/administracao/regras', $values) ->assertRedirect() ->assertSessionHas('success'); expect(app(ReservationPolicy::class)->integer('reservation.min_notice_hours'))->toBe(24) ->and(app(ReservationPolicy::class)->integer('reservation.turnaround_minutes'))->toBe(30); $this->assertDatabaseHas('portal_settings', [ 'key' => 'reservation.min_notice_hours', 'updated_by' => $administrator->id, ]); $this->assertDatabaseHas('audit_logs', ['event' => 'settings.reservation_policy_updated']); }); it('rejeita períodos fora da antecedência e duração configuradas', function () { $administrator = policyAdministrator(); app(ReservationPolicy::class)->update(policyValues([ 'reservation.min_notice_hours' => 24, 'reservation.max_duration_hours' => 4, ]), $administrator); $requester = User::factory()->create(); $asset = SnipeAsset::factory()->create(); $this->actingAs($requester) ->from('/requisicoes/nova') ->post('/requisicoes', [ 'purpose' => 'Atividade audiovisual com duração demasiado longa.', 'usage_location' => 'Auditório', 'starts_at' => now()->addHours(2)->toDateTimeString(), 'ends_at' => now()->addHours(8)->toDateTimeString(), 'asset_ids' => [$asset->id], 'terms_accepted' => true, ]) ->assertRedirect('/requisicoes/nova') ->assertSessionHasErrors(['starts_at', 'ends_at']); $this->assertDatabaseCount('reservations', 0); }); it('aplica margem temporal na consulta e na garantia de conflito', function () { $administrator = policyAdministrator(); app(ReservationPolicy::class)->update(policyValues([ 'reservation.turnaround_minutes' => 60, ]), $administrator); $requester = User::factory()->create(); $asset = SnipeAsset::factory()->create(); $reservation = Reservation::create([ 'reference' => 'REQ-POLICY-MARGIN', 'requester_id' => $requester->id, 'purpose' => 'Reserva existente para validar a margem.', 'usage_location' => 'Estúdio', 'starts_at' => '2026-09-10 09:00:00', 'ends_at' => '2026-09-10 11:00:00', 'status' => ReservationStatus::Approved, ]); $reservation->items()->create([ 'snipe_asset_id' => $asset->id, 'starts_at' => '2026-09-10 09:00:00', 'ends_at' => '2026-09-10 11:00:00', 'buffer_starts_at' => '2026-09-10 09:00:00', 'buffer_ends_at' => '2026-09-10 11:00:00', 'status' => ReservationItemStatus::Reserved, ]); $availability = app(AvailabilityService::class); expect($availability->isAvailable( $asset, new DateTimeImmutable('2026-09-10 11:30:00'), new DateTimeImmutable('2026-09-10 12:30:00'), ))->toBeFalse() ->and($availability->isAvailable( $asset, new DateTimeImmutable('2026-09-10 12:00:00'), new DateTimeImmutable('2026-09-10 13:00:00'), ))->toBeTrue(); }); it('grava buffers e prazo de aprovação definidos pela política', function () { $administrator = policyAdministrator(); app(ReservationPolicy::class)->update(policyValues([ 'reservation.turnaround_minutes' => 30, 'reservation.approval_deadline_hours' => 12, ]), $administrator); $startsAt = CarbonImmutable::parse('2026-09-20 10:00:00'); $endsAt = $startsAt->addHours(2); $reservation = app(ReservationService::class)->createPending(User::factory()->create(), [ 'purpose' => 'Atividade para validar políticas configuráveis.', 'usage_location' => 'Sala multimédia', 'starts_at' => $startsAt->toDateTimeString(), 'ends_at' => $endsAt->toDateTimeString(), 'asset_ids' => [SnipeAsset::factory()->create()->id], ]); $item = $reservation->items()->firstOrFail(); expect($item->buffer_starts_at->equalTo($startsAt->subMinutes(30)))->toBeTrue() ->and($item->buffer_ends_at->equalTo($endsAt->addMinutes(30)))->toBeTrue() ->and($reservation->created_at->diffInHours($reservation->approval_deadline))->toBe(12.0); });