Files
reservas/tests/Feature/AdminReservationPolicyTest.php
João Miranda fb04d71a8d
Some checks failed
tests / ci (push) Has been cancelled
first commit
2026-10-27 11:01:11 +00:00

142 lines
5.5 KiB
PHP

<?php
use App\Domain\Administration\Services\ReservationPolicy;
use App\Domain\Identity\Enums\RoleName;
use App\Domain\Identity\Models\Role;
use App\Domain\Inventory\Models\SnipeAsset;
use App\Domain\Reservations\Enums\ReservationItemStatus;
use App\Domain\Reservations\Enums\ReservationStatus;
use App\Domain\Reservations\Models\Reservation;
use App\Domain\Reservations\Services\AvailabilityService;
use App\Domain\Reservations\Services\ReservationService;
use App\Models\User;
use Carbon\CarbonImmutable;
function policyAdministrator(): User
{
$administrator = User::factory()->create();
$role = Role::firstOrCreate(['name' => RoleName::Administrator->value]);
$administrator->roles()->attach($role);
return $administrator;
}
/** @return array<string, int> */
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::now()->addDays(5)->setTime(10, 0, 0);
$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);
});