Query next approved shift assignment with future time slot, ordered by date and start time, and return formatted shift data in the portal me response for the dashboard "Komende shift" card. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
270 lines
8.5 KiB
PHP
270 lines
8.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Feature\Api\V1;
|
|
|
|
use App\Enums\ShiftAssignmentStatus;
|
|
use App\Models\CrowdType;
|
|
use App\Models\Event;
|
|
use App\Models\FestivalSection;
|
|
use App\Models\Location;
|
|
use App\Models\Organisation;
|
|
use App\Models\Person;
|
|
use App\Models\Shift;
|
|
use App\Models\ShiftAssignment;
|
|
use App\Models\TimeSlot;
|
|
use App\Models\User;
|
|
use Database\Seeders\RoleSeeder;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Laravel\Sanctum\Sanctum;
|
|
use Tests\TestCase;
|
|
|
|
class PortalMeUpcomingShiftTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
private Organisation $organisation;
|
|
private Event $event;
|
|
private CrowdType $volunteerCrowdType;
|
|
private User $user;
|
|
private Person $person;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
$this->seed(RoleSeeder::class);
|
|
|
|
$this->organisation = Organisation::factory()->create();
|
|
$this->volunteerCrowdType = CrowdType::factory()->systemType('VOLUNTEER')->create([
|
|
'organisation_id' => $this->organisation->id,
|
|
]);
|
|
$this->event = Event::factory()->create([
|
|
'organisation_id' => $this->organisation->id,
|
|
'status' => 'published',
|
|
]);
|
|
$this->user = User::factory()->create();
|
|
$this->person = Person::factory()->approved()->create([
|
|
'event_id' => $this->event->id,
|
|
'crowd_type_id' => $this->volunteerCrowdType->id,
|
|
'user_id' => $this->user->id,
|
|
'email' => $this->user->email,
|
|
]);
|
|
}
|
|
|
|
public function test_portal_me_returns_upcoming_shift_for_approved_person(): void
|
|
{
|
|
$futureDate = now()->addDays(7)->toDateString();
|
|
|
|
$timeSlot = TimeSlot::factory()->create([
|
|
'event_id' => $this->event->id,
|
|
'date' => $futureDate,
|
|
'start_time' => '18:00:00',
|
|
'end_time' => '02:00:00',
|
|
]);
|
|
|
|
$section = FestivalSection::factory()->create([
|
|
'event_id' => $this->event->id,
|
|
'name' => 'Bar Hoofdpodium',
|
|
]);
|
|
|
|
$location = Location::factory()->create([
|
|
'event_id' => $this->event->id,
|
|
'name' => 'Festivalterrein',
|
|
]);
|
|
|
|
$shift = Shift::factory()->create([
|
|
'festival_section_id' => $section->id,
|
|
'time_slot_id' => $timeSlot->id,
|
|
'location_id' => $location->id,
|
|
'title' => 'Tapper',
|
|
]);
|
|
|
|
ShiftAssignment::factory()->approved()->create([
|
|
'shift_id' => $shift->id,
|
|
'person_id' => $this->person->id,
|
|
'time_slot_id' => $timeSlot->id,
|
|
]);
|
|
|
|
Sanctum::actingAs($this->user);
|
|
|
|
$response = $this->getJson("/api/v1/portal/me?event_id={$this->event->id}");
|
|
|
|
$response->assertOk();
|
|
$response->assertJsonPath('data.upcoming_shift.date', $futureDate);
|
|
$response->assertJsonPath('data.upcoming_shift.time', '18:00 - 02:00');
|
|
$response->assertJsonPath('data.upcoming_shift.title', 'Tapper');
|
|
$response->assertJsonPath('data.upcoming_shift.section', 'Bar Hoofdpodium');
|
|
$response->assertJsonPath('data.upcoming_shift.location', 'Festivalterrein');
|
|
}
|
|
|
|
public function test_portal_me_returns_null_upcoming_shift_when_no_future_shifts(): void
|
|
{
|
|
// Create a past shift assignment
|
|
$pastDate = now()->subDays(7)->toDateString();
|
|
|
|
$timeSlot = TimeSlot::factory()->create([
|
|
'event_id' => $this->event->id,
|
|
'date' => $pastDate,
|
|
'start_time' => '18:00:00',
|
|
'end_time' => '02:00:00',
|
|
]);
|
|
|
|
$section = FestivalSection::factory()->create([
|
|
'event_id' => $this->event->id,
|
|
]);
|
|
|
|
$shift = Shift::factory()->create([
|
|
'festival_section_id' => $section->id,
|
|
'time_slot_id' => $timeSlot->id,
|
|
'title' => 'Tapper',
|
|
]);
|
|
|
|
ShiftAssignment::factory()->approved()->create([
|
|
'shift_id' => $shift->id,
|
|
'person_id' => $this->person->id,
|
|
'time_slot_id' => $timeSlot->id,
|
|
]);
|
|
|
|
Sanctum::actingAs($this->user);
|
|
|
|
$response = $this->getJson("/api/v1/portal/me?event_id={$this->event->id}");
|
|
|
|
$response->assertOk();
|
|
$response->assertJsonPath('data.upcoming_shift', null);
|
|
}
|
|
|
|
public function test_portal_me_returns_null_upcoming_shift_for_pending_person(): void
|
|
{
|
|
$pendingPerson = Person::factory()->create([
|
|
'event_id' => $this->event->id,
|
|
'crowd_type_id' => $this->volunteerCrowdType->id,
|
|
'user_id' => null,
|
|
'status' => 'pending',
|
|
]);
|
|
|
|
$pendingUser = User::factory()->create(['email' => $pendingPerson->email]);
|
|
$pendingPerson->update(['user_id' => $pendingUser->id]);
|
|
|
|
$futureDate = now()->addDays(7)->toDateString();
|
|
|
|
$timeSlot = TimeSlot::factory()->create([
|
|
'event_id' => $this->event->id,
|
|
'date' => $futureDate,
|
|
]);
|
|
|
|
$section = FestivalSection::factory()->create([
|
|
'event_id' => $this->event->id,
|
|
]);
|
|
|
|
$shift = Shift::factory()->create([
|
|
'festival_section_id' => $section->id,
|
|
'time_slot_id' => $timeSlot->id,
|
|
]);
|
|
|
|
// Assignment is pending_approval, not approved
|
|
ShiftAssignment::factory()->create([
|
|
'shift_id' => $shift->id,
|
|
'person_id' => $pendingPerson->id,
|
|
'time_slot_id' => $timeSlot->id,
|
|
'status' => ShiftAssignmentStatus::PENDING_APPROVAL,
|
|
]);
|
|
|
|
Sanctum::actingAs($pendingUser);
|
|
|
|
$response = $this->getJson("/api/v1/portal/me?event_id={$this->event->id}");
|
|
|
|
$response->assertOk();
|
|
$response->assertJsonPath('data.upcoming_shift', null);
|
|
}
|
|
|
|
public function test_portal_me_returns_nearest_future_shift(): void
|
|
{
|
|
$nearDate = now()->addDays(3)->toDateString();
|
|
$farDate = now()->addDays(10)->toDateString();
|
|
|
|
$nearTimeSlot = TimeSlot::factory()->create([
|
|
'event_id' => $this->event->id,
|
|
'date' => $nearDate,
|
|
'start_time' => '10:00:00',
|
|
'end_time' => '18:00:00',
|
|
]);
|
|
|
|
$farTimeSlot = TimeSlot::factory()->create([
|
|
'event_id' => $this->event->id,
|
|
'date' => $farDate,
|
|
'start_time' => '18:00:00',
|
|
'end_time' => '02:00:00',
|
|
]);
|
|
|
|
$section = FestivalSection::factory()->create([
|
|
'event_id' => $this->event->id,
|
|
]);
|
|
|
|
$nearShift = Shift::factory()->create([
|
|
'festival_section_id' => $section->id,
|
|
'time_slot_id' => $nearTimeSlot->id,
|
|
'title' => 'Ochtend Runner',
|
|
]);
|
|
|
|
$farShift = Shift::factory()->create([
|
|
'festival_section_id' => $section->id,
|
|
'time_slot_id' => $farTimeSlot->id,
|
|
'title' => 'Avond Tapper',
|
|
]);
|
|
|
|
ShiftAssignment::factory()->approved()->create([
|
|
'shift_id' => $farShift->id,
|
|
'person_id' => $this->person->id,
|
|
'time_slot_id' => $farTimeSlot->id,
|
|
]);
|
|
|
|
ShiftAssignment::factory()->approved()->create([
|
|
'shift_id' => $nearShift->id,
|
|
'person_id' => $this->person->id,
|
|
'time_slot_id' => $nearTimeSlot->id,
|
|
]);
|
|
|
|
Sanctum::actingAs($this->user);
|
|
|
|
$response = $this->getJson("/api/v1/portal/me?event_id={$this->event->id}");
|
|
|
|
$response->assertOk();
|
|
$response->assertJsonPath('data.upcoming_shift.title', 'Ochtend Runner');
|
|
$response->assertJsonPath('data.upcoming_shift.date', $nearDate);
|
|
}
|
|
|
|
public function test_portal_me_ignores_cancelled_shift_assignments(): void
|
|
{
|
|
$futureDate = now()->addDays(7)->toDateString();
|
|
|
|
$timeSlot = TimeSlot::factory()->create([
|
|
'event_id' => $this->event->id,
|
|
'date' => $futureDate,
|
|
]);
|
|
|
|
$section = FestivalSection::factory()->create([
|
|
'event_id' => $this->event->id,
|
|
]);
|
|
|
|
$shift = Shift::factory()->create([
|
|
'festival_section_id' => $section->id,
|
|
'time_slot_id' => $timeSlot->id,
|
|
]);
|
|
|
|
ShiftAssignment::factory()->create([
|
|
'shift_id' => $shift->id,
|
|
'person_id' => $this->person->id,
|
|
'time_slot_id' => $timeSlot->id,
|
|
'status' => ShiftAssignmentStatus::CANCELLED,
|
|
]);
|
|
|
|
Sanctum::actingAs($this->user);
|
|
|
|
$response = $this->getJson("/api/v1/portal/me?event_id={$this->event->id}");
|
|
|
|
$response->assertOk();
|
|
$response->assertJsonPath('data.upcoming_shift', null);
|
|
}
|
|
}
|