create([ 'email' => 'manager@test.com', 'password' => bcrypt('password123'), 'role' => 'manager', 'active' => true, ]); $response = $this->postJson('/api/auth/login', [ 'email' => 'manager@test.com', 'password' => 'password123', ]); return $response->json('access_token'); } // 7.1.9 GET /api/utilization/running calculates YTD public function test_get_utilization_running_calculates_ytd(): void { $token = $this->loginAsManager(); $role = Role::factory()->create(); $member = TeamMember::factory()->create(['role_id' => $role->id, 'active' => true]); // Create allocations for Jan, Feb, Mar 2026 Allocation::factory()->create([ 'team_member_id' => $member->id, 'month' => '2026-01-01', 'allocated_hours' => 140, ]); Allocation::factory()->create([ 'team_member_id' => $member->id, 'month' => '2026-02-01', 'allocated_hours' => 150, ]); Allocation::factory()->create([ 'team_member_id' => $member->id, 'month' => '2026-03-01', 'allocated_hours' => 160, ]); $response = $this->withHeader('Authorization', "Bearer {$token}") ->getJson("/api/utilization/running?team_member_id={$member->id}&month=2026-03"); $response->assertStatus(200) ->assertJsonStructure([ 'capacity_ytd', 'allocated_ytd', 'utilization', 'indicator', 'months_included', ]) ->assertJson([ 'allocated_ytd' => 450.0, 'months_included' => 3, ]); } // 7.1.10 GET /api/utilization/overall calculates monthly public function test_get_utilization_overall_calculates_monthly(): void { $token = $this->loginAsManager(); $role = Role::factory()->create(); $member = TeamMember::factory()->create(['role_id' => $role->id, 'active' => true]); Allocation::factory()->create([ 'team_member_id' => $member->id, 'month' => '2026-02-01', 'allocated_hours' => 140, ]); $response = $this->withHeader('Authorization', "Bearer {$token}") ->getJson("/api/utilization/overall?team_member_id={$member->id}&month=2026-02"); $response->assertStatus(200) ->assertJsonStructure([ 'capacity', 'allocated', 'utilization', 'indicator', ]) ->assertJson([ 'allocated' => 140.0, ]); } // 7.1.11 Utilization includes in allocation response public function test_utilization_included_in_team_members_response(): void { $token = $this->loginAsManager(); $role = Role::factory()->create(); $member = TeamMember::factory()->create(['role_id' => $role->id, 'active' => true]); Allocation::factory()->create([ 'team_member_id' => $member->id, 'month' => '2026-02-01', 'allocated_hours' => 140, ]); // Get team member with utilization data $response = $this->withHeader('Authorization', "Bearer {$token}") ->getJson("/api/team-members/{$member->id}?include_utilization=true&month=2026-02"); $response->assertStatus(200) ->assertJsonStructure([ 'data' => [ 'id', 'name', 'utilization' => [ 'overall', 'running', ], ], ]); } public function test_utilization_team_endpoint_returns_aggregate(): void { $token = $this->loginAsManager(); $role = Role::factory()->create(); $memberA = TeamMember::factory()->create(['role_id' => $role->id, 'active' => true]); $memberB = TeamMember::factory()->create(['role_id' => $role->id, 'active' => true]); Allocation::factory()->create([ 'team_member_id' => $memberA->id, 'month' => '2026-02-01', 'allocated_hours' => 140, ]); Allocation::factory()->create([ 'team_member_id' => $memberB->id, 'month' => '2026-02-01', 'allocated_hours' => 150, ]); $response = $this->withHeader('Authorization', "Bearer {$token}") ->getJson('/api/utilization/team?month=2026-02'); $response->assertStatus(200) ->assertJsonStructure([ 'average_utilization', 'average_indicator', 'member_count', 'by_member', ]) ->assertJson([ 'member_count' => 2, ]); } public function test_utilization_requires_authentication(): void { $response = $this->getJson('/api/utilization/overall?team_member_id=123&month=2026-02'); $response->assertStatus(401); } public function test_utilization_validates_required_parameters(): void { $token = $this->loginAsManager(); $response = $this->withHeader('Authorization', "Bearer {$token}") ->getJson('/api/utilization/overall'); $response->assertStatus(422) ->assertJsonValidationErrors(['team_member_id', 'month']); } }