@@ -580,4 +580,121 @@ void testBatchSizeLimit() {
580580 });
581581 Assert .assertEquals (lee .getRequestLimit (), 1000L , "The changes request should have the expected limit." );
582582 }
583+
584+ /**
585+ * Tests for getLastSeqNewerThan method
586+ */
587+
588+ /**
589+ * Verify that getLastSeqNewerThan throws IllegalArgumentException when passed null.
590+ */
591+ @ Test
592+ void testGetLastSeqNewerThanWithNull () {
593+ Cloudant mockClient = new ChangesRequestMockClient (new PerpetualSupplier (true ));
594+ ChangesFollower testFollower = new ChangesFollower (mockClient , TestOptions .MINIMUM .getOptions ());
595+ IllegalArgumentException iae = Assert .expectThrows (IllegalArgumentException .class , () -> {
596+ testFollower .latestSequenceFrom (null );
597+ });
598+ Assert .assertEquals (iae .getMessage (),
599+ "Provided sequence ID must be a non-empty string." ,
600+ "The error message should be correct." );
601+ }
602+
603+ /**
604+ * Verify that getLastSeqNewerThan throws IllegalArgumentException when passed an empty string.
605+ */
606+ @ Test
607+ void testGetLastSeqNewerThanWithEmptyString () {
608+ Cloudant mockClient = new ChangesRequestMockClient (new PerpetualSupplier (true ));
609+ ChangesFollower testFollower = new ChangesFollower (mockClient , TestOptions .MINIMUM .getOptions ());
610+ IllegalArgumentException iae = Assert .expectThrows (IllegalArgumentException .class , () -> {
611+ testFollower .latestSequenceFrom ("" );
612+ });
613+ Assert .assertEquals (iae .getMessage (),
614+ "Provided sequence ID must be a non-empty string." ,
615+ "The error message should be correct." );
616+ }
617+
618+ /**
619+ * Verify that getLastSeqNewerThan returns the input sequence unchanged when called before the feed starts.
620+ */
621+ @ Test
622+ void testGetLastSeqNewerThanBeforeStart () {
623+ Cloudant mockClient = new ChangesRequestMockClient (new PerpetualSupplier (true ));
624+ ChangesFollower testFollower = new ChangesFollower (mockClient , TestOptions .MINIMUM .getOptions ());
625+ String inputSeq = "100-abc" ;
626+ String result = testFollower .latestSequenceFrom (inputSeq );
627+ Assert .assertEquals (result , inputSeq ,
628+ "Should return the input sequence unchanged when feed hasn't started." );
629+ }
630+
631+ /**
632+ * Verify that getLastSeqNewerThan returns the input sequence when no newer sequence is available.
633+ */
634+ @ Test
635+ void testGetLastSeqNewerThanAfterStartNoNewerSeq () {
636+ // Create a mock client with a single batch
637+ Cloudant mockClient = new ChangesRequestMockClient (ChangesRequestMockClient .makeBatchSupplier (1 ));
638+ ChangesFollower testFollower = new ChangesFollower (mockClient , TestOptions .MINIMUM .getOptions ());
639+
640+ // Start the feed and consume some changes
641+ Stream <ChangesResultItem > stream = testFollower .startOneOff ();
642+ stream .limit (5 ).forEach (item -> {
643+ // Just consume the items
644+ });
645+
646+ // Query with a sequence that's not in seqMarkers
647+ String unknownSeq = "999-unknown" ;
648+ String result = testFollower .latestSequenceFrom (unknownSeq );
649+ Assert .assertEquals (result , unknownSeq ,
650+ "Should return the input sequence when it's not found in seqMarkers." );
651+ }
652+
653+ /**
654+ * Verify that getLastSeqNewerThan returns the associated last_seq when querying
655+ * with a user-facing sequence. The method returns the last_seq value associated
656+ * with the user sequence, not necessarily advancing through empty pages.
657+ */
658+ @ Test
659+ void testGetLastSeqNewerThanAfterStartWithNewerSeq () {
660+ // Create a mock client with a single batch
661+ Cloudant mockClient = new ChangesRequestMockClient (ChangesRequestMockClient .makeBatchSupplier (1 ));
662+ ChangesFollower testFollower = new ChangesFollower (mockClient , TestOptions .MINIMUM .getOptions ());
663+
664+ // Start the feed and consume some changes
665+ Stream <ChangesResultItem > stream = testFollower .startOneOff ();
666+ long count = stream .limit (5 ).count ();
667+ Assert .assertEquals (count , 5L , "Should have consumed 5 changes." );
668+
669+ // Query with a user-facing sequence that exists in seqMarkers
670+ // The last item in the batch has seq "10000-g" which maps to last_seq "10000-g"
671+ String result = testFollower .latestSequenceFrom ("10000-g" );
672+ Assert .assertEquals (result , "10000-g" ,
673+ "Should return the last_seq associated with the user sequence." );
674+ }
675+
676+ /**
677+ * Verify that getLastSeqNewerThan returns the input sequence unchanged when
678+ * the sequence is not found in seqMarkers (e.g., querying with a sequence
679+ * from the middle of a batch that wasn't the last item).
680+ */
681+ @ Test
682+ void testGetLastSeqNewerThanAfterStartWithEmptyPages () {
683+ // Create a mock client with a single batch
684+ Cloudant mockClient = new ChangesRequestMockClient (ChangesRequestMockClient .makeBatchSupplier (1 ));
685+ ChangesFollower testFollower = new ChangesFollower (mockClient , TestOptions .MINIMUM .getOptions ());
686+
687+ // Start the feed and consume all changes
688+ Stream <ChangesResultItem > stream = testFollower .startOneOff ();
689+ stream .forEach (item -> {
690+ // Just consume the items
691+ });
692+
693+ // Query with a sequence that's not in seqMarkers (e.g., from middle of batch)
694+ // Only the LAST item's sequence is stored in seqMarkers
695+ String unknownSeq = "5000-g" ;
696+ String result = testFollower .latestSequenceFrom (unknownSeq );
697+ Assert .assertEquals (result , unknownSeq ,
698+ "Should return the input sequence unchanged when not found in seqMarkers." );
699+ }
583700}
0 commit comments