fix(2meet-data-optimizer): filter REST listings collection by post_status and post_type
- GET /wpdo/v1/listings rejects unregistered or non-viewable post types (400) and joins posts so Zone A only lists published posts of that type - WP_Query fallback now returns only show_in_rest hot columns, matching the Zone A path - Verify: core unit 605 / integration 432 OK (new RestApi tests); 1M bench: page 7ms, total COUNT 0.12s → 1.7s (tracked in PR-030) - PR: PR-015
This commit is contained in:
@@ -72,12 +72,29 @@ class RestApiIntegrationTest extends TestCase {
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
|
||||
);
|
||||
|
||||
// Seed 5 rows.
|
||||
for ( $i = 1; $i <= 5; $i++ ) {
|
||||
// Posts table: listings are filtered by post_status against it.
|
||||
$wpdb->query( "DROP TABLE IF EXISTS `{$wpdb->posts}`" );
|
||||
$wpdb->query(
|
||||
"CREATE TABLE `{$wpdb->posts}` (
|
||||
ID BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
post_type VARCHAR(20) NOT NULL DEFAULT 'post',
|
||||
post_status VARCHAR(20) NOT NULL DEFAULT 'publish',
|
||||
PRIMARY KEY (ID)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
|
||||
);
|
||||
|
||||
// Seed 5 published rows, plus draft/private/trash rows (ids 6-8) and
|
||||
// a published post of another type (id 9) that must never be listed.
|
||||
$statuses = array( 6 => 'draft', 7 => 'private', 8 => 'trash' );
|
||||
for ( $i = 1; $i <= 8; $i++ ) {
|
||||
$price = $i * 100;
|
||||
$featured = $i % 2;
|
||||
$status = $statuses[ $i ] ?? 'publish';
|
||||
$wpdb->query( "INSERT INTO `{$wpdb->posts}` (ID, post_type, post_status) VALUES ($i, 'restapi', '$status')" );
|
||||
$wpdb->query( "INSERT INTO `" . self::$hot_table . "` (post_id, rp_price, rp_featured) VALUES ($i, $price, $featured)" );
|
||||
}
|
||||
$wpdb->query( "INSERT INTO `{$wpdb->posts}` (ID, post_type, post_status) VALUES (9, 'post', 'publish')" );
|
||||
$wpdb->query( "INSERT INTO `" . self::$hot_table . "` (post_id, rp_price, rp_featured) VALUES (9, 900, 1)" );
|
||||
|
||||
// Set module to cutover so REST API reads from Zone A.
|
||||
WPDO_Feature_Flags::set( 'hot_restapi', 'cutover' );
|
||||
@@ -87,6 +104,7 @@ class RestApiIntegrationTest extends TestCase {
|
||||
global $wpdb;
|
||||
$wpdb->query( "DROP TABLE IF EXISTS `" . self::$hot_table . "`" );
|
||||
$wpdb->query( "DROP TABLE IF EXISTS `" . self::$warm_table . "`" );
|
||||
$wpdb->query( "DROP TABLE IF EXISTS `{$wpdb->posts}`" );
|
||||
WPDO_Feature_Flags::reset( 'hot_restapi' );
|
||||
}
|
||||
|
||||
@@ -97,6 +115,7 @@ class RestApiIntegrationTest extends TestCase {
|
||||
$GLOBALS['_wp_current_user_can'] = [];
|
||||
$GLOBALS['_wp_valid_nonces'] = [];
|
||||
$GLOBALS['_wp_transients'] = []; // Reset rate-limit transients between tests.
|
||||
$GLOBALS['_wp_registered_post_types'] = [ 'restapi' => true ];
|
||||
// Note: do NOT reset _wp_options here — Feature Flags state is stored there.
|
||||
|
||||
// Invalidate Feature Flags static request cache so each test reads fresh.
|
||||
@@ -232,6 +251,37 @@ class RestApiIntegrationTest extends TestCase {
|
||||
$this->assertSame( $sorted, $prices );
|
||||
}
|
||||
|
||||
public function test_listings_excludes_unpublished_posts(): void {
|
||||
$req = new WP_REST_Request( 'GET', '/wpdo/v1/listings' );
|
||||
$req->set_param( 'post_type', 'restapi' );
|
||||
$req->set_param( 'per_page', 20 );
|
||||
|
||||
$response = self::$api->get_listings( $req );
|
||||
$ids = array_column( $response->get_data(), 'id' );
|
||||
sort( $ids );
|
||||
|
||||
$this->assertSame( array( 1, 2, 3, 4, 5 ), $ids, 'draft/private/trash and other post types must not be listed.' );
|
||||
$this->assertSame( '5', $response->get_headers()['X-WP-Total'] );
|
||||
}
|
||||
|
||||
public function test_listings_filters_still_apply_with_status_join(): void {
|
||||
$req = new WP_REST_Request( 'GET', '/wpdo/v1/listings' );
|
||||
$req->set_param( 'post_type', 'restapi' );
|
||||
$req->set_param( 'rp_price_min', 400 );
|
||||
$req->set_param( 'per_page', 20 );
|
||||
|
||||
$ids = array_column( self::$api->get_listings( $req )->get_data(), 'id' );
|
||||
sort( $ids );
|
||||
$this->assertSame( array( 4, 5 ), $ids );
|
||||
}
|
||||
|
||||
public function test_listings_rejects_unregistered_post_type(): void {
|
||||
$req = new WP_REST_Request( 'GET', '/wpdo/v1/listings' );
|
||||
$req->set_param( 'post_type', 'nope' );
|
||||
|
||||
$this->assertSame( 400, self::$api->get_listings( $req )->get_status() );
|
||||
}
|
||||
|
||||
// ── GET /listings/{id} ───────────────────────────────────────────────────
|
||||
|
||||
public function test_get_listing_404_for_unknown_post(): void {
|
||||
|
||||
@@ -337,6 +337,9 @@ if ( ! function_exists( 'is_post_publicly_viewable' ) ) {
|
||||
return 'publish' === ( $GLOBALS['_wp_post_status'][ (int) $post_id ] ?? 'publish' );
|
||||
}
|
||||
}
|
||||
// Registered post types: name => publicly viewable (bool).
|
||||
if ( ! function_exists( 'post_type_exists' ) ) { function post_type_exists( string $post_type ): bool { return isset( $GLOBALS['_wp_registered_post_types'][ $post_type ] ); } }
|
||||
if ( ! function_exists( 'is_post_type_viewable' ) ) { function is_post_type_viewable( $post_type ): bool { return ! empty( $GLOBALS['_wp_registered_post_types'][ (string) $post_type ] ); } }
|
||||
if ( ! function_exists( 'current_user_can' ) ) {
|
||||
function current_user_can( string $cap, ...$args ): bool {
|
||||
if ( ! empty( $args ) ) { $key = $cap . ':' . implode( ',', array_map( 'strval', $args ) ); if ( isset( $GLOBALS['_wp_current_user_can'][ $key ] ) ) { return (bool) $GLOBALS['_wp_current_user_can'][ $key ]; } }
|
||||
|
||||
Reference in New Issue
Block a user