Skip to content

Commit

Permalink
[Debt-ture] updatePoolCandidateStatus policy enhancment (#12494)
Browse files Browse the repository at this point in the history
* add parent policy method for status

* add testing

* test policy on mutation itself

* renaming

(cherry picked from commit b24b60c)
  • Loading branch information
vd1992 authored and brindasasi committed Jan 16, 2025
1 parent 0eca1fa commit 605633e
Show file tree
Hide file tree
Showing 3 changed files with 380 additions and 13 deletions.
46 changes: 45 additions & 1 deletion api/app/Policies/PoolCandidatePolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Policies;

use App\Enums\PoolCandidateStatus;
use App\Models\PoolCandidate;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
Expand Down Expand Up @@ -188,7 +189,7 @@ public function viewStatus(User $user, PoolCandidate $poolCandidate)
*
* @return \Illuminate\Auth\Access\Response|bool
*/
public function updateStatus(User $user, PoolCandidate $poolCandidate)
public function updateStatusLegacy(User $user, PoolCandidate $poolCandidate)
{
if ($user->isAbleTo('update-any-applicationStatus')) {
return true;
Expand Down Expand Up @@ -318,4 +319,47 @@ public function updatePlacement(User $user, PoolCandidate $poolCandidate)

return $teamPermission || $legacyTeamPermission || $communityPermission;
}

/**
* Parent function to handle assessing status update authorization
* Branches depending on input status
*
* @param array{id: ?string, expiry_date: ?string, pool_candidate_status: ?string } $args
* @return \Illuminate\Auth\Access\Response|bool
*/
public function updateStatus(User $user, PoolCandidate $poolCandidate, $args)
{
$inputStatus = $args['pool_candidate_status'] ?? null;

if ($inputStatus) {

$placedStatuses = PoolCandidateStatus::placedGroup();
$draftOrExpired = [
PoolCandidateStatus::DRAFT->name,
PoolCandidateStatus::DRAFT_EXPIRED->name,
PoolCandidateStatus::EXPIRED->name,
];

if (in_array($inputStatus, $placedStatuses)) {
return $this->updatePlacement($user, $poolCandidate);
}

if (in_array($inputStatus, $draftOrExpired)) {
return $this->updateStatusLegacy($user, $poolCandidate);
}

return $this->updateDecision($user, $poolCandidate);
}

$inputExpiryDate = $args['expiry_date'] ?? null;

// attempting to update just the expiry date, which falls to updateStatusLegacy()
if ($inputExpiryDate) {

return $this->updateStatusLegacy($user, $poolCandidate);

}

return false;
}
}
170 changes: 170 additions & 0 deletions api/tests/Feature/PoolCandidateUpdateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use App\Enums\PlacementType;
use App\Enums\PoolCandidateStatus;
use App\Facades\Notify;
use App\Models\Community;
use App\Models\CommunityExperience;
use App\Models\Department;
use App\Models\EducationExperience;
Expand Down Expand Up @@ -51,12 +52,20 @@ class PoolCandidateUpdateTest extends TestCase

protected $adminUser;

protected $community;

protected $team;

protected $teamPool;

protected $poolCandidate;

protected $processOperatorUser;

protected $communityRecruiterUser;

protected $communityAdminUser;

protected $unauthorizedMessage;

protected $manualStatusUpdateMutation;
Expand Down Expand Up @@ -98,6 +107,8 @@ protected function setUp(): void
]);

$this->team = Team::factory()->create(['name' => 'test-team']);
$this->community = Community::factory()->create(['name' => ['en' => 'test-team EN', 'fr' => 'test-team FR']]);

