11package org .opensearch .dataprepper .plugins .source .source_crawler .base ;
22
3+ import com .fasterxml .jackson .core .JsonProcessingException ;
4+ import com .fasterxml .jackson .databind .ObjectMapper ;
5+ import com .fasterxml .jackson .datatype .jsr310 .JavaTimeModule ;
36import org .junit .jupiter .api .BeforeEach ;
47import org .junit .jupiter .api .Test ;
58import org .junit .jupiter .api .extension .ExtendWith ;
1417import org .opensearch .dataprepper .model .record .Record ;
1518import org .opensearch .dataprepper .model .source .coordinator .enhanced .EnhancedSourceCoordinator ;
1619import org .opensearch .dataprepper .plugins .source .source_crawler .coordination .partition .LeaderPartition ;
20+ import org .opensearch .dataprepper .plugins .source .source_crawler .coordination .partition .SaasSourcePartition ;
21+ import org .opensearch .dataprepper .plugins .source .source_crawler .coordination .state .PaginationCrawlerWorkerProgressState ;
1722import org .opensearch .dataprepper .plugins .source .source_crawler .coordination .state .TokenPaginationCrawlerLeaderProgressState ;
1823import org .opensearch .dataprepper .plugins .source .source_crawler .model .ItemInfo ;
1924import org .opensearch .dataprepper .plugins .source .source_crawler .model .TestItemInfo ;
2934
3035import static org .junit .jupiter .api .Assertions .assertEquals ;
3136import static org .junit .jupiter .api .Assertions .assertThrows ;
37+ import static org .junit .jupiter .api .Assertions .fail ;
3238import static org .mockito .ArgumentMatchers .any ;
3339import static org .mockito .ArgumentMatchers .eq ;
3440import static org .mockito .Mockito .verify ;
3541import static org .mockito .Mockito .when ;
3642import static org .mockito .Mockito .doThrow ;
3743import static org .mockito .Mockito .never ;
44+ import static org .mockito .Mockito .doAnswer ;
3845import static org .mockito .internal .verification .VerificationModeFactory .times ;
3946
4047@ ExtendWith (MockitoExtension .class )
@@ -144,29 +151,79 @@ void testProgressStateUpdate() {
144151 }
145152
146153 @ Test
147- void testNegativeAcknowledgment () {
148- List <ItemInfo > items = createTestItems (BATCH_SIZE + 1 );
149- when (client .listItems (INITIAL_TOKEN )).thenReturn (items .iterator ());
150- when (acknowledgementSetManager .create (any (), eq (TEST_TIMEOUT )))
151- .thenReturn (acknowledgementSet );
154+ void testRetryPartitionStateSerializationAndDeserialization () {
155+ List <ItemInfo > batch = createTestItems ( 1 );
152156
153- ArgumentCaptor <Consumer <Boolean >> callbackCaptor = ArgumentCaptor .forClass (Consumer .class );
157+ // Mock client to return our test items
158+ when (client .listItems (INITIAL_TOKEN )).thenReturn (batch .iterator ());
154159
160+ // Capture the created partition
161+ ArgumentCaptor <SaasSourcePartition > partitionCaptor = ArgumentCaptor .forClass (SaasSourcePartition .class );
162+
163+ // Simulate negative acknowledgment
164+ when (acknowledgementSetManager .create (any (), any ())).thenAnswer (inv -> {
165+ Consumer <Boolean > callback = inv .getArgument (0 );
166+ callback .accept (false );
167+ return acknowledgementSet ;
168+ });
169+
170+ // Create retry partition
155171 crawler .setAcknowledgementsEnabled (true );
156172 crawler .crawl (leaderPartition , coordinator );
157173
158- verify (acknowledgementSetManager ).create (callbackCaptor .capture (), eq (TEST_TIMEOUT ));
174+ // Verify partition creation and get original state
175+ verify (coordinator ).createPartition (partitionCaptor .capture ());
176+ SaasSourcePartition originalPartition = partitionCaptor .getValue ();
177+ PaginationCrawlerWorkerProgressState originalState =
178+ (PaginationCrawlerWorkerProgressState ) originalPartition .getProgressState ().get ();
179+
180+ // Test serialization/deserialization
181+ ObjectMapper objectMapper = new ObjectMapper ()
182+ .registerModule (new JavaTimeModule ()); // For Instant serialization
183+
184+ try {
185+ // Serialize state
186+ String serializedState = objectMapper .writeValueAsString (originalState );
187+
188+ // Deserialize state
189+ PaginationCrawlerWorkerProgressState deserializedState =
190+ objectMapper .readValue (serializedState , PaginationCrawlerWorkerProgressState .class );
191+
192+ // Verify deserialized state matches original
193+ assertEquals (originalState .getItemIds (), deserializedState .getItemIds ());
194+ assertEquals (originalState .getKeyAttributes (), deserializedState .getKeyAttributes ());
195+ assertEquals (originalState .getExportStartTime (), deserializedState .getExportStartTime ());
196+ assertEquals (originalState .getLoadedItems (), deserializedState .getLoadedItems ());
197+ } catch (JsonProcessingException e ) {
198+ fail ("Serialization/deserialization failed" , e );
199+ }
200+ }
159201
160- // Simulate negative acknowledgment
161- callbackCaptor .getValue ().accept (false );
202+ @ Test
203+ void testNegativeAcknowledgment () {
204+ List <ItemInfo > items = createTestItems (1 );
205+ when (client .listItems (INITIAL_TOKEN )).thenReturn (items .iterator ());
206+
207+ // Setup immediate negative acknowledgment
208+ doAnswer (invocation -> {
209+ Consumer <Boolean > callback = invocation .getArgument (0 );
210+ callback .accept (false ); // Trigger negative ack immediately
211+ return acknowledgementSet ;
212+ }).when (acknowledgementSetManager ).create (any (), eq (TEST_TIMEOUT ));
162213
214+ crawler .setAcknowledgementsEnabled (true );
215+ crawler .crawl (leaderPartition , coordinator );
216+
217+ // Verify behavior
163218 verify (client , times (1 )).writeBatchToBuffer (any (), any (), any ());
164- verify (coordinator , never ()).saveProgressStateForPartition (eq (leaderPartition ), any (Duration .class ));
219+ verify (coordinator , times (1 )).createPartition (any ());
220+ verify (acknowledgementSet , times (1 )).complete ();
165221 }
166222
223+
167224 @ Test
168225 void testAcknowledgmentTimeout () {
169- List <ItemInfo > items = createTestItems (BATCH_SIZE + 1 );
226+ List <ItemInfo > items = createTestItems ( 1 );
170227 when (client .listItems (INITIAL_TOKEN )).thenReturn (items .iterator ());
171228 when (acknowledgementSetManager .create (any (), eq (TEST_TIMEOUT )))
172229 .thenReturn (acknowledgementSet );
@@ -178,13 +235,10 @@ void testAcknowledgmentTimeout() {
178235
179236 verify (acknowledgementSetManager ).create (callbackCaptor .capture (), eq (TEST_TIMEOUT ));
180237
181- // Verify:
182- // 1. Only first batch was processed
238+ // Verify timeout behavior
183239 verify (client , times (1 )).writeBatchToBuffer (any (), any (), any ());
184- // 2. No checkpoint update happened
185- verify (coordinator , never ()).saveProgressStateForPartition (eq (leaderPartition ), any (Duration .class ));
186- // 3. Acknowledgment set was completed
187240 verify (acknowledgementSet ).complete ();
241+ verify (coordinator ).createPartition (any ());
188242 }
189243
190244
0 commit comments