1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.adservices.exceptions; 18 19 import static android.adservices.exceptions.AdServicesNetworkException.ERROR_TOO_MANY_REQUESTS; 20 import static android.adservices.exceptions.RetryableAdServicesNetworkException.INVALID_ERROR_CODE_MESSAGE; 21 import static android.adservices.exceptions.RetryableAdServicesNetworkException.INVALID_RETRY_AFTER_MESSAGE; 22 23 import static com.google.common.truth.Truth.assertThat; 24 25 import static org.junit.Assert.assertThrows; 26 27 import static java.util.Locale.ENGLISH; 28 29 import com.android.adservices.common.AdServicesUnitTestCase; 30 31 import org.junit.Test; 32 33 import java.time.Duration; 34 import java.time.temporal.ChronoUnit; 35 36 // TODO(b/278016822): Move to CTS tests once public APIs are unhidden 37 public class RetryableAdServicesNetworkExceptionTest extends AdServicesUnitTestCase { 38 private static final int VALID_ERROR_CODE = ERROR_TOO_MANY_REQUESTS; 39 private static final int INVALID_ERROR_CODE = 1000; 40 private static final Duration VALID_RETRY_AFTER = Duration.of(1000, ChronoUnit.MILLIS); 41 private static final Duration UNSET_RETRY_AFTER = Duration.ZERO; 42 private static final Duration NEGATIVE_RETRY_AFTER = Duration.of(-1, ChronoUnit.MILLIS); 43 private static final long MIN_RETRY_AFTER_DURATION_MS = 30 * 1000; 44 private static final long MAX_RETRY_AFTER_DURATION_MS = 90 * 1000; 45 46 @Test testExceptionWithErrorCodeAndRetryAfter_valid()47 public void testExceptionWithErrorCodeAndRetryAfter_valid() { 48 RetryableAdServicesNetworkException exception = 49 new RetryableAdServicesNetworkException(VALID_ERROR_CODE, VALID_RETRY_AFTER); 50 51 expect.that(exception.getErrorCode()).isEqualTo(VALID_ERROR_CODE); 52 expect.that(exception.getRetryAfter()).isEqualTo(VALID_RETRY_AFTER); 53 expect.that(exception).hasMessageThat().isNull(); 54 expect.that(exception.toString()) 55 .isEqualTo( 56 getHumanReadableRetryableNetworkException( 57 VALID_ERROR_CODE, VALID_RETRY_AFTER)); 58 } 59 60 @Test testExceptionWithErrorCodeAndRetryAfter_errorCodeInvalid()61 public void testExceptionWithErrorCodeAndRetryAfter_errorCodeInvalid() { 62 Exception exception = 63 assertThrows( 64 IllegalArgumentException.class, 65 () -> 66 new RetryableAdServicesNetworkException( 67 INVALID_ERROR_CODE, VALID_RETRY_AFTER)); 68 assertThat(exception).hasMessageThat().isEqualTo(INVALID_ERROR_CODE_MESSAGE); 69 } 70 71 @Test testExceptionWithErrorCodeAndRetryAfter_retryAfterUnset()72 public void testExceptionWithErrorCodeAndRetryAfter_retryAfterUnset() { 73 Exception exception = 74 assertThrows( 75 IllegalArgumentException.class, 76 () -> 77 new RetryableAdServicesNetworkException( 78 VALID_ERROR_CODE, UNSET_RETRY_AFTER)); 79 assertThat(exception).hasMessageThat().isEqualTo(INVALID_RETRY_AFTER_MESSAGE); 80 } 81 82 @Test testExceptionWithErrorCodeAndRetryAfter_retryAfterNegative()83 public void testExceptionWithErrorCodeAndRetryAfter_retryAfterNegative() { 84 Exception exception = 85 assertThrows( 86 IllegalArgumentException.class, 87 () -> 88 new RetryableAdServicesNetworkException( 89 VALID_ERROR_CODE, NEGATIVE_RETRY_AFTER)); 90 assertThat(exception).hasMessageThat().isEqualTo(INVALID_RETRY_AFTER_MESSAGE); 91 } 92 93 @Test testSetRetryAfterToValidDurationStaysSame()94 public void testSetRetryAfterToValidDurationStaysSame() { 95 long validMs = 60 * 1000; 96 RetryableAdServicesNetworkException exception = 97 new RetryableAdServicesNetworkException( 98 VALID_ERROR_CODE, Duration.ofMillis(validMs)); 99 100 exception.setRetryAfterToValidDuration( 101 MIN_RETRY_AFTER_DURATION_MS, MAX_RETRY_AFTER_DURATION_MS); 102 assertThat(exception.getRetryAfter().toMillis()).isEqualTo(validMs); 103 } 104 105 @Test testSetRetryAfterToValidDurationGetsCappedToDefault()106 public void testSetRetryAfterToValidDurationGetsCappedToDefault() { 107 long tooSmallMs = 20 * 1000; 108 RetryableAdServicesNetworkException exception = 109 new RetryableAdServicesNetworkException( 110 VALID_ERROR_CODE, Duration.ofMillis(tooSmallMs)); 111 112 exception.setRetryAfterToValidDuration( 113 MIN_RETRY_AFTER_DURATION_MS, MAX_RETRY_AFTER_DURATION_MS); 114 assertThat(exception.getRetryAfter().toMillis()).isEqualTo(MIN_RETRY_AFTER_DURATION_MS); 115 } 116 117 @Test testSetRetryAfterToValidDurationGetsCappedToMax()118 public void testSetRetryAfterToValidDurationGetsCappedToMax() { 119 long tooBigSecondsMs = 100 * 1000; 120 RetryableAdServicesNetworkException exception = 121 new RetryableAdServicesNetworkException( 122 VALID_ERROR_CODE, Duration.ofMillis(tooBigSecondsMs)); 123 124 exception.setRetryAfterToValidDuration( 125 MIN_RETRY_AFTER_DURATION_MS, MAX_RETRY_AFTER_DURATION_MS); 126 assertThat(exception.getRetryAfter().toMillis()).isEqualTo(MAX_RETRY_AFTER_DURATION_MS); 127 } 128 getHumanReadableRetryableNetworkException(int errorCode, Duration retryAfter)129 private String getHumanReadableRetryableNetworkException(int errorCode, Duration retryAfter) { 130 return String.format( 131 ENGLISH, 132 "%s: {Error code: %s, Retry after: %sms}", 133 RetryableAdServicesNetworkException.class.getCanonicalName(), 134 errorCode, 135 retryAfter.toMillis()); 136 } 137 } 138