$this->poolOperatorUser = User::factory()
->asPoolOperator($this->team->name)
->create([
Expand Down Expand Up @@ -129,8 +140,27 @@ protected function setUp(): void
$this->teamPool = Pool::factory()->create([
'user_id' => $this->poolOperatorUser->id,
'team_id' => $this->team->id,
'community_id' => $this->community->id,
]);

$this->processOperatorUser = User::factory()
->asProcessOperator($this->teamPool->id)
->create([
'email' => '[email protected]',
]);

$this->communityRecruiterUser = User::factory()
->asCommunityRecruiter($this->community->id)
->create([
'email' => '[email protected]',
]);

$this->communityAdminUser = User::factory()
->asCommunityAdmin($this->community->id)
->create([
'email' => '[email protected]',
]);

$this->poolCandidate = PoolCandidate::factory()->create([
'user_id' => $this->candidateUser->id,
'pool_id' => $this->teamPool->id,
Expand Down Expand Up @@ -1143,4 +1173,144 @@ public static function manualStatusProvider()
],
];
}

// test policy correctly allows sample manual status updates to work, when expected and fail otherwise
public function testManualStatusUpdatePolicy(): void
{
$past = config('constants.past_datetime');
$this->poolCandidate->pool_candidate_status = PoolCandidateStatus::NEW_APPLICATION->name;
$this->poolCandidate->submitted_at = $past;
$this->poolCandidate->save();

// process operator
// can set to SCREENED IN, QUALIFIED, REMOVED only
// cannot set PLACED TERM, or DRAFT
$this->actingAs($this->processOperatorUser, 'api')
->graphQL($this->manualStatusUpdateMutation, [
'id' => $this->poolCandidate->id,
'candidate' => ['status' => PoolCandidateStatus::DRAFT->name],
])->assertGraphQLErrorMessage($this->unauthorizedMessage);
$this->actingAs($this->processOperatorUser, 'api')
->graphQL($this->manualStatusUpdateMutation, [
'id' => $this->poolCandidate->id,
'candidate' => ['status' => PoolCandidateStatus::SCREENED_IN->name],
])->assertJsonFragment([
'status' => [
'value' => PoolCandidateStatus::SCREENED_IN->name,
],
]);
$this->actingAs($this->processOperatorUser, 'api')
->graphQL($this->manualStatusUpdateMutation, [
'id' => $this->poolCandidate->id,
'candidate' => ['status' => PoolCandidateStatus::QUALIFIED_AVAILABLE->name],
])->assertJsonFragment([
'status' => [
'value' => PoolCandidateStatus::QUALIFIED_AVAILABLE->name,
],
]);
$this->actingAs($this->processOperatorUser, 'api')
->graphQL($this->manualStatusUpdateMutation, [
'id' => $this->poolCandidate->id,
'candidate' => ['status' => PoolCandidateStatus::REMOVED->name],
])->assertJsonFragment([
'status' => [
'value' => PoolCandidateStatus::REMOVED->name,
],
]);
$this->actingAs($this->processOperatorUser, 'api')
->graphQL($this->manualStatusUpdateMutation, [
'id' => $this->poolCandidate->id,
'candidate' => ['status' => PoolCandidateStatus::PLACED_TERM->name],
])->assertGraphQLErrorMessage($this->unauthorizedMessage);

// community recruiter
// can set to SCREENED IN, QUALIFIED, REMOVED, PLACED TERM only
// cannot set DRAFT
$this->actingAs($this->communityRecruiterUser, 'api')
->graphQL($this->manualStatusUpdateMutation, [
'id' => $this->poolCandidate->id,
'candidate' => ['status' => PoolCandidateStatus::DRAFT->name],
])->assertGraphQLErrorMessage($this->unauthorizedMessage);
$this->actingAs($this->communityRecruiterUser, 'api')
->graphQL($this->manualStatusUpdateMutation, [
'id' => $this->poolCandidate->id,
'candidate' => ['status' => PoolCandidateStatus::SCREENED_IN->name],
])->assertJsonFragment([
'status' => [
'value' => PoolCandidateStatus::SCREENED_IN->name,
],
]);
$this->actingAs($this->communityRecruiterUser, 'api')
->graphQL($this->manualStatusUpdateMutation, [
'id' => $this->poolCandidate->id,
'candidate' => ['status' => PoolCandidateStatus::QUALIFIED_AVAILABLE->name],
])->assertJsonFragment([
'status' => [
'value' => PoolCandidateStatus::QUALIFIED_AVAILABLE->name,
],
]);
$this->actingAs($this->communityRecruiterUser, 'api')
->graphQL($this->manualStatusUpdateMutation, [
'id' => $this->poolCandidate->id,
'candidate' => ['status' => PoolCandidateStatus::REMOVED->name],
])->assertJsonFragment([
'status' => [
'value' => PoolCandidateStatus::REMOVED->name,
],
]);
$this->actingAs($this->communityRecruiterUser, 'api')
->graphQL($this->manualStatusUpdateMutation, [
'id' => $this->poolCandidate->id,
'candidate' => ['status' => PoolCandidateStatus::PLACED_TERM->name],
])->assertJsonFragment([
'status' => [
'value' => PoolCandidateStatus::PLACED_TERM->name,
],
]);

// community admin
// can set to SCREENED IN, QUALIFIED, REMOVED, PLACED TERM only
// cannot set DRAFT
$this->actingAs($this->communityAdminUser, 'api')
->graphQL($this->manualStatusUpdateMutation, [
'id' => $this->poolCandidate->id,
'candidate' => ['status' => PoolCandidateStatus::DRAFT->name],
])->assertGraphQLErrorMessage($this->unauthorizedMessage);
$this->actingAs($this->communityAdminUser, 'api')
->graphQL($this->manualStatusUpdateMutation, [
'id' => $this->poolCandidate->id,
'candidate' => ['status' => PoolCandidateStatus::SCREENED_IN->name],
])->assertJsonFragment([
'status' => [
'value' => PoolCandidateStatus::SCREENED_IN->name,
],
]);
$this->actingAs($this->communityAdminUser, 'api')
->graphQL($this->manualStatusUpdateMutation, [
'id' => $this->poolCandidate->id,
'candidate' => ['status' => PoolCandidateStatus::QUALIFIED_AVAILABLE->name],
])->assertJsonFragment([
'status' => [
'value' => PoolCandidateStatus::QUALIFIED_AVAILABLE->name,
],
]);
$this->actingAs($this->communityAdminUser, 'api')
->graphQL($this->manualStatusUpdateMutation, [
'id' => $this->poolCandidate->id,
'candidate' => ['status' => PoolCandidateStatus::REMOVED->name],
])->assertJsonFragment([
'status' => [
'value' => PoolCandidateStatus::REMOVED->name,
],
]);
$this->actingAs($this->communityAdminUser, 'api')
->graphQL($this->manualStatusUpdateMutation, [
'id' => $this->poolCandidate->id,
'candidate' => ['status' => PoolCandidateStatus::PLACED_TERM->name],
])->assertJsonFragment([
'status' => [
'value' => PoolCandidateStatus::PLACED_TERM->name,
],
]);
}
}
Loading

0 comments on commit 605633e

Please sign in to comment.