/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.beam.sdk.io.kinesis; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.reset; import com.amazonaws.AmazonServiceException; import com.amazonaws.AmazonServiceException.ErrorType; import com.amazonaws.services.kinesis.AmazonKinesis; import com.amazonaws.services.kinesis.model.DescribeStreamResult; import com.amazonaws.services.kinesis.model.ExpiredIteratorException; import com.amazonaws.services.kinesis.model.GetShardIteratorRequest; import com.amazonaws.services.kinesis.model.GetShardIteratorResult; import com.amazonaws.services.kinesis.model.LimitExceededException; import com.amazonaws.services.kinesis.model.ProvisionedThroughputExceededException; import com.amazonaws.services.kinesis.model.Shard; import com.amazonaws.services.kinesis.model.ShardIteratorType; import com.amazonaws.services.kinesis.model.StreamDescription; import java.util.List; import org.joda.time.Instant; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; /*** */ @RunWith(MockitoJUnitRunner.class) public class SimplifiedKinesisClientTest { private static final String STREAM = "stream"; private static final String SHARD_1 = "shard-01"; private static final String SHARD_2 = "shard-02"; private static final String SHARD_3 = "shard-03"; private static final String SHARD_ITERATOR = "iterator"; private static final String SEQUENCE_NUMBER = "abc123"; @Mock private AmazonKinesis kinesis; @InjectMocks private SimplifiedKinesisClient underTest; @Test public void shouldReturnIteratorStartingWithSequenceNumber() throws Exception { given(kinesis.getShardIterator(new GetShardIteratorRequest() .withStreamName(STREAM) .withShardId(SHARD_1) .withShardIteratorType(ShardIteratorType.AT_SEQUENCE_NUMBER) .withStartingSequenceNumber(SEQUENCE_NUMBER) )).willReturn(new GetShardIteratorResult() .withShardIterator(SHARD_ITERATOR)); String stream = underTest.getShardIterator(STREAM, SHARD_1, ShardIteratorType.AT_SEQUENCE_NUMBER, SEQUENCE_NUMBER, null); assertThat(stream).isEqualTo(SHARD_ITERATOR); } @Test public void shouldReturnIteratorStartingWithTimestamp() throws Exception { Instant timestamp = Instant.now(); given(kinesis.getShardIterator(new GetShardIteratorRequest() .withStreamName(STREAM) .withShardId(SHARD_1) .withShardIteratorType(ShardIteratorType.AT_SEQUENCE_NUMBER) .withTimestamp(timestamp.toDate()) )).willReturn(new GetShardIteratorResult() .withShardIterator(SHARD_ITERATOR)); String stream = underTest.getShardIterator(STREAM, SHARD_1, ShardIteratorType.AT_SEQUENCE_NUMBER, null, timestamp); assertThat(stream).isEqualTo(SHARD_ITERATOR); } @Test public void shouldHandleExpiredIterationExceptionForGetShardIterator() { shouldHandleGetShardIteratorError(new ExpiredIteratorException(""), ExpiredIteratorException.class); } @Test public void shouldHandleLimitExceededExceptionForGetShardIterator() { shouldHandleGetShardIteratorError(new LimitExceededException(""), TransientKinesisException.class); } @Test public void shouldHandleProvisionedThroughputExceededExceptionForGetShardIterator() { shouldHandleGetShardIteratorError(new ProvisionedThroughputExceededException(""), TransientKinesisException.class); } @Test public void shouldHandleServiceErrorForGetShardIterator() { shouldHandleGetShardIteratorError(newAmazonServiceException(ErrorType.Service), TransientKinesisException.class); } @Test public void shouldHandleClientErrorForGetShardIterator() { shouldHandleGetShardIteratorError(newAmazonServiceException(ErrorType.Client), RuntimeException.class); } @Test public void shouldHandleUnexpectedExceptionForGetShardIterator() { shouldHandleGetShardIteratorError(new NullPointerException(), RuntimeException.class); } private void shouldHandleGetShardIteratorError( Exception thrownException, Class<? extends Exception> expectedExceptionClass) { GetShardIteratorRequest request = new GetShardIteratorRequest() .withStreamName(STREAM) .withShardId(SHARD_1) .withShardIteratorType(ShardIteratorType.LATEST); given(kinesis.getShardIterator(request)).willThrow(thrownException); try { underTest.getShardIterator(STREAM, SHARD_1, ShardIteratorType.LATEST, null, null); failBecauseExceptionWasNotThrown(expectedExceptionClass); } catch (Exception e) { assertThat(e).isExactlyInstanceOf(expectedExceptionClass); } finally { reset(kinesis); } } @Test public void shouldListAllShards() throws Exception { Shard shard1 = new Shard().withShardId(SHARD_1); Shard shard2 = new Shard().withShardId(SHARD_2); Shard shard3 = new Shard().withShardId(SHARD_3); given(kinesis.describeStream(STREAM, null)).willReturn(new DescribeStreamResult() .withStreamDescription(new StreamDescription() .withShards(shard1, shard2) .withHasMoreShards(true))); given(kinesis.describeStream(STREAM, SHARD_2)).willReturn(new DescribeStreamResult() .withStreamDescription(new StreamDescription() .withShards(shard3) .withHasMoreShards(false))); List<Shard> shards = underTest.listShards(STREAM); assertThat(shards).containsOnly(shard1, shard2, shard3); } @Test public void shouldHandleExpiredIterationExceptionForShardListing() { shouldHandleShardListingError(new ExpiredIteratorException(""), ExpiredIteratorException.class); } @Test public void shouldHandleLimitExceededExceptionForShardListing() { shouldHandleShardListingError(new LimitExceededException(""), TransientKinesisException.class); } @Test public void shouldHandleProvisionedThroughputExceededExceptionForShardListing() { shouldHandleShardListingError(new ProvisionedThroughputExceededException(""), TransientKinesisException.class); } @Test public void shouldHandleServiceErrorForShardListing() { shouldHandleShardListingError(newAmazonServiceException(ErrorType.Service), TransientKinesisException.class); } @Test public void shouldHandleClientErrorForShardListing() { shouldHandleShardListingError(newAmazonServiceException(ErrorType.Client), RuntimeException.class); } @Test public void shouldHandleUnexpectedExceptionForShardListing() { shouldHandleShardListingError(new NullPointerException(), RuntimeException.class); } private void shouldHandleShardListingError( Exception thrownException, Class<? extends Exception> expectedExceptionClass) { given(kinesis.describeStream(STREAM, null)).willThrow(thrownException); try { underTest.listShards(STREAM); failBecauseExceptionWasNotThrown(expectedExceptionClass); } catch (Exception e) { assertThat(e).isExactlyInstanceOf(expectedExceptionClass); } finally { reset(kinesis); } } private AmazonServiceException newAmazonServiceException(ErrorType errorType) { AmazonServiceException exception = new AmazonServiceException(""); exception.setErrorType(errorType); return exception; } }