INFRA-3232 | Dhruv | adds tests for changeRequestSlackService

This commit is contained in:
dhruvjoshi
2024-08-25 14:58:25 +05:30
parent 4588cf326a
commit 6f6ce394f1
5 changed files with 371 additions and 24 deletions

View File

@@ -58,7 +58,7 @@ public class ManifestController {
}
return new ResponseEntity<>(manifestResponse, HttpStatus.OK);
} catch (AccessDeniedException e) {
log.error("AccessDeniedException while saving manifest by user {}", getUserEmail());
log.error("AccessDeniedException while saving manifest by {}", getUserEmail());
return new ResponseEntity<>(FORBIDDEN);
}
}

View File

@@ -7,10 +7,10 @@ import java.io.IOException;
import java.util.List;
public interface ChangeRequestSlackService {
void sendSlackApprovalMessages(Manifest manifest, ChangeRequest changeRequest,
ChangeRequest sendSlackApprovalMessages(Manifest manifest, ChangeRequest changeRequest,
List<ApprovalRequest> approvalRequests) throws IOException;
void approveRequest(String userEmail, Long requestId);
ApprovalRequest approveRequest(String userEmail, Long requestId);
void handleApproveRequest(Long changeRequestId, Long approvalRequestId, Long userId,
Long teamId) throws IOException;
@@ -18,8 +18,8 @@ public interface ChangeRequestSlackService {
void handleChangeRequestUpdate(Manifest manifest, ChangeRequest changeRequest)
throws IOException;
void rejectRequest(String userEmail, Long requestId);
ApprovalRequest rejectRequest(String userEmail, Long requestId);
void closeRequest(String userEmail, Long requestId);
ChangeRequest closeRequest(String userEmail, Long requestId);
}

View File

@@ -201,10 +201,11 @@ public class ChangeRequestSlackServiceImpl implements ChangeRequestSlackService
changeRequestSlackRepository.save(request);
});
updateCommonSlackChannelMessage(changeRequest, manifest);
updateCommonSlackChannelMessage(changeRequest, manifest, manifestDiff);
}
private void updateCommonSlackChannelMessage(ChangeRequest changeRequest, Manifest manifest) {
private void updateCommonSlackChannelMessage(ChangeRequest changeRequest, Manifest manifest,
String manifestDiff) {
var allRequests = approvalRequestService.findAllByRequestTypeAndRequestIds(
RequestType.CHANGE_REQUEST, List.of(changeRequest.getId()));
@@ -220,7 +221,6 @@ public class ChangeRequestSlackServiceImpl implements ChangeRequestSlackService
)
));
var manifestDiff = changeRequestSlackUtil.getManifestDiff(changeRequest, manifest);
var channelInfoMessage = changeRequestSlackUtil.createInfoMessage(
changeRequest, manifest.getTeam(), new ArrayList<>(actionTeamsUsersMap
.getOrDefault(RequestStatus.PENDING, Collections.emptyMap()).keySet()),
@@ -272,7 +272,6 @@ public class ChangeRequestSlackServiceImpl implements ChangeRequestSlackService
manifest, changeRequest, slackChangeRequest.getApprovalRequest(),
true, SlackColor.OPEN);
postReviewerDmOnSlack(slackChangeRequest, reviewMessage);
changeRequestSlackRepository.save(slackChangeRequest);
log.info("Slack DM sent and saved for CR: {} and slackRequestId: {}",
changeRequest.getId(), slackChangeRequest.getId());
} catch (IOException e) {
@@ -282,7 +281,7 @@ public class ChangeRequestSlackServiceImpl implements ChangeRequestSlackService
}
@Override
public void sendSlackApprovalMessages(Manifest manifest, ChangeRequest changeRequest,
public ChangeRequest sendSlackApprovalMessages(Manifest manifest, ChangeRequest changeRequest,
List<ApprovalRequest> approvalRequests) throws IOException {
var teamList = approvalRequests.stream().map(ApprovalRequest::getTeamId)
@@ -292,10 +291,12 @@ public class ChangeRequestSlackServiceImpl implements ChangeRequestSlackService
var teamsWithNoReviewers = findTeamsWithNoReviewers(teamApproversMap);
var missingTeams = getMissingTeams(teamList, teamApproversMap);
if (!missingTeams.isEmpty()) {
log.error("{} are missing relevant permissions for cr approval",
log.error("{} teams are missing for cr approval",
String.join(", ", missingTeams));
throw new RuntimeException("Missing permissions for teams: "
+ String.join(", ", missingTeams));
throw new RuntimeException(
String.format("Missing teams for cr %d approval: %s", changeRequest.getId(),
String.join(", ", missingTeams))
);
}
if (!teamsWithNoReviewers.isEmpty()) {
log.info("Reviewer absent for teams : {}, but is required for CR with id: {}",
@@ -320,39 +321,45 @@ public class ChangeRequestSlackServiceImpl implements ChangeRequestSlackService
slackApprovalRequestsWithId.forEach(slackChangeRequest -> {
handleSendingReviewerDm(manifestDiff, manifest, changeRequest, slackChangeRequest);
});
return changeRequest;
}
public void approveRequest(String userEmail, Long requestId) {
public ApprovalRequest approveRequest(String userEmail, Long approvalRequestId) {
log.info("Approve triggered for Approval request {} by user {}",
approvalRequestId, userEmail);
var user = userService.findUserByEmail(userEmail);
setSecurityContextForSlackReviewer(user);
try {
approvalRequestService.allowApproveRequest(requestId, user.getId());
return approvalRequestService.allowApproveRequest(approvalRequestId, user.getId());
} catch (NotFoundException | AccessDeniedException e) {
log.error("Error approving Approval request {} for user {}", requestId,
log.error("Error approving Approval request {} for user {} : {}", approvalRequestId,
userEmail, e);
throw e;
}
}
public void rejectRequest(String userEmail, Long requestId) {
public ApprovalRequest rejectRequest(String userEmail, Long approvalRequestId) {
log.info("Reject triggered for Approval request {} by {}",
approvalRequestId, userEmail);
var user = userService.findUserByEmail(userEmail);
setSecurityContextForSlackReviewer(user);
try {
approvalRequestService.reject(requestId, user.getId());
return approvalRequestService.reject(approvalRequestId, user.getId());
} catch (NotFoundException | AccessDeniedException e) {
log.error("Error approving Approval request {} for user {}", requestId,
log.error("Error approving Approval request {} for user {} : {}", approvalRequestId,
userEmail, e);
throw e;
}
}
public void closeRequest(String userEmail, Long requestId) {
public ChangeRequest closeRequest(String userEmail, Long changeRequestId) {
log.info("Close triggered for change request {} by {}", changeRequestId, userEmail);
var user = userService.findUserByEmail(userEmail);
setSecurityContextForSlackReviewer(user);
try {
changeRequestService.close(requestId, user.getId());
return changeRequestService.close(changeRequestId, user.getId());
} catch (NotFoundException | AccessDeniedException e) {
log.error("Error approving Approval request {} for user {}", requestId,
log.error("Error approving Change request {} for user {} : {}", changeRequestId,
userEmail, e);
throw e;
}
@@ -387,6 +394,6 @@ public class ChangeRequestSlackServiceImpl implements ChangeRequestSlackService
}
changeRequestSlackRepository.save(request);
});
updateCommonSlackChannelMessage(changeRequest, manifest);
updateCommonSlackChannelMessage(changeRequest, manifest, manifestDiff);
}
}

View File

@@ -486,7 +486,7 @@ class JitServiceImpl implements JitService {
throw new IllegalArgumentException("Invalid user");
}
if (userService.findUserByEmail(jitRequestDto.getRequestedBy()) == null) {
log.error("Invalid request by user {}", jitRequestDto.getRequestedBy());
log.error("Invalid request by {}", jitRequestDto.getRequestedBy());
throw new IllegalArgumentException("Invalid user");
}
Team team = teamService.findByName(jitRequestDto.getTeam());

View File

@@ -1,24 +1,75 @@
package com.navi.infra.portal.v2.jit.service;
import static com.navi.infra.portal.v2.changerequest.entity.RequestStatus.APPROVED;
import static com.navi.infra.portal.v2.changerequest.entity.RequestStatus.PENDING;
import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.navi.infra.portal.domain.manifest.Manifest;
import com.navi.infra.portal.domain.user.Team;
import com.navi.infra.portal.domain.user.User;
import com.navi.infra.portal.service.manifest.ManifestService;
import com.navi.infra.portal.service.user.PrivilegeUtilService;
import com.navi.infra.portal.service.user.UserService;
import com.navi.infra.portal.v2.approvalflow.entity.ApprovalRequest;
import com.navi.infra.portal.v2.approvalflow.entity.ApprovalRequestBuilder;
import com.navi.infra.portal.v2.approvalflow.entity.RequestType;
import com.navi.infra.portal.v2.approvalflow.service.ApprovalRequestService;
import com.navi.infra.portal.v2.changerequest.entity.ChangeRequest;
import com.navi.infra.portal.v2.changerequest.entity.ChangeRequestBuilder;
import com.navi.infra.portal.v2.changerequest.entity.RequestStatus;
import com.navi.infra.portal.v2.changerequest.service.ChangeRequestService;
import com.navi.infra.portal.v2.exception.NotFoundException;
import com.navi.infra.portal.v2.jit.entity.SlackChangeRequest;
import com.navi.infra.portal.v2.jit.repository.ChangeRequestSlackRepository;
import com.navi.infra.portal.v2.jit.utils.ChangeRequestSlackUtil;
import com.navi.infra.portal.v2.slackbotclient.SlackBotClient;
import com.navi.infra.portal.v2.team.TeamService;
import com.slack.api.methods.response.chat.ChatPostMessageResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
@ExtendWith(MockitoExtension.class)
class ChangeRequestSlackServiceImplTest {
final Map<String, Object> diff = Map.of("path", "/key",
"op", "op",
"value", "value");
final Manifest manifest = new Manifest();
final ChatPostMessageResponse slackPostResponse = new ChatPostMessageResponse();
User user1, user2, user3, user4;
Team team1, team2;
ChangeRequest changeRequest;
ApprovalRequest approvalRequest1, approvalRequest2, approvalRequest3;
@Mock
ChangeRequestSlackUtil changeRequestSlackUtil;
@Mock
User mockUser;
private ChangeRequestSlackServiceImpl changeRequestSlackService;
@Mock
private TeamService teamService;
@@ -43,5 +94,294 @@ class ChangeRequestSlackServiceImplTest {
new ChangeRequestSlackServiceImpl(teamService, userService,privilegeUtilService,
approvalRequestService, changeRequestService, manifestService, slackBotClient,
changeRequestSlackUtil, changeRequestSlackRepository, "");
team1 = new Team();
team1.setId(1L);
team1.setName("team1");
team2 = new Team();
team2.setId(2L);
team2.setName("team2");
user1 = new User();
user1.setEmail("user1@navi.com");
user1.setId(1L);
user1.setTeams(List.of(team1));
user2 = new User();
user2.setEmail("user2@navi.com");
user2.setId(2L);
user2.setTeams(List.of(team2));
user3 = new User();
user3.setEmail("user3@navi.com");
user3.setId(3L);
user3.setTeams(List.of(team1));
user4 = new User();
user4.setEmail("user4@navi.com");
user4.setId(4L);
user4.setTeams(List.of(team2));
mockUser = mock(User.class);
Collection<? extends GrantedAuthority> authorities = mockUser.getAuthorities();
Authentication newAuthentication = new UsernamePasswordAuthenticationToken(
"mockUser@navi.com", "admin", authorities);
SecurityContextHolder.getContext().setAuthentication(newAuthentication);
manifest.setTeam(team1.getName());
manifest.setId(1L);
changeRequest = new ChangeRequestBuilder()
.setId(1L)
.setCreatedBy(user1.getId())
.setManifestId(1L)
.setStatus(PENDING)
.setDiff(List.of(diff))
.createChangeRequest();
approvalRequest1 = new ApprovalRequestBuilder()
.setId(1L)
.setRequestId(1L)
.setTeamId(1L)
.setStatus(PENDING)
.createApprovalRequest();
approvalRequest2 = new ApprovalRequestBuilder()
.setId(2L)
.setRequestId(1L)
.setTeamId(2L)
.setStatus(PENDING)
.createApprovalRequest();
approvalRequest3 = new ApprovalRequestBuilder()
.setId(3L)
.setRequestId(1L)
.setTeamId(2L)
.setStatus(PENDING)
.createApprovalRequest();
slackPostResponse.setTs("1234");
}
private SlackChangeRequest createSlackChangeRequest(RequestStatus status,
ChangeRequest changeRequest, ApprovalRequest approvalRequest, User user, Team team) {
var slackChangeRequest = new SlackChangeRequest();
slackChangeRequest.setStatus(status);
slackChangeRequest.setChangeRequest(changeRequest);
slackChangeRequest.setApprovalRequest(approvalRequest);
slackChangeRequest.setUser(user);
slackChangeRequest.setTeam(team);
return slackChangeRequest;
}
@Test
@DisplayName("should not be able to approve if not authorized")
void userNotAuthorisedToApprove() {
when(userService.findUserByEmail("mockUser@navi.com")).thenReturn(mockUser);
when(mockUser.getEmail()).thenReturn("mockUser@navi.com");
when(approvalRequestService.allowApproveRequest(1L, mockUser.getId()))
.thenThrow(new AccessDeniedException(""));
assertThrows(AccessDeniedException.class ,
()-> changeRequestSlackService.approveRequest("mockUser@navi.com", 1L));
verify(approvalRequestService, times(1)).allowApproveRequest(
any(), any()
);
}
@Test
@DisplayName("should not be able to reject if not authorized")
void userNotAuthorisedToReject() {
when(userService.findUserByEmail("mockUser@navi.com")).thenReturn(mockUser);
when(mockUser.getEmail()).thenReturn("mockUser@navi.com");
when(approvalRequestService.reject(1L, mockUser.getId()))
.thenThrow(new AccessDeniedException(""));
assertThrows(AccessDeniedException.class ,
()-> changeRequestSlackService.rejectRequest("mockUser@navi.com", 1L));
verify(approvalRequestService, times(1)).reject(
any(), any()
);
}
@Test
@DisplayName("should not be able to review if cr already closed")
void crAlreadyClose() {
when(userService.findUserByEmail("mockUser@navi.com")).thenReturn(mockUser);
when(mockUser.getEmail()).thenReturn("mockUser@navi.com");
when(changeRequestService.close(1L, mockUser.getId()))
.thenThrow(new NotFoundException(""));
assertThrows(NotFoundException.class ,
()-> changeRequestSlackService.closeRequest("mockUser@navi.com", 1L));
verify(changeRequestService, times(1)).close(
any(), any()
);
}
@Test
@DisplayName("should throw exception if user doesn't exists")
void reviewerUserNotFound() {
final var someRandomEmail = "someRandomEmail@random.com";
when(userService.findUserByEmail(someRandomEmail)).thenThrow(
new UsernameNotFoundException(someRandomEmail+" not found"));
assertThrows(UsernameNotFoundException.class ,
()-> changeRequestSlackService.approveRequest(someRandomEmail, 1L));
assertThrows(UsernameNotFoundException.class ,
()-> changeRequestSlackService.rejectRequest(someRandomEmail, 1L));
verify(approvalRequestService, times(0)).allowApproveRequest(
any(), any()
);
}
@Test
@DisplayName("should throw exception if slack message doesn't exists for the given cr")
void slackMessageNotFound() {
when(changeRequestSlackRepository.findAllByChangeRequestId(1L)).thenReturn(Collections.emptyList());
assertThrows(IllegalStateException.class ,
()-> changeRequestSlackService.handleApproveRequest(1L, 1L, 1L, 1L));
}
@Test
@DisplayName("have missing authorised approvers for some team")
void shouldThrowExceptionDueToMissingAuthorisedReviewer() {
var pendingChangeRequest = new ChangeRequestBuilder().from(changeRequest)
.setCreatedBy(user1.getId())
.createChangeRequest();
Map<Long, Map<Long, List<String>>> teamUserPrivilegesMap = Map.of(1L,
Map.of(1L, List.of("yes"), 3L, List.of("no")),
2L, Map.of(2L, List.of("no"), 4L, List.of("no")));
when(teamService.findById(team2.getId())).thenReturn(team2);
when(teamService.findTeamBasedUserPrivilegesForEachTeam(List.of(team1.getId(),team2.getId())))
.thenReturn(teamUserPrivilegesMap);
when(privilegeUtilService.canApproveChangeRequests(manifest, List.of("yes")))
.thenReturn(true);
when(privilegeUtilService.canApproveChangeRequests(manifest, List.of("no")))
.thenReturn(false);
RuntimeException exception = assertThrows(RuntimeException.class, () ->
changeRequestSlackService.sendSlackApprovalMessages(manifest, pendingChangeRequest,
List.of(approvalRequest1, approvalRequest2))
);
assertTrue(exception.getMessage().contains("Reviewer absent for teams : team2"));
verify(privilegeUtilService, times(1)).canApproveChangeRequests(
eq(manifest), eq(List.of("yes")));
verify(privilegeUtilService, times(3)).canApproveChangeRequests(
eq(manifest), eq(List.of("no")));
}
@Test
@DisplayName("have missing teams for approval")
void shouldThrowExceptionDueToMissingTeams() throws IOException {
var pendingChangeRequest = new ChangeRequestBuilder().from(changeRequest)
.setCreatedBy(user1.getId())
.createChangeRequest();
Map<Long, Map<Long, List<String>>> teamUserPrivilegesMap = Map.of(1L,
Map.of(1L, List.of("yes"), 3L, List.of("no")));
when(teamService.findById(team2.getId())).thenReturn(team2);
when(teamService.findTeamBasedUserPrivilegesForEachTeam(List.of(team1.getId(),team2.getId()))).thenReturn(
teamUserPrivilegesMap);
when(privilegeUtilService.canApproveChangeRequests(manifest, List.of("yes")))
.thenReturn(true);
when(privilegeUtilService.canApproveChangeRequests(manifest, List.of("no")))
.thenReturn(false);
RuntimeException exception = assertThrows(RuntimeException.class, () ->
changeRequestSlackService.sendSlackApprovalMessages(manifest, pendingChangeRequest,
List.of(approvalRequest1, approvalRequest2))
);
verify(privilegeUtilService, times(1)).canApproveChangeRequests(
eq(manifest), eq(List.of("yes")));
verify(privilegeUtilService, times(1)).canApproveChangeRequests(
eq(manifest), eq(List.of("no")));
assertTrue(exception.getMessage().contains("Missing teams for cr 1 approval: team2"));
}
@Test
@DisplayName("should be able to send slack approval messages")
void shouldAbleToSendSlackApprovalMessages() throws IOException {
final var pendingChangeRequest = new ChangeRequestBuilder().from(changeRequest)
.setCreatedBy(user1.getId())
.createChangeRequest();
Map<Long, Map<Long, List<String>>> teamUserPrivilegesMap = Map.of(1L,
Map.of(1L, List.of("yes"), 3L, List.of("no")),
2L, Map.of(2L, List.of("yes"), 4L, List.of("yes")));
final var expectedUsers = List.of(user1, user2, user4);
var slackRequest1 = createSlackChangeRequest(PENDING, pendingChangeRequest,
approvalRequest1, user1, team1);
var slackRequest2 = createSlackChangeRequest(PENDING, pendingChangeRequest,
approvalRequest2, user2, team2);
var slackRequest3 = createSlackChangeRequest(PENDING, pendingChangeRequest,
approvalRequest3, user3, team1);
var slackRequests = List.of(slackRequest1, slackRequest2, slackRequest3);
when(teamService.findById(team1.getId())).thenReturn(team1);
when(teamService.findById(team2.getId())).thenReturn(team2);
when(userService.findById(user1.getId())).thenReturn(user1);
when(userService.findById(user2.getId())).thenReturn(user2);
when(userService.findById(user4.getId())).thenReturn(user4);
when(changeRequestSlackRepository.saveAll(argThat(list -> {
var reviewerRequestsToSave = StreamSupport.stream(list.spliterator(), false)
.collect(Collectors.toList());
assertEquals(3L, StreamSupport.stream(list.spliterator(), false).count());
return expectedUsers.stream().allMatch(user ->
reviewerRequestsToSave.stream().anyMatch(request -> request.getUser().equals(user))
);
}))).thenReturn(slackRequests);
when(userService.getUsersSlackId(any())).thenReturn("slackId");
when(teamService.findTeamBasedUserPrivilegesForEachTeam(List.of(team1.getId(),team2.getId())))
.thenReturn(teamUserPrivilegesMap);
when(privilegeUtilService.canApproveChangeRequests(manifest, List.of("yes")))
.thenReturn(true);
when(privilegeUtilService.canApproveChangeRequests(manifest, List.of("no")))
.thenReturn(false);
when(slackBotClient.postMessage(any(),any())).thenReturn(slackPostResponse);
changeRequest = changeRequestSlackService.sendSlackApprovalMessages(manifest, pendingChangeRequest,
List.of(approvalRequest1, approvalRequest2));
verify(changeRequestSlackRepository, times(1)).saveAll(any());
assertEquals(changeRequest.getSlackChannelMessageTimestamp(), slackPostResponse.getTs());
verify(changeRequestSlackRepository, times(3)).save(any(SlackChangeRequest.class));
verify(slackBotClient, times(4)).postMessage(any(), any());
verify(changeRequestSlackUtil, times(1))
.getManifestDiff(changeRequest,manifest);
}
@Test
@DisplayName("should update review dm and channel message in case cr content/approver changed "
+ "or cr is in terminal state")
void shouldUpdateSlackOnCrChange() throws IOException {
final var rejectedChangeRequest = new ChangeRequestBuilder().from(changeRequest)
.setCreatedBy(user1.getId())
.setStatus(RequestStatus.REJECTED)
.setUpdatedBy(user4.getId())
.createChangeRequest();
approvalRequest1 = new ApprovalRequestBuilder().from(approvalRequest1)
.setUpdatedBy(user1.getId())
.setStatus(APPROVED)
.createApprovalRequest();
approvalRequest2 = new ApprovalRequestBuilder().from(approvalRequest1)
.setUpdatedBy(user2.getId())
.createApprovalRequest();
approvalRequest3 = new ApprovalRequestBuilder().from(approvalRequest1)
.setUpdatedBy(user3.getId())
.createApprovalRequest();
var slackRequest1 = createSlackChangeRequest(APPROVED, rejectedChangeRequest,
approvalRequest1, user1, team1);
var slackRequest2 = createSlackChangeRequest(PENDING, rejectedChangeRequest,
approvalRequest2, user2, team2);
var slackRequest3 = createSlackChangeRequest(PENDING, rejectedChangeRequest,
approvalRequest3, user3, team1);
var slackRequests = List.of(slackRequest1, slackRequest2, slackRequest3);
when(teamService.findById(team1.getId())).thenReturn(team1);
when(userService.findById(user1.getId())).thenReturn(user1);
when(userService.findById(user2.getId())).thenReturn(user2);
when(userService.findById(user3.getId())).thenReturn(user3);
when(changeRequestSlackRepository.findAllByChangeRequestId(changeRequest.getId()))
.thenReturn(slackRequests);
when(approvalRequestService.findAllByRequestTypeAndRequestIds(RequestType.CHANGE_REQUEST,
List.of(rejectedChangeRequest.getId()))).thenReturn(List.of(approvalRequest1, approvalRequest2, approvalRequest3));
when(changeRequestSlackUtil.getManifestDiff(rejectedChangeRequest,manifest)).thenReturn("diff");
changeRequestSlackService.handleChangeRequestUpdate(manifest, rejectedChangeRequest);
verify(changeRequestSlackUtil, times(1)).getManifestDiff(rejectedChangeRequest, manifest);
}
}