88import com .linecorp .armeria .client .retry .Backoff ;
99import io .micrometer .core .instrument .Counter ;
1010import io .micrometer .core .instrument .DistributionSummary ;
11+ import io .micrometer .core .instrument .Meter ;
12+ import io .micrometer .core .instrument .Tags ;
1113import io .micrometer .core .instrument .Timer ;
14+ import io .micrometer .core .instrument .noop .NoopTimer ;
1215import org .junit .jupiter .api .BeforeEach ;
1316import org .junit .jupiter .api .Disabled ;
1417import org .junit .jupiter .api .Test ;
1821import org .mockito .ArgumentCaptor ;
1922import org .mockito .Mock ;
2023import org .mockito .junit .jupiter .MockitoExtension ;
24+ import org .opensearch .dataprepper .model .buffer .Buffer ;
2125import org .opensearch .dataprepper .core .acknowledgements .DefaultAcknowledgementSetManager ;
2226import org .opensearch .dataprepper .metrics .PluginMetrics ;
2327import org .opensearch .dataprepper .model .acknowledgements .AcknowledgementSet ;
2428import org .opensearch .dataprepper .model .acknowledgements .AcknowledgementSetManager ;
2529import org .opensearch .dataprepper .model .event .Event ;
30+ import org .opensearch .dataprepper .model .record .Record ;
2631import org .opensearch .dataprepper .model .event .JacksonEvent ;
2732import org .opensearch .dataprepper .plugins .source .s3 .configuration .NotificationSourceOption ;
2833import org .opensearch .dataprepper .plugins .source .s3 .configuration .OnErrorOption ;
2934import org .opensearch .dataprepper .plugins .source .s3 .configuration .SqsOptions ;
35+ import org .opensearch .dataprepper .plugins .source .s3 .ownership .BucketOwnerProvider ;
3036import org .opensearch .dataprepper .plugins .source .sqs .common .SqsBackoff ;
37+ import software .amazon .awssdk .services .s3 .model .DeleteObjectRequest ;
3138import software .amazon .awssdk .regions .Region ;
3239import software .amazon .awssdk .services .s3 .S3Client ;
3340import software .amazon .awssdk .services .sqs .SqsClient ;
3441
3542import java .io .IOException ;
3643import java .time .Duration ;
3744import java .time .Instant ;
45+ import java .util .Collection ;
3846import java .util .List ;
3947import java .util .ArrayList ;
48+ import java .util .Optional ;
4049import java .util .UUID ;
4150import java .util .concurrent .ScheduledExecutorService ;
4251import java .util .concurrent .Executors ;
4352import java .util .concurrent .atomic .AtomicBoolean ;
4453
54+ import static org .mockito .ArgumentMatchers .anyCollection ;
55+ import static org .mockito .ArgumentMatchers .anyInt ;
4556import static org .awaitility .Awaitility .await ;
4657import static org .hamcrest .CoreMatchers .equalTo ;
4758import static org .hamcrest .MatcherAssert .assertThat ;
6071
6172@ ExtendWith (MockitoExtension .class )
6273class SqsWorkerIT {
74+ private static final int TIMEOUT_IN_MILLIS = 200 ;
6375 private SqsClient sqsClient ;
6476 @ Mock
6577 private S3Service s3Service ;
6678 @ Mock
6779 private SqsOptions sqsOptions ;
80+ @ Mock
81+ DistributionSummary distributionSummary ;
82+
6883 private S3SourceConfig s3SourceConfig ;
6984 private PluginMetrics pluginMetrics ;
7085 private S3ObjectGenerator s3ObjectGenerator ;
86+ private S3ObjectPluginMetrics s3ObjectPluginMetrics ;
7187 private String bucket ;
7288 private Backoff backoff ;
7389 private AcknowledgementSetManager acknowledgementSetManager ;
@@ -79,11 +95,14 @@ class SqsWorkerIT {
7995 private AtomicBoolean ready = new AtomicBoolean (false );
8096 private int numEventsAdded ;
8197 private List <Event > events ;
98+ private int recordsReceived ;
99+ S3Client s3Client ;
82100
83101 @ BeforeEach
84102 void setUp () {
103+ distributionSummary = mock (DistributionSummary .class );
85104 acknowledgementSetManager = mock (AcknowledgementSetManager .class );
86- final S3Client s3Client = S3Client .builder ()
105+ s3Client = S3Client .builder ()
87106 .region (Region .of (System .getProperty ("tests.s3source.region" )))
88107 .build ();
89108 bucket = System .getProperty ("tests.s3source.bucket" );
@@ -101,7 +120,6 @@ void setUp() {
101120
102121 pluginMetrics = mock (PluginMetrics .class );
103122 final Counter sharedCounter = mock (Counter .class );
104- final DistributionSummary distributionSummary = mock (DistributionSummary .class );
105123 final Timer sqsMessageDelayTimer = mock (Timer .class );
106124
107125 lenient ().when (pluginMetrics .counter (anyString ())).thenReturn (sharedCounter );
@@ -148,6 +166,66 @@ void processSqsMessages_should_return_at_least_one_message(final int numberOfObj
148166 assertThat (sqsMessagesProcessed , lessThanOrEqualTo (numberOfObjectsToWrite ));
149167 }
150168
169+ @ ParameterizedTest
170+ @ ValueSource (ints = {10 })
171+ void processSqsMessages_with_metadataOnly_option (final int numberOfObjectsToWrite ) throws Exception {
172+ final Counter counter = mock (Counter .class );
173+ s3ObjectPluginMetrics = mock (S3ObjectPluginMetrics .class );
174+ final Timer timer = new NoopTimer (new Meter .Id ("test" , Tags .empty (), null , null , Meter .Type .TIMER ));
175+ when (sqsOptions .getMaxReceiveAttempts ()).thenReturn (5 );
176+ lenient ().when (s3ObjectPluginMetrics .getS3ObjectsSucceededCounter ()).thenReturn (counter );
177+ lenient ().when (s3ObjectPluginMetrics .getS3ObjectSizeSummary ()).thenReturn (distributionSummary );
178+ lenient ().when (s3ObjectPluginMetrics .getS3ObjectEventsSummary ()).thenReturn (distributionSummary );
179+ lenient ().when (s3ObjectPluginMetrics .getS3ObjectReadTimer ()).thenReturn (timer );
180+ Buffer <Record <Event >> buffer = mock (Buffer .class );
181+ when (s3SourceConfig .getAcknowledgements ()).thenReturn (true );
182+ final Counter receivedCounter = mock (Counter .class );
183+ final Counter deletedCounter = mock (Counter .class );
184+ final Counter ackCallbackCounter = mock (Counter .class );
185+ lenient ().doAnswer (a -> {
186+ final Collection <Record <Event >> recordsCollection = a .getArgument (0 );
187+ recordsReceived += recordsCollection .size ();
188+ return null ;
189+ }).when (buffer ).writeAll (anyCollection (), anyInt ());
190+ when (pluginMetrics .counter (SqsWorker .SQS_MESSAGES_RECEIVED_METRIC_NAME )).thenReturn (receivedCounter );
191+ when (pluginMetrics .counter (SqsWorker .SQS_MESSAGES_DELETED_METRIC_NAME )).thenReturn (deletedCounter );
192+ when (pluginMetrics .counter (SqsWorker .ACKNOWLEDGEMENT_SET_CALLACK_METRIC_NAME )).thenReturn (ackCallbackCounter );
193+ lenient ().doAnswer ((val ) -> {
194+ receivedCount += (double )val .getArgument (0 );
195+ return null ;
196+ }).when (receivedCounter ).increment (any (Double .class ));
197+ lenient ().doAnswer ((val ) -> {
198+ ackCallbackCount += 1 ;
199+ return null ;
200+ }).when (ackCallbackCounter ).increment ();
201+
202+ ScheduledExecutorService executor = Executors .newScheduledThreadPool (2 );
203+ acknowledgementSetManager = new DefaultAcknowledgementSetManager (executor );
204+ BucketOwnerProvider bucketOwnerProvider = b -> Optional .empty ();
205+ final S3ObjectRequest s3ObjectRequest = new S3ObjectRequest .Builder (buffer , 1 ,
206+ Duration .ofMillis (TIMEOUT_IN_MILLIS ), s3ObjectPluginMetrics )
207+ .bucketOwnerProvider (bucketOwnerProvider )
208+ .s3Client (s3Client )
209+ .build ();
210+ S3Service s3MetadataService = new S3Service (new S3ObjectMetadataWorker (s3ObjectRequest ));
211+ final SqsWorker objectUnderTest = new SqsWorker (acknowledgementSetManager , sqsClient , s3MetadataService , s3SourceConfig , pluginMetrics , backoff );
212+ List <String > keyList = writeToS3 (numberOfObjectsToWrite );
213+ await ().atMost (Duration .ofSeconds (60 ))
214+ .untilAsserted (() -> {
215+ final int sqsMessagesProcessed = objectUnderTest .processSqsMessages ();
216+ assertThat (recordsReceived , equalTo (numberOfObjectsToWrite ));
217+ });
218+
219+ assertThat (deletedCount , equalTo ((double )0.0 ));
220+ // Delete the objects created
221+ for (String key : keyList ) {
222+ final DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest .builder ()
223+ .bucket (bucket )
224+ .key (key ).build ();
225+ s3Client .deleteObject (deleteObjectRequest );
226+ }
227+ }
228+
151229 @ ParameterizedTest
152230 @ ValueSource (ints = {1 })
153231 void processSqsMessages_should_return_at_least_one_message_with_acks_with_callback_invoked_after_processS3Object_finishes (final int numberOfObjectsToWrite ) throws IOException , InterruptedException {
@@ -455,14 +533,17 @@ void processSqsMessages_should_return_zero_if_no_objects_are_written() {
455533 assertThat (sqsMessagesProcessed , equalTo (0 ));
456534 }
457535
458- private void writeToS3 (final int numberOfObjectsToWrite ) throws IOException {
536+ private List < String > writeToS3 (final int numberOfObjectsToWrite ) throws IOException {
459537 final int numberOfRecords = 100 ;
460538 final NewlineDelimitedRecordsGenerator newlineDelimitedRecordsGenerator = new NewlineDelimitedRecordsGenerator ();
539+ List <String > keyList = new ArrayList <>();
461540 for (int i = 0 ; i < numberOfObjectsToWrite ; i ++) {
462541 final String key = "s3 source/sqs/" + UUID .randomUUID () + "_" + Instant .now ().toString () + newlineDelimitedRecordsGenerator .getFileExtension ();
463542 // isCompressionEnabled is set to false since we test for compression in S3ObjectWorkerIT
464543 s3ObjectGenerator .write (numberOfRecords , key , newlineDelimitedRecordsGenerator , false );
544+ keyList .add (key );
465545 }
546+ return keyList ;
466547 }
467548
468549 private void clearSqsQueue () {
0 commit comments