getProperty( 'cache' ); $cache->setAccessible( true ); $cache->setValue( null, null ); WPDO_Entity_Registry::init(); } private function set_post_mode( string $mode ): void { $ref = new ReflectionClass( WPDO_Mode_Manager::class ); $cache = $ref->getProperty( 'cache' ); $cache->setAccessible( true ); $cache->setValue( null, array( 'post' => $mode, 'user' => 'aeav_only', 'term' => 'dual_write', 'comment' => 'dual_write', ) ); } private function make_query( array $vars ): WP_Query { $q = new WP_Query(); foreach ( $vars as $k => $v ) { $q->set( $k, $v ); } return $q; } // ── pre_get_posts gate (mode-aware) ─────────────────────────────────────── public function test_pass_through_when_mode_disabled(): void { $this->set_post_mode( 'disabled' ); $router = new WPDO_Post_Query_Router(); $query = $this->make_query( array( 'post_type' => 'product', 'meta_query' => array( array( 'key' => '_price', 'value' => '50', 'compare' => '>=', 'type' => 'NUMERIC' ), ), ) ); $original = $query->get( 'meta_query' ); $router->pre_get_posts( $query ); $this->assertSame( $original, $query->get( 'meta_query' ), 'mode=disabled: meta_query must be untouched.' ); $this->assertSame( '', $query->get( 'wpdo_post_clauses' ) ); } public function test_pass_through_when_mode_dual_write(): void { $this->set_post_mode( 'dual_write' ); $router = new WPDO_Post_Query_Router(); $query = $this->make_query( array( 'post_type' => 'product', 'meta_query' => array( array( 'key' => '_price', 'value' => '50', 'compare' => '>=', 'type' => 'NUMERIC' ), ), ) ); $original = $query->get( 'meta_query' ); $router->pre_get_posts( $query ); $this->assertSame( $original, $query->get( 'meta_query' ), 'mode=dual_write: wp_postmeta is still source-of-truth, no rewrite.' ); } // ── pre_get_posts rewrite (mode=aeav_only) ─────────────────────────────── public function test_rewrites_meta_query_when_mode_aeav_only(): void { $this->set_post_mode( 'aeav_only' ); $router = new WPDO_Post_Query_Router(); $query = $this->make_query( array( 'post_type' => 'product', 'meta_query' => array( array( 'key' => '_price', 'value' => '50', 'compare' => '>=', 'type' => 'NUMERIC' ), ), ) ); $router->pre_get_posts( $query ); // Original meta_query stripped of registered keys. $remaining = $query->get( 'meta_query' ); $this->assertEmpty( $remaining, 'aeav_only: registered keys removed from meta_query.' ); // Routed clauses captured under wpdo_post_clauses query var. $routed = $query->get( 'wpdo_post_clauses' ); $this->assertIsArray( $routed ); $this->assertNotEmpty( $routed ); } public function test_keeps_unmanaged_keys_in_meta_query(): void { $this->set_post_mode( 'aeav_only' ); $router = new WPDO_Post_Query_Router(); $query = $this->make_query( array( 'post_type' => 'product', 'meta_query' => array( array( 'key' => '_price', 'value' => '50', 'compare' => '>=', 'type' => 'NUMERIC' ), array( 'key' => 'unmanaged_attr', 'value' => 'x' ), ), ) ); $router->pre_get_posts( $query ); $remaining = $query->get( 'meta_query' ); $this->assertCount( 1, $remaining ); // Original index preserved (k=1 since k=0 was the routed _price clause). $first_clause = reset( $remaining ); $this->assertSame( 'unmanaged_attr', $first_clause['key'], 'Unmanaged key remains in meta_query (Hook Bus pass-through).' ); } public function test_skips_admin_requests(): void { $this->set_post_mode( 'aeav_only' ); $prev_admin = $GLOBALS['_wp_is_admin'] ?? false; $GLOBALS['_wp_is_admin'] = true; $router = new WPDO_Post_Query_Router(); $query = $this->make_query( array( 'post_type' => 'product', 'meta_query' => array( array( 'key' => '_price', 'value' => '50', 'compare' => '>=', 'type' => 'NUMERIC' ), ), ) ); $original = $query->get( 'meta_query' ); $router->pre_get_posts( $query ); $this->assertSame( $original, $query->get( 'meta_query' ), 'is_admin requests should not be rewritten.' ); $GLOBALS['_wp_is_admin'] = $prev_admin; } // ── posts_join / posts_where (SQL emission) ────────────────────────────── public function test_posts_join_emits_left_join_for_each_routed_post_type(): void { $this->set_post_mode( 'aeav_only' ); $router = new WPDO_Post_Query_Router(); $query = $this->make_query( array( 'post_type' => 'product', 'meta_query' => array( array( 'key' => '_price', 'value' => '50', 'type' => 'NUMERIC' ), ), 'wpdo_post_clauses' => array(), ) ); $router->pre_get_posts( $query ); $join = $router->posts_join( '', $query ); $this->assertStringContainsString( 'LEFT JOIN', $join ); $this->assertStringContainsString( 'wpdo_post_wc_product', $join ); } public function test_posts_join_joins_every_routed_group(): void { $this->set_post_mode( 'aeav_only' ); $router = new WPDO_Post_Query_Router(); $query = $this->make_query( array( 'post_type' => 'product', 'meta_query' => array( array( 'key' => '_price', 'value' => '50', 'compare' => '>=', 'type' => 'NUMERIC' ), array( 'key' => '_thumbnail_id', 'compare' => 'EXISTS' ), ), ) ); $router->pre_get_posts( $query ); $join = $router->posts_join( '', $query ); $where = $router->posts_where( '', $query ); $this->assertStringContainsString( 'wpdo_post_wc_product', $join ); $this->assertStringContainsString( 'wpdo_post_wp_core', $join ); $this->assertStringContainsString( '`wpdoflat_product_wp_core`.`_thumbnail_id` IS NOT NULL', $where ); } public function test_no_group_by_is_added_for_one_row_per_post_joins(): void { unset( $GLOBALS['_wp_filter_callbacks']['posts_groupby'] ); ( new WPDO_Post_Query_Router() )->register_hooks(); $this->assertEmpty( $GLOBALS['_wp_filter_callbacks']['posts_groupby'] ?? array() ); } public function test_posts_where_appends_condition_for_routed_clause(): void { $this->set_post_mode( 'aeav_only' ); $router = new WPDO_Post_Query_Router(); $query = $this->make_query( array( 'post_type' => 'product', 'meta_query' => array( array( 'key' => '_price', 'value' => '99', 'compare' => '=', 'type' => 'NUMERIC' ), ), ) ); $router->pre_get_posts( $query ); $where = $router->posts_where( '', $query ); $this->assertStringContainsString( '`_price`', $where ); $this->assertStringContainsString( "'99'", $where ); } public function test_pass_through_when_no_meta_query(): void { $this->set_post_mode( 'aeav_only' ); $router = new WPDO_Post_Query_Router(); $query = $this->make_query( array( 'post_type' => 'product' ) ); $router->pre_get_posts( $query ); $this->assertSame( '', $query->get( 'wpdo_post_clauses' ) ); } // ── all-or-nothing: native unless every registered clause is routable ──── /** * Run pre_get_posts in aeav_only and assert the query was left to WordPress. * * @param array $vars Query vars. */ private function assert_not_routed( array $vars, string $why ): void { $this->set_post_mode( 'aeav_only' ); $query = $this->make_query( $vars ); ( new WPDO_Post_Query_Router() )->pre_get_posts( $query ); $this->assertSame( $vars['meta_query'], $query->get( 'meta_query' ), $why . ': meta_query untouched.' ); $this->assertSame( '', $query->get( 'wpdo_post_clauses' ), $why . ': nothing routed.' ); } public function test_or_relation_is_not_routed(): void { $this->assert_not_routed( array( 'post_type' => 'product', 'meta_query' => array( 'relation' => 'OR', array( 'key' => '_stock', 'value' => '1' ), array( 'key' => '_sku', 'value' => 'x' ), ), ), 'OR' ); } public function test_several_post_types_are_not_routed(): void { $this->assert_not_routed( array( 'post_type' => array( 'product', 'post' ), 'meta_query' => array( array( 'key' => '_price', 'value' => '5', 'compare' => '>=', 'type' => 'NUMERIC' ) ), ), 'several post types' ); } public function test_untyped_range_on_a_numeric_column_is_not_routed(): void { $this->assert_not_routed( array( 'post_type' => 'product', 'meta_query' => array( array( 'key' => '_price', 'value' => '9000', 'compare' => '>' ) ), ), 'CHAR > on DECIMAL' ); } public function test_untyped_equality_on_a_decimal_column_is_not_routed(): void { $this->assert_not_routed( array( 'post_type' => 'product', 'meta_query' => array( array( 'key' => '_price', 'value' => '10' ) ), ), 'CHAR = on DECIMAL (native "10.00" != "10")' ); } public function test_numeric_type_on_a_text_column_is_not_routed(): void { $this->assert_not_routed( array( 'post_type' => 'product', 'meta_query' => array( array( 'key' => '_sku', 'value' => '5', 'compare' => '>', 'type' => 'NUMERIC' ) ), ), 'NUMERIC on VARCHAR' ); } public function test_unknown_compare_is_not_routed(): void { $this->assert_not_routed( array( 'post_type' => 'product', 'meta_query' => array( array( 'key' => '_sku', 'value' => '^a', 'compare' => 'REGEXP' ) ), ), 'REGEXP' ); } public function test_one_unroutable_registered_clause_keeps_the_whole_query_native(): void { $this->assert_not_routed( array( 'post_type' => 'product', 'meta_query' => array( array( 'key' => '_stock', 'value' => '3', 'compare' => '>=', 'type' => 'NUMERIC' ), array( 'key' => '_price', 'value' => '9000', 'compare' => '>' ), ), ), 'one unroutable clause' ); } public function test_nested_group_with_a_registered_key_is_not_routed(): void { $this->assert_not_routed( array( 'post_type' => 'product', 'meta_query' => array( array( 'key' => '_stock', 'value' => '3', 'compare' => '>=', 'type' => 'NUMERIC' ), array( 'relation' => 'OR', array( 'key' => '_price', 'compare' => 'NOT EXISTS' ), array( 'key' => '_price', 'compare' => 'EXISTS' ), ), ), ), 'nested group' ); } public function test_untyped_integer_equality_on_an_integer_column_is_routed(): void { $this->set_post_mode( 'aeav_only' ); $query = $this->make_query( array( 'post_type' => 'product', 'meta_query' => array( array( 'key' => '_stock', 'value' => '3' ), array( 'key' => 'unmanaged_attr', 'value' => 'x' ), ), ) ); ( new WPDO_Post_Query_Router() )->pre_get_posts( $query ); $this->assertNotEmpty( $query->get( 'wpdo_post_clauses' ) ); $this->assertCount( 1, $query->get( 'meta_query' ), 'unregistered AND clause stays native' ); } public function test_like_matches_an_escaped_substring(): void { $this->set_post_mode( 'aeav_only' ); $router = new WPDO_Post_Query_Router(); $query = $this->make_query( array( 'post_type' => 'product', 'meta_query' => array( array( 'key' => '_sku', 'value' => 'B1_1%', 'compare' => 'LIKE' ) ), ) ); $router->pre_get_posts( $query ); $this->assertStringContainsString( "LIKE '%B1\\\\_1\\\\%%'", $router->posts_where( '', $query ) ); } }