create([ 'email' => 'manager@example.com', 'password' => bcrypt('password123'), 'role' => 'manager', 'active' => true, ]); $response = $this->postJson('/api/auth/login', [ 'email' => 'manager@example.com', 'password' => 'password123', ]); return $response->json('access_token'); } // 2.1.9 API test: POST /api/team-members creates member public function test_post_team_members_creates_member() { $token = $this->loginAsManager(); $role = Role::factory()->create(['name' => 'Backend Developer']); $response = $this->withHeader('Authorization', "Bearer {$token}") ->postJson('/api/team-members', [ 'name' => 'John Doe', 'role_id' => $role->id, 'hourly_rate' => 150.00, 'active' => true, ]); $response->assertStatus(201); $response->assertJson([ 'data' => [ 'name' => 'John Doe', 'hourly_rate' => '150.00', 'active' => true, ], ]); $response->assertJsonPath('data.role.id', $role->id); $this->assertDatabaseHas('team_members', [ 'name' => 'John Doe', 'role_id' => $role->id, 'active' => true, ]); } // 2.1.10 API test: Validate hourly_rate > 0 public function test_validate_hourly_rate_must_be_greater_than_zero() { $token = $this->loginAsManager(); $role = Role::factory()->create(['name' => 'Backend Developer']); // Test with zero $response = $this->withHeader('Authorization', "Bearer {$token}") ->postJson('/api/team-members', [ 'name' => 'John Doe', 'role_id' => $role->id, 'hourly_rate' => 0, ]); $response->assertStatus(422); $response->assertJsonValidationErrors(['hourly_rate']); // Test with negative $response = $this->withHeader('Authorization', "Bearer {$token}") ->postJson('/api/team-members', [ 'name' => 'John Doe', 'role_id' => $role->id, 'hourly_rate' => -50, ]); $response->assertStatus(422); $response->assertJsonValidationErrors(['hourly_rate']); $response->assertJsonFragment([ 'hourly_rate' => ['Hourly rate must be greater than 0'], ]); } // 2.1.11 API test: Validate required fields public function test_validate_required_fields() { $token = $this->loginAsManager(); $response = $this->withHeader('Authorization', "Bearer {$token}") ->postJson('/api/team-members', []); $response->assertStatus(422); $response->assertJsonValidationErrors(['name', 'role_id', 'hourly_rate']); } // 2.1.12 API test: GET /api/team-members returns all members public function test_get_team_members_returns_all_members() { $token = $this->loginAsManager(); $role = Role::factory()->create(); // Create active and inactive team members TeamMember::factory()->count(2)->create(['role_id' => $role->id, 'active' => true]); TeamMember::factory()->create(['role_id' => $role->id, 'active' => false]); $response = $this->withHeader('Authorization', "Bearer {$token}") ->getJson('/api/team-members'); $response->assertStatus(200); $response->assertJsonCount(3, 'data'); } // 2.1.13 API test: Filter by active status public function test_filter_by_active_status() { $token = $this->loginAsManager(); $role = Role::factory()->create(); // Create active and inactive team members TeamMember::factory()->count(2)->create(['role_id' => $role->id, 'active' => true]); TeamMember::factory()->create(['role_id' => $role->id, 'active' => false]); // Get only active $response = $this->withHeader('Authorization', "Bearer {$token}") ->getJson('/api/team-members?active=true'); $response->assertStatus(200); $response->assertJsonCount(2, 'data'); // Get only inactive $response = $this->withHeader('Authorization', "Bearer {$token}") ->getJson('/api/team-members?active=false'); $response->assertStatus(200); $response->assertJsonCount(1, 'data'); } // 2.1.14 API test: PUT /api/team-members/{id} updates member public function test_put_team_members_updates_member() { $token = $this->loginAsManager(); $role = Role::factory()->create(); $teamMember = TeamMember::factory()->create([ 'role_id' => $role->id, 'hourly_rate' => 150.00, ]); $response = $this->withHeader('Authorization', "Bearer {$token}") ->putJson("/api/team-members/{$teamMember->id}", [ 'hourly_rate' => 175.00, ]); $response->assertStatus(200); $response->assertJson([ 'data' => [ 'id' => $teamMember->id, 'hourly_rate' => '175.00', ], ]); $this->assertDatabaseHas('team_members', [ 'id' => $teamMember->id, 'hourly_rate' => '175.00', ]); } // 2.1.15 API test: Deactivate sets active=false public function test_deactivate_sets_active_to_false() { $token = $this->loginAsManager(); $role = Role::factory()->create(); $teamMember = TeamMember::factory()->create([ 'role_id' => $role->id, 'active' => true, ]); $response = $this->withHeader('Authorization', "Bearer {$token}") ->putJson("/api/team-members/{$teamMember->id}", [ 'active' => false, ]); $response->assertStatus(200); $response->assertJson([ 'data' => [ 'id' => $teamMember->id, 'active' => false, ], ]); $this->assertDatabaseHas('team_members', [ 'id' => $teamMember->id, 'active' => false, ]); } // 2.1.16 API test: DELETE rejected if allocations exist public function test_delete_rejected_if_allocations_exist() { $token = $this->loginAsManager(); $role = Role::factory()->create(); $teamMember = TeamMember::factory()->create(['role_id' => $role->id]); $project = Project::factory()->create(); // Create an allocation for the team member Allocation::factory()->create([ 'team_member_id' => $teamMember->id, 'project_id' => $project->id, 'month' => '2024-01', 'allocated_hours' => 40, ]); $response = $this->withHeader('Authorization', "Bearer {$token}") ->deleteJson("/api/team-members/{$teamMember->id}"); $response->assertStatus(422); $response->assertJson([ 'message' => 'Cannot delete team member with active allocations', 'suggestion' => 'Consider deactivating the team member instead', ]); // Verify the team member still exists $this->assertDatabaseHas('team_members', [ 'id' => $teamMember->id, ]); } public function test_team_member_cache_is_invalidated_after_updates(): void { $token = $this->loginAsManager(); $role = Role::factory()->create(); $teamMember = TeamMember::factory()->create([ 'role_id' => $role->id, 'active' => true, ]); $this->withHeader('Authorization', "Bearer {$token}") ->getJson('/api/team-members?active=true') ->assertStatus(200) ->assertJsonCount(1, 'data'); $this->withHeader('Authorization', "Bearer {$token}") ->putJson("/api/team-members/{$teamMember->id}", [ 'active' => false, ]) ->assertStatus(200); $this->withHeader('Authorization', "Bearer {$token}") ->getJson('/api/team-members?active=true') ->assertStatus(200) ->assertJsonCount(0, 'data'); $this->withHeader('Authorization', "Bearer {$token}") ->getJson('/api/team-members?active=false') ->assertStatus(200) ->assertJsonCount(1, 'data'); } }