Skip to content

Commit bdc0550

Browse files
committed
Add search relevance tests to deterministic ordering suite
Introduced new tests to verify deterministic ordering when posts are ordered by search relevance. Created shared fixtures for posts with identical content to ensure consistent relevance scores, preventing duplicates across paginated results. Updated the test suite to include scenarios for both explicit and empty orderby parameters, ensuring robust coverage of search-related ordering behavior.
1 parent 864399f commit bdc0550

File tree

1 file changed

+140
-4
lines changed

1 file changed

+140
-4
lines changed

tests/phpunit/tests/query/deterministicOrdering.php

Lines changed: 140 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ class Tests_Query_DeterministicOrdering extends WP_UnitTestCase {
3838
*/
3939
protected static $menu_order_post_ids = array();
4040

41+
/**
42+
* Post IDs for search relevance tests.
43+
*
44+
* @var array
45+
*/
46+
protected static $search_relevance_post_ids = array();
47+
4148
/**
4249
* Set up shared fixtures for all tests.
4350
*/
@@ -106,6 +113,20 @@ public static function set_up_before_class() {
106113
)
107114
);
108115
}
116+
117+
// Create posts for search relevance tests.
118+
// All posts will have the same content to ensure same relevance scores.
119+
$identical_content = 'This is a search test post with identical content';
120+
for ( $i = 1; $i <= 20; $i++ ) {
121+
self::$search_relevance_post_ids[] = self::factory()->post->create(
122+
array(
123+
'post_type' => 'wptests_time_ident',
124+
'post_title' => "Search Post $i",
125+
'post_content' => $identical_content,
126+
'post_excerpt' => $identical_content,
127+
)
128+
);
129+
}
109130
}
110131

111132
/**
@@ -115,10 +136,11 @@ public static function tear_down_after_class() {
115136
_unregister_post_type( 'wptests_time_ident' );
116137
_unregister_post_type( 'wptests_title_ident' );
117138

118-
self::$date_identical_post_ids = array();
119-
self::$title_identical_post_ids = array();
120-
self::$search_post_ids = array();
121-
self::$menu_order_post_ids = array();
139+
self::$date_identical_post_ids = array();
140+
self::$title_identical_post_ids = array();
141+
self::$search_post_ids = array();
142+
self::$menu_order_post_ids = array();
143+
self::$search_relevance_post_ids = array();
122144

123145
parent::tear_down_after_class();
124146
}
@@ -497,4 +519,118 @@ public function test_deterministic_ordering_with_metadata() {
497519
$this->assertEquals( 10, count( $page1_ids ), 'First page should have 10 posts' );
498520
$this->assertEquals( 10, count( $page2_ids ), 'Second page should have 10 posts' );
499521
}
522+
523+
/**
524+
* Test that deterministic ordering works with search relevance ordering.
525+
*
526+
* When ordering by search relevance, multiple posts can have the same relevance score,
527+
* causing duplicate records across pages without deterministic ordering.
528+
*
529+
* @ticket xxxxx
530+
*/
531+
public function test_deterministic_ordering_with_search_relevance() {
532+
// Use shared fixtures with identical content (same relevance scores)
533+
// Get first page ordering by relevance
534+
$query1 = new WP_Query(
535+
array(
536+
'post_type' => 'wptests_time_ident',
537+
'post__in' => self::$search_relevance_post_ids,
538+
's' => 'search test',
539+
'orderby' => 'relevance',
540+
'order' => 'DESC',
541+
'posts_per_page' => 10,
542+
'paged' => 1,
543+
)
544+
);
545+
546+
// Get second page ordering by relevance
547+
$query2 = new WP_Query(
548+
array(
549+
'post_type' => 'wptests_time_ident',
550+
'post__in' => self::$search_relevance_post_ids,
551+
's' => 'search test',
552+
'orderby' => 'relevance',
553+
'order' => 'DESC',
554+
'posts_per_page' => 10,
555+
'paged' => 2,
556+
)
557+
);
558+
559+
$page1_ids = wp_list_pluck( $query1->posts, 'ID' );
560+
$page2_ids = wp_list_pluck( $query2->posts, 'ID' );
561+
562+
// Verify no overlap between pages (no duplicates)
563+
$overlap = array_intersect( $page1_ids, $page2_ids );
564+
$this->assertEmpty( $overlap, 'Pages should not contain duplicate posts when ordering by search relevance' );
565+
566+
// Verify total count is correct
567+
$this->assertEquals( 20, $query1->found_posts, 'Total posts should be 20' );
568+
$this->assertEquals( 10, count( $page1_ids ), 'First page should have 10 posts' );
569+
$this->assertEquals( 10, count( $page2_ids ), 'Second page should have 10 posts' );
570+
571+
// Verify deterministic ordering: same query should return same results
572+
$query1_repeat = new WP_Query(
573+
array(
574+
'post_type' => 'wptests_time_ident',
575+
'post__in' => self::$search_relevance_post_ids,
576+
's' => 'search test',
577+
'orderby' => 'relevance',
578+
'order' => 'DESC',
579+
'posts_per_page' => 10,
580+
'paged' => 1,
581+
)
582+
);
583+
$page1_repeat_ids = wp_list_pluck( $query1_repeat->posts, 'ID' );
584+
585+
$this->assertEquals( $page1_ids, $page1_repeat_ids, 'Same query should return same results when ordering by search relevance' );
586+
}
587+
588+
/**
589+
* Test that deterministic ordering works with search when orderby is empty (defaults to relevance).
590+
*
591+
* When orderby is empty and search is present, WordPress orders by relevance.
592+
* Multiple posts can have the same relevance score, causing duplicate records across pages.
593+
*
594+
* @ticket xxxxx
595+
*/
596+
public function test_deterministic_ordering_with_search_empty_orderby() {
597+
// Use shared fixtures with identical content (same relevance scores)
598+
// Get first page with empty orderby (defaults to relevance)
599+
$query1 = new WP_Query(
600+
array(
601+
'post_type' => 'wptests_time_ident',
602+
'post__in' => self::$search_relevance_post_ids,
603+
's' => 'search test',
604+
'orderby' => '', // Empty orderby with search defaults to relevance
605+
'order' => 'DESC',
606+
'posts_per_page' => 10,
607+
'paged' => 1,
608+
)
609+
);
610+
611+
// Get second page with empty orderby
612+
$query2 = new WP_Query(
613+
array(
614+
'post_type' => 'wptests_time_ident',
615+
'post__in' => self::$search_relevance_post_ids,
616+
's' => 'search test',
617+
'orderby' => '', // Empty orderby with search defaults to relevance
618+
'order' => 'DESC',
619+
'posts_per_page' => 10,
620+
'paged' => 2,
621+
)
622+
);
623+
624+
$page1_ids = wp_list_pluck( $query1->posts, 'ID' );
625+
$page2_ids = wp_list_pluck( $query2->posts, 'ID' );
626+
627+
// Verify no overlap between pages (no duplicates)
628+
$overlap = array_intersect( $page1_ids, $page2_ids );
629+
$this->assertEmpty( $overlap, 'Pages should not contain duplicate posts when ordering by search relevance (empty orderby)' );
630+
631+
// Verify total count is correct
632+
$this->assertEquals( 20, $query1->found_posts, 'Total posts should be 20' );
633+
$this->assertEquals( 10, count( $page1_ids ), 'First page should have 10 posts' );
634+
$this->assertEquals( 10, count( $page2_ids ), 'Second page should have 10 posts' );
635+
}
500636
}

0 commit comments

Comments
 (0)