@Test
public void testFailureDuringAlerting() throws Exception {

  this.activeFlows.clear();
  this.activeFlows.put(EXECUTION_ID_11, new Pair<>(
      new ExecutionReference(EXECUTION_ID_11, this.executor1), this.flow1));
  // Force a failure of the executor ping API
  ExecutorManagerException healthcheckException = new ExecutorManagerException("test exception");
  when(this.apiGateway.callWithExecutionId(
      this.executor1.getHost(),
      this.executor1.getPort(),
      ConnectorParams.PING_ACTION, null, null))
      .thenThrow(healthcheckException);
  // Force an unchecked exception when sending alert emails for the healthcheck failure
  // Note that we can't use this.alerterHolder.get("email") in the when() as mockito
  // doesn't like nested mocks.
  doThrow(new RuntimeException("test runtime exception"))
      .when(this.mailAlerter)
      .alertOnFailedExecutorHealthCheck(
          this.executor1,
          Arrays.asList(this.flow1),
          healthcheckException,
          Arrays.asList(AZ_ADMIN_ALERT_EMAIL.split(",")));
  when(this.loader.fetchExecutableFlow(EXECUTION_ID_11)).thenReturn(this.flow1);
  for (int failureCount = 0;
      failureCount < props.getInt(ConfigurationKeys.AZKABAN_EXECUTOR_MAX_FAILURE_COUNT);
      failureCount++) {
    this.executorHealthChecker.checkExecutorHealth();
  }
  // Confirm that cleanup for the executor is attempted despite failure to send emails.
  // verify() can't be called on executorHealthCheck.cleanUpForMissingExecutor as it's not being
  // mocked. Directly checking the flow update through the mocked 'loader' is a suitable proxy
  // for this.
  verify(this.loader).updateExecutableFlow(this.flow1);
  assertThat(this.flow1.getStatus()).isEqualTo(Status.FAILED);
}