diff --git a/lib/build.gradle b/lib/build.gradle index f97c5a7..2b76355 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -8,6 +8,7 @@ dependencies { testImplementation(platform("org.junit:junit-bom:${junitVersion}")) testImplementation 'org.junit.jupiter:junit-jupiter' testImplementation "org.mockito:mockito-core:${mockitoVersion}" + testImplementation "org.mockito:mockito-inline:${mockitoVersion}" } test { diff --git a/lib/src/main/java/com/scalar/admin/kubernetes/GetTargetAfterPauseFailedException.java b/lib/src/main/java/com/scalar/admin/kubernetes/GetTargetAfterPauseFailedException.java new file mode 100644 index 0000000..0e9486f --- /dev/null +++ b/lib/src/main/java/com/scalar/admin/kubernetes/GetTargetAfterPauseFailedException.java @@ -0,0 +1,11 @@ +package com.scalar.admin.kubernetes; + +public class GetTargetAfterPauseFailedException extends PauserException { + public GetTargetAfterPauseFailedException(String message) { + super(message); + } + + public GetTargetAfterPauseFailedException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/lib/src/main/java/com/scalar/admin/kubernetes/PauseFailedException.java b/lib/src/main/java/com/scalar/admin/kubernetes/PauseFailedException.java new file mode 100644 index 0000000..0641edb --- /dev/null +++ b/lib/src/main/java/com/scalar/admin/kubernetes/PauseFailedException.java @@ -0,0 +1,11 @@ +package com.scalar.admin.kubernetes; + +public class PauseFailedException extends PauserException { + public PauseFailedException(String message) { + super(message); + } + + public PauseFailedException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/lib/src/main/java/com/scalar/admin/kubernetes/Pauser.java b/lib/src/main/java/com/scalar/admin/kubernetes/Pauser.java index 0cda1a3..6730fb4 100644 --- a/lib/src/main/java/com/scalar/admin/kubernetes/Pauser.java +++ b/lib/src/main/java/com/scalar/admin/kubernetes/Pauser.java @@ -1,5 +1,6 @@ package com.scalar.admin.kubernetes; +import com.google.common.annotations.VisibleForTesting; import com.google.common.util.concurrent.Uninterruptibles; import com.scalar.admin.RequestCoordinator; import io.kubernetes.client.openapi.Configuration; @@ -13,8 +14,6 @@ import java.util.stream.Collectors; import javax.annotation.Nullable; import javax.annotation.concurrent.NotThreadSafe; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * This class implements a pause operation for Scalar product pods in a Kubernetes cluster. The @@ -34,11 +33,38 @@ @NotThreadSafe public class Pauser { - private static final int MAX_UNPAUSE_RETRY_COUNT = 3; + @VisibleForTesting static final int MAX_UNPAUSE_RETRY_COUNT = 3; - private final Logger logger = LoggerFactory.getLogger(Pauser.class); private final TargetSelector targetSelector; + @VisibleForTesting + static final String UNPAUSE_ERROR_MESSAGE = + "Unpause operation failed. Scalar products might still be in a paused state. You" + + " must restart related pods by using the `kubectl rollout restart deployment" + + " ` command to unpause all pods."; + + @VisibleForTesting + static final String PAUSE_ERROR_MESSAGE = + "Pause operation failed. You cannot use the backup that was taken during this pause" + + " duration. You need to retry the pause operation from the beginning to" + + " take a backup."; + + @VisibleForTesting + static final String GET_TARGET_AFTER_PAUSE_ERROR_MESSAGE = + "Failed to find the target pods to examine if the targets pods were updated during" + + " paused."; + + @VisibleForTesting + static final String STATUS_CHECK_ERROR_MESSAGE = + "Status check failed. You cannot use the backup that was taken during this pause" + + " duration. You need to retry the pause operation from the beginning to" + + " take a backup."; + + @VisibleForTesting + static final String STATUS_UNMATCHED_ERROR_MESSAGE = + "The target pods were updated during the pause duration. You cannot use the backup that" + + " was taken during this pause duration."; + /** * @param namespace The namespace where the pods are deployed. * @param helmReleaseName The Helm release name used to deploy the pods. @@ -77,67 +103,191 @@ public PausedDuration pause(int pauseDuration, @Nullable Long maxPauseWaitTime) "pauseDuration is required to be greater than 0 millisecond."); } - TargetSnapshot target; + // Get pods and deployment information before pause. + TargetSnapshot targetBeforePause; try { - target = targetSelector.select(); + targetBeforePause = getTarget(); } catch (Exception e) { throw new PauserException("Failed to find the target pods to pause.", e); } - RequestCoordinator coordinator = getRequestCoordinator(target); - - coordinator.pause(true, maxPauseWaitTime); - - Instant startTime = Instant.now(); + // Get RequestCoordinator of Scalar Admin to pause. + RequestCoordinator requestCoordinator; + try { + requestCoordinator = getRequestCoordinator(targetBeforePause); + } catch (Exception e) { + throw new PauserException("Failed to initialize the request coordinator.", e); + } - Uninterruptibles.sleepUninterruptibly(pauseDuration, TimeUnit.MILLISECONDS); + // From here, we cannot throw exceptions right after they occur because we need to take care of + // the unpause operation failure. We will throw the exception after the unpause operation or at + // the end of this method. - Instant endTime = Instant.now(); + // Run a pause operation. + PausedDuration pausedDuration = null; + PauseFailedException pauseFailedException = null; + try { + pausedDuration = pauseInternal(requestCoordinator, pauseDuration, maxPauseWaitTime); + } catch (Exception e) { + pauseFailedException = new PauseFailedException(PAUSE_ERROR_MESSAGE, e); + } - unpauseWithRetry(coordinator, MAX_UNPAUSE_RETRY_COUNT, target); + // Run an unpause operation. + UnpauseFailedException unpauseFailedException = null; + try { + unpauseWithRetry(requestCoordinator, MAX_UNPAUSE_RETRY_COUNT); + } catch (Exception e) { + unpauseFailedException = new UnpauseFailedException(UNPAUSE_ERROR_MESSAGE, e); + } - TargetSnapshot targetAfterPause; + // Get pods and deployment information after pause. + TargetSnapshot targetAfterPause = null; + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = null; try { - targetAfterPause = targetSelector.select(); + targetAfterPause = getTarget(); } catch (Exception e) { - throw new PauserException( - "Failed to find the target pods to examine if the targets pods were updated during" - + " paused.", - e); + getTargetAfterPauseFailedException = + new GetTargetAfterPauseFailedException(GET_TARGET_AFTER_PAUSE_ERROR_MESSAGE, e); } - if (!target.getStatus().equals(targetAfterPause.getStatus())) { - throw new PauserException("The target pods were updated during paused. Please retry."); + // Check if pods and deployment information are the same between before pause and after pause. + StatusCheckFailedException statusCheckFailedException = null; + StatusUnmatchedException statusUnmatchedException = null; + if (targetAfterPause != null) { + try { + statusUnmatchedException = targetStatusEquals(targetBeforePause, targetAfterPause); + } catch (Exception e) { + statusCheckFailedException = new StatusCheckFailedException(STATUS_CHECK_ERROR_MESSAGE, e); + } } - return new PausedDuration(startTime, endTime); + // We use the exceptions as conditions instead of using boolean flags like `isPauseOk`, etc. If + // we use boolean flags, it might cause a bit large number of combinations. For example, if we + // have three flags, they generate 2^3 = 8 combinations. It also might make the nested if + // statement or a lot of branches of the switch statement. That's why we don't use status flags + // for now. + PauserException pauserException = + buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Return the final result based on each process. + if (pauserException != null) { + // Some operations failed. + throw pauserException; + } else { + // All operations succeeded. + return pausedDuration; + } } - private void unpauseWithRetry( - RequestCoordinator coordinator, int maxRetryCount, TargetSnapshot target) { + @VisibleForTesting + void unpauseWithRetry(RequestCoordinator coordinator, int maxRetryCount) { int retryCounter = 0; - while (true) { try { coordinator.unpause(); return; } catch (Exception e) { if (++retryCounter >= maxRetryCount) { - logger.warn( - "Failed to unpause Scalar product. They are still in paused. You must restart related" - + " pods by using the `kubectl rollout restart deployment {}`" - + " command to unpase all pods.", - target.getDeployment().getMetadata().getName()); - return; + throw e; } } } } + @VisibleForTesting + TargetSnapshot getTarget() throws PauserException { + return targetSelector.select(); + } + + @VisibleForTesting RequestCoordinator getRequestCoordinator(TargetSnapshot target) { return new RequestCoordinator( target.getPods().stream() .map(p -> new InetSocketAddress(p.getStatus().getPodIP(), target.getAdminPort())) .collect(Collectors.toList())); } + + @VisibleForTesting + PausedDuration pauseInternal( + RequestCoordinator requestCoordinator, int pauseDuration, @Nullable Long maxPauseWaitTime) { + requestCoordinator.pause(true, maxPauseWaitTime); + Instant startTime = Instant.now(); + Uninterruptibles.sleepUninterruptibly(pauseDuration, TimeUnit.MILLISECONDS); + Instant endTime = Instant.now(); + return new PausedDuration(startTime, endTime); + } + + @VisibleForTesting + @Nullable + StatusUnmatchedException targetStatusEquals(TargetSnapshot before, TargetSnapshot after) { + if (before.getStatus().equals(after.getStatus())) { + return null; + } else { + return new StatusUnmatchedException(STATUS_UNMATCHED_ERROR_MESSAGE); + } + } + + @Nullable + @VisibleForTesting + PauserException buildException( + @Nullable UnpauseFailedException unpauseFailedException, + @Nullable PauseFailedException pauseFailedException, + @Nullable GetTargetAfterPauseFailedException getTargetAfterPauseFailedException, + @Nullable StatusCheckFailedException statusCheckFailedException, + @Nullable StatusUnmatchedException statusUnmatchedException) { + PauserException pauserException = null; + + // Treat the unpause failure as most critical because it might cause system unavailability. + if (unpauseFailedException != null) { + pauserException = unpauseFailedException; + } + + // Treat the pause failure as the second priority because the issue might be caused by a + // configuration mistake. + if (pauseFailedException != null) { + if (pauserException == null) { + pauserException = pauseFailedException; + } else { + pauserException.addSuppressed(pauseFailedException); + } + } + + // Treat the getting target failure as the third priority because this issue might be caused by + // a temporary glitch, for example, network failures. + if (getTargetAfterPauseFailedException != null) { + if (pauserException == null) { + pauserException = getTargetAfterPauseFailedException; + } else { + pauserException.addSuppressed(getTargetAfterPauseFailedException); + } + } + + // Treat the status checking failure as the third priority because this issue might be caused by + // a temporary glitch, for example, getting target information by using the Kubernetes API fails + // after the pause operation. + if (statusCheckFailedException != null) { + if (pauserException == null) { + pauserException = statusCheckFailedException; + } else { + pauserException.addSuppressed(statusCheckFailedException); + } + } + + // Treat the status unmatched issue as the third priority because this issue might be caused by + // temporary glitch, for example, pod restarts. + if (statusUnmatchedException != null) { + if (pauserException == null) { + pauserException = statusUnmatchedException; + } else { + pauserException.addSuppressed(statusUnmatchedException); + } + } + + return pauserException; + } } diff --git a/lib/src/main/java/com/scalar/admin/kubernetes/StatusCheckFailedException.java b/lib/src/main/java/com/scalar/admin/kubernetes/StatusCheckFailedException.java new file mode 100644 index 0000000..c30ed07 --- /dev/null +++ b/lib/src/main/java/com/scalar/admin/kubernetes/StatusCheckFailedException.java @@ -0,0 +1,11 @@ +package com.scalar.admin.kubernetes; + +public class StatusCheckFailedException extends PauserException { + public StatusCheckFailedException(String message) { + super(message); + } + + public StatusCheckFailedException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/lib/src/main/java/com/scalar/admin/kubernetes/StatusUnmatchedException.java b/lib/src/main/java/com/scalar/admin/kubernetes/StatusUnmatchedException.java new file mode 100644 index 0000000..19f9997 --- /dev/null +++ b/lib/src/main/java/com/scalar/admin/kubernetes/StatusUnmatchedException.java @@ -0,0 +1,11 @@ +package com.scalar.admin.kubernetes; + +public class StatusUnmatchedException extends PauserException { + public StatusUnmatchedException(String message) { + super(message); + } + + public StatusUnmatchedException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/lib/src/main/java/com/scalar/admin/kubernetes/UnpauseFailedException.java b/lib/src/main/java/com/scalar/admin/kubernetes/UnpauseFailedException.java new file mode 100644 index 0000000..bccd72b --- /dev/null +++ b/lib/src/main/java/com/scalar/admin/kubernetes/UnpauseFailedException.java @@ -0,0 +1,11 @@ +package com.scalar.admin.kubernetes; + +public class UnpauseFailedException extends PauserException { + public UnpauseFailedException(String message) { + super(message); + } + + public UnpauseFailedException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/lib/src/test/java/com/scalar/admin/kubernetes/PauserTest.java b/lib/src/test/java/com/scalar/admin/kubernetes/PauserTest.java new file mode 100644 index 0000000..712c922 --- /dev/null +++ b/lib/src/test/java/com/scalar/admin/kubernetes/PauserTest.java @@ -0,0 +1,1599 @@ +package com.scalar.admin.kubernetes; + +import static com.scalar.admin.kubernetes.Pauser.*; +import static java.time.temporal.ChronoUnit.SECONDS; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import com.google.common.util.concurrent.Uninterruptibles; +import com.scalar.admin.RequestCoordinator; +import io.kubernetes.client.openapi.models.V1Deployment; +import io.kubernetes.client.openapi.models.V1ObjectMeta; +import io.kubernetes.client.util.Config; +import java.io.IOException; +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +class PauserTest { + + private MockedStatic mockedConfig; + private RequestCoordinator requestCoordinator; + private TargetSnapshot targetBeforePause; + private TargetSnapshot targetAfterPause; + private V1Deployment deployment; + private V1ObjectMeta objectMeta; + private static final String DUMMY_OBJECT_NAME = "dummyObjectName"; + + @BeforeEach + void beforeEach() throws PauserException { + mockedConfig = mockStatic(Config.class); + mockedConfig.when(() -> Config.defaultClient()).thenReturn(null); + requestCoordinator = mock(RequestCoordinator.class); + deployment = mock(V1Deployment.class); + objectMeta = mock(V1ObjectMeta.class); + targetBeforePause = mock(TargetSnapshot.class); + targetAfterPause = mock(TargetSnapshot.class); + + doReturn(deployment).when(targetBeforePause).getDeployment(); + doReturn(deployment).when(targetAfterPause).getDeployment(); + doReturn(objectMeta).when(deployment).getMetadata(); + doReturn(DUMMY_OBJECT_NAME).when(objectMeta).getName(); + } + + @AfterEach + void afterEach() { + mockedConfig.close(); + } + + @Nested + class Constructor { + @Test + void constructor_WithCorrectArgs_ReturnPauser() throws PauserException, IOException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + + // Act & Assert + assertDoesNotThrow(() -> new Pauser(namespace, helmReleaseName)); + } + + @Test + void constructor_WithNullNamespace_ShouldThrowIllegalArgumentException() { + // Arrange + String namespace = null; + String helmReleaseName = "dummyRelease"; + + // Act & Assert + IllegalArgumentException thrown = + assertThrows( + IllegalArgumentException.class, () -> new Pauser(namespace, helmReleaseName)); + assertEquals("namespace is required", thrown.getMessage()); + } + + @Test + void constructor_WithNullHelmReleaseName_ShouldThrowIllegalArgumentException() { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = null; + + // Act & Assert + IllegalArgumentException thrown = + assertThrows( + IllegalArgumentException.class, () -> new Pauser(namespace, helmReleaseName)); + assertEquals("helmReleaseName is required", thrown.getMessage()); + } + + @Test + void constructor_WhenSetDefaultApiClientThrowIOException_ShouldThrowPauserException() { + // Arrange + mockedConfig.when(() -> Config.defaultClient()).thenThrow(IOException.class); + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + + // Act & Assert + PauserException thrown = + assertThrows(PauserException.class, () -> new Pauser(namespace, helmReleaseName)); + assertEquals("Failed to set default Kubernetes client.", thrown.getMessage()); + } + } + + @Nested + class Pause { + + @Test + void pause_WhenPauseSucceeded_ReturnPausedDuration() throws PauserException { + Map podRestartCounts = + new HashMap() { + { + put("dummyKey", 1); + } + }; + Map podResourceVersions = + new HashMap() { + { + put("dummyKey", "dummyValue"); + } + }; + TargetStatus beforeTargetStatus = + new TargetStatus(podRestartCounts, podResourceVersions, "sameValue"); + TargetStatus afterTargetStatus = + new TargetStatus(podRestartCounts, podResourceVersions, "sameValue"); + doReturn(beforeTargetStatus).when(targetBeforePause).getStatus(); + doReturn(afterTargetStatus).when(targetAfterPause).getStatus(); + + Instant startTime = Instant.now().minus(5, SECONDS); + Instant endTime = Instant.now().plus(5, SECONDS); + MockedStatic mockedTime = mockStatic(Instant.class); + mockedTime.when(() -> Instant.now()).thenReturn(startTime).thenReturn(endTime); + + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + int pauseDuration = 1; + Pauser pauser = spy(new Pauser(namespace, helmReleaseName)); + doReturn(targetBeforePause).doReturn(targetAfterPause).when(pauser).getTarget(); + doReturn(requestCoordinator).when(pauser).getRequestCoordinator(targetBeforePause); + doNothing().when(requestCoordinator).pause(true, null); + doNothing().when(pauser).unpauseWithRetry(any(), anyInt()); + + // Act & Assert + PausedDuration actual = assertDoesNotThrow(() -> pauser.pause(pauseDuration, 3000L)); + PausedDuration expected = new PausedDuration(startTime, endTime); + assertEquals(actual.getStartTime(), expected.getStartTime()); + assertEquals(actual.getEndTime(), expected.getEndTime()); + + mockedTime.close(); + } + + @Test + void pause_LessThanOnePauseDuration_ShouldThrowIllegalArgumentException() + throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + int pauseDuration = 0; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + // Act & Assert + IllegalArgumentException thrown = + assertThrows(IllegalArgumentException.class, () -> pauser.pause(pauseDuration, null)); + assertEquals( + "pauseDuration is required to be greater than 0 millisecond.", thrown.getMessage()); + } + + @Test + void pause_WhenFirstGetTargetThrowException_ShouldThrowPauserException() + throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + int pauseDuration = 1; + Pauser pauser = spy(new Pauser(namespace, helmReleaseName)); + doThrow(RuntimeException.class).when(pauser).getTarget(); + + // Act & Assert + PauserException thrown = + assertThrows(PauserException.class, () -> pauser.pause(pauseDuration, null)); + assertEquals("Failed to find the target pods to pause.", thrown.getMessage()); + } + + @Test + void pause_WhenGetRequestCoordinatorThrowException_ShouldThrowPauserException() + throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + int pauseDuration = 1; + Pauser pauser = spy(new Pauser(namespace, helmReleaseName)); + doReturn(targetBeforePause).when(pauser).getTarget(); + doThrow(RuntimeException.class).when(pauser).getRequestCoordinator(targetBeforePause); + + // Act & Assert + PauserException thrown = + assertThrows(PauserException.class, () -> pauser.pause(pauseDuration, null)); + assertEquals("Failed to initialize the request coordinator.", thrown.getMessage()); + } + + @Test + void pause_WhenPauseInternalThrowException_ShouldThrowPauseFailedException() + throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + int pauseDuration = 1; + Pauser pauser = spy(new Pauser(namespace, helmReleaseName)); + doReturn(targetBeforePause).doReturn(targetAfterPause).when(pauser).getTarget(); + doReturn(requestCoordinator).when(pauser).getRequestCoordinator(targetBeforePause); + doThrow(RuntimeException.class) + .when(pauser) + .pauseInternal(requestCoordinator, pauseDuration, null); + doNothing().when(pauser).unpauseWithRetry(any(), anyInt()); + doReturn(null).when(pauser).targetStatusEquals(any(), any()); + + // Act & Assert + PauseFailedException thrown = + assertThrows(PauseFailedException.class, () -> pauser.pause(pauseDuration, null)); + assertEquals(PAUSE_ERROR_MESSAGE, thrown.getMessage()); + } + + @Test + void pause_WhenRequestCoordinatorPauseThrowException_ShouldThrowPauseFailedException() + throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + int pauseDuration = 1; + Pauser pauser = spy(new Pauser(namespace, helmReleaseName)); + doReturn(targetBeforePause).doReturn(targetAfterPause).when(pauser).getTarget(); + doReturn(requestCoordinator).when(pauser).getRequestCoordinator(targetBeforePause); + doThrow(RuntimeException.class).when(requestCoordinator).pause(true, null); + doNothing().when(pauser).unpauseWithRetry(any(), anyInt()); + doReturn(null).when(pauser).targetStatusEquals(any(), any()); + + // Act & Assert + PauseFailedException thrown = + assertThrows(PauseFailedException.class, () -> pauser.pause(pauseDuration, null)); + assertEquals(PAUSE_ERROR_MESSAGE, thrown.getMessage()); + } + + @Test + void pause_WhenInstantNowThrowException_ShouldThrowPauseFailedException() + throws PauserException { + // Arrange + MockedStatic mockedTime = mockStatic(Instant.class); + mockedTime.when(() -> Instant.now()).thenThrow(RuntimeException.class); + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + int pauseDuration = 1; + Pauser pauser = spy(new Pauser(namespace, helmReleaseName)); + doReturn(targetBeforePause).doReturn(targetAfterPause).when(pauser).getTarget(); + doReturn(requestCoordinator).when(pauser).getRequestCoordinator(targetBeforePause); + doNothing().when(pauser).unpauseWithRetry(any(), anyInt()); + doReturn(null).when(pauser).targetStatusEquals(any(), any()); + + // Act & Assert + PauseFailedException thrown = + assertThrows(PauseFailedException.class, () -> pauser.pause(pauseDuration, null)); + assertEquals(PAUSE_ERROR_MESSAGE, thrown.getMessage()); + + mockedTime.close(); + } + + @Test + void pause_WhenSleepThrowException_ShouldThrowPauseFailedException() throws PauserException { + // Arrange + int pauseDuration = 1; + MockedStatic mockedSleep = mockStatic(Uninterruptibles.class); + mockedSleep + .when(() -> Uninterruptibles.sleepUninterruptibly(pauseDuration, TimeUnit.MILLISECONDS)) + .thenThrow(RuntimeException.class); + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = spy(new Pauser(namespace, helmReleaseName)); + doReturn(targetBeforePause).doReturn(targetAfterPause).when(pauser).getTarget(); + doReturn(requestCoordinator).when(pauser).getRequestCoordinator(targetBeforePause); + doNothing().when(pauser).unpauseWithRetry(any(), anyInt()); + doReturn(null).when(pauser).targetStatusEquals(any(), any()); + + // Act & Assert + PauseFailedException thrown = + assertThrows(PauseFailedException.class, () -> pauser.pause(pauseDuration, null)); + assertEquals(PAUSE_ERROR_MESSAGE, thrown.getMessage()); + + mockedSleep.close(); + } + + @Test + void pause_WhenUnpauseWithRetryThrowException_ShouldThrowUnpauseFailedException() + throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + int pauseDuration = 1; + Instant startTime = Instant.now().minus(5, SECONDS); + Instant endTime = Instant.now().plus(5, SECONDS); + + Pauser pauser = spy(new Pauser(namespace, helmReleaseName)); + PausedDuration pausedDuration = new PausedDuration(startTime, endTime); + + doReturn(targetBeforePause).doReturn(targetAfterPause).when(pauser).getTarget(); + doReturn(requestCoordinator).when(pauser).getRequestCoordinator(targetBeforePause); + doReturn(pausedDuration).when(pauser).pauseInternal(any(), anyInt(), anyLong()); + doThrow(RuntimeException.class) + .when(pauser) + .unpauseWithRetry(requestCoordinator, MAX_UNPAUSE_RETRY_COUNT); + doReturn(null).when(pauser).targetStatusEquals(any(), any()); + + // Act & Assert + UnpauseFailedException thrown = + assertThrows(UnpauseFailedException.class, () -> pauser.pause(pauseDuration, null)); + assertEquals(UNPAUSE_ERROR_MESSAGE, thrown.getMessage()); + } + + @Test + void pause_WhenSecondGetTargetThrowException_ShouldThrowGetTargetAfterPauseFailedException() + throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + int pauseDuration = 1; + Instant startTime = Instant.now().minus(5, SECONDS); + Instant endTime = Instant.now().plus(5, SECONDS); + + Pauser pauser = spy(new Pauser(namespace, helmReleaseName)); + PausedDuration pausedDuration = new PausedDuration(startTime, endTime); + + doReturn(targetBeforePause).doThrow(RuntimeException.class).when(pauser).getTarget(); + doReturn(requestCoordinator).when(pauser).getRequestCoordinator(targetBeforePause); + doReturn(pausedDuration).when(pauser).pauseInternal(any(), anyInt(), anyLong()); + doNothing().when(pauser).unpauseWithRetry(any(), anyInt()); + + // Act & Assert + GetTargetAfterPauseFailedException thrown = + assertThrows( + GetTargetAfterPauseFailedException.class, () -> pauser.pause(pauseDuration, null)); + assertEquals(GET_TARGET_AFTER_PAUSE_ERROR_MESSAGE, thrown.getMessage()); + } + + @Test + void pause_WhenTargetStatusEqualsThrowException_ShouldThrowStatusCheckFailedException() + throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + int pauseDuration = 1; + Instant startTime = Instant.now().minus(5, SECONDS); + Instant endTime = Instant.now().plus(5, SECONDS); + + Pauser pauser = spy(new Pauser(namespace, helmReleaseName)); + PausedDuration pausedDuration = new PausedDuration(startTime, endTime); + + doReturn(targetBeforePause).doReturn(targetAfterPause).when(pauser).getTarget(); + doReturn(requestCoordinator).when(pauser).getRequestCoordinator(targetBeforePause); + doReturn(pausedDuration).when(pauser).pauseInternal(any(), anyInt(), anyLong()); + doNothing().when(pauser).unpauseWithRetry(any(), anyInt()); + doThrow(RuntimeException.class).when(pauser).targetStatusEquals(any(), any()); + + // Act & Assert + StatusCheckFailedException thrown = + assertThrows(StatusCheckFailedException.class, () -> pauser.pause(pauseDuration, null)); + assertEquals(STATUS_CHECK_ERROR_MESSAGE, thrown.getMessage()); + } + + @Test + void pause_WhenTargetPodStatusChanged_ShouldThrowStatusUnmatchedException() + throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + int pauseDuration = 1; + Instant startTime = Instant.now().minus(5, SECONDS); + Instant endTime = Instant.now().plus(5, SECONDS); + + Pauser pauser = spy(new Pauser(namespace, helmReleaseName)); + PausedDuration pausedDuration = new PausedDuration(startTime, endTime); + + doReturn(targetBeforePause).doReturn(targetAfterPause).when(pauser).getTarget(); + doReturn(requestCoordinator).when(pauser).getRequestCoordinator(targetBeforePause); + + Map podRestartCounts = + new HashMap() { + { + put("dummyKey", 1); + } + }; + Map podResourceVersions = + new HashMap() { + { + put("dummyKey", "dummyValue"); + } + }; + TargetStatus beforeTargetStatus = + new TargetStatus(podRestartCounts, podResourceVersions, "beforeDifferentValue"); + TargetStatus afterTargetStatus = + new TargetStatus(podRestartCounts, podResourceVersions, "afterDifferentValue"); + doReturn(beforeTargetStatus).when(targetBeforePause).getStatus(); + doReturn(afterTargetStatus).when(targetAfterPause).getStatus(); + + doReturn(pausedDuration).when(pauser).pauseInternal(any(), anyInt(), anyLong()); + doNothing().when(pauser).unpauseWithRetry(any(), anyInt()); + + // Act & Assert + StatusUnmatchedException thrown = + assertThrows(StatusUnmatchedException.class, () -> pauser.pause(pauseDuration, null)); + assertEquals(STATUS_UNMATCHED_ERROR_MESSAGE, thrown.getMessage()); + } + + @Test + void pause_WhenPauseAndUnpauseThrowException_ShouldThrowUnpauseFailedException() + throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + int pauseDuration = 1; + Pauser pauser = spy(new Pauser(namespace, helmReleaseName)); + doReturn(targetBeforePause).doReturn(targetAfterPause).when(pauser).getTarget(); + doReturn(requestCoordinator).when(pauser).getRequestCoordinator(targetBeforePause); + doThrow(RuntimeException.class) + .when(pauser) + .pauseInternal(requestCoordinator, pauseDuration, null); + doThrow(RuntimeException.class) + .when(pauser) + .unpauseWithRetry(requestCoordinator, MAX_UNPAUSE_RETRY_COUNT); + doReturn(null).when(pauser).targetStatusEquals(any(), any()); + + // Act & Assert + UnpauseFailedException thrown = + assertThrows(UnpauseFailedException.class, () -> pauser.pause(pauseDuration, null)); + assertEquals(UNPAUSE_ERROR_MESSAGE, thrown.getMessage()); + assertEquals(PauseFailedException.class, thrown.getSuppressed()[0].getClass()); + } + + @Test + void + pause_WhenSecondGetTargetAndUnpauseWithRetryThrowException_ShouldThrowUnpauseFailedException() + throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + int pauseDuration = 1; + Instant startTime = Instant.now().minus(5, SECONDS); + Instant endTime = Instant.now().plus(5, SECONDS); + + Pauser pauser = spy(new Pauser(namespace, helmReleaseName)); + PausedDuration pausedDuration = new PausedDuration(startTime, endTime); + + doReturn(targetBeforePause).doThrow(RuntimeException.class).when(pauser).getTarget(); + doReturn(requestCoordinator).when(pauser).getRequestCoordinator(targetBeforePause); + doReturn(pausedDuration).when(pauser).pauseInternal(any(), anyInt(), anyLong()); + doThrow(RuntimeException.class) + .when(pauser) + .unpauseWithRetry(requestCoordinator, MAX_UNPAUSE_RETRY_COUNT); + + // Act & Assert + UnpauseFailedException thrown = + assertThrows(UnpauseFailedException.class, () -> pauser.pause(pauseDuration, null)); + assertEquals(UNPAUSE_ERROR_MESSAGE, thrown.getMessage()); + assertEquals(GetTargetAfterPauseFailedException.class, thrown.getSuppressed()[0].getClass()); + } + + @Test + void + pause_WhenTargetStatusEqualsAndUnpauseWithRetryThrowException_ShouldThrowUnpauseFailedException() + throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + int pauseDuration = 1; + Instant startTime = Instant.now().minus(5, SECONDS); + Instant endTime = Instant.now().plus(5, SECONDS); + + Pauser pauser = spy(new Pauser(namespace, helmReleaseName)); + PausedDuration pausedDuration = new PausedDuration(startTime, endTime); + + doReturn(targetBeforePause).doReturn(targetAfterPause).when(pauser).getTarget(); + doReturn(requestCoordinator).when(pauser).getRequestCoordinator(targetBeforePause); + doReturn(pausedDuration).when(pauser).pauseInternal(any(), anyInt(), anyLong()); + doThrow(RuntimeException.class) + .when(pauser) + .unpauseWithRetry(requestCoordinator, MAX_UNPAUSE_RETRY_COUNT); + doThrow(RuntimeException.class).when(pauser).targetStatusEquals(any(), any()); + + // Act & Assert + UnpauseFailedException thrown = + assertThrows(UnpauseFailedException.class, () -> pauser.pause(pauseDuration, null)); + assertEquals(UNPAUSE_ERROR_MESSAGE, thrown.getMessage()); + assertEquals(StatusCheckFailedException.class, thrown.getSuppressed()[0].getClass()); + } + + @Test + void pause_WhenUnpauseFailedAndTargetPodStatusChanged_ShouldThrowUnpauseFailedException() + throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + int pauseDuration = 1; + Instant startTime = Instant.now().minus(5, SECONDS); + Instant endTime = Instant.now().plus(5, SECONDS); + + Pauser pauser = spy(new Pauser(namespace, helmReleaseName)); + PausedDuration pausedDuration = new PausedDuration(startTime, endTime); + + doReturn(targetBeforePause).doReturn(targetAfterPause).when(pauser).getTarget(); + doReturn(requestCoordinator).when(pauser).getRequestCoordinator(targetBeforePause); + doReturn(pausedDuration).when(pauser).pauseInternal(any(), anyInt(), anyLong()); + doThrow(RuntimeException.class) + .when(pauser) + .unpauseWithRetry(requestCoordinator, MAX_UNPAUSE_RETRY_COUNT); + doReturn(new StatusUnmatchedException(STATUS_UNMATCHED_ERROR_MESSAGE)) + .when(pauser) + .targetStatusEquals(any(), any()); + + // Act & Assert + UnpauseFailedException thrown = + assertThrows(UnpauseFailedException.class, () -> pauser.pause(pauseDuration, null)); + assertEquals(UNPAUSE_ERROR_MESSAGE, thrown.getMessage()); + assertEquals(StatusUnmatchedException.class, thrown.getSuppressed()[0].getClass()); + } + + @Test + void pause_WhenPauseAndUnpauseAndTargetPodStatusChanged_ShouldThrowUnpauseFailedException() + throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + int pauseDuration = 1; + Pauser pauser = spy(new Pauser(namespace, helmReleaseName)); + doReturn(targetBeforePause).doReturn(targetAfterPause).when(pauser).getTarget(); + doReturn(requestCoordinator).when(pauser).getRequestCoordinator(targetBeforePause); + doThrow(RuntimeException.class) + .when(pauser) + .pauseInternal(requestCoordinator, pauseDuration, null); + doThrow(RuntimeException.class) + .when(pauser) + .unpauseWithRetry(requestCoordinator, MAX_UNPAUSE_RETRY_COUNT); + doReturn(new StatusUnmatchedException(STATUS_UNMATCHED_ERROR_MESSAGE)) + .when(pauser) + .targetStatusEquals(any(), any()); + + // Act & Assert + UnpauseFailedException thrown = + assertThrows(UnpauseFailedException.class, () -> pauser.pause(pauseDuration, null)); + assertEquals(UNPAUSE_ERROR_MESSAGE, thrown.getMessage()); + } + + @Test + void pause_WhenPauseFailedAndTargetPodStatusChanged_ShouldThrowPauseFailedException() + throws PauserException { // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + int pauseDuration = 1; + Pauser pauser = spy(new Pauser(namespace, helmReleaseName)); + doReturn(targetBeforePause).doReturn(targetAfterPause).when(pauser).getTarget(); + doReturn(requestCoordinator).when(pauser).getRequestCoordinator(targetBeforePause); + doThrow(RuntimeException.class) + .when(pauser) + .pauseInternal(requestCoordinator, pauseDuration, null); + doNothing().when(pauser).unpauseWithRetry(any(), anyInt()); + doReturn(new StatusUnmatchedException(STATUS_UNMATCHED_ERROR_MESSAGE)) + .when(pauser) + .targetStatusEquals(any(), any()); + + // Act & Assert + PauseFailedException thrown = + assertThrows(PauseFailedException.class, () -> pauser.pause(pauseDuration, null)); + assertEquals(PAUSE_ERROR_MESSAGE, thrown.getMessage()); + } + } + + @Nested + class UnpauseWithRetry { + @Test + void unpauseWithRetry_WhenUnpauseSucceeded_ReturnWithoutException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + doNothing().when(requestCoordinator).unpause(); + + // Act & Assert + assertDoesNotThrow( + () -> pauser.unpauseWithRetry(requestCoordinator, MAX_UNPAUSE_RETRY_COUNT)); + } + + @Test + void unpauseWithRetry_WhenExceptionOccur_ShouldRetryThreeTimes() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + doThrow(RuntimeException.class).when(requestCoordinator).unpause(); + + // Act & Assert + RuntimeException thrown = + assertThrows( + RuntimeException.class, + () -> pauser.unpauseWithRetry(requestCoordinator, MAX_UNPAUSE_RETRY_COUNT)); + verify(requestCoordinator, times(MAX_UNPAUSE_RETRY_COUNT)).unpause(); + } + } + + @Nested + class buildException { + @Test + void buildException_00000_ReturnNull() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + UnpauseFailedException unpauseFailedException = null; + PauseFailedException pauseFailedException = null; + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = null; + StatusCheckFailedException statusCheckFailedException = null; + StatusUnmatchedException statusUnmatchedException = null; + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertNull(actual); + } + + @Test + void buildException_00001_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = null; + PauseFailedException pauseFailedException = null; + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = null; + StatusCheckFailedException statusCheckFailedException = null; + StatusUnmatchedException statusUnmatchedException = + new StatusUnmatchedException(dummyMessage); + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(StatusUnmatchedException.class, actual.getClass()); + } + + @Test + void buildException_00010_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = null; + PauseFailedException pauseFailedException = null; + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = null; + StatusCheckFailedException statusCheckFailedException = + new StatusCheckFailedException(dummyMessage); + StatusUnmatchedException statusUnmatchedException = null; + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(StatusCheckFailedException.class, actual.getClass()); + } + + @Test + void buildException_00011_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = null; + PauseFailedException pauseFailedException = null; + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = null; + StatusCheckFailedException statusCheckFailedException = + new StatusCheckFailedException(dummyMessage); + StatusUnmatchedException statusUnmatchedException = + new StatusUnmatchedException(dummyMessage); + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(StatusCheckFailedException.class, actual.getClass()); + assertEquals(StatusUnmatchedException.class, actual.getSuppressed()[0].getClass()); + } + + @Test + void buildException_00100_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = null; + PauseFailedException pauseFailedException = null; + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = + new GetTargetAfterPauseFailedException(dummyMessage); + StatusCheckFailedException statusCheckFailedException = null; + StatusUnmatchedException statusUnmatchedException = null; + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(GetTargetAfterPauseFailedException.class, actual.getClass()); + } + + @Test + void buildException_00110_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = null; + PauseFailedException pauseFailedException = null; + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = + new GetTargetAfterPauseFailedException(dummyMessage); + StatusCheckFailedException statusCheckFailedException = + new StatusCheckFailedException(dummyMessage); + StatusUnmatchedException statusUnmatchedException = null; + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(GetTargetAfterPauseFailedException.class, actual.getClass()); + assertEquals(StatusCheckFailedException.class, actual.getSuppressed()[0].getClass()); + } + + @Test + void buildException_00101_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = null; + PauseFailedException pauseFailedException = null; + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = + new GetTargetAfterPauseFailedException(dummyMessage); + StatusCheckFailedException statusCheckFailedException = null; + StatusUnmatchedException statusUnmatchedException = + new StatusUnmatchedException(dummyMessage); + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(GetTargetAfterPauseFailedException.class, actual.getClass()); + assertEquals(StatusUnmatchedException.class, actual.getSuppressed()[0].getClass()); + } + + @Test + void buildException_00111_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = null; + PauseFailedException pauseFailedException = null; + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = + new GetTargetAfterPauseFailedException(dummyMessage); + StatusCheckFailedException statusCheckFailedException = + new StatusCheckFailedException(dummyMessage); + StatusUnmatchedException statusUnmatchedException = + new StatusUnmatchedException(dummyMessage); + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(GetTargetAfterPauseFailedException.class, actual.getClass()); + assertEquals(StatusCheckFailedException.class, actual.getSuppressed()[0].getClass()); + assertEquals(StatusUnmatchedException.class, actual.getSuppressed()[1].getClass()); + } + + @Test + void buildException_01000_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = null; + PauseFailedException pauseFailedException = new PauseFailedException(dummyMessage); + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = null; + StatusCheckFailedException statusCheckFailedException = null; + StatusUnmatchedException statusUnmatchedException = null; + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(PauseFailedException.class, actual.getClass()); + } + + @Test + void buildException_01001_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = null; + PauseFailedException pauseFailedException = new PauseFailedException(dummyMessage); + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = null; + StatusCheckFailedException statusCheckFailedException = null; + StatusUnmatchedException statusUnmatchedException = + new StatusUnmatchedException(dummyMessage); + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(PauseFailedException.class, actual.getClass()); + assertEquals(StatusUnmatchedException.class, actual.getSuppressed()[0].getClass()); + } + + @Test + void buildException_01010_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + UnpauseFailedException unpauseFailedException = null; + PauseFailedException pauseFailedException = new PauseFailedException(dummyMessage); + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = null; + StatusCheckFailedException statusCheckFailedException = + new StatusCheckFailedException(dummyMessage); + StatusUnmatchedException statusUnmatchedException = null; + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(PauseFailedException.class, actual.getClass()); + assertEquals(StatusCheckFailedException.class, actual.getSuppressed()[0].getClass()); + } + + @Test + void buildException_01011_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = null; + PauseFailedException pauseFailedException = new PauseFailedException(dummyMessage); + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = null; + StatusCheckFailedException statusCheckFailedException = + new StatusCheckFailedException(dummyMessage); + StatusUnmatchedException statusUnmatchedException = + new StatusUnmatchedException(dummyMessage); + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(PauseFailedException.class, actual.getClass()); + assertEquals(StatusCheckFailedException.class, actual.getSuppressed()[0].getClass()); + assertEquals(StatusUnmatchedException.class, actual.getSuppressed()[1].getClass()); + } + + @Test + void buildException_01100_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = null; + PauseFailedException pauseFailedException = new PauseFailedException(dummyMessage); + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = + new GetTargetAfterPauseFailedException(dummyMessage); + StatusCheckFailedException statusCheckFailedException = null; + StatusUnmatchedException statusUnmatchedException = null; + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(PauseFailedException.class, actual.getClass()); + assertEquals(GetTargetAfterPauseFailedException.class, actual.getSuppressed()[0].getClass()); + } + + @Test + void buildException_01101_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = null; + PauseFailedException pauseFailedException = new PauseFailedException(dummyMessage); + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = + new GetTargetAfterPauseFailedException(dummyMessage); + StatusCheckFailedException statusCheckFailedException = null; + StatusUnmatchedException statusUnmatchedException = + new StatusUnmatchedException(dummyMessage); + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(PauseFailedException.class, actual.getClass()); + assertEquals(GetTargetAfterPauseFailedException.class, actual.getSuppressed()[0].getClass()); + assertEquals(StatusUnmatchedException.class, actual.getSuppressed()[1].getClass()); + } + + @Test + void buildException_01110_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = null; + PauseFailedException pauseFailedException = new PauseFailedException(dummyMessage); + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = + new GetTargetAfterPauseFailedException(dummyMessage); + StatusCheckFailedException statusCheckFailedException = + new StatusCheckFailedException(dummyMessage); + StatusUnmatchedException statusUnmatchedException = null; + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(PauseFailedException.class, actual.getClass()); + assertEquals(GetTargetAfterPauseFailedException.class, actual.getSuppressed()[0].getClass()); + assertEquals(StatusCheckFailedException.class, actual.getSuppressed()[1].getClass()); + } + + @Test + void buildException_01111_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = null; + PauseFailedException pauseFailedException = new PauseFailedException(dummyMessage); + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = + new GetTargetAfterPauseFailedException(dummyMessage); + StatusCheckFailedException statusCheckFailedException = + new StatusCheckFailedException(dummyMessage); + StatusUnmatchedException statusUnmatchedException = + new StatusUnmatchedException(dummyMessage); + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(PauseFailedException.class, actual.getClass()); + assertEquals(GetTargetAfterPauseFailedException.class, actual.getSuppressed()[0].getClass()); + assertEquals(StatusCheckFailedException.class, actual.getSuppressed()[1].getClass()); + assertEquals(StatusUnmatchedException.class, actual.getSuppressed()[2].getClass()); + } + + @Test + void buildException_10000_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + UnpauseFailedException unpauseFailedException = new UnpauseFailedException(dummyMessage); + PauseFailedException pauseFailedException = null; + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = null; + StatusCheckFailedException statusCheckFailedException = null; + StatusUnmatchedException statusUnmatchedException = null; + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(UnpauseFailedException.class, actual.getClass()); + } + + @Test + void buildException_10001_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = new UnpauseFailedException(dummyMessage); + PauseFailedException pauseFailedException = null; + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = null; + StatusCheckFailedException statusCheckFailedException = null; + StatusUnmatchedException statusUnmatchedException = + new StatusUnmatchedException(dummyMessage); + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(UnpauseFailedException.class, actual.getClass()); + assertEquals(StatusUnmatchedException.class, actual.getSuppressed()[0].getClass()); + } + + @Test + void buildException_10010_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = new UnpauseFailedException(dummyMessage); + PauseFailedException pauseFailedException = null; + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = null; + StatusCheckFailedException statusCheckFailedException = + new StatusCheckFailedException(dummyMessage); + StatusUnmatchedException statusUnmatchedException = null; + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(UnpauseFailedException.class, actual.getClass()); + assertEquals(StatusCheckFailedException.class, actual.getSuppressed()[0].getClass()); + } + + @Test + void buildException_10011_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = new UnpauseFailedException(dummyMessage); + PauseFailedException pauseFailedException = null; + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = null; + StatusCheckFailedException statusCheckFailedException = + new StatusCheckFailedException(dummyMessage); + StatusUnmatchedException statusUnmatchedException = + new StatusUnmatchedException(dummyMessage); + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(UnpauseFailedException.class, actual.getClass()); + assertEquals(StatusCheckFailedException.class, actual.getSuppressed()[0].getClass()); + assertEquals(StatusUnmatchedException.class, actual.getSuppressed()[1].getClass()); + } + + @Test + void buildException_10100_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = new UnpauseFailedException(dummyMessage); + PauseFailedException pauseFailedException = null; + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = + new GetTargetAfterPauseFailedException(dummyMessage); + StatusCheckFailedException statusCheckFailedException = null; + StatusUnmatchedException statusUnmatchedException = null; + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(UnpauseFailedException.class, actual.getClass()); + assertEquals(GetTargetAfterPauseFailedException.class, actual.getSuppressed()[0].getClass()); + } + + @Test + void buildException_10101_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = new UnpauseFailedException(dummyMessage); + PauseFailedException pauseFailedException = null; + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = + new GetTargetAfterPauseFailedException(dummyMessage); + StatusCheckFailedException statusCheckFailedException = null; + StatusUnmatchedException statusUnmatchedException = + new StatusUnmatchedException(dummyMessage); + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(UnpauseFailedException.class, actual.getClass()); + assertEquals(GetTargetAfterPauseFailedException.class, actual.getSuppressed()[0].getClass()); + assertEquals(StatusUnmatchedException.class, actual.getSuppressed()[1].getClass()); + } + + @Test + void buildException_10110_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = new UnpauseFailedException(dummyMessage); + PauseFailedException pauseFailedException = null; + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = + new GetTargetAfterPauseFailedException(dummyMessage); + StatusCheckFailedException statusCheckFailedException = + new StatusCheckFailedException(dummyMessage); + StatusUnmatchedException statusUnmatchedException = null; + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(UnpauseFailedException.class, actual.getClass()); + assertEquals(GetTargetAfterPauseFailedException.class, actual.getSuppressed()[0].getClass()); + assertEquals(StatusCheckFailedException.class, actual.getSuppressed()[1].getClass()); + } + + @Test + void buildException_10111_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = new UnpauseFailedException(dummyMessage); + PauseFailedException pauseFailedException = null; + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = + new GetTargetAfterPauseFailedException(dummyMessage); + StatusCheckFailedException statusCheckFailedException = + new StatusCheckFailedException(dummyMessage); + StatusUnmatchedException statusUnmatchedException = + new StatusUnmatchedException(dummyMessage); + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(UnpauseFailedException.class, actual.getClass()); + assertEquals(GetTargetAfterPauseFailedException.class, actual.getSuppressed()[0].getClass()); + assertEquals(StatusCheckFailedException.class, actual.getSuppressed()[1].getClass()); + assertEquals(StatusUnmatchedException.class, actual.getSuppressed()[2].getClass()); + } + + @Test + void buildException_11000_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = new UnpauseFailedException(dummyMessage); + PauseFailedException pauseFailedException = new PauseFailedException(dummyMessage); + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = null; + StatusCheckFailedException statusCheckFailedException = null; + StatusUnmatchedException statusUnmatchedException = null; + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(UnpauseFailedException.class, actual.getClass()); + assertEquals(PauseFailedException.class, actual.getSuppressed()[0].getClass()); + } + + @Test + void buildException_11001_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = new UnpauseFailedException(dummyMessage); + PauseFailedException pauseFailedException = new PauseFailedException(dummyMessage); + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = null; + StatusCheckFailedException statusCheckFailedException = null; + StatusUnmatchedException statusUnmatchedException = + new StatusUnmatchedException(dummyMessage); + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(UnpauseFailedException.class, actual.getClass()); + assertEquals(PauseFailedException.class, actual.getSuppressed()[0].getClass()); + assertEquals(StatusUnmatchedException.class, actual.getSuppressed()[1].getClass()); + } + + @Test + void buildException_11010_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = new UnpauseFailedException(dummyMessage); + PauseFailedException pauseFailedException = new PauseFailedException(dummyMessage); + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = null; + StatusCheckFailedException statusCheckFailedException = + new StatusCheckFailedException(dummyMessage); + StatusUnmatchedException statusUnmatchedException = null; + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(UnpauseFailedException.class, actual.getClass()); + assertEquals(PauseFailedException.class, actual.getSuppressed()[0].getClass()); + assertEquals(StatusCheckFailedException.class, actual.getSuppressed()[1].getClass()); + } + + @Test + void buildException_11011_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = new UnpauseFailedException(dummyMessage); + PauseFailedException pauseFailedException = new PauseFailedException(dummyMessage); + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = null; + StatusCheckFailedException statusCheckFailedException = + new StatusCheckFailedException(dummyMessage); + StatusUnmatchedException statusUnmatchedException = + new StatusUnmatchedException(dummyMessage); + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(UnpauseFailedException.class, actual.getClass()); + assertEquals(PauseFailedException.class, actual.getSuppressed()[0].getClass()); + assertEquals(StatusCheckFailedException.class, actual.getSuppressed()[1].getClass()); + assertEquals(StatusUnmatchedException.class, actual.getSuppressed()[2].getClass()); + } + + @Test + void buildException_11100_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = new UnpauseFailedException(dummyMessage); + PauseFailedException pauseFailedException = new PauseFailedException(dummyMessage); + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = + new GetTargetAfterPauseFailedException(dummyMessage); + StatusCheckFailedException statusCheckFailedException = null; + StatusUnmatchedException statusUnmatchedException = null; + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(UnpauseFailedException.class, actual.getClass()); + assertEquals(PauseFailedException.class, actual.getSuppressed()[0].getClass()); + assertEquals(GetTargetAfterPauseFailedException.class, actual.getSuppressed()[1].getClass()); + } + + @Test + void buildException_11101_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = new UnpauseFailedException(dummyMessage); + PauseFailedException pauseFailedException = new PauseFailedException(dummyMessage); + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = + new GetTargetAfterPauseFailedException(dummyMessage); + StatusCheckFailedException statusCheckFailedException = null; + StatusUnmatchedException statusUnmatchedException = + new StatusUnmatchedException(dummyMessage); + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(UnpauseFailedException.class, actual.getClass()); + assertEquals(PauseFailedException.class, actual.getSuppressed()[0].getClass()); + assertEquals(GetTargetAfterPauseFailedException.class, actual.getSuppressed()[1].getClass()); + assertEquals(StatusUnmatchedException.class, actual.getSuppressed()[2].getClass()); + } + + @Test + void buildException_11110_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = new UnpauseFailedException(dummyMessage); + PauseFailedException pauseFailedException = new PauseFailedException(dummyMessage); + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = + new GetTargetAfterPauseFailedException(dummyMessage); + StatusCheckFailedException statusCheckFailedException = + new StatusCheckFailedException(dummyMessage); + StatusUnmatchedException statusUnmatchedException = null; + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(UnpauseFailedException.class, actual.getClass()); + assertEquals(PauseFailedException.class, actual.getSuppressed()[0].getClass()); + assertEquals(GetTargetAfterPauseFailedException.class, actual.getSuppressed()[1].getClass()); + assertEquals(StatusCheckFailedException.class, actual.getSuppressed()[2].getClass()); + } + + @Test + void buildException_11111_ThrowException() throws PauserException { + // Arrange + String namespace = "dummyNs"; + String helmReleaseName = "dummyRelease"; + Pauser pauser = new Pauser(namespace, helmReleaseName); + + String dummyMessage = "dummyMessage"; + + UnpauseFailedException unpauseFailedException = new UnpauseFailedException(dummyMessage); + PauseFailedException pauseFailedException = new PauseFailedException(dummyMessage); + GetTargetAfterPauseFailedException getTargetAfterPauseFailedException = + new GetTargetAfterPauseFailedException(dummyMessage); + StatusCheckFailedException statusCheckFailedException = + new StatusCheckFailedException(dummyMessage); + StatusUnmatchedException statusUnmatchedException = + new StatusUnmatchedException(dummyMessage); + + // Act + Exception actual = + pauser.buildException( + unpauseFailedException, + pauseFailedException, + getTargetAfterPauseFailedException, + statusCheckFailedException, + statusUnmatchedException); + + // Assert + assertEquals(UnpauseFailedException.class, actual.getClass()); + assertEquals(PauseFailedException.class, actual.getSuppressed()[0].getClass()); + assertEquals(GetTargetAfterPauseFailedException.class, actual.getSuppressed()[1].getClass()); + assertEquals(StatusCheckFailedException.class, actual.getSuppressed()[2].getClass()); + assertEquals(StatusUnmatchedException.class, actual.getSuppressed()[3].getClass()); + } + } +}