@@ -622,6 +622,233 @@ public function test_get_adjacent_post_with_identical_dates() {
622622 $ this ->assertEquals ( $ post_ids [3 ], $ next ->ID );
623623 }
624624
625+ /**
626+ * Test that deterministic ID fallback is applied when WHERE filter doesn't modify the clause.
627+ *
628+ * @ticket 64390
629+ */
630+ public function test_get_adjacent_post_identical_dates_applies_deterministic_where_when_filter_unmodified () {
631+ $ identical_date = '2024-01-01 12:00:00 ' ;
632+
633+ // Create posts with identical dates but different IDs.
634+ $ post_ids = array ();
635+ for ( $ i = 1 ; $ i <= 5 ; $ i ++ ) {
636+ $ post_ids [] = self ::factory ()->post ->create (
637+ array (
638+ 'post_title ' => "Post $ i " ,
639+ 'post_date ' => $ identical_date ,
640+ )
641+ );
642+ }
643+
644+ // Add a filter that doesn't modify the WHERE clause (returns unchanged).
645+ add_filter (
646+ 'get_next_post_where ' ,
647+ static function ( $ where ) {
648+ // Return unchanged - deterministic fallback should be applied.
649+ return $ where ;
650+ }
651+ );
652+
653+ // Test navigation from the middle post (ID: 3rd post).
654+ $ current_post_id = $ post_ids [2 ]; // 3rd post
655+ $ this ->go_to ( get_permalink ( $ current_post_id ) );
656+
657+ // Next post should be the 4th post (higher ID, same date) - deterministic.
658+ $ next = get_adjacent_post ( false , '' , false );
659+ $ this ->assertInstanceOf ( 'WP_Post ' , $ next );
660+ $ this ->assertEquals ( $ post_ids [3 ], $ next ->ID );
661+
662+ remove_all_filters ( 'get_next_post_where ' );
663+ }
664+
665+ /**
666+ * Test that deterministic ID fallback is NOT applied when WHERE filter modifies the clause.
667+ *
668+ * @ticket 64390
669+ */
670+ public function test_get_adjacent_post_identical_dates_respects_modified_where_filter () {
671+ $ identical_date = '2024-01-01 12:00:00 ' ;
672+
673+ // Create posts with identical dates but different IDs.
674+ $ post_ids = array ();
675+ for ( $ i = 1 ; $ i <= 5 ; $ i ++ ) {
676+ $ post_ids [] = self ::factory ()->post ->create (
677+ array (
678+ 'post_title ' => "Post $ i " ,
679+ 'post_date ' => $ identical_date ,
680+ )
681+ );
682+ }
683+
684+ // Capture what the filter receives and what it returns.
685+ $ filter_received = '' ;
686+ $ filter_returned = '' ;
687+ add_filter (
688+ 'get_next_post_where ' ,
689+ static function ( $ where ) use ( &$ filter_received , &$ filter_returned ) {
690+ $ filter_received = $ where ;
691+ // Modify the WHERE clause - deterministic fallback should NOT be applied.
692+ // Add a harmless condition that won't affect results but proves the filter was applied.
693+ $ filter_returned = $ where . ' AND 1=1 ' ;
694+ return $ filter_returned ;
695+ }
696+ );
697+
698+ // Test navigation from the middle post (ID: 3rd post).
699+ $ current_post_id = $ post_ids [2 ]; // 3rd post
700+ $ this ->go_to ( get_permalink ( $ current_post_id ) );
701+
702+ // Call get_adjacent_post to trigger the filter.
703+ get_adjacent_post ( false , '' , false );
704+
705+ // Verify the filter received the non-deterministic WHERE clause (without ID fallback).
706+ $ this ->assertNotEmpty ( $ filter_received , 'Filter should have been called. ' );
707+ $ this ->assertStringNotContainsString ( 'AND p.ID ' , $ filter_received , 'Filter should receive WHERE clause without deterministic ID fallback. ' );
708+ // Verify the filter's modification is preserved (proves deterministic logic wasn't applied on top).
709+ $ this ->assertStringContainsString ( 'AND 1=1 ' , $ filter_returned , 'Filter modification should be preserved. ' );
710+
711+ remove_all_filters ( 'get_next_post_where ' );
712+ }
713+
714+ /**
715+ * Test that deterministic ID sort is applied when SORT filter doesn't modify the clause.
716+ *
717+ * @ticket 64390
718+ */
719+ public function test_get_adjacent_post_identical_dates_applies_deterministic_sort_when_filter_unmodified () {
720+ $ identical_date = '2024-01-01 12:00:00 ' ;
721+
722+ // Create posts with identical dates but different IDs.
723+ $ post_ids = array ();
724+ for ( $ i = 1 ; $ i <= 5 ; $ i ++ ) {
725+ $ post_ids [] = self ::factory ()->post ->create (
726+ array (
727+ 'post_title ' => "Post $ i " ,
728+ 'post_date ' => $ identical_date ,
729+ )
730+ );
731+ }
732+
733+ // Add a filter that doesn't modify the SORT clause (returns unchanged).
734+ add_filter (
735+ 'get_next_post_sort ' ,
736+ static function ( $ sort ) {
737+ // Return unchanged - deterministic ID sort should be applied.
738+ return $ sort ;
739+ }
740+ );
741+
742+ // Test navigation from the middle post (ID: 3rd post).
743+ $ current_post_id = $ post_ids [2 ]; // 3rd post
744+ $ this ->go_to ( get_permalink ( $ current_post_id ) );
745+
746+ // Next post should be the 4th post (higher ID, same date) - deterministic.
747+ $ next = get_adjacent_post ( false , '' , false );
748+ $ this ->assertInstanceOf ( 'WP_Post ' , $ next );
749+ $ this ->assertEquals ( $ post_ids [3 ], $ next ->ID );
750+
751+ remove_all_filters ( 'get_next_post_sort ' );
752+ }
753+
754+ /**
755+ * Test that deterministic ID sort is NOT applied when SORT filter modifies the clause.
756+ *
757+ * @ticket 64390
758+ */
759+ public function test_get_adjacent_post_identical_dates_respects_modified_sort_filter () {
760+ $ identical_date = '2024-01-01 12:00:00 ' ;
761+
762+ // Create posts with identical dates but different IDs.
763+ $ post_ids = array ();
764+ for ( $ i = 1 ; $ i <= 5 ; $ i ++ ) {
765+ $ post_ids [] = self ::factory ()->post ->create (
766+ array (
767+ 'post_title ' => "Post $ i " ,
768+ 'post_date ' => $ identical_date ,
769+ )
770+ );
771+ }
772+
773+ // Capture what the filter receives and what it returns.
774+ $ filter_received = '' ;
775+ $ filter_returned = '' ;
776+ add_filter (
777+ 'get_next_post_sort ' ,
778+ static function ( $ sort , $ post , $ order ) use ( &$ filter_received , &$ filter_returned ) {
779+ $ filter_received = $ sort ;
780+ // Modify to remove ID - deterministic ID sort should NOT be applied.
781+ $ filter_returned = "ORDER BY p.post_date $ order LIMIT 1 " ;
782+ return $ filter_returned ;
783+ },
784+ 10 ,
785+ 3
786+ );
787+
788+ // Test navigation from the middle post (ID: 3rd post).
789+ $ current_post_id = $ post_ids [2 ]; // 3rd post
790+ $ this ->go_to ( get_permalink ( $ current_post_id ) );
791+
792+ // Call get_adjacent_post to trigger the filter.
793+ get_adjacent_post ( false , '' , false );
794+
795+ // Verify the filter received the non-deterministic SORT clause (without ID).
796+ $ this ->assertNotEmpty ( $ filter_received , 'Filter should have been called. ' );
797+ $ this ->assertStringNotContainsString ( 'p.ID ' , $ filter_received , 'Filter should receive SORT clause without deterministic ID sort. ' );
798+ // Verify the filter's modification is preserved (proves deterministic logic wasn't applied on top).
799+ $ this ->assertStringNotContainsString ( 'p.ID ' , $ filter_returned , 'Filter modification should not include ID when filter removes it. ' );
800+ $ this ->assertStringContainsString ( 'ORDER BY p.post_date ' , $ filter_returned , 'Filter modification should be preserved. ' );
801+
802+ remove_all_filters ( 'get_next_post_sort ' );
803+ }
804+
805+ /**
806+ * Test that both WHERE and SORT filters work together correctly.
807+ *
808+ * @ticket 64390
809+ */
810+ public function test_get_adjacent_post_identical_dates_with_both_filters_unmodified () {
811+ $ identical_date = '2024-01-01 12:00:00 ' ;
812+
813+ // Create posts with identical dates but different IDs.
814+ $ post_ids = array ();
815+ for ( $ i = 1 ; $ i <= 5 ; $ i ++ ) {
816+ $ post_ids [] = self ::factory ()->post ->create (
817+ array (
818+ 'post_title ' => "Post $ i " ,
819+ 'post_date ' => $ identical_date ,
820+ )
821+ );
822+ }
823+
824+ // Add filters that don't modify the clauses.
825+ add_filter (
826+ 'get_previous_post_where ' ,
827+ static function ( $ where ) {
828+ return $ where ;
829+ }
830+ );
831+
832+ add_filter (
833+ 'get_previous_post_sort ' ,
834+ static function ( $ sort ) {
835+ return $ sort ;
836+ }
837+ );
838+
839+ // Test navigation from the middle post (ID: 3rd post).
840+ $ current_post_id = $ post_ids [2 ]; // 3rd post
841+ $ this ->go_to ( get_permalink ( $ current_post_id ) );
842+
843+ // Previous post should be the 2nd post (lower ID, same date) - deterministic.
844+ $ previous = get_adjacent_post ( false , '' , true );
845+ $ this ->assertInstanceOf ( 'WP_Post ' , $ previous );
846+ $ this ->assertEquals ( $ post_ids [1 ], $ previous ->ID );
847+
848+ remove_all_filters ( 'get_previous_post_where ' );
849+ remove_all_filters ( 'get_previous_post_sort ' );
850+ }
851+
625852 /**
626853 * Test get_adjacent_post with mixed dates and identical dates.
627854 *
0 commit comments