Make WordPress Core

Changeset 59899


Ignore:
Timestamp:
03/02/2025 10:05:08 PM (8 weeks ago)
Author:
TimothyBlynJacobs
Message:

REST API: Improve performance for HEAD requests.

By default, the REST API responds to HEAD rqeuests by calling the GET handler and omitting the body from the response. While convenient, this ends up performing needless work that slows down the API response time.

This commit adjusts the Core controllers to specifically handle HEAD requests by not preparing the response body.

Fixes #56481.
Props antonvlasenko, janusdev, ironprogrammer, swissspidy, spacedmonkey, mukesh27, mamaduka, timothyblynjacobs.

Location:
trunk
Files:
43 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/rest-api/class-wp-rest-request.php

    r58706 r59899  
    163163
    164164    /**
     165     * Determines if the request is the given method.
     166     *
     167     * @since 6.8.0
     168     *
     169     * @param string $method HTTP method.
     170     * @return bool Whether the request is of the given method.
     171     */
     172    public function is_method( $method ) {
     173        return $this->get_method() === strtoupper( $method );
     174    }
     175
     176    /**
    165177     * Canonicalizes the header name.
    166178     *
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php

    r56819 r59899  
    306306        }
    307307
     308        if ( $request->is_method( 'HEAD' ) ) {
     309            // Return early as this handler doesn't add any response headers.
     310            return new WP_REST_Response();
     311        }
    308312        $response  = array();
    309313        $parent_id = $parent->ID;
     
    449453        $post = $item;
    450454
     455        // Don't prepare the response body for HEAD requests.
     456        if ( $request->is_method( 'HEAD' ) ) {
     457            /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php */
     458            return apply_filters( 'rest_prepare_autosave', new WP_REST_Response(), $post, $request );
     459        }
    451460        $response = $this->revisions_controller->prepare_item_for_response( $post, $request );
    452461        $fields   = $this->get_fields_for_response( $request );
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-block-pattern-categories-controller.php

    r58704 r59899  
    8282     */
    8383    public function get_items( $request ) {
     84        if ( $request->is_method( 'HEAD' ) ) {
     85            // Return early as this handler doesn't add any response headers.
     86            return new WP_REST_Response();
     87        }
     88
    8489        $response   = array();
    8590        $categories = WP_Block_Pattern_Categories_Registry::get_instance()->get_all_registered();
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-block-types-controller.php

    r58694 r59899  
    132132     */
    133133    public function get_items( $request ) {
     134        if ( $request->is_method( 'HEAD' ) ) {
     135            // Return early as this handler doesn't add any response headers.
     136            return new WP_REST_Response();
     137        }
     138
    134139        $data        = array();
    135140        $block_types = $this->block_registry->get_all_registered();
     
    250255        // Restores the more descriptive, specific name for use within this method.
    251256        $block_type = $item;
     257
     258        // Don't prepare the response body for HEAD requests.
     259        if ( $request->is_method( 'HEAD' ) ) {
     260            /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-block-types-controller.php */
     261            return apply_filters( 'rest_prepare_block_type', new WP_REST_Response(), $block_type, $request );
     262        }
    252263
    253264        $fields = $this->get_fields_for_response( $request );
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php

    r59882 r59899  
    263263        }
    264264
     265        $is_head_request = $request->is_method( 'HEAD' );
     266        if ( $is_head_request ) {
     267            // Force the 'fields' argument. For HEAD requests, only post IDs are required to calculate pagination.
     268            $prepared_args['fields'] = 'ids';
     269            // Disable priming comment meta for HEAD requests to improve performance.
     270            $prepared_args['update_comment_meta_cache'] = false;
     271        }
     272
    265273        /**
    266274         * Filters WP_Comment_Query arguments when querying comments via the REST API.
     
    278286        $query_result = $query->query( $prepared_args );
    279287
    280         $comments = array();
    281 
    282         foreach ( $query_result as $comment ) {
    283             if ( ! $this->check_read_permission( $comment, $request ) ) {
    284                 continue;
    285             }
    286 
    287             $data       = $this->prepare_item_for_response( $comment, $request );
    288             $comments[] = $this->prepare_response_for_collection( $data );
     288        if ( ! $is_head_request ) {
     289            $comments = array();
     290
     291            foreach ( $query_result as $comment ) {
     292                if ( ! $this->check_read_permission( $comment, $request ) ) {
     293                    continue;
     294                }
     295
     296                $data       = $this->prepare_item_for_response( $comment, $request );
     297                $comments[] = $this->prepare_response_for_collection( $data );
     298            }
    289299        }
    290300
     
    304314        }
    305315
    306         $response = rest_ensure_response( $comments );
     316        $response = $is_head_request ? new WP_REST_Response() : rest_ensure_response( $comments );
    307317        $response->header( 'X-WP-Total', $total_comments );
    308318        $response->header( 'X-WP-TotalPages', $max_pages );
     
    10411051        // Restores the more descriptive, specific name for use within this method.
    10421052        $comment = $item;
     1053
     1054        // Don't prepare the response body for HEAD requests.
     1055        if ( $request->is_method( 'HEAD' ) ) {
     1056            /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php */
     1057            return apply_filters( 'rest_prepare_comment', new WP_REST_Response(), $comment, $request );
     1058        }
    10431059
    10441060        $fields = $this->get_fields_for_response( $request );
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-font-collections-controller.php

    r57686 r59899  
    9090        $collections_page = array_slice( $collections_all, ( $page - 1 ) * $per_page, $per_page );
    9191
     92        $is_head_request = $request->is_method( 'HEAD' );
     93
    9294        $items = array();
    9395        foreach ( $collections_page as $collection ) {
     
    98100                continue;
    99101            }
     102
     103            /*
     104             * Skip preparing the response body for HEAD requests.
     105             * Cannot exit earlier due to backward compatibility reasons,
     106             * as validation occurs in the prepare_item_for_response method.
     107             */
     108            if ( $is_head_request ) {
     109                continue;
     110            }
     111
    100112            $item    = $this->prepare_response_for_collection( $item );
    101113            $items[] = $item;
    102114        }
    103115
    104         $response = rest_ensure_response( $items );
     116        $response = $is_head_request ? new WP_REST_Response() : rest_ensure_response( $items );
    105117
    106118        $response->header( 'X-WP-Total', (int) $total_items );
     
    176188            }
    177189
     190            /**
     191             * Don't prepare the response body for HEAD requests.
     192             * Can't exit at the beginning of the method due to the potential need to return a WP_Error object.
     193             */
     194            if ( $request->is_method( 'HEAD' ) ) {
     195                /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-font-collections-controller.php */
     196                return apply_filters( 'rest_prepare_font_collection', new WP_REST_Response(), $item, $request );
     197            }
     198
    178199            foreach ( $data_fields as $field ) {
    179200                if ( rest_is_field_included( $field, $fields ) ) {
     
    181202                }
    182203            }
     204        }
     205
     206        /**
     207         * Don't prepare the response body for HEAD requests.
     208         * Can't exit at the beginning of the method due to the potential need to return a WP_Error object.
     209         */
     210        if ( $request->is_method( 'HEAD' ) ) {
     211            /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-font-collections-controller.php */
     212            return apply_filters( 'rest_prepare_font_collection', new WP_REST_Response(), $item, $request );
    183213        }
    184214
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-global-styles-revisions-controller.php

    r59630 r59899  
    164164        }
    165165
     166        $is_head_request = $request->is_method( 'HEAD' );
     167
    166168        if ( wp_revisions_enabled( $parent ) ) {
    167169            $registered = $this->get_collection_params();
     
    185187                    $query_args[ $wp_param ] = $request[ $api_param ];
    186188                }
     189            }
     190
     191            if ( $is_head_request ) {
     192                // Force the 'fields' argument. For HEAD requests, only post IDs are required to calculate pagination.
     193                $query_args['fields'] = 'ids';
     194                // Disable priming post meta for HEAD requests to improve performance.
     195                $query_args['update_post_term_cache'] = false;
     196                $query_args['update_post_meta_cache'] = false;
    187197            }
    188198
     
    229239        }
    230240
    231         $response = array();
    232 
    233         foreach ( $revisions as $revision ) {
    234             $data       = $this->prepare_item_for_response( $revision, $request );
    235             $response[] = $this->prepare_response_for_collection( $data );
    236         }
    237 
    238         $response = rest_ensure_response( $response );
     241        if ( ! $is_head_request ) {
     242            $response = array();
     243
     244            foreach ( $revisions as $revision ) {
     245                $data       = $this->prepare_item_for_response( $revision, $request );
     246                $response[] = $this->prepare_response_for_collection( $data );
     247            }
     248
     249            $response = rest_ensure_response( $response );
     250        } else {
     251            $response = new WP_REST_Response();
     252        }
    239253
    240254        $response->header( 'X-WP-Total', (int) $total_revisions );
     
    276290     */
    277291    public function prepare_item_for_response( $post, $request ) {
     292        // Don't prepare the response body for HEAD requests.
     293        if ( $request->is_method( 'HEAD' ) ) {
     294            return new WP_REST_Response();
     295        }
     296
    278297        $parent               = $this->get_parent( $request['parent'] );
    279298        $global_styles_config = $this->get_decoded_global_styles_json( $post->post_content );
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-pattern-directory-controller.php

    r59203 r59899  
    160160
    161161            return $raw_patterns;
     162        }
     163
     164        if ( $request->is_method( 'HEAD' ) ) {
     165            // Return early as this handler doesn't add any response headers.
     166            return new WP_REST_Response();
    162167        }
    163168
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-post-types-controller.php

    r58452 r59899  
    110110     */
    111111    public function get_items( $request ) {
     112        if ( $request->is_method( 'HEAD' ) ) {
     113            // Return early as this handler doesn't add any response headers.
     114            return new WP_REST_Response();
     115        }
     116
    112117        $data  = array();
    113118        $types = get_post_types( array( 'show_in_rest' => true ), 'objects' );
     
    178183        // Restores the more descriptive, specific name for use within this method.
    179184        $post_type = $item;
     185
     186        // Don't prepare the response body for HEAD requests.
     187        if ( $request->is_method( 'HEAD' ) ) {
     188            /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-post-types-controller.php */
     189            return apply_filters( 'rest_prepare_post_type', new WP_REST_Response(), $post_type, $request );
     190        }
    180191
    181192        $taxonomies = wp_list_filter( get_object_taxonomies( $post_type->name, 'objects' ), array( 'show_in_rest' => true ) );
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php

    r59801 r59899  
    412412        $args['post_type'] = $this->post_type;
    413413
     414        $is_head_request = $request->is_method( 'HEAD' );
     415        if ( $is_head_request ) {
     416            // Force the 'fields' argument. For HEAD requests, only post IDs are required to calculate pagination.
     417            $args['fields'] = 'ids';
     418            // Disable priming post meta for HEAD requests to improve performance.
     419            $args['update_post_term_cache'] = false;
     420            $args['update_post_meta_cache'] = false;
     421        }
     422
    414423        /**
    415424         * Filters WP_Query arguments when querying posts via the REST API.
     
    444453        }
    445454
    446         $posts = array();
    447 
    448         update_post_author_caches( $query_result );
    449         update_post_parent_caches( $query_result );
    450 
    451         if ( post_type_supports( $this->post_type, 'thumbnail' ) ) {
    452             update_post_thumbnail_cache( $posts_query );
    453         }
    454 
    455         foreach ( $query_result as $post ) {
    456             if ( ! $this->check_read_permission( $post ) ) {
    457                 continue;
    458             }
    459 
    460             $data    = $this->prepare_item_for_response( $post, $request );
    461             $posts[] = $this->prepare_response_for_collection( $data );
     455        if ( ! $is_head_request ) {
     456            $posts = array();
     457
     458            update_post_author_caches( $query_result );
     459            update_post_parent_caches( $query_result );
     460
     461            if ( post_type_supports( $this->post_type, 'thumbnail' ) ) {
     462                update_post_thumbnail_cache( $posts_query );
     463            }
     464
     465            foreach ( $query_result as $post ) {
     466                if ( ! $this->check_read_permission( $post ) ) {
     467                    continue;
     468                }
     469
     470                $data    = $this->prepare_item_for_response( $post, $request );
     471                $posts[] = $this->prepare_response_for_collection( $data );
     472            }
    462473        }
    463474
     
    489500        }
    490501
    491         $response = rest_ensure_response( $posts );
     502        $response = $is_head_request ? new WP_REST_Response() : rest_ensure_response( $posts );
    492503
    493504        $response->header( 'X-WP-Total', (int) $total_posts );
     
    18331844
    18341845        setup_postdata( $post );
     1846
     1847        // Don't prepare the response body for HEAD requests.
     1848        if ( $request->is_method( 'HEAD' ) ) {
     1849            /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php */
     1850            return apply_filters( "rest_prepare_{$this->post_type}", new WP_REST_Response(), $post, $request );
     1851        }
    18351852
    18361853        $fields = $this->get_fields_for_response( $request );
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php

    r59630 r59899  
    254254        }
    255255
     256        $is_head_request = $request->is_method( 'HEAD' );
     257
    256258        if ( wp_revisions_enabled( $parent ) ) {
    257259            $registered = $this->get_collection_params();
     
    286288            if ( isset( $args['orderby'] ) && 'date' === $args['orderby'] ) {
    287289                $args['orderby'] = 'date ID';
     290            }
     291
     292            if ( $is_head_request ) {
     293                // Force the 'fields' argument. For HEAD requests, only post IDs are required to calculate pagination.
     294                $args['fields'] = 'ids';
     295                // Disable priming post meta for HEAD requests to improve performance.
     296                $args['update_post_term_cache'] = false;
     297                $args['update_post_meta_cache'] = false;
    288298            }
    289299
     
    336346        }
    337347
    338         $response = array();
    339 
    340         foreach ( $revisions as $revision ) {
    341             $data       = $this->prepare_item_for_response( $revision, $request );
    342             $response[] = $this->prepare_response_for_collection( $data );
    343         }
    344 
    345         $response = rest_ensure_response( $response );
     348        if ( ! $is_head_request ) {
     349            $response = array();
     350
     351            foreach ( $revisions as $revision ) {
     352                $data       = $this->prepare_item_for_response( $revision, $request );
     353                $response[] = $this->prepare_response_for_collection( $data );
     354            }
     355
     356            $response = rest_ensure_response( $response );
     357        } else {
     358            $response = new WP_REST_Response();
     359        }
    346360
    347361        $response->header( 'X-WP-Total', (int) $total_revisions );
     
    574588
    575589        setup_postdata( $post );
     590
     591        // Don't prepare the response body for HEAD requests.
     592        if ( $request->is_method( 'HEAD' ) ) {
     593            /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php */
     594            return apply_filters( 'rest_prepare_revision', new WP_REST_Response(), $post, $request );
     595        }
    576596
    577597        $fields = $this->get_fields_for_response( $request );
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-search-controller.php

    r57839 r59899  
    143143        $ids = $result[ WP_REST_Search_Handler::RESULT_IDS ];
    144144
    145         $results = array();
    146 
    147         foreach ( $ids as $id ) {
    148             $data      = $this->prepare_item_for_response( $id, $request );
    149             $results[] = $this->prepare_response_for_collection( $data );
     145        $is_head_request = $request->is_method( 'HEAD' );
     146        if ( ! $is_head_request ) {
     147            $results = array();
     148
     149            foreach ( $ids as $id ) {
     150                $data      = $this->prepare_item_for_response( $id, $request );
     151                $results[] = $this->prepare_response_for_collection( $data );
     152            }
    150153        }
    151154
     
    163166        }
    164167
    165         $response = rest_ensure_response( $results );
     168        $response = $is_head_request ? new WP_REST_Response() : rest_ensure_response( $results );
    166169        $response->header( 'X-WP-Total', $total );
    167170        $response->header( 'X-WP-TotalPages', $max_pages );
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-sidebars-controller.php

    r56586 r59899  
    120120     */
    121121    public function get_items( $request ) {
     122        if ( $request->is_method( 'HEAD' ) ) {
     123            // Return early as this handler doesn't add any response headers.
     124            return new WP_REST_Response();
     125        }
     126
    122127        $this->retrieve_widgets();
    123128
     
    321326        // Restores the more descriptive, specific name for use within this method.
    322327        $raw_sidebar = $item;
     328
     329        // Don't prepare the response body for HEAD requests.
     330        if ( $request->is_method( 'HEAD' ) ) {
     331            /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-sidebars-controller.php */
     332            return apply_filters( 'rest_prepare_sidebar', new WP_REST_Response(), $raw_sidebar, $request );
     333        }
    323334
    324335        $id      = $raw_sidebar['id'];
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-taxonomies-controller.php

    r58704 r59899  
    114114     */
    115115    public function get_items( $request ) {
     116        if ( $request->is_method( 'HEAD' ) ) {
     117            // Return early as this handler doesn't add any response headers.
     118            return new WP_REST_Response();
     119        }
    116120
    117121        // Retrieve the list of registered collection query parameters.
     
    210214        // Restores the more descriptive, specific name for use within this method.
    211215        $taxonomy = $item;
     216
     217        // Don't prepare the response body for HEAD requests.
     218        if ( $request->is_method( 'HEAD' ) ) {
     219            /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-taxonomies-controller.php */
     220            return apply_filters( 'rest_prepare_taxonomy', new WP_REST_Response(), $taxonomy, $request );
     221        }
    212222
    213223        $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-template-autosaves-controller.php

    r56819 r59899  
    176176        $response = $this->parent_controller->prepare_item_for_response( $template, $request );
    177177
     178        // Don't prepare the response body for HEAD requests.
     179        if ( $request->is_method( 'HEAD' ) ) {
     180            return $response;
     181        }
     182
    178183        $fields = $this->get_fields_for_response( $request );
    179184        $data   = $response->get_data();
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-template-revisions-controller.php

    r59605 r59899  
    201201        $response = $this->parent_controller->prepare_item_for_response( $template, $request );
    202202
     203        // Don't prepare the response body for HEAD requests.
     204        if ( $request->is_method( 'HEAD' ) ) {
     205            return $response;
     206        }
     207
    203208        $fields = $this->get_fields_for_response( $request );
    204209        $data   = $response->get_data();
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php

    r59461 r59899  
    270270     */
    271271    public function get_items( $request ) {
     272        if ( $request->is_method( 'HEAD' ) ) {
     273            // Return early as this handler doesn't add any response headers.
     274            return new WP_REST_Response();
     275        }
     276
    272277        $query = array();
    273278        if ( isset( $request['wp_id'] ) ) {
     
    669674     */
    670675    public function prepare_item_for_response( $item, $request ) {
     676        // Don't prepare the response body for HEAD requests.
     677        if ( $request->is_method( 'HEAD' ) ) {
     678            return new WP_REST_Response();
     679        }
     680
    671681        /*
    672682         * Resolve pattern blocks so they don't need to be resolved client-side
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php

    r59458 r59899  
    313313        }
    314314
     315        $is_head_request = $request->is_method( 'HEAD' );
     316        if ( $is_head_request ) {
     317            // Force the 'fields' argument. For HEAD requests, only term IDs are required.
     318            $prepared_args['fields'] = 'ids';
     319            // Disable priming term meta for HEAD requests to improve performance.
     320            $prepared_args['update_term_meta_cache'] = false;
     321        }
     322
    315323        /**
    316324         * Filters get_terms() arguments when querying terms via the REST API.
     
    355363        }
    356364
    357         $response = array();
    358 
    359         foreach ( $query_result as $term ) {
    360             $data       = $this->prepare_item_for_response( $term, $request );
    361             $response[] = $this->prepare_response_for_collection( $data );
    362         }
    363 
    364         $response = rest_ensure_response( $response );
     365        if ( ! $is_head_request ) {
     366            $response = array();
     367            foreach ( $query_result as $term ) {
     368                $data       = $this->prepare_item_for_response( $term, $request );
     369                $response[] = $this->prepare_response_for_collection( $data );
     370            }
     371        }
     372
     373        $response = $is_head_request ? new WP_REST_Response() : rest_ensure_response( $response );
    365374
    366375        // Store pagination values for headers.
     
    887896     */
    888897    public function prepare_item_for_response( $item, $request ) {
     898
     899        // Don't prepare the response body for HEAD requests.
     900        if ( $request->is_method( 'HEAD' ) ) {
     901            /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php */
     902            return apply_filters( "rest_prepare_{$this->taxonomy}", new WP_REST_Response(), $item, $request );
     903        }
    889904
    890905        $fields = $this->get_fields_for_response( $request );
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php

    r59892 r59899  
    356356            $prepared_args['search'] = '*' . $prepared_args['search'] . '*';
    357357        }
     358
     359        $is_head_request = $request->is_method( 'HEAD' );
     360        if ( $is_head_request ) {
     361            // Force the 'fields' argument. For HEAD requests, only user IDs are required.
     362            $prepared_args['fields'] = 'id';
     363        }
    358364        /**
    359365         * Filters WP_User_Query arguments when querying users via the REST API.
     
    370376        $query = new WP_User_Query( $prepared_args );
    371377
    372         $users = array();
    373 
    374         foreach ( $query->get_results() as $user ) {
    375             $data    = $this->prepare_item_for_response( $user, $request );
    376             $users[] = $this->prepare_response_for_collection( $data );
    377         }
    378 
    379         $response = rest_ensure_response( $users );
     378        if ( ! $is_head_request ) {
     379            $users = array();
     380
     381            foreach ( $query->get_results() as $user ) {
     382                $data    = $this->prepare_item_for_response( $user, $request );
     383                $users[] = $this->prepare_response_for_collection( $data );
     384            }
     385        }
     386
     387        $response = $is_head_request ? new WP_REST_Response() : rest_ensure_response( $users );
    380388
    381389        // Store pagination values for headers then unset for count query.
     
    10211029        // Restores the more descriptive, specific name for use within this method.
    10221030        $user = $item;
     1031
     1032        // Don't prepare the response body for HEAD requests.
     1033        if ( $request->is_method( 'HEAD' ) ) {
     1034            /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php */
     1035            return apply_filters( 'rest_prepare_user', new WP_REST_Response(), $user, $request );
     1036        }
    10231037
    10241038        $fields = $this->get_fields_for_response( $request );
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-widget-types-controller.php

    r56586 r59899  
    146146     */
    147147    public function get_items( $request ) {
     148        if ( $request->is_method( 'HEAD' ) ) {
     149            // Return early as this handler doesn't add any response headers.
     150            return new WP_REST_Response();
     151        }
     152
    148153        $data = array();
    149154        foreach ( $this->get_widgets() as $widget ) {
     
    298303        // Restores the more descriptive, specific name for use within this method.
    299304        $widget_type = $item;
     305
     306        // Don't prepare the response body for HEAD requests.
     307        if ( $request->is_method( 'HEAD' ) ) {
     308            /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-widget-types-controller.php */
     309            return apply_filters( 'rest_prepare_widget_type', new WP_REST_Response(), $widget_type, $request );
     310        }
    300311
    301312        $fields = $this->get_fields_for_response( $request );
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-widgets-controller.php

    r58704 r59899  
    137137     */
    138138    public function get_items( $request ) {
     139        if ( $request->is_method( 'HEAD' ) ) {
     140            // Return early as this handler doesn't add any response headers.
     141            return new WP_REST_Response();
     142        }
     143
    139144        $this->retrieve_widgets();
    140145
     
    679684
    680685        $widget    = $wp_registered_widgets[ $widget_id ];
     686        // Don't prepare the response body for HEAD requests.
     687        if ( $request->is_method( 'HEAD' ) ) {
     688            /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-widgets-controller.php */
     689            return apply_filters( 'rest_prepare_widget', new WP_REST_Response(), $widget, $request );
     690        }
     691
    681692        $parsed_id = wp_parse_widget_id( $widget_id );
    682693        $fields    = $this->get_fields_for_response( $request );
  • trunk/tests/phpunit/tests/fonts/font-library/wpRestFontCollectionsController.php

    r57686 r59899  
    7979
    8080    /**
     81     * @dataProvider data_readable_http_methods
    8182     * @covers WP_REST_Font_Collections_Controller::get_items
    82      */
    83     public function test_get_items_should_only_return_valid_collections() {
     83     * @ticket 56481
     84     *
     85     * @param string $method The HTTP method to use.
     86     */
     87    public function test_get_items_should_only_return_valid_collections( $method ) {
    8488        $this->setExpectedIncorrectUsage( 'WP_Font_Collection::load_from_json' );
    8589
     
    9397        );
    9498
    95         $request  = new WP_REST_Request( 'GET', '/wp/v2/font-collections' );
     99        $request  = new WP_REST_Request( $method, '/wp/v2/font-collections' );
    96100        $response = rest_get_server()->dispatch( $request );
    97101        $content  = $response->get_data();
     
    100104
    101105        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
    102         $this->assertCount( 1, $content, 'The response should only contain valid collections.' );
     106        if ( 'HEAD' !== $method ) {
     107            $this->assertCount( 1, $content, 'The response should only contain valid collections.' );
     108            return null;
     109        }
     110
     111        $this->assertNull( $content, 'The response should be empty.' );
     112        $headers = $response->get_headers();
     113        $this->assertArrayHasKey( 'X-WP-Total', $headers, 'The "X-WP-Total" header should be present in the response.' );
     114        // Includes non-valid collections.
     115        $this->assertSame( 2, $headers['X-WP-Total'], 'The "X-WP-Total" header value should be equal to 1.' );
    103116    }
    104117
     
    128141
    129142    /**
     143     * @dataProvider data_readable_http_methods
     144     * @ticket 56481
     145     *
     146     * @param string $method The HTTP method to use.
     147     */
     148    public function test_get_item_should_allow_adding_headers_via_filter( $method ) {
     149        $hook_name = 'rest_prepare_font_collection';
     150        $filter    = new MockAction();
     151        $callback  = array( $filter, 'filter' );
     152        add_filter( $hook_name, $callback );
     153        $header_filter = new class() {
     154            public static function add_custom_header( $response ) {
     155                $response->header( 'X-Test-Header', 'Test' );
     156
     157                return $response;
     158            }
     159        };
     160        add_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     161        wp_set_current_user( self::$admin_id );
     162        $request  = new WP_REST_Request( $method, '/wp/v2/font-collections/mock-col-slug' );
     163        $response = rest_get_server()->dispatch( $request );
     164        remove_filter( $hook_name, $callback );
     165        remove_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     166
     167        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     168        $this->assertSame( 1, $filter->get_call_count(), 'The "' . $hook_name . '" filter was not called when it should be for GET/HEAD requests.' );
     169        $headers = $response->get_headers();
     170        $this->assertArrayHasKey( 'X-Test-Header', $headers, 'The "X-Test-Header" header should be present in the response.' );
     171        $this->assertSame( 'Test', $headers['X-Test-Header'], 'The "X-Test-Header" header value should be equal to "Test".' );
     172        if ( 'HEAD' !== $method ) {
     173            return null;
     174        }
     175        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     176    }
     177
     178    /**
     179     * Data provider intended to provide HTTP method names for testing GET and HEAD requests.
     180     *
     181     * @return array
     182     */
     183    public static function data_readable_http_methods() {
     184        return array(
     185            'GET request'  => array( 'GET' ),
     186            'HEAD request' => array( 'HEAD' ),
     187        );
     188    }
     189
     190    /**
     191     * @dataProvider data_readable_http_methods
    130192     * @covers WP_REST_Font_Collections_Controller::get_item
    131      */
    132     public function test_get_item_invalid_slug() {
    133         wp_set_current_user( self::$admin_id );
    134         $request  = new WP_REST_Request( 'GET', '/wp/v2/font-collections/non-existing-collection' );
     193     * @ticket 56481
     194     *
     195     * @param string $method The HTTP method to use.
     196     */
     197    public function test_get_item_invalid_slug( $method ) {
     198        wp_set_current_user( self::$admin_id );
     199        $request  = new WP_REST_Request( $method, '/wp/v2/font-collections/non-existing-collection' );
    135200        $response = rest_get_server()->dispatch( $request );
    136201        $this->assertErrorResponse( 'rest_font_collection_not_found', $response, 404 );
     
    138203
    139204    /**
     205     * @dataProvider data_readable_http_methods
    140206     * @covers WP_REST_Font_Collections_Controller::get_item
    141      */
    142     public function test_get_item_invalid_collection() {
     207     * @ticket 56481
     208     *
     209     * @param string $method The HTTP method to use.
     210     */
     211    public function test_get_item_invalid_collection( $method ) {
    143212        $this->setExpectedIncorrectUsage( 'WP_Font_Collection::load_from_json' );
    144213
     
    153222        );
    154223
    155         $request  = new WP_REST_Request( 'GET', '/wp/v2/font-collections/' . $slug );
     224        $request  = new WP_REST_Request( $method, '/wp/v2/font-collections/' . $slug );
    156225        $response = rest_get_server()->dispatch( $request );
    157226
     
    162231
    163232    /**
     233     * @dataProvider data_readable_http_methods
    164234     * @covers WP_REST_Font_Collections_Controller::get_item
    165      */
    166     public function test_get_item_invalid_id_permission() {
    167         $request = new WP_REST_Request( 'GET', '/wp/v2/font-collections/mock-col-slug' );
     235     * @ticket 56481
     236     *
     237     * @param string $method The HTTP method to use.
     238     */
     239    public function test_get_item_invalid_id_permission( $method ) {
     240        $request = new WP_REST_Request( $method, '/wp/v2/font-collections/mock-col-slug' );
    168241
    169242        wp_set_current_user( 0 );
  • trunk/tests/phpunit/tests/rest-api/rest-autosaves-controller.php

    r56745 r59899  
    180180    }
    181181
    182     public function test_get_items_no_permission() {
     182    /**
     183     * @ticket 56481
     184     */
     185    public function test_get_items_with_head_request_should_not_prepare_autosaves_data() {
     186        $request = new WP_REST_Request( 'HEAD', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     187
     188        $hook_name = 'rest_prepare_autosave';
     189        $filter    = new MockAction();
     190        $callback  = array( $filter, 'filter' );
     191
     192        add_filter( $hook_name, $callback );
     193        $response = rest_get_server()->dispatch( $request );
     194        remove_filter( $hook_name, $callback );
     195
     196        $this->assertNotWPError( $response );
     197        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     198        $this->assertSame( 0, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' );
     199        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     200    }
     201
     202    /**
     203     * @dataProvider data_readable_http_methods
     204     * @ticket 56481
     205     *
     206     * @param string $method The HTTP method to use.
     207     */
     208    public function test_get_items_no_permission( $method ) {
    183209        wp_set_current_user( 0 );
    184         $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves' );
     210        $request  = new WP_REST_Request( $method, '/wp/v2/posts/' . self::$post_id . '/autosaves' );
    185211        $response = rest_get_server()->dispatch( $request );
    186212        $this->assertErrorResponse( 'rest_cannot_read', $response, 401 );
     
    190216    }
    191217
    192     public function test_get_items_missing_parent() {
    193         wp_set_current_user( self::$editor_id );
    194         $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/autosaves' );
     218    /**
     219     * Data provider intended to provide HTTP method names for testing GET and HEAD requests.
     220     *
     221     * @return array
     222     */
     223    public static function data_readable_http_methods() {
     224        return array(
     225            'GET request'  => array( 'GET' ),
     226            'HEAD request' => array( 'HEAD' ),
     227        );
     228    }
     229
     230    /**
     231     * @dataProvider data_readable_http_methods
     232     * @ticket 56481
     233     *
     234     * @param string $method The HTTP method to use.
     235     */
     236    public function test_get_items_missing_parent( $method ) {
     237        wp_set_current_user( self::$editor_id );
     238        $request  = new WP_REST_Request( $method, '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/autosaves' );
    195239        $response = rest_get_server()->dispatch( $request );
    196240        $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
    197241    }
    198242
    199     public function test_get_items_invalid_parent_post_type() {
    200         wp_set_current_user( self::$editor_id );
    201         $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$page_id . '/autosaves' );
     243    /**
     244     * @dataProvider data_readable_http_methods
     245     * @ticket 56481
     246     *
     247     * @param string $method The HTTP method to use.
     248     */
     249    public function test_get_items_invalid_parent_post_type( $method ) {
     250        wp_set_current_user( self::$editor_id );
     251        $request  = new WP_REST_Request( $method, '/wp/v2/posts/' . self::$page_id . '/autosaves' );
    202252        $response = rest_get_server()->dispatch( $request );
    203253        $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
     
    231281    }
    232282
     283    /**
     284     * @dataProvider data_readable_http_methods
     285     * @ticket 56481
     286     *
     287     * @param string $method The HTTP method to use.
     288     */
     289    public function test_get_item_should_allow_adding_headers_via_filter( $method ) {
     290        wp_set_current_user( self::$editor_id );
     291        $request = new WP_REST_Request( $method, '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     292
     293        $hook_name = 'rest_prepare_autosave';
     294        $filter    = new MockAction();
     295        $callback  = array( $filter, 'filter' );
     296        add_filter( $hook_name, $callback );
     297        $header_filter = new class() {
     298            public static function add_custom_header( $response ) {
     299                $response->header( 'X-Test-Header', 'Test' );
     300
     301                return $response;
     302            }
     303        };
     304        add_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     305        $response = rest_get_server()->dispatch( $request );
     306        remove_filter( $hook_name, $callback );
     307        remove_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     308
     309        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     310        $this->assertSame( 1, $filter->get_call_count(), 'The "' . $hook_name . '" filter was not called when it should be for GET/HEAD requests.' );
     311        $headers = $response->get_headers();
     312        $this->assertArrayHasKey( 'X-Test-Header', $headers, 'The "X-Test-Header" header should be present in the response.' );
     313        $this->assertSame( 'Test', $headers['X-Test-Header'], 'The "X-Test-Header" header value should be equal to "Test".' );
     314        if ( 'HEAD' !== $method ) {
     315            return null;
     316        }
     317        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     318    }
     319
    233320    public function test_get_item_embed_context() {
    234321        wp_set_current_user( self::$editor_id );
     
    249336    }
    250337
    251     public function test_get_item_no_permission() {
    252         $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
     338    /**
     339     * @dataProvider data_readable_http_methods
     340     * @ticket 56481
     341     *
     342     * @param string $method The HTTP method to use.
     343     */
     344    public function test_get_item_no_permission( $method ) {
     345        $request = new WP_REST_Request( $method, '/wp/v2/posts/' . self::$post_id . '/autosaves/' . self::$autosave_post_id );
    253346        wp_set_current_user( self::$contributor_id );
    254347        $response = rest_get_server()->dispatch( $request );
     
    256349    }
    257350
    258     public function test_get_item_missing_parent() {
    259         wp_set_current_user( self::$editor_id );
    260         $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/autosaves/' . self::$autosave_post_id );
     351    /**
     352     * @dataProvider data_readable_http_methods
     353     * @ticket 56481
     354     *
     355     * @param string $method The HTTP method to use.
     356     */
     357    public function test_get_item_missing_parent( $method ) {
     358        wp_set_current_user( self::$editor_id );
     359        $request  = new WP_REST_Request( $method, '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/autosaves/' . self::$autosave_post_id );
    261360        $response = rest_get_server()->dispatch( $request );
    262361        $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
    263362    }
    264363
    265     public function test_get_item_invalid_parent_post_type() {
    266         wp_set_current_user( self::$editor_id );
    267         $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$page_id . '/autosaves' );
     364    /**
     365     * @dataProvider data_readable_http_methods
     366     * @ticket 56481
     367     *
     368     * @param string $method The HTTP method to use.
     369     */
     370    public function test_get_item_invalid_parent_post_type( $method ) {
     371        wp_set_current_user( self::$editor_id );
     372        $request  = new WP_REST_Request( $method, '/wp/v2/posts/' . self::$page_id . '/autosaves' );
    268373        $response = rest_get_server()->dispatch( $request );
    269374        $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
  • trunk/tests/phpunit/tests/rest-api/rest-block-type-controller.php

    r57565 r59899  
    603603
    604604    /**
    605      * @ticket 47620
    606      */
    607     public function test_get_items_wrong_permission() {
     605     * @dataProvider data_readable_http_methods
     606     * @ticket 56481
     607     *
     608     * @param string $method The HTTP method to use.
     609     */
     610    public function test_get_item_should_allow_adding_headers_via_filter( $method ) {
     611        $block_name = 'fake/test';
     612        wp_set_current_user( self::$admin_id );
     613
     614        $hook_name = 'rest_prepare_block_type';
     615        $filter    = new MockAction();
     616        $callback  = array( $filter, 'filter' );
     617        add_filter( $hook_name, $callback );
     618        $header_filter = new class() {
     619            public static function add_custom_header( $response ) {
     620                $response->header( 'X-Test-Header', 'Test' );
     621
     622                return $response;
     623            }
     624        };
     625        add_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     626        $request  = new WP_REST_Request( $method, '/wp/v2/block-types/' . $block_name );
     627        $response = rest_get_server()->dispatch( $request );
     628        remove_filter( $hook_name, $callback );
     629        remove_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     630
     631        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     632        $this->assertSame( 1, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' );
     633        $headers = $response->get_headers();
     634        $this->assertArrayHasKey( 'X-Test-Header', $headers, 'The "X-Test-Header" header should be present in the response.' );
     635        $this->assertSame( 'Test', $headers['X-Test-Header'], 'The "X-Test-Header" header value should be equal to "Test".' );
     636        if ( 'HEAD' !== $method ) {
     637            return null;
     638        }
     639        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     640    }
     641
     642    /**
     643     * Data provider intended to provide HTTP method names for testing GET and HEAD requests.
     644     *
     645     * @return array
     646     */
     647    public static function data_readable_http_methods() {
     648        return array(
     649            'GET request'  => array( 'GET' ),
     650            'HEAD request' => array( 'HEAD' ),
     651        );
     652    }
     653
     654    /**
     655     * @ticket 56481
     656     */
     657    public function test_get_items_with_head_request_should_not_prepare_block_type_data() {
     658        wp_set_current_user( self::$admin_id );
     659        $request  = new WP_REST_Request( 'HEAD', '/wp/v2/block-types' );
     660        $response = rest_get_server()->dispatch( $request );
     661        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     662        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     663    }
     664
     665    /**
     666     * @dataProvider data_readable_http_methods
     667     * @ticket 47620
     668     * @ticket 56481
     669     *
     670     * @param string $method HTTP method to use.
     671     */
     672    public function test_get_items_wrong_permission( $method ) {
    608673        wp_set_current_user( self::$subscriber_id );
    609         $request  = new WP_REST_Request( 'GET', '/wp/v2/block-types' );
     674        $request  = new WP_REST_Request( $method, '/wp/v2/block-types' );
    610675        $response = rest_get_server()->dispatch( $request );
    611676        $this->assertErrorResponse( 'rest_block_type_cannot_view', $response, 403 );
     
    613678
    614679    /**
    615      * @ticket 47620
    616      */
    617     public function test_get_item_wrong_permission() {
     680     * @dataProvider data_readable_http_methods
     681     * @ticket 47620
     682     * @ticket 56481
     683     *
     684     * @param string $method HTTP method to use.
     685     */
     686    public function test_get_item_wrong_permission( $method ) {
    618687        wp_set_current_user( self::$subscriber_id );
    619         $request  = new WP_REST_Request( 'GET', '/wp/v2/block-types/fake/test' );
     688        $request  = new WP_REST_Request( $method, '/wp/v2/block-types/fake/test' );
    620689        $response = rest_get_server()->dispatch( $request );
    621690        $this->assertErrorResponse( 'rest_block_type_cannot_view', $response, 403 );
     
    623692
    624693    /**
    625      * @ticket 47620
    626      */
    627     public function test_get_items_no_permission() {
     694     * @dataProvider data_readable_http_methods
     695     * @ticket 47620
     696     * @ticket 56481
     697     *
     698     * @param string $method HTTP method to use.
     699     */
     700    public function test_get_items_no_permission( $method ) {
    628701        wp_set_current_user( 0 );
    629         $request  = new WP_REST_Request( 'GET', '/wp/v2/block-types' );
     702        $request  = new WP_REST_Request( $method, '/wp/v2/block-types' );
    630703        $response = rest_get_server()->dispatch( $request );
    631704        $this->assertErrorResponse( 'rest_block_type_cannot_view', $response, 401 );
     
    633706
    634707    /**
    635      * @ticket 47620
    636      */
    637     public function test_get_item_no_permission() {
     708     * @dataProvider data_readable_http_methods
     709     * @ticket 47620
     710     * @ticket 56481
     711     *
     712     * @param string $method HTTP method to use.
     713     */
     714    public function test_get_item_no_permission( $method ) {
    638715        wp_set_current_user( 0 );
    639         $request  = new WP_REST_Request( 'GET', '/wp/v2/block-types/fake/test' );
     716        $request  = new WP_REST_Request( $method, '/wp/v2/block-types/fake/test' );
    640717        $response = rest_get_server()->dispatch( $request );
    641718        $this->assertErrorResponse( 'rest_block_type_cannot_view', $response, 401 );
     
    643720
    644721    /**
    645      * @ticket 47620
     722     * @dataProvider data_readable_http_methods
     723     * @ticket 47620
     724     * @ticket 56481
     725     *
     726     * @param string $method HTTP method to use.
    646727     */
    647728    public function test_prepare_item() {
  • trunk/tests/phpunit/tests/rest-api/rest-categories-controller.php

    r56746 r59899  
    577577    }
    578578
    579     public function test_get_terms_invalid_parent_arg() {
    580         $request = new WP_REST_Request( 'GET', '/wp/v2/categories' );
     579    /**
     580     * @dataProvider data_readable_http_methods
     581     * @ticket 56481
     582     *
     583     * @param string $method HTTP method to use.
     584     */
     585    public function test_get_terms_invalid_parent_arg( $method ) {
     586        $request = new WP_REST_Request( $method, '/wp/v2/categories' );
    581587        $request->set_param( 'parent', 'invalid-parent' );
    582588        $response = rest_get_server()->dispatch( $request );
     
    610616    }
    611617
    612     public function test_get_terms_pagination_headers() {
     618    /**
     619     * @dataProvider data_readable_http_methods
     620     * @ticket 56481
     621     *
     622     * @param string $method HTTP method to use.
     623     */
     624    public function test_get_terms_pagination_headers( $method ) {
    613625        $total_categories = self::$total_categories;
    614626        $total_pages      = (int) ceil( $total_categories / 10 );
    615627
    616628        // Start of the index + Uncategorized default term.
    617         $request  = new WP_REST_Request( 'GET', '/wp/v2/categories' );
     629        $request  = new WP_REST_Request( $method, '/wp/v2/categories' );
    618630        $response = rest_get_server()->dispatch( $request );
    619631        $headers  = $response->get_headers();
    620632        $this->assertSame( $total_categories, $headers['X-WP-Total'] );
    621633        $this->assertSame( $total_pages, $headers['X-WP-TotalPages'] );
    622         $this->assertCount( 10, $response->get_data() );
     634        if ( 'HEAD' !== $method ) {
     635            $this->assertCount( 10, $response->get_data() );
     636        }
    623637        $next_link = add_query_arg(
    624638            array(
     
    663677        $this->assertSame( $total_categories, $headers['X-WP-Total'] );
    664678        $this->assertSame( $total_pages, $headers['X-WP-TotalPages'] );
    665         $this->assertCount( 1, $response->get_data() );
     679        if ( 'HEAD' !== $method ) {
     680            $this->assertCount( 1, $response->get_data() );
     681        }
    666682        $prev_link = add_query_arg(
    667683            array(
     
    680696        $this->assertSame( $total_categories, $headers['X-WP-Total'] );
    681697        $this->assertSame( $total_pages, $headers['X-WP-TotalPages'] );
    682         $this->assertCount( 0, $response->get_data() );
     698        if ( 'HEAD' !== $method ) {
     699            $this->assertCount( 0, $response->get_data() );
     700        }
    683701        $prev_link = add_query_arg(
    684702            array(
     
    12371255        $this->check_taxonomy_term( $category, $data, $response->get_links() );
    12381256    }
     1257
     1258    /**
     1259     * @dataProvider data_readable_http_methods
     1260     * @ticket 56481
     1261     *
     1262     * @param string $method HTTP method to use.
     1263     */
     1264    public function test_get_items_only_fetches_ids_for_head_requests( $method ) {
     1265        $is_head_request = 'HEAD' === $method;
     1266        $request         = new WP_REST_Request( $method, '/wp/v2/categories' );
     1267
     1268        $filter = new MockAction();
     1269
     1270        add_filter( 'terms_pre_query', array( $filter, 'filter' ), 10, 2 );
     1271
     1272        $response = rest_get_server()->dispatch( $request );
     1273
     1274        $this->assertSame( 200, $response->get_status() );
     1275        if ( $is_head_request ) {
     1276            $this->assertEmpty( $response->get_data() );
     1277        } else {
     1278            $this->assertNotEmpty( $response->get_data() );
     1279        }
     1280
     1281        $args = $filter->get_args();
     1282        $this->assertTrue( isset( $args[0][1] ), 'Query parameters were not captured.' );
     1283        $this->assertInstanceOf( WP_Term_Query::class, $args[0][1], 'Query parameters were not captured.' );
     1284
     1285        /** @var WP_Term_Query $query */
     1286        $query = $args[0][1];
     1287
     1288        if ( $is_head_request ) {
     1289            $this->assertArrayHasKey( 'fields', $query->query_vars, 'The fields parameter is not set in the query vars.' );
     1290            $this->assertSame( 'ids', $query->query_vars['fields'], 'The query must fetch only term IDs.' );
     1291            $this->assertArrayHasKey( 'update_term_meta_cache', $query->query_vars, 'The update_term_meta_cache key is missing in the query vars.' );
     1292            $this->assertFalse( $query->query_vars['update_term_meta_cache'], 'The update_term_meta_cache value should be false for HEAD requests.' );
     1293        } else {
     1294            $this->assertTrue(
     1295                ! array_key_exists( 'fields', $query->query_vars ) || 'ids' !== $query->query_vars['fields'],
     1296                'The fields parameter should not be forced to "ids" for non-HEAD requests.'
     1297            );
     1298            $this->assertArrayHasKey( 'update_term_meta_cache', $query->query_vars, 'The update_term_meta_cache key is missing in the query vars.' );
     1299            $this->assertTrue( $query->query_vars['update_term_meta_cache'], 'The update_term_meta_cache value should be true for HEAD requests.' );
     1300        }
     1301
     1302        if ( ! $is_head_request ) {
     1303            return;
     1304        }
     1305
     1306        global $wpdb;
     1307        $terms_table = preg_quote( $wpdb->terms, '/' );
     1308
     1309        $pattern = '/SELECT\s+t\.term_id.+FROM\s+' . $terms_table . '\s+AS\s+t\s+INNER\s+JOIN/is';
     1310
     1311        // Assert that the SQL query only fetches the term_id column.
     1312        $this->assertMatchesRegularExpression( $pattern, $query->request, 'The SQL query does not match the expected string.' );
     1313    }
     1314
     1315    /**
     1316     * Data provider intended to provide HTTP method names for testing GET and HEAD requests.
     1317     *
     1318     * @return array
     1319     */
     1320    public static function data_readable_http_methods() {
     1321        return array(
     1322            'GET request'  => array( 'GET' ),
     1323            'HEAD request' => array( 'HEAD' ),
     1324        );
     1325    }
     1326
     1327    /**
     1328     * @dataProvider data_readable_http_methods
     1329     * @ticket 56481
     1330     *
     1331     * @param string $method The HTTP method to use.
     1332     */
     1333    public function test_get_item_should_allow_adding_headers_via_filter( $method ) {
     1334        $category_id = self::factory()->category->create();
     1335
     1336        $request = new WP_REST_Request( $method, sprintf( '/wp/v2/categories/%d', $category_id ) );
     1337
     1338        $hook_name = 'rest_prepare_category';
     1339
     1340        $filter   = new MockAction();
     1341        $callback = array( $filter, 'filter' );
     1342        add_filter( $hook_name, $callback );
     1343        $header_filter = new class() {
     1344            public static function add_custom_header( $response ) {
     1345                $response->header( 'X-Test-Header', 'Test' );
     1346
     1347                return $response;
     1348            }
     1349        };
     1350        add_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     1351        $response = rest_get_server()->dispatch( $request );
     1352        remove_filter( $hook_name, $callback );
     1353        remove_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     1354
     1355        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     1356        $this->assertSame( 1, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' );
     1357        $headers = $response->get_headers();
     1358        $this->assertArrayHasKey( 'X-Test-Header', $headers, 'The "X-Test-Header" header should be present in the response.' );
     1359        $this->assertSame( 'Test', $headers['X-Test-Header'], 'The "X-Test-Header" header value should be equal to "Test".' );
     1360        if ( 'HEAD' !== $method ) {
     1361            return null;
     1362        }
     1363        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     1364    }
    12391365}
  • trunk/tests/phpunit/tests/rest-api/rest-comments-controller.php

    r57176 r59899  
    433433    }
    434434
    435     public function test_get_items_no_permission_for_no_post() {
     435    /**
     436     * @dataProvider data_readable_http_methods
     437     * @ticket 56481
     438     *
     439     * @param string $method HTTP method to use.
     440     */
     441    public function test_get_items_no_permission_for_no_post( $method ) {
    436442        wp_set_current_user( 0 );
    437443
    438         $request = new WP_REST_Request( 'GET', '/wp/v2/comments' );
     444        $request = new WP_REST_Request( $method, '/wp/v2/comments' );
    439445        $request->set_param( 'post', 0 );
    440446        $response = rest_get_server()->dispatch( $request );
     
    442448    }
    443449
    444     public function test_get_items_edit_context() {
    445         wp_set_current_user( self::$admin_id );
    446 
    447         $request = new WP_REST_Request( 'GET', '/wp/v2/comments' );
     450    /**
     451     * Data provider intended to provide HTTP method names for testing GET and HEAD requests.
     452     *
     453     * @return array
     454     */
     455    public static function data_readable_http_methods() {
     456        return array(
     457            'GET request'  => array( 'GET' ),
     458            'HEAD request' => array( 'HEAD' ),
     459        );
     460    }
     461
     462    /**
     463     * @dataProvider data_readable_http_methods
     464     * @ticket 56481
     465     *
     466     * @param string $method HTTP method to use.
     467     */
     468    public function test_get_items_edit_context( $method ) {
     469        wp_set_current_user( self::$admin_id );
     470
     471        $request = new WP_REST_Request( $method, '/wp/v2/comments' );
    448472        $request->set_param( 'context', 'edit' );
    449473        $response = rest_get_server()->dispatch( $request );
     
    594618    }
    595619
    596     public function test_get_items_private_post_no_permissions() {
     620    /**
     621     * @dataProvider data_readable_http_methods
     622     * @ticket 56481
     623     *
     624     * @param string $method HTTP method to use.
     625     */
     626    public function test_get_items_private_post_no_permissions( $method ) {
    597627        wp_set_current_user( 0 );
    598628
    599629        $post_id = self::factory()->post->create( array( 'post_status' => 'private' ) );
    600630
    601         $request = new WP_REST_Request( 'GET', '/wp/v2/comments' );
     631        $request = new WP_REST_Request( $method, '/wp/v2/comments' );
    602632        $request->set_param( 'post', $post_id );
    603633        $response = rest_get_server()->dispatch( $request );
     
    802832    }
    803833
    804     public function test_get_comments_pagination_headers() {
     834    /**
     835     * @dataProvider data_readable_http_methods
     836     * @ticket 56481
     837     *
     838     * @param string $method HTTP method to use.
     839     */
     840    public function test_get_comments_pagination_headers( $method ) {
    805841        $total_comments = self::$total_comments;
    806842        $total_pages    = (int) ceil( $total_comments / 10 );
     
    809845
    810846        // Start of the index.
    811         $request  = new WP_REST_Request( 'GET', '/wp/v2/comments' );
     847        $request  = new WP_REST_Request( $method, '/wp/v2/comments' );
    812848        $response = rest_get_server()->dispatch( $request );
    813849        $headers  = $response->get_headers();
     
    885921    }
    886922
    887     public function test_get_comments_invalid_date() {
    888         $request = new WP_REST_Request( 'GET', '/wp/v2/comments' );
     923    /**
     924     * @dataProvider data_readable_http_methods
     925     * @ticket 56481
     926     *
     927     * @param string $method HTTP method to use.
     928     */
     929    public function test_get_comments_invalid_date( $method ) {
     930        $request = new WP_REST_Request( $method, '/wp/v2/comments' );
    889931        $request->set_param( 'after', 'foo' );
    890932        $request->set_param( 'before', 'bar' );
     
    9981040    }
    9991041
    1000     public function test_get_comment_invalid_id() {
    1001         $request = new WP_REST_Request( 'GET', '/wp/v2/comments/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER );
     1042    /**
     1043     * @dataProvider data_readable_http_methods
     1044     * @ticket 56481
     1045     *
     1046     * @param string $method HTTP method to use.
     1047     */
     1048    public function test_get_comment_invalid_id( $method ) {
     1049        $request = new WP_REST_Request( $method, '/wp/v2/comments/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER );
    10021050
    10031051        $response = rest_get_server()->dispatch( $request );
     
    10051053    }
    10061054
    1007     public function test_get_comment_invalid_context() {
     1055    /**
     1056     * @dataProvider data_readable_http_methods
     1057     * @ticket 56481
     1058     *
     1059     * @param string $method HTTP method to use.
     1060     */
     1061    public function test_get_comment_invalid_context( $method ) {
    10081062        wp_set_current_user( 0 );
    10091063
    1010         $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/comments/%s', self::$approved_id ) );
     1064        $request = new WP_REST_Request( $method, sprintf( '/wp/v2/comments/%s', self::$approved_id ) );
    10111065        $request->set_param( 'context', 'edit' );
    10121066        $response = rest_get_server()->dispatch( $request );
     
    10141068    }
    10151069
    1016     public function test_get_comment_invalid_post_id() {
     1070    /**
     1071     * @dataProvider data_readable_http_methods
     1072     * @ticket 56481
     1073     *
     1074     * @param string $method HTTP method to use.
     1075     */
     1076    public function test_get_comment_invalid_post_id( $method ) {
    10171077        wp_set_current_user( 0 );
    10181078
     
    10241084        );
    10251085
    1026         $request  = new WP_REST_Request( 'GET', '/wp/v2/comments/' . $comment_id );
     1086        $request  = new WP_REST_Request( $method, '/wp/v2/comments/' . $comment_id );
    10271087        $response = rest_get_server()->dispatch( $request );
    10281088        $this->assertErrorResponse( 'rest_post_invalid_id', $response, 404 );
    10291089    }
    10301090
    1031     public function test_get_comment_invalid_post_id_as_admin() {
     1091    /**
     1092     * @dataProvider data_readable_http_methods
     1093     * @ticket 56481
     1094     *
     1095     * @param string $method HTTP method to use.
     1096     */
     1097    public function test_get_comment_invalid_post_id_as_admin( $method ) {
    10321098        wp_set_current_user( self::$admin_id );
    10331099
     
    10391105        );
    10401106
    1041         $request  = new WP_REST_Request( 'GET', '/wp/v2/comments/' . $comment_id );
     1107        $request  = new WP_REST_Request( $method, '/wp/v2/comments/' . $comment_id );
    10421108        $response = rest_get_server()->dispatch( $request );
    10431109        $this->assertErrorResponse( 'rest_post_invalid_id', $response, 404 );
    10441110    }
    10451111
    1046     public function test_get_comment_not_approved() {
     1112    /**
     1113     * @dataProvider data_readable_http_methods
     1114     * @ticket 56481
     1115     *
     1116     * @param string $method HTTP method to use.
     1117     */
     1118    public function test_get_comment_not_approved( $method ) {
    10471119        wp_set_current_user( 0 );
    10481120
    1049         $request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/comments/%d', self::$hold_id ) );
     1121        $request  = new WP_REST_Request( $method, sprintf( '/wp/v2/comments/%d', self::$hold_id ) );
    10501122        $response = rest_get_server()->dispatch( $request );
    10511123        $this->assertErrorResponse( 'rest_cannot_read', $response, 401 );
    10521124    }
    10531125
    1054     public function test_get_comment_not_approved_same_user() {
    1055         wp_set_current_user( self::$admin_id );
    1056 
    1057         $request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/comments/%d', self::$hold_id ) );
     1126    /**
     1127     * @dataProvider data_readable_http_methods
     1128     * @ticket 56481
     1129     *
     1130     * @param string $method HTTP method to use.
     1131     */
     1132    public function test_get_comment_not_approved_same_user( $method ) {
     1133        wp_set_current_user( self::$admin_id );
     1134
     1135        $request  = new WP_REST_Request( $method, sprintf( '/wp/v2/comments/%d', self::$hold_id ) );
    10581136        $response = rest_get_server()->dispatch( $request );
    10591137        $this->assertSame( 200, $response->get_status() );
     
    10991177    }
    11001178
    1101     public function test_get_comment_with_password_without_edit_post_permission() {
     1179    /**
     1180     * @dataProvider data_readable_http_methods
     1181     * @ticket 56481
     1182     *
     1183     * @param string $method HTTP method to use.
     1184     */
     1185    public function test_get_comment_with_password_without_edit_post_permission( $method ) {
    11021186        wp_set_current_user( self::$subscriber_id );
    11031187
     
    11091193        $password_comment = self::factory()->comment->create( $args );
    11101194
    1111         $request  = new WP_REST_Request( 'GET', sprintf( '/wp/v2/comments/%s', $password_comment ) );
     1195        $request  = new WP_REST_Request( $method, sprintf( '/wp/v2/comments/%s', $password_comment ) );
    11121196        $response = rest_get_server()->dispatch( $request );
    11131197        $this->assertErrorResponse( 'rest_cannot_read', $response, 403 );
     
    11151199
    11161200    /**
     1201     * @dataProvider data_readable_http_methods
    11171202     * @ticket 38692
    1118      */
    1119     public function test_get_comment_with_password_with_valid_password() {
     1203     * @ticket 56481
     1204     *
     1205     * @param string $method HTTP method to use.
     1206     */
     1207    public function test_get_comment_with_password_with_valid_password( $method ) {
    11201208        wp_set_current_user( self::$subscriber_id );
    11211209
     
    11271215        $password_comment = self::factory()->comment->create( $args );
    11281216
    1129         $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/comments/%s', $password_comment ) );
     1217        $request = new WP_REST_Request( $method, sprintf( '/wp/v2/comments/%s', $password_comment ) );
    11301218        $request->set_param( 'password', 'toomanysecrets' );
    11311219
     
    33663454
    33673455    /**
     3456     * @dataProvider data_readable_http_methods
    33683457     * @ticket 42238
    3369      */
    3370     public function test_check_read_post_permission_with_invalid_post_type() {
     3458     * @ticket 56481
     3459     *
     3460     * @param string $method HTTP method to use.
     3461     */
     3462    public function test_check_read_post_permission_with_invalid_post_type( $method ) {
    33713463        register_post_type(
    33723464            'bug-post',
     
    33873479
    33883480        wp_set_current_user( self::$admin_id );
    3389         $request  = new WP_REST_Request( 'GET', '/wp/v2/comments/' . $comment_id );
     3481        $request  = new WP_REST_Request( $method, '/wp/v2/comments/' . $comment_id );
    33903482        $response = rest_get_server()->dispatch( $request );
    33913483        $this->assertSame( 403, $response->get_status() );
    33923484    }
     3485
     3486    /**
     3487     * @dataProvider data_readable_http_methods
     3488     * @ticket 56481
     3489     *
     3490     * @param string $method HTTP method to use.
     3491     */
     3492    public function test_get_items_only_fetches_ids_for_head_requests( $method ) {
     3493        $is_head_request = 'HEAD' === $method;
     3494        $request         = new WP_REST_Request( $method, '/wp/v2/comments' );
     3495
     3496        $filter = new MockAction();
     3497
     3498        add_filter( 'comments_pre_query', array( $filter, 'filter' ), 10, 2 );
     3499
     3500        $response = rest_get_server()->dispatch( $request );
     3501
     3502        $this->assertSame( 200, $response->get_status() );
     3503        if ( $is_head_request ) {
     3504            $this->assertEmpty( $response->get_data() );
     3505        } else {
     3506            $this->assertNotEmpty( $response->get_data() );
     3507        }
     3508
     3509        $args = $filter->get_args();
     3510        $this->assertTrue( isset( $args[0][1] ), 'Query parameters were not captured.' );
     3511        $this->assertInstanceOf( WP_Comment_Query::class, $args[0][1], 'Query parameters were not captured.' );
     3512
     3513        /** @var WP_Comment_Query $query */
     3514        $query = $args[0][1];
     3515
     3516        if ( $is_head_request ) {
     3517            $this->assertArrayHasKey( 'fields', $query->query_vars, 'The fields parameter is not set in the query vars.' );
     3518            $this->assertSame( 'ids', $query->query_vars['fields'], 'The query must fetch only post IDs.' );
     3519            $this->assertArrayHasKey( 'update_comment_meta_cache', $query->query_vars, 'The update_comment_meta_cache key is missing in the query vars.' );
     3520            $this->assertFalse( $query->query_vars['update_comment_meta_cache'], 'The update_comment_meta_cache value should be false for HEAD requests.' );
     3521        } else {
     3522            $this->assertTrue( ! array_key_exists( 'fields', $query->query_vars ) || 'ids' !== $query->query_vars['fields'], 'The fields parameter should not be forced to "ids" for non-HEAD requests.' );
     3523            $this->assertArrayHasKey( 'update_comment_meta_cache', $query->query_vars, 'The update_comment_meta_cache key is missing in the query vars.' );
     3524            $this->assertTrue( $query->query_vars['update_comment_meta_cache'], 'The update_comment_meta_cache value should be true for non-HEAD requests.' );
     3525            return;
     3526        }
     3527
     3528        global $wpdb;
     3529        $comments_table = preg_quote( $wpdb->comments, '/' );
     3530        $pattern        = '/^SELECT\s+SQL_CALC_FOUND_ROWS\s+' . $comments_table . '\.comment_ID\s+FROM\s+' . $comments_table . '\s+WHERE/i';
     3531
     3532        // Assert that the SQL query only fetches the ID column.
     3533        $this->assertMatchesRegularExpression( $pattern, $query->request, 'The SQL query does not match the expected string.' );
     3534    }
     3535
     3536    /**
     3537     * @dataProvider data_readable_http_methods
     3538     * @ticket 56481
     3539     *
     3540     * @param string $method The HTTP method to use.
     3541     */
     3542    public function test_get_item_should_allow_adding_headers_via_filter( $method ) {
     3543        $request = new WP_REST_Request( $method, sprintf( '/wp/v2/comments/%d', self::$approved_id ) );
     3544
     3545        $hook_name = 'rest_prepare_comment';
     3546
     3547        $filter   = new MockAction();
     3548        $callback = array( $filter, 'filter' );
     3549        add_filter( $hook_name, $callback );
     3550        $header_filter = new class() {
     3551            public static function add_custom_header( $response ) {
     3552                $response->header( 'X-Test-Header', 'Test' );
     3553
     3554                return $response;
     3555            }
     3556        };
     3557        add_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     3558        $response = rest_get_server()->dispatch( $request );
     3559        remove_filter( $hook_name, $callback );
     3560        remove_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     3561
     3562        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     3563        $headers = $response->get_headers();
     3564        $this->assertSame( 1, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' );
     3565        $this->assertArrayHasKey( 'X-Test-Header', $headers, 'The "X-Test-Header" header should be present in the response.' );
     3566        $this->assertSame( 'Test', $headers['X-Test-Header'], 'The "X-Test-Header" header value should be equal to "Test".' );
     3567        if ( 'HEAD' !== $method ) {
     3568            return null;
     3569        }
     3570        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     3571    }
    33933572}
  • trunk/tests/phpunit/tests/rest-api/rest-global-styles-revisions-controller.php

    r59630 r59899  
    246246
    247247    /**
    248      * @ticket 58524
    249      *
    250      * @covers WP_REST_Global_Styles_Controller::get_items
    251      */
    252     public function test_get_items_missing_parent() {
    253         wp_set_current_user( self::$admin_id );
    254         $request  = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/revisions' );
     248     * @dataProvider data_readable_http_methods
     249     * @ticket 58524
     250     * @ticket 56481
     251     *
     252     * @covers WP_REST_Global_Styles_Controller::get_items
     253     *
     254     * @param string $method The HTTP method to use.
     255     */
     256    public function test_get_items_missing_parent( $method ) {
     257        wp_set_current_user( self::$admin_id );
     258        $request  = new WP_REST_Request( $method, '/wp/v2/global-styles/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/revisions' );
    255259        $response = rest_get_server()->dispatch( $request );
    256260        $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
     261    }
     262
     263    /**
     264     * Data provider intended to provide HTTP method names for testing GET and HEAD requests.
     265     *
     266     * @return array
     267     */
     268    public static function data_readable_http_methods() {
     269        return array(
     270            'GET request'  => array( 'GET' ),
     271            'HEAD request' => array( 'HEAD' ),
     272        );
    257273    }
    258274
     
    312328
    313329    /**
     330     * @ticket 56481
     331     *
     332     * @covers WP_REST_Global_Styles_Controller::prepare_item_for_response
     333     */
     334    public function test_get_items_should_return_no_response_body_for_head_requests() {
     335        wp_set_current_user( self::$admin_id );
     336        $request  = new WP_REST_Request( 'HEAD', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' );
     337        $response = rest_get_server()->dispatch( $request );
     338        $this->assertSame( 200, $response->get_status(), 'Response status is 200.' );
     339        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     340    }
     341
     342    /**
    314343     * @ticket 59810
    315344     *
     
    328357
    329358    /**
     359     * @ticket 56481
     360     *
     361     * @covers WP_REST_Global_Styles_Controller::get_item
     362     * @covers WP_REST_Global_Styles_Controller::prepare_item_for_response
     363     */
     364    public function test_get_item_should_return_no_response_body_for_head_requests() {
     365        wp_set_current_user( self::$admin_id );
     366        $request  = new WP_REST_Request( 'HEAD', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions/' . $this->revision_1_id );
     367        $response = rest_get_server()->dispatch( $request );
     368        $this->assertSame( 200, $response->get_status(), 'Response status is 200.' );
     369        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     370    }
     371
     372    /**
     373     * @dataProvider data_readable_http_methods
    330374     * @ticket 59810
     375     * @ticket 56481
    331376     *
    332377     * @covers WP_REST_Global_Styles_Controller::get_revision
    333      */
    334     public function test_get_item_invalid_revision_id_should_error() {
     378     *
     379     * @param string $method The HTTP method to use.
     380     */
     381    public function test_get_item_invalid_revision_id_should_error( $method ) {
    335382        wp_set_current_user( self::$admin_id );
    336383
    337384        $expected_error  = 'rest_post_invalid_id';
    338385        $expected_status = 404;
    339         $request         = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions/20000001' );
     386        $request         = new WP_REST_Request( $method, '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions/20000001' );
    340387        $response        = rest_get_server()->dispatch( $request );
    341388
     
    420467
    421468    /**
     469     * @dataProvider data_readable_http_methods
    422470     * @ticket 58524
    423471     * @ticket 60131
     472     * @ticket 56481
    424473     *
    425474     * @covers WP_REST_Global_Styles_Controller::get_item_permissions_check
    426      */
    427     public function test_get_item_permissions_check() {
     475     *
     476     * @param string $method The HTTP method to use.
     477     */
     478    public function test_get_item_permissions_check( $method ) {
    428479        wp_set_current_user( self::$author_id );
    429         $request  = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' );
     480        $request  = new WP_REST_Request( $method, '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' );
    430481        $response = rest_get_server()->dispatch( $request );
    431482
     
    436487     * Tests the pagination header of the first page.
    437488     *
    438      * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_pagination_header_of_the_first_page
    439      *
    440      * @ticket 58524
    441      *
    442      * @covers WP_REST_Global_Styles_Controller::get_items
    443      */
    444     public function test_get_items_pagination_header_of_the_first_page() {
     489     * @dataProvider data_readable_http_methods
     490     * @ticket 58524
     491     * @ticket 56481
     492     *
     493     * @covers WP_REST_Global_Styles_Controller::get_items
     494     *
     495     * @param string $method The HTTP method to use.
     496     */
     497    public function test_get_items_pagination_header_of_the_first_page( $method ) {
    445498        wp_set_current_user( self::$admin_id );
    446499
     
    450503        $page        = 1;  // First page.
    451504
    452         $request = new WP_REST_Request( 'GET', $rest_route );
     505        $request = new WP_REST_Request( $method, $rest_route );
    453506        $request->set_query_params(
    454507            array(
     
    477530     * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_pagination_header_of_the_last_page
    478531     *
    479      * @ticket 58524
    480      *
    481      * @covers WP_REST_Global_Styles_Controller::get_items
    482      */
    483     public function test_get_items_pagination_header_of_the_last_page() {
     532     * @dataProvider data_readable_http_methods
     533     * @ticket 58524
     534     * @ticket 56481
     535     *
     536     * @covers WP_REST_Global_Styles_Controller::get_items
     537     *
     538     * @param string $method The HTTP method to use.
     539     */
     540    public function test_get_items_pagination_header_of_the_last_page( $method ) {
    484541        wp_set_current_user( self::$admin_id );
    485542
     
    489546        $page        = 2;  // Last page.
    490547
    491         $request = new WP_REST_Request( 'GET', $rest_route );
     548        $request = new WP_REST_Request( $method, $rest_route );
    492549        $request->set_query_params(
    493550            array(
     
    515572     * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_invalid_per_page_should_error
    516573     *
    517      * @ticket 58524
    518      *
    519      * @covers WP_REST_Global_Styles_Controller::get_items
    520      */
    521     public function test_get_items_invalid_per_page_should_error() {
     574     * @dataProvider data_readable_http_methods
     575     * @ticket 58524
     576     * @ticket 56481
     577     *
     578     * @covers WP_REST_Global_Styles_Controller::get_items
     579     *
     580     * @param string $method The HTTP method to use.
     581     */
     582    public function test_get_items_invalid_per_page_should_error( $method ) {
    522583        wp_set_current_user( self::$admin_id );
    523584
     
    526587        $expected_status = 400;
    527588
    528         $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' );
     589        $request = new WP_REST_Request( $method, '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' );
    529590        $request->set_param( 'per_page', $per_page );
    530591        $response = rest_get_server()->dispatch( $request );
     
    537598     * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_out_of_bounds_page_should_error
    538599     *
    539      * @ticket 58524
    540      *
    541      * @covers WP_REST_Global_Styles_Controller::get_items
    542      */
    543     public function test_get_items_out_of_bounds_page_should_error() {
     600     * @dataProvider data_readable_http_methods
     601     * @ticket 58524
     602     * @ticket 56481
     603     *
     604     * @covers WP_REST_Global_Styles_Controller::get_items
     605     *
     606     * @param string $method The HTTP method to use.
     607     */
     608    public function test_get_items_out_of_bounds_page_should_error( $method ) {
    544609        wp_set_current_user( self::$admin_id );
    545610
     
    550615        $expected_status = 400;
    551616
    552         $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' );
     617        $request = new WP_REST_Request( $method, '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' );
    553618        $request->set_query_params(
    554619            array(
     
    566631     * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_invalid_max_pages_should_error
    567632     *
    568      * @ticket 58524
    569      *
    570      * @covers WP_REST_Global_Styles_Controller::get_items
    571      */
    572     public function test_get_items_invalid_max_pages_should_error() {
     633     * @dataProvider data_readable_http_methods
     634     * @ticket 58524
     635     * @ticket 56481
     636     *
     637     * @covers WP_REST_Global_Styles_Controller::get_items
     638     *
     639     * @param string $method The HTTP method to use.
     640     */
     641    public function test_get_items_invalid_max_pages_should_error( $method ) {
    573642        wp_set_current_user( self::$admin_id );
    574643
     
    578647        $expected_status = 400;
    579648
    580         $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' );
     649        $request = new WP_REST_Request( $method, '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' );
    581650        $request->set_query_params(
    582651            array(
     
    690759     * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_total_revisions_offset_should_return_empty_data
    691760     *
    692      * @ticket 58524
    693      *
    694      * @covers WP_REST_Global_Styles_Controller::get_items
    695      */
    696     public function test_get_items_total_revisions_offset_should_return_empty_data() {
     761     * @dataProvider data_readable_http_methods
     762     * @ticket 58524
     763     * @ticket 56481
     764     *
     765     * @covers WP_REST_Global_Styles_Controller::get_items
     766     *
     767     * @param string $method The HTTP method to use.
     768     */
     769    public function test_get_items_total_revisions_offset_should_return_empty_data( $method ) {
    697770        wp_set_current_user( self::$admin_id );
    698771
     
    702775        $expected_status = 400;
    703776
    704         $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' );
     777        $request = new WP_REST_Request( $method, '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' );
    705778        $request->set_query_params(
    706779            array(
     
    718791     * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_out_of_bound_offset_should_error
    719792     *
    720      * @ticket 58524
    721      *
    722      * @covers WP_REST_Global_Styles_Controller::get_items
    723      */
    724     public function test_get_items_out_of_bound_offset_should_error() {
     793     * @dataProvider data_readable_http_methods
     794     * @ticket 58524
     795     * @ticket 56481
     796     *
     797     * @covers WP_REST_Global_Styles_Controller::get_items
     798     *
     799     * @param string $method The HTTP method to use.
     800     */
     801    public function test_get_items_out_of_bound_offset_should_error( $method ) {
    725802        wp_set_current_user( self::$admin_id );
    726803
     
    730807        $expected_status = 400;
    731808
    732         $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' );
     809        $request = new WP_REST_Request( $method, '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' );
    733810        $request->set_query_params(
    734811            array(
     
    746823     * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_impossible_high_number_offset_should_error
    747824     *
    748      * @ticket 58524
    749      *
    750      * @covers WP_REST_Global_Styles_Controller::get_items
    751      */
    752     public function test_get_items_impossible_high_number_offset_should_error() {
     825     * @dataProvider data_readable_http_methods
     826     * @ticket 58524
     827     * @ticket 56481
     828     *
     829     * @covers WP_REST_Global_Styles_Controller::get_items
     830     *
     831     * @param string $method The HTTP method to use.
     832     */
     833    public function test_get_items_impossible_high_number_offset_should_error( $method ) {
    753834        wp_set_current_user( self::$admin_id );
    754835
     
    758839        $expected_status = 400;
    759840
    760         $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' );
     841        $request = new WP_REST_Request( $method, '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' );
    761842        $request->set_query_params(
    762843            array(
     
    774855     * Duplicate of WP_Test_REST_Revisions_Controller::test_get_items_invalid_offset_should_error
    775856     *
    776      * @ticket 58524
    777      *
    778      * @covers WP_REST_Global_Styles_Controller::get_items
    779      */
    780     public function test_get_items_invalid_offset_should_error() {
     857     * @dataProvider data_readable_http_methods
     858     * @ticket 58524
     859     * @ticket 56481
     860     *
     861     * @covers WP_REST_Global_Styles_Controller::get_items
     862     *
     863     * @param string $method The HTTP method to use.
     864     */
     865    public function test_get_items_invalid_offset_should_error( $method ) {
    781866        wp_set_current_user( self::$admin_id );
    782867
     
    786871        $expected_status = 400;
    787872
    788         $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' );
     873        $request = new WP_REST_Request( $method, '/wp/v2/global-styles/' . self::$global_styles_id . '/revisions' );
    789874        $request->set_query_params(
    790875            array(
  • trunk/tests/phpunit/tests/rest-api/rest-pattern-directory-controller.php

    r56559 r59899  
    146146
    147147    /**
     148     * @ticket 56481
     149     */
     150    public function test_get_items_with_head_request_should_not_prepare_block_patterns_data() {
     151        wp_set_current_user( self::$contributor_id );
     152        self::mock_successful_response( 'browse-all', true );
     153
     154        $request = new WP_REST_Request( 'HEAD', '/wp/v2/pattern-directory/patterns' );
     155
     156        $hook_name = 'rest_prepare_block_pattern';
     157        $filter    = new MockAction();
     158        $callback  = array( $filter, 'filter' );
     159
     160        add_filter( $hook_name, $callback );
     161        $response = rest_get_server()->dispatch( $request );
     162        remove_filter( $hook_name, $callback );
     163
     164        $this->assertNotWPError( $response );
     165        $response = rest_ensure_response( $response );
     166
     167        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     168
     169        $this->assertSame( 0, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' );
     170        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     171    }
     172
     173    /**
    148174     * @covers WP_REST_Pattern_Directory_Controller::get_items
    149175     *
     
    220246
    221247    /**
    222      * @covers WP_REST_Pattern_Directory_Controller::get_items
    223      *
    224      * @since 5.8.0
    225      */
    226     public function test_get_items_wdotorg_unavailable() {
     248     * @dataProvider data_readable_http_methods
     249     * @ticket 56481
     250     *
     251     * @covers WP_REST_Pattern_Directory_Controller::get_items
     252     *
     253     * @since 5.8.0
     254     *
     255     * @param string $method The HTTP method to use.
     256     */
     257    public function test_get_items_wdotorg_unavailable( $method ) {
    227258        wp_set_current_user( self::$contributor_id );
    228259        self::prevent_requests_to_host( 'api.wordpress.org' );
    229260
    230         $request  = new WP_REST_Request( 'GET', '/wp/v2/pattern-directory/patterns' );
     261        $request  = new WP_REST_Request( $method, '/wp/v2/pattern-directory/patterns' );
    231262        $response = rest_do_request( $request );
    232263
     
    235266
    236267    /**
    237      * @covers WP_REST_Pattern_Directory_Controller::get_items
    238      *
    239      * @since 5.8.0
    240      */
    241     public function test_get_items_logged_out() {
    242         $request = new WP_REST_Request( 'GET', '/wp/v2/pattern-directory/patterns' );
     268     * Data provider intended to provide HTTP method names for testing GET and HEAD requests.
     269     *
     270     * @return array
     271     */
     272    public static function data_readable_http_methods() {
     273        return array(
     274            'GET request'  => array( 'GET' ),
     275            'HEAD request' => array( 'HEAD' ),
     276        );
     277    }
     278
     279    /**
     280     * @dataProvider data_readable_http_methods
     281     * @ticket 56481
     282     *
     283     * @covers WP_REST_Pattern_Directory_Controller::get_items
     284     *
     285     * @since 5.8.0
     286     *
     287     * @param string $method The HTTP method to use.
     288     */
     289    public function test_get_items_logged_out( $method ) {
     290        $request = new WP_REST_Request( $method, '/wp/v2/pattern-directory/patterns' );
    243291        $request->set_query_params( array( 'search' => 'button' ) );
    244292        $response = rest_do_request( $request );
     
    284332
    285333    /**
    286      * @covers WP_REST_Pattern_Directory_Controller::get_items
    287      *
    288      * @since 5.8.0
    289      */
    290     public function test_get_items_invalid_response_data() {
     334     * @dataProvider data_readable_http_methods
     335     * @ticket 56481
     336     *
     337     * @covers WP_REST_Pattern_Directory_Controller::get_items
     338     *
     339     * @since 5.8.0
     340     *
     341     * @param string $method The HTTP method to use.
     342     */
     343    public function test_get_items_invalid_response_data( $method ) {
    291344        wp_set_current_user( self::$contributor_id );
    292345        self::mock_successful_response( 'invalid-data', true );
    293346
    294         $request  = new WP_REST_Request( 'GET', '/wp/v2/pattern-directory/patterns' );
     347        $request  = new WP_REST_Request( $method, '/wp/v2/pattern-directory/patterns' );
    295348        $response = rest_do_request( $request );
    296349
  • trunk/tests/phpunit/tests/rest-api/rest-post-types-controller.php

    r58452 r59899  
    4545    }
    4646
    47     public function test_get_items_invalid_permission_for_context() {
     47    /**
     48     * @dataProvider data_readable_http_methods
     49     * @ticket 56481
     50     *
     51     * @param string $method HTTP method to use.
     52     */
     53    public function test_get_items_invalid_permission_for_context( $method ) {
    4854        wp_set_current_user( 0 );
    49         $request = new WP_REST_Request( 'GET', '/wp/v2/types' );
     55        $request = new WP_REST_Request( $method, '/wp/v2/types' );
    5056        $request->set_param( 'context', 'edit' );
    5157        $response = rest_get_server()->dispatch( $request );
    5258        $this->assertErrorResponse( 'rest_cannot_view', $response, 401 );
     59    }
     60
     61    /**
     62     * Data provider intended to provide HTTP method names for testing GET and HEAD requests.
     63     *
     64     * @return array
     65     */
     66    public static function data_readable_http_methods() {
     67        return array(
     68            'GET request'  => array( 'GET' ),
     69            'HEAD request' => array( 'HEAD' ),
     70        );
    5371    }
    5472
     
    5977        $data = $response->get_data();
    6078        $this->assertSame( array( 'category', 'post_tag' ), $data['taxonomies'] );
     79    }
     80
     81    /**
     82     * @dataProvider data_readable_http_methods
     83     * @ticket 56481
     84     *
     85     * @param string $method The HTTP method to use.
     86     */
     87    public function test_get_item_should_allow_adding_headers_via_filter( $method ) {
     88        $request = new WP_REST_Request( $method, '/wp/v2/types/post' );
     89
     90        $hook_name = 'rest_prepare_post_type';
     91        $filter    = new MockAction();
     92        $callback  = array( $filter, 'filter' );
     93        add_filter( $hook_name, $callback );
     94        $header_filter = new class() {
     95            public static function add_custom_header( $response ) {
     96                $response->header( 'X-Test-Header', 'Test' );
     97
     98                return $response;
     99            }
     100        };
     101        add_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     102        $response = rest_get_server()->dispatch( $request );
     103        remove_filter( $hook_name, $callback );
     104        remove_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     105
     106        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     107        $this->assertSame( 1, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' );
     108        $headers = $response->get_headers();
     109        $this->assertArrayHasKey( 'X-Test-Header', $headers, 'The "X-Test-Header" header should be present in the response.' );
     110        $this->assertSame( 'Test', $headers['X-Test-Header'], 'The "X-Test-Header" header value should be equal to "Test".' );
     111        if ( 'HEAD' !== $method ) {
     112            return null;
     113        }
     114        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
    61115    }
    62116
     
    107161    }
    108162
    109     public function test_get_item_invalid_type() {
    110         $request  = new WP_REST_Request( 'GET', '/wp/v2/types/invalid' );
     163    /**
     164     * @dataProvider data_readable_http_methods
     165     * @ticket 56481
     166     *
     167     * @param string $method HTTP method to use.
     168     */
     169    public function test_get_item_invalid_type( $method ) {
     170        $request  = new WP_REST_Request( $method, '/wp/v2/types/invalid' );
    111171        $response = rest_get_server()->dispatch( $request );
    112172        $this->assertErrorResponse( 'rest_type_invalid', $response, 404 );
     
    122182    }
    123183
    124     public function test_get_item_invalid_permission_for_context() {
     184    /**
     185     * @dataProvider data_readable_http_methods
     186     * @ticket 56481
     187     *
     188     * @param string $method HTTP method to use.
     189     */
     190    public function test_get_item_invalid_permission_for_context( $method ) {
    125191        wp_set_current_user( 0 );
    126         $request = new WP_REST_Request( 'GET', '/wp/v2/types/post' );
     192        $request = new WP_REST_Request( $method, '/wp/v2/types/post' );
    127193        $request->set_param( 'context', 'edit' );
    128194        $response = rest_get_server()->dispatch( $request );
     
    244310    public function additional_field_get_callback( $response_data ) {
    245311        return 123;
     312    }
     313
     314    /**
     315     * @ticket 56481
     316     */
     317    public function test_get_items_with_head_request_should_not_prepare_post_types_data() {
     318        $request   = new WP_REST_Request( 'HEAD', '/wp/v2/types' );
     319        $hook_name = 'rest_prepare_post_type';
     320        $filter    = new MockAction();
     321        $callback  = array( $filter, 'filter' );
     322        add_filter( $hook_name, $callback );
     323        $response = rest_get_server()->dispatch( $request );
     324        remove_filter( $hook_name, $callback );
     325        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     326        $this->assertSame( 0, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' );
     327        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
    246328    }
    247329
  • trunk/tests/phpunit/tests/rest-api/rest-posts-controller.php

    r59801 r59899  
    275275
    276276    /**
     277     * @ticket 56481
     278     */
     279    public function test_get_items_with_head_request_should_not_prepare_post_data() {
     280        $request = new WP_REST_Request( 'HEAD', '/wp/v2/posts' );
     281
     282        $hook_name = 'rest_prepare_post';
     283        $filter    = new MockAction();
     284        $callback  = array( $filter, 'filter' );
     285
     286        add_filter( $hook_name, $callback );
     287        $response = rest_get_server()->dispatch( $request );
     288        remove_filter( $hook_name, $callback );
     289
     290        $this->assertNotWPError( $response );
     291        $response = rest_ensure_response( $response );
     292
     293        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     294
     295        $headers = $response->get_headers();
     296        $this->assertSame( 0, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' );
     297        $this->assertArrayHasKey( 'Link', $headers, 'The "Link" header should be present in the response.' );
     298        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     299    }
     300
     301    /**
    277302     * A valid query that returns 0 results should return an empty JSON list.
     303     * In case of a HEAD request, the response should not contain a body.
    278304     *
     305     * @dataProvider data_readable_http_methods
    279306     * @link https://github.com/WP-API/WP-API/issues/862
    280      */
    281     public function test_get_items_empty_query() {
    282         $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     307     * @ticket 56481
     308     *
     309     * @covers WP_REST_Posts_Controller::get_items
     310     *
     311     * @param string $method The HTTP method to use.
     312     */
     313    public function test_get_items_empty_query( $method ) {
     314        $request = new WP_REST_Request( $method, '/wp/v2/posts' );
    283315        $request->set_query_params(
    284316            array(
     
    288320        $response = rest_get_server()->dispatch( $request );
    289321
    290         $this->assertEmpty( $response->get_data() );
    291         $this->assertSame( 200, $response->get_status() );
    292     }
    293 
    294     public function test_get_items_author_query() {
     322        if ( $request->is_method( 'HEAD' ) ) {
     323            $this->assertNull( $response->get_data(), 'Failed asserting that response data is null for HEAD request.' );
     324        } else {
     325            $this->assertSame( array(), $response->get_data(), 'Failed asserting that response data is an empty array for GET request.' );
     326        }
     327
     328        $headers = $response->get_headers();
     329        $this->assertSame( 0, $headers['X-WP-Total'], 'Failed asserting that X-WP-Total header is 0.' );
     330        $this->assertSame( 0, $headers['X-WP-TotalPages'], 'Failed asserting that X-WP-TotalPages header is 0.' );
     331    }
     332
     333    /**
     334     * @dataProvider data_readable_http_methods
     335     * @ticket 56481
     336     *
     337     * @param string $method The HTTP method to use.
     338     */
     339    public function test_get_items_author_query( $method ) {
    295340        self::factory()->post->create( array( 'post_author' => self::$editor_id ) );
    296341        self::factory()->post->create( array( 'post_author' => self::$author_id ) );
     
    299344
    300345        // All posts in the database.
    301         $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     346        $request = new WP_REST_Request( $method, '/wp/v2/posts' );
    302347        $request->set_param( 'per_page', self::$per_page );
    303348        $response = rest_get_server()->dispatch( $request );
    304349        $this->assertSame( 200, $response->get_status() );
    305         $this->assertCount( $total_posts, $response->get_data() );
     350        if ( $request->is_method( 'get' ) ) {
     351            $this->assertCount( $total_posts, $response->get_data() );
     352
     353        } else {
     354            $this->assertNull( $response->get_data(), 'Failed asserting that response data is null for HEAD request.' );
     355            $headers = $response->get_headers();
     356            $this->assertSame( $total_posts, $headers['X-WP-Total'] );
     357        }
    306358
    307359        // Limit to editor and author.
    308         $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     360        $request = new WP_REST_Request( $method, '/wp/v2/posts' );
    309361        $request->set_param( 'author', array( self::$editor_id, self::$author_id ) );
    310362        $response = rest_get_server()->dispatch( $request );
    311363        $this->assertSame( 200, $response->get_status() );
    312364        $data = $response->get_data();
    313         $this->assertCount( 2, $data );
    314         $this->assertSameSets( array( self::$editor_id, self::$author_id ), wp_list_pluck( $data, 'author' ) );
     365        if ( $request->is_method( 'get' ) ) {
     366            $this->assertCount( 2, $data );
     367            $this->assertSameSets( array( self::$editor_id, self::$author_id ), wp_list_pluck( $data, 'author' ) );
     368        } else {
     369            $this->assertNull( $data, 'Failed asserting that response data is null for HEAD request.' );
     370            $headers = $response->get_headers();
     371            $this->assertSame( 2, $headers['X-WP-Total'], 'Failed asserting that X-WP-Total header is 2.' );
     372        }
    315373
    316374        // Limit to editor.
    317         $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     375        $request = new WP_REST_Request( $method, '/wp/v2/posts' );
    318376        $request->set_param( 'author', self::$editor_id );
    319377        $response = rest_get_server()->dispatch( $request );
    320378        $this->assertSame( 200, $response->get_status() );
    321379        $data = $response->get_data();
    322         $this->assertCount( 1, $data );
    323         $this->assertSame( self::$editor_id, $data[0]['author'] );
    324     }
    325 
    326     public function test_get_items_author_exclude_query() {
     380        if ( $request->is_method( 'get' ) ) {
     381            $this->assertCount( 1, $data );
     382            $this->assertSame( self::$editor_id, $data[0]['author'] );
     383        } else {
     384            $this->assertNull( $data, 'Failed asserting that response data is null for HEAD request.' );
     385            $headers = $response->get_headers();
     386            $this->assertSame( 1, $headers['X-WP-Total'], 'Failed asserting that X-WP-Total header is 1.' );
     387        }
     388    }
     389
     390    /**
     391     * @dataProvider data_readable_http_methods
     392     * @ticket 56481
     393     *
     394     * @param string $method The HTTP method to use.
     395     */
     396    public function test_get_items_author_exclude_query( $method ) {
    327397        self::factory()->post->create( array( 'post_author' => self::$editor_id ) );
    328398        self::factory()->post->create( array( 'post_author' => self::$author_id ) );
     
    331401
    332402        // All posts in the database.
    333         $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     403        $request = new WP_REST_Request( $method, '/wp/v2/posts' );
    334404        $request->set_param( 'per_page', self::$per_page );
    335405        $response = rest_get_server()->dispatch( $request );
    336406        $this->assertSame( 200, $response->get_status() );
    337         $this->assertCount( $total_posts, $response->get_data() );
     407        if ( $request->is_method( 'get' ) ) {
     408            $this->assertCount( $total_posts, $response->get_data() );
     409        } else {
     410            $this->assertNull( $response->get_data(), 'Failed asserting that response data is null for HEAD request.' );
     411            $headers = $response->get_headers();
     412            $this->assertSame( $total_posts, $headers['X-WP-Total'], 'Failed asserting that the number of posts is correct.' );
     413        }
    338414
    339415        // Exclude editor and author.
    340         $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     416        $request = new WP_REST_Request( $method, '/wp/v2/posts' );
    341417        $request->set_param( 'per_page', self::$per_page );
    342418        $request->set_param( 'author_exclude', array( self::$editor_id, self::$author_id ) );
     
    344420        $this->assertSame( 200, $response->get_status() );
    345421        $data = $response->get_data();
    346         $this->assertCount( $total_posts - 2, $data );
    347         $this->assertNotEquals( self::$editor_id, $data[0]['author'] );
    348         $this->assertNotEquals( self::$author_id, $data[0]['author'] );
     422        if ( $request->is_method( 'get' ) ) {
     423            $this->assertCount( $total_posts - 2, $data );
     424            $this->assertNotEquals( self::$editor_id, $data[0]['author'] );
     425            $this->assertNotEquals( self::$author_id, $data[0]['author'] );
     426        } else {
     427            $this->assertNull( $response->get_data(), 'Failed asserting that response data is null for HEAD request.' );
     428            $headers = $response->get_headers();
     429            $this->assertSame( $total_posts - 2, $headers['X-WP-Total'], 'Failed asserting that the number of posts is correct.' );
     430        }
    349431
    350432        // Exclude editor.
    351         $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     433        $request = new WP_REST_Request( $method, '/wp/v2/posts' );
    352434        $request->set_param( 'per_page', self::$per_page );
    353435        $request->set_param( 'author_exclude', self::$editor_id );
     
    355437        $this->assertSame( 200, $response->get_status() );
    356438        $data = $response->get_data();
    357         $this->assertCount( $total_posts - 1, $data );
    358         $this->assertNotEquals( self::$editor_id, $data[0]['author'] );
    359         $this->assertNotEquals( self::$editor_id, $data[1]['author'] );
     439        if ( $request->is_method( 'get' ) ) {
     440            $this->assertCount( $total_posts - 1, $data );
     441            $this->assertNotEquals( self::$editor_id, $data[0]['author'] );
     442            $this->assertNotEquals( self::$editor_id, $data[1]['author'] );
     443        } else {
     444            $this->assertNull( $response->get_data(), 'Failed asserting that response data is null for HEAD request.' );
     445            $headers = $response->get_headers();
     446            $this->assertSame( $total_posts - 1, $headers['X-WP-Total'], 'Failed asserting that the number of posts is correct.' );
     447        }
    360448
    361449        // Invalid 'author_exclude' should error.
    362         $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     450        $request = new WP_REST_Request( $method, '/wp/v2/posts' );
    363451        $request->set_param( 'author_exclude', 'invalid' );
    364452        $response = rest_get_server()->dispatch( $request );
     
    366454    }
    367455
    368     public function test_get_items_include_query() {
     456    /**
     457     * @dataProvider data_readable_http_methods
     458     * @ticket 56481
     459     *
     460     * @param string $method The HTTP method to use.
     461     */
     462    public function test_get_items_include_query( $method ) {
    369463        $id1 = self::factory()->post->create(
    370464            array(
     
    380474        );
    381475
    382         $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     476        $request = new WP_REST_Request( $method, '/wp/v2/posts' );
    383477
    384478        // Order defaults to date descending.
     
    386480        $response = rest_get_server()->dispatch( $request );
    387481        $data     = $response->get_data();
    388         $this->assertCount( 2, $data );
    389         $this->assertSame( $id2, $data[0]['id'] );
     482        if ( $request->is_method( 'get' ) ) {
     483            $this->assertCount( 2, $data );
     484            $this->assertSame( $id2, $data[0]['id'] );
     485        } else {
     486            $this->assertNull( $data, 'Failed asserting that response data is null for HEAD request.' );
     487            $headers = $response->get_headers();
     488            $this->assertSame( 2, $headers['X-WP-Total'], 'Failed asserting that the number of posts is correct.' );
     489        }
     490
    390491        $this->assertPostsOrderedBy( '{posts}.post_date DESC' );
    391492
     
    394495        $response = rest_get_server()->dispatch( $request );
    395496        $data     = $response->get_data();
    396         $this->assertCount( 2, $data );
    397         $this->assertSame( $id1, $data[0]['id'] );
     497        if ( $request->is_method( 'get' ) ) {
     498            $this->assertCount( 2, $data );
     499            $this->assertSame( $id1, $data[0]['id'] );
     500        } else {
     501            $this->assertNull( $data, 'Failed asserting that response data is null for HEAD request.' );
     502            $headers = $response->get_headers();
     503            $this->assertSame( 2, $headers['X-WP-Total'], 'Failed asserting that the number of posts is correct.' );
     504        }
     505
    398506        $this->assertPostsOrderedBy( "FIELD({posts}.ID,$id1,$id2)" );
    399507
    400508        // Invalid 'include' should error.
    401         $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     509        $request = new WP_REST_Request( $method, '/wp/v2/posts' );
    402510        $request->set_param( 'include', 'invalid' );
    403511        $response = rest_get_server()->dispatch( $request );
     
    17301838    }
    17311839
    1732     public function test_get_items_pagination_headers() {
     1840    /**
     1841     * @dataProvider data_readable_http_methods
     1842     * @ticket 56481
     1843     *
     1844     * @param string $method HTTP method to use.
     1845     */
     1846    public function test_get_items_pagination_headers( $method ) {
    17331847        $total_posts = self::$total_posts;
    17341848        $total_pages = (int) ceil( $total_posts / 10 );
    17351849
    17361850        // Start of the index.
    1737         $request  = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     1851        $request  = new WP_REST_Request( $method, '/wp/v2/posts' );
    17381852        $response = rest_get_server()->dispatch( $request );
    17391853        $headers  = $response->get_headers();
     
    17531867        ++$total_posts;
    17541868        ++$total_pages;
    1755         $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     1869        $request = new WP_REST_Request( $method, '/wp/v2/posts' );
    17561870        $request->set_param( 'page', 3 );
    17571871        $response = rest_get_server()->dispatch( $request );
     
    17751889
    17761890        // Last page.
    1777         $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     1891        $request = new WP_REST_Request( $method, '/wp/v2/posts' );
    17781892        $request->set_param( 'page', $total_pages );
    17791893        $response = rest_get_server()->dispatch( $request );
     
    17911905
    17921906        // Out of bounds.
    1793         $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     1907        $request = new WP_REST_Request( $method, '/wp/v2/posts' );
    17941908        $request->set_param( 'page', 100 );
    17951909        $response = rest_get_server()->dispatch( $request );
     
    17991913        // With query params.
    18001914        $total_pages = (int) ceil( $total_posts / 5 );
    1801         $request     = new WP_REST_Request( 'GET', '/wp/v2/posts' );
     1915        $request     = new WP_REST_Request( $method, '/wp/v2/posts' );
    18021916        $request->set_query_params(
    18031917            array(
     
    18261940        );
    18271941        $this->assertStringContainsString( '<' . $next_link . '>; rel="next"', $headers['Link'] );
     1942    }
     1943
     1944    /**
     1945     * Data provider intended to provide HTTP method names for testing GET and HEAD requests.
     1946     *
     1947     * @return array
     1948     */
     1949    public static function data_readable_http_methods() {
     1950        return array(
     1951            'GET request'  => array( 'GET' ),
     1952            'HEAD request' => array( 'HEAD' ),
     1953        );
     1954    }
     1955
     1956    /**
     1957     * @dataProvider data_readable_http_methods
     1958     * @ticket 56481
     1959     *
     1960     * @param string $method HTTP method to use.
     1961     */
     1962    public function test_get_items_only_fetches_ids_for_head_requests( $method ) {
     1963        $is_head_request = 'HEAD' === $method;
     1964        $request         = new WP_REST_Request( $method, '/wp/v2/posts' );
     1965
     1966        $filter = new MockAction();
     1967
     1968        add_filter( 'posts_pre_query', array( $filter, 'filter' ), 10, 2 );
     1969
     1970        $response = rest_get_server()->dispatch( $request );
     1971
     1972        $this->assertSame( 200, $response->get_status() );
     1973        if ( $is_head_request ) {
     1974            $this->assertEmpty( $response->get_data() );
     1975        } else {
     1976            $this->assertNotEmpty( $response->get_data() );
     1977        }
     1978
     1979        $args = $filter->get_args();
     1980        $this->assertTrue( isset( $args[0][1] ), 'Query parameters were not captured.' );
     1981        $this->assertInstanceOf( WP_Query::class, $args[0][1], 'Query parameters were not captured.' );
     1982
     1983        /** @var WP_Query $query */
     1984        $query = $args[0][1];
     1985
     1986        if ( $is_head_request ) {
     1987            $this->assertArrayHasKey( 'fields', $query->query, 'The fields parameter is not set in the query vars.' );
     1988            $this->assertSame( 'ids', $query->query['fields'], 'The query must fetch only post IDs.' );
     1989            $this->assertArrayHasKey( 'fields', $query->query_vars, 'The fields parameter is not set in the query vars.' );
     1990            $this->assertSame( 'ids', $query->query_vars['fields'], 'The query must fetch only post IDs.' );
     1991            $this->assertArrayHasKey( 'update_post_term_cache', $query->query_vars, 'The "update_post_term_cache" parameter is missing in the query vars.' );
     1992            $this->assertFalse( $query->query_vars['update_post_term_cache'], 'The "update_post_term_cache" parameter must be false for HEAD requests.' );
     1993            $this->assertArrayHasKey( 'update_post_meta_cache', $query->query_vars, 'The "update_post_meta_cache" parameter is missing in the query vars.' );
     1994            $this->assertFalse( $query->query_vars['update_post_meta_cache'], 'The "update_post_meta_cache" parameter must be false for HEAD requests.' );
     1995        } else {
     1996            $this->assertTrue( ! array_key_exists( 'fields', $query->query ) || 'ids' !== $query->query['fields'], 'The fields parameter should not be forced to "ids" for non-HEAD requests.' );
     1997            $this->assertTrue( ! array_key_exists( 'fields', $query->query_vars ) || 'ids' !== $query->query_vars['fields'], 'The fields parameter should not be forced to "ids" for non-HEAD requests.' );
     1998            $this->assertArrayHasKey( 'update_post_term_cache', $query->query_vars, 'The "update_post_term_cache" parameter is missing in the query vars.' );
     1999            $this->assertTrue( $query->query_vars['update_post_term_cache'], 'The "update_post_term_cache" parameter must be true for non-HEAD requests.' );
     2000            $this->assertArrayHasKey( 'update_post_meta_cache', $query->query_vars, 'The "update_post_meta_cache" parameter is missing in the query vars.' );
     2001            $this->assertTrue( $query->query_vars['update_post_meta_cache'], 'The "update_post_meta_cache" parameter must be true for non-HEAD requests.' );
     2002        }
     2003
     2004        if ( ! $is_head_request ) {
     2005            return;
     2006        }
     2007
     2008        global $wpdb;
     2009        $posts_table = preg_quote( $wpdb->posts, '/' );
     2010        $pattern     = '/^SELECT\s+SQL_CALC_FOUND_ROWS\s+' . $posts_table . '\.ID\s+FROM\s+' . $posts_table . '\s+WHERE/i';
     2011
     2012        // Assert that the SQL query only fetches the ID column.
     2013        $this->assertMatchesRegularExpression( $pattern, $query->request, 'The SQL query does not match the expected string.' );
    18282014    }
    18292015
     
    19722158
    19732159        $this->check_get_post_response( $response, 'view' );
     2160    }
     2161
     2162    /**
     2163     * @dataProvider data_readable_http_methods
     2164     * @ticket 56481
     2165     *
     2166     * @param string $method The HTTP method to use.
     2167     */
     2168    public function test_get_item_should_allow_adding_headers_via_filter( $method ) {
     2169        $request = new WP_REST_Request( $method, sprintf( '/wp/v2/posts/%d', self::$post_id ) );
     2170
     2171        $hook_name = 'rest_prepare_' . get_post_type( self::$post_id );
     2172        $filter    = new MockAction();
     2173        $callback  = array( $filter, 'filter' );
     2174        add_filter( $hook_name, $callback );
     2175        $header_filter = new class() {
     2176            public static function add_custom_header( $response ) {
     2177                $response->header( 'X-Test-Header', 'Test' );
     2178
     2179                return $response;
     2180            }
     2181        };
     2182        add_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     2183        $response = rest_get_server()->dispatch( $request );
     2184        remove_filter( $hook_name, $callback );
     2185        remove_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     2186
     2187        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     2188        $this->assertSame( 1, $filter->get_call_count(), 'The "' . $hook_name . '" filter was not called when it should be for GET/HEAD requests.' );
     2189        $headers = $response->get_headers();
     2190        $this->assertArrayHasKey( 'Link', $headers, 'The "Link" header should be present in the response.' );
     2191        $this->assertArrayHasKey( 'X-Test-Header', $headers, 'The "X-Test-Header" header should be present in the response.' );
     2192        $this->assertSame( 'Test', $headers['X-Test-Header'], 'The "X-Test-Header" header value should be equal to "Test".' );
     2193        if ( 'HEAD' !== $method ) {
     2194            return null;
     2195        }
     2196        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
    19742197    }
    19752198
  • trunk/tests/phpunit/tests/rest-api/rest-request.php

    r55562 r59899  
    10821082        $this->assertSame( 'rest_invalid_param', $valid->get_error_code() );
    10831083    }
     1084
     1085    /**
     1086     * Tests that WP_REST_Request::is_method() correctly detects the request method,
     1087     * regardless of case sensitivity.
     1088     *
     1089     * @dataProvider data_is_method_should_detect_method_ignoring_case
     1090     * @ticket 56481
     1091     *
     1092     * @param string $method       The expected HTTP method of the request.
     1093     * @param string $input_method The HTTP method to check against.
     1094     * @param bool   $expected     The expected result of the is_method() check.
     1095     */
     1096    public function test_is_method_should_detect_method_ignoring_case( $method, $input_method, $expected ) {
     1097        $request = new WP_REST_Request();
     1098        $request->set_method( $method );
     1099        $result = $request->is_method( $input_method );
     1100
     1101        $this->assertSame( $expected, $result, 'Failed asserting that the WP_REST_Request::is_method() method correctly detects the request method.' );
     1102    }
     1103
     1104    /**
     1105     * Provides test cases for verifying HTTP method comparison is case-insensitive.
     1106     *
     1107     * @return array
     1108     */
     1109    public function data_is_method_should_detect_method_ignoring_case() {
     1110        return array(
     1111            // GET.
     1112            'GET same case'          => array( 'GET', 'GET', true ),
     1113            'GET different case'     => array( 'GET', 'get', true ),
     1114            'GET different case #2'  => array( 'GET', 'get', true ),
     1115            'GET different case #3'  => array( 'GET', 'gEt', true ),
     1116            'GET wrong method'       => array( 'GET', 'POST', false ),
     1117            // POST.
     1118            'POST same case'         => array( 'POST', 'POST', true ),
     1119            'POST different case'    => array( 'POST', 'post', true ),
     1120            'POST different case #2' => array( 'POST', 'pOsT', true ),
     1121            'POST wrong method'      => array( 'POST', 'GET', false ),
     1122            // HEAD.
     1123            'HEAD same case'         => array( 'HEAD', 'HEAD', true ),
     1124            'HEAD different case'    => array( 'HEAD', 'head', true ),
     1125            'HEAD different case #2' => array( 'HEAD', 'HeAd', true ),
     1126            'HEAD wrong method'      => array( 'HEAD', 'GET', false ),
     1127        );
     1128    }
    10841129}
  • trunk/tests/phpunit/tests/rest-api/rest-revisions-controller.php

    r59630 r59899  
    163163    }
    164164
    165     public function test_get_items_no_permission() {
     165    /**
     166     * @ticket 56481
     167     */
     168    public function test_get_items_with_head_request_should_not_prepare_revisions_data() {
     169        wp_set_current_user( self::$editor_id );
     170
     171        $hook_name = 'rest_prepare_revision';
     172        $filter    = new MockAction();
     173        $callback  = array( $filter, 'filter' );
     174
     175        add_filter( $hook_name, $callback );
     176        $request  = new WP_REST_Request( 'HEAD', '/wp/v2/posts/' . self::$post_id . '/revisions' );
     177        $response = rest_get_server()->dispatch( $request );
     178        remove_filter( $hook_name, $callback );
     179
     180        $this->assertNotWPError( $response );
     181        $response = rest_ensure_response( $response );
     182
     183        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     184        $this->assertSame( 0, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' );
     185        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     186    }
     187
     188    /**
     189     * @dataProvider data_readable_http_methods
     190     * @ticket 56481
     191     *
     192     * @param string $method The HTTP method to use.
     193     */
     194    public function test_get_items_no_permission( $method ) {
    166195        wp_set_current_user( 0 );
    167         $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' );
     196        $request  = new WP_REST_Request( $method, '/wp/v2/posts/' . self::$post_id . '/revisions' );
    168197        $response = rest_get_server()->dispatch( $request );
    169198
     
    174203    }
    175204
    176     public function test_get_items_missing_parent() {
    177         wp_set_current_user( self::$editor_id );
    178         $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/revisions' );
     205    /**
     206     * Data provider intended to provide HTTP method names for testing GET and HEAD requests.
     207     *
     208     * @return array
     209     */
     210    public static function data_readable_http_methods() {
     211        return array(
     212            'GET request'  => array( 'GET' ),
     213            'HEAD request' => array( 'HEAD' ),
     214        );
     215    }
     216
     217    /**
     218     * @dataProvider data_readable_http_methods
     219     * @ticket 56481
     220     *
     221     * @param string $method The HTTP method to use.
     222     */
     223    public function test_get_items_missing_parent( $method ) {
     224        wp_set_current_user( self::$editor_id );
     225        $request  = new WP_REST_Request( $method, '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/revisions' );
    179226        $response = rest_get_server()->dispatch( $request );
    180227        $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
    181228    }
    182229
    183     public function test_get_items_invalid_parent_post_type() {
    184         wp_set_current_user( self::$editor_id );
    185         $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$page_id . '/revisions' );
     230    /**
     231     * @dataProvider data_readable_http_methods
     232     * @ticket 56481
     233     *
     234     * @param string $method The HTTP method to use.
     235     */
     236    public function test_get_items_invalid_parent_post_type( $method ) {
     237        wp_set_current_user( self::$editor_id );
     238        $request  = new WP_REST_Request( $method, '/wp/v2/posts/' . self::$page_id . '/revisions' );
    186239        $response = rest_get_server()->dispatch( $request );
    187240        $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
     
    214267    }
    215268
     269    /**
     270     * @dataProvider data_readable_http_methods
     271     * @ticket 56481
     272     *
     273     * @param string $method The HTTP method to use.
     274     */
     275    public function test_get_item_should_allow_adding_headers_via_filter( $method ) {
     276        wp_set_current_user( self::$editor_id );
     277        $request = new WP_REST_Request( $method, '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 );
     278
     279        $hook_name = 'rest_prepare_revision';
     280        $filter    = new MockAction();
     281        $callback  = array( $filter, 'filter' );
     282        add_filter( $hook_name, $callback );
     283        $header_filter = new class() {
     284            public static function add_custom_header( $response ) {
     285                $response->header( 'X-Test-Header', 'Test' );
     286
     287                return $response;
     288            }
     289        };
     290        add_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     291        $response = rest_get_server()->dispatch( $request );
     292        remove_filter( $hook_name, $callback );
     293        remove_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     294
     295        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     296        $this->assertSame( 1, $filter->get_call_count(), 'The "' . $hook_name . '" filter was not called when it should be for GET/HEAD requests.' );
     297        $headers = $response->get_headers();
     298        $this->assertArrayHasKey( 'X-Test-Header', $headers, 'The "X-Test-Header" header should be present in the response.' );
     299        $this->assertSame( 'Test', $headers['X-Test-Header'], 'The "X-Test-Header" header value should be equal to "Test".' );
     300        if ( 'GET' === $method ) {
     301            return null;
     302        }
     303        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     304    }
     305
    216306    public function test_get_item_embed_context() {
    217307        wp_set_current_user( self::$editor_id );
     
    232322    }
    233323
    234     public function test_get_item_no_permission() {
     324    /**
     325     * @dataProvider data_readable_http_methods
     326     * @ticket 56481
     327     *
     328     * @param string $method The HTTP method to use.
     329     */
     330    public function test_get_item_no_permission( $method ) {
    235331        wp_set_current_user( 0 );
    236         $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 );
     332        $request = new WP_REST_Request( $method, '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 );
    237333
    238334        $response = rest_get_server()->dispatch( $request );
     
    243339    }
    244340
    245     public function test_get_item_missing_parent() {
    246         wp_set_current_user( self::$editor_id );
    247         $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/revisions/' . $this->revision_id1 );
     341    /**
     342     * @dataProvider data_readable_http_methods
     343     * @ticket 56481
     344     *
     345     * @param string $method The HTTP method to use.
     346     */
     347    public function test_get_item_missing_parent( $method ) {
     348        wp_set_current_user( self::$editor_id );
     349        $request  = new WP_REST_Request( $method, '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/revisions/' . $this->revision_id1 );
    248350        $response = rest_get_server()->dispatch( $request );
    249351        $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
    250352    }
    251353
    252     public function test_get_item_invalid_parent_post_type() {
    253         wp_set_current_user( self::$editor_id );
    254         $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$page_id . '/revisions/' . $this->revision_id1 );
     354    /**
     355     * @dataProvider data_readable_http_methods
     356     * @ticket 56481
     357     *
     358     * @param string $method The HTTP method to use.
     359     */
     360    public function test_get_item_invalid_parent_post_type( $method ) {
     361        wp_set_current_user( self::$editor_id );
     362        $request  = new WP_REST_Request( $method, '/wp/v2/posts/' . self::$page_id . '/revisions/' . $this->revision_id1 );
    255363        $response = rest_get_server()->dispatch( $request );
    256364        $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 );
     
    270378
    271379    /**
     380     * @dataProvider data_readable_http_methods
    272381     * @ticket 59875
    273      */
    274     public function test_get_item_invalid_parent_id() {
    275         wp_set_current_user( self::$editor_id );
    276         $request  = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_2_1_id );
     382     * @ticket 56481
     383     *
     384     * @param string $method The HTTP method to use.
     385     */
     386    public function test_get_item_invalid_parent_id( $method ) {
     387        wp_set_current_user( self::$editor_id );
     388        $request  = new WP_REST_Request( $method, '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_2_1_id );
    277389        $response = rest_get_server()->dispatch( $request );
    278390        $this->assertErrorResponse( 'rest_revision_parent_id_mismatch', $response, 404 );
     
    511623     * Test the pagination header of the first page.
    512624     *
    513      * @ticket 40510
    514      */
    515     public function test_get_items_pagination_header_of_the_first_page() {
     625     * @dataProvider data_readable_http_methods
     626     * @ticket 40510
     627     * @ticket 56481
     628     *
     629     * @param string $method The HTTP method to use.
     630     */
     631    public function test_get_items_pagination_header_of_the_first_page( $method ) {
    516632        wp_set_current_user( self::$editor_id );
    517633
     
    521637        $page        = 1;  // First page.
    522638
    523         $request = new WP_REST_Request( 'GET', $rest_route );
     639        $request = new WP_REST_Request( $method, $rest_route );
    524640        $request->set_query_params(
    525641            array(
     
    546662     * Test the pagination header of the last page.
    547663     *
    548      * @ticket 40510
    549      */
    550     public function test_get_items_pagination_header_of_the_last_page() {
     664     * @dataProvider data_readable_http_methods
     665     * @ticket 40510
     666     * @ticket 56481
     667     *
     668     * @param string $method The HTTP method to use.
     669     */
     670    public function test_get_items_pagination_header_of_the_last_page( $method ) {
    551671        wp_set_current_user( self::$editor_id );
    552672
     
    556676        $page        = 2;  // Last page.
    557677
    558         $request = new WP_REST_Request( 'GET', $rest_route );
     678        $request = new WP_REST_Request( $method, $rest_route );
    559679        $request->set_query_params(
    560680            array(
     
    577697    }
    578698
     699
    579700    /**
    580701     * Test that invalid 'per_page' query should error.
    581702     *
    582      * @ticket 40510
    583      */
    584     public function test_get_items_invalid_per_page_should_error() {
     703     * @dataProvider data_readable_http_methods
     704     * @ticket 40510
     705     * @ticket 56481
     706     *
     707     * @param string $method The HTTP method to use.
     708     */
     709    public function test_get_items_invalid_per_page_should_error( $method ) {
    585710        wp_set_current_user( self::$editor_id );
    586711
     
    589714        $expected_status = 400;
    590715
    591         $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' );
     716        $request = new WP_REST_Request( $method, '/wp/v2/posts/' . self::$post_id . '/revisions' );
    592717        $request->set_param( 'per_page', $per_page );
    593718        $response = rest_get_server()->dispatch( $request );
     
    598723     * Test that out of bounds 'page' query should error.
    599724     *
    600      * @ticket 40510
    601      */
    602     public function test_get_items_out_of_bounds_page_should_error() {
     725     * @dataProvider data_readable_http_methods
     726     * @ticket 40510
     727     * @ticket 56481
     728     *
     729     * @param string $method The HTTP method to use.
     730     */
     731    public function test_get_items_out_of_bounds_page_should_error( $method ) {
    603732        wp_set_current_user( self::$editor_id );
    604733
     
    609738        $expected_status = 400;
    610739
    611         $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' );
     740        $request = new WP_REST_Request( $method, '/wp/v2/posts/' . self::$post_id . '/revisions' );
    612741        $request->set_query_params(
    613742            array(
     
    623752     * Test that impossibly high 'page' query should error.
    624753     *
    625      * @ticket 40510
    626      */
    627     public function test_get_items_invalid_max_pages_should_error() {
     754     * @dataProvider data_readable_http_methods
     755     * @ticket 40510
     756     * @ticket 56481
     757     *
     758     * @param string $method The HTTP method to use.
     759     */
     760    public function test_get_items_invalid_max_pages_should_error( $method ) {
    628761        wp_set_current_user( self::$editor_id );
    629762
     
    633766        $expected_status = 400;
    634767
    635         $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' );
     768        $request = new WP_REST_Request( $method, '/wp/v2/posts/' . self::$post_id . '/revisions' );
    636769        $request->set_query_params(
    637770            array(
     
    771904     * Test that out of bound 'offset' query should error.
    772905     *
    773      * @ticket 40510
    774      */
    775     public function test_get_items_out_of_bound_offset_should_error() {
     906     * @dataProvider data_readable_http_methods
     907     * @ticket 40510
     908     * @ticket 56481
     909     *
     910     * @param string $method The HTTP method to use.
     911     */
     912    public function test_get_items_out_of_bound_offset_should_error( $method ) {
    776913        wp_set_current_user( self::$editor_id );
    777914
     
    781918        $expected_status = 400;
    782919
    783         $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' );
     920        $request = new WP_REST_Request( $method, '/wp/v2/posts/' . self::$post_id . '/revisions' );
    784921        $request->set_query_params(
    785922            array(
     
    795932     * Test that impossible high number for 'offset' query should error.
    796933     *
    797      * @ticket 40510
    798      */
    799     public function test_get_items_impossible_high_number_offset_should_error() {
     934     * @dataProvider data_readable_http_methods
     935     * @ticket 40510
     936     * @ticket 56481
     937     *
     938     * @param string $method The HTTP method to use.
     939     */
     940    public function test_get_items_impossible_high_number_offset_should_error( $method ) {
    800941        wp_set_current_user( self::$editor_id );
    801942
     
    805946        $expected_status = 400;
    806947
    807         $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' );
     948        $request = new WP_REST_Request( $method, '/wp/v2/posts/' . self::$post_id . '/revisions' );
    808949        $request->set_query_params(
    809950            array(
     
    819960     * Test that invalid 'offset' query should error.
    820961     *
    821      * @ticket 40510
    822      */
    823     public function test_get_items_invalid_offset_should_error() {
     962     * @dataProvider data_readable_http_methods
     963     * @ticket 40510
     964     * @ticket 56481
     965     *
     966     * @param string $method The HTTP method to use.
     967     */
     968    public function test_get_items_invalid_offset_should_error( $method ) {
    824969        wp_set_current_user( self::$editor_id );
    825970
     
    829974        $expected_status = 400;
    830975
    831         $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' );
     976        $request = new WP_REST_Request( $method, '/wp/v2/posts/' . self::$post_id . '/revisions' );
    832977        $request->set_query_params(
    833978            array(
  • trunk/tests/phpunit/tests/rest-api/rest-search-controller.php

    r57839 r59899  
    162162
    163163    /**
     164     * Test pagination headers.
     165     *
     166     * @dataProvider data_readable_http_methods
     167     * @ticket 56481
     168     *
     169     * @param string $method HTTP method to use.
     170     */
     171    public function test_get_items_pagination_headers( $method ) {
     172        $total_posts = count( self::$my_title_post_ids ) + count( self::$my_title_page_ids ) + count( self::$my_content_post_ids );
     173        $per_page    = 3;
     174        $total_pages = (int) ceil( $total_posts / $per_page );
     175
     176        // Start of the index.
     177        $response = $this->do_request_with_params(
     178            array(
     179                'per_page' => $per_page,
     180            ),
     181            $method
     182        );
     183        $headers  = $response->get_headers();
     184        $this->assertSame( $total_posts, $headers['X-WP-Total'] );
     185        $this->assertSame( $total_pages, $headers['X-WP-TotalPages'] );
     186
     187        $next_link = add_query_arg(
     188            array(
     189                'per_page' => $per_page,
     190                'page'     => 2,
     191            ),
     192            rest_url( '/wp/v2/search' )
     193        );
     194        $this->assertStringNotContainsString( 'rel="prev"', $headers['Link'] );
     195        $this->assertStringContainsString( '<' . $next_link . '>; rel="next"', $headers['Link'] );
     196
     197        $response = $this->do_request_with_params(
     198            array(
     199                'per_page' => $per_page,
     200                'page'     => 3,
     201            ),
     202            $method
     203        );
     204        $headers  = $response->get_headers();
     205        $this->assertSame( $total_posts, $headers['X-WP-Total'] );
     206        $this->assertSame( $total_pages, $headers['X-WP-TotalPages'] );
     207        $prev_link = add_query_arg(
     208            array(
     209                'per_page' => $per_page,
     210                'page'     => 2,
     211            ),
     212            rest_url( '/wp/v2/search' )
     213        );
     214        $this->assertStringContainsString( '<' . $prev_link . '>; rel="prev"', $headers['Link'] );
     215        $next_link = add_query_arg(
     216            array(
     217                'per_page' => $per_page,
     218                'page'     => 4,
     219            ),
     220            rest_url( '/wp/v2/search' )
     221        );
     222        $this->assertStringContainsString( '<' . $next_link . '>; rel="next"', $headers['Link'] );
     223
     224        // Last page.
     225        $response = $this->do_request_with_params(
     226            array(
     227                'per_page' => $per_page,
     228                'page'     => $total_pages,
     229            ),
     230            $method
     231        );
     232        $headers  = $response->get_headers();
     233        $this->assertSame( $total_posts, $headers['X-WP-Total'] );
     234        $this->assertSame( $total_pages, $headers['X-WP-TotalPages'] );
     235        $prev_link = add_query_arg(
     236            array(
     237                'per_page' => $per_page,
     238                'page'     => $total_pages - 1,
     239            ),
     240            rest_url( '/wp/v2/search' )
     241        );
     242        $this->assertStringContainsString( '<' . $prev_link . '>; rel="prev"', $headers['Link'] );
     243        $this->assertStringNotContainsString( 'rel="next"', $headers['Link'] );
     244    }
     245
     246    /**
     247     * Data provider intended to provide HTTP method names for testing GET and HEAD requests.
     248     *
     249     * @return array
     250     */
     251    public static function data_readable_http_methods() {
     252        return array(
     253            'GET request'  => array( 'GET' ),
     254            'HEAD request' => array( 'HEAD' ),
     255        );
     256    }
     257
     258    /**
    164259     * Search through all content with a low limit.
    165260     */
     
    240335    /**
    241336     * Search through an invalid type
    242      */
    243     public function test_get_items_search_type_invalid() {
     337     *
     338     * @dataProvider data_readable_http_methods
     339     * @ticket 56481
     340     *
     341     * @param string $method HTTP method to use.
     342     */
     343    public function test_get_items_search_type_invalid( $method ) {
    244344        $response = $this->do_request_with_params(
    245345            array(
    246346                'per_page' => 100,
    247347                'type'     => 'invalid',
    248             )
     348            ),
     349            $method
    249350        );
    250351
     
    254355    /**
    255356     * Search through posts of an invalid post type.
     357     *
     358     * @dataProvider data_readable_http_methods
     359     * @ticket 56481
     360     *
     361     * @param string $method HTTP method to use.
    256362     */
    257363    public function test_get_items_search_type_post_subtype_invalid() {
     
    463569    /**
    464570     * Tests that non-public post types are not allowed.
    465      */
    466     public function test_non_public_post_type() {
     571     *
     572     * @dataProvider data_readable_http_methods
     573     * @ticket 56481
     574     *
     575     * @param string $method HTTP method to use.
     576     */
     577    public function test_non_public_post_type( $method ) {
    467578        $response = $this->do_request_with_params(
    468579            array(
    469580                'type'    => 'post',
    470581                'subtype' => 'post,nav_menu_item',
    471             )
     582            ),
     583            $method
    472584        );
    473585        $this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
     
    633745     * Search through posts of an invalid post type.
    634746     *
     747     *
     748     * @dataProvider data_readable_http_methods
    635749     * @ticket 51458
    636      */
    637     public function test_get_items_search_term_subtype_invalid() {
     750     * @ticket 56481
     751     *
     752     * @param string $method HTTP method to use.
     753     */
     754    public function test_get_items_search_term_subtype_invalid( $method ) {
    638755        $response = $this->do_request_with_params(
    639756            array(
     
    641758                'type'     => 'term',
    642759                'subtype'  => 'invalid',
    643             )
     760            ),
     761            $method
    644762        );
    645763
     
    8911009
    8921010    /**
     1011     * @dataProvider data_readable_http_methods
    8931012     * @ticket 60771
    894      */
    895     public function test_sanitize_subtypes_validates_type() {
     1013     * @ticket 56481
     1014     *
     1015     * @param string $method HTTP method to use.
     1016     */
     1017    public function test_sanitize_subtypes_validates_type( $method ) {
    8961018        $response = $this->do_request_with_params(
    8971019            array(
    8981020                'subtype' => 'page',
    8991021                'type'    => array( 'invalid' ),
    900             )
     1022            ),
     1023            $method
    9011024        );
    9021025
  • trunk/tests/phpunit/tests/rest-api/rest-sidebars-controller.php

    r58876 r59899  
    153153
    154154    /**
    155      * @ticket 41683
    156      */
    157     public function test_get_items_no_permission() {
     155     * @ticket 56481
     156     */
     157    public function test_get_items_with_head_request_should_not_prepare_sidebar_data() {
     158        wp_widgets_init();
     159
     160        $request = new WP_REST_Request( 'HEAD', '/wp/v2/sidebars' );
     161
     162        $hook_name = 'rest_prepare_sidebar';
     163        $filter    = new MockAction();
     164        $callback  = array( $filter, 'filter' );
     165
     166        add_filter( $hook_name, $callback );
     167        $response = rest_get_server()->dispatch( $request );
     168        remove_filter( $hook_name, $callback );
     169
     170        $this->assertNotWPError( $response );
     171
     172        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     173        $this->assertSame( 0, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' );
     174        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     175    }
     176
     177    /**
     178     * @dataProvider data_readable_http_methods
     179     * @ticket 41683
     180     * @ticket 56481
     181     *
     182     * @param string $method HTTP method to use.
     183     */
     184    public function test_get_items_no_permission( $method ) {
    158185        wp_set_current_user( 0 );
    159         $request  = new WP_REST_Request( 'GET', '/wp/v2/sidebars' );
     186        $request  = new WP_REST_Request( $method, '/wp/v2/sidebars' );
    160187        $response = rest_get_server()->dispatch( $request );
    161188        $this->assertErrorResponse( 'rest_cannot_manage_widgets', $response, 401 );
     
    502529
    503530    /**
    504      * @ticket 41683
    505      */
    506     public function test_get_item_no_permission() {
     531     * @dataProvider data_readable_http_methods
     532     * @ticket 56481
     533     *
     534     * @param string $method The HTTP method to use.
     535     */
     536    public function test_get_item_should_allow_adding_headers_via_filter( $method ) {
     537        $hook_name = 'rest_prepare_sidebar';
     538        $filter    = new MockAction();
     539        $callback  = array( $filter, 'filter' );
     540        add_filter( $hook_name, $callback );
     541        $header_filter = new class() {
     542            public static function add_custom_header( $response ) {
     543                $response->header( 'X-Test-Header', 'Test' );
     544
     545                return $response;
     546            }
     547        };
     548        add_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     549
     550        $this->setup_sidebar(
     551            'sidebar-1',
     552            array(
     553                'name' => 'Test sidebar',
     554            )
     555        );
     556
     557        $request  = new WP_REST_Request( $method, '/wp/v2/sidebars/sidebar-1' );
     558        $response = rest_get_server()->dispatch( $request );
     559        remove_filter( $hook_name, $callback );
     560        remove_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     561
     562        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     563        $this->assertSame( 1, $filter->get_call_count(), 'The "' . $hook_name . '" filter was not called when it should be for GET/HEAD requests.' );
     564        $headers = $response->get_headers();
     565        $this->assertArrayHasKey( 'X-Test-Header', $headers, 'The "X-Test-Header" header should be present in the response.' );
     566        $this->assertSame( 'Test', $headers['X-Test-Header'], 'The "X-Test-Header" header value should be equal to "Test".' );
     567        if ( 'HEAD' !== $method ) {
     568            return null;
     569        }
     570        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     571    }
     572
     573    /**
     574     * Data provider intended to provide HTTP method names for testing GET and HEAD requests.
     575     *
     576     * @return array
     577     */
     578    public static function data_readable_http_methods() {
     579        return array(
     580            'GET request'  => array( 'GET' ),
     581            'HEAD request' => array( 'HEAD' ),
     582        );
     583    }
     584
     585    /**
     586     * @dataProvider data_readable_http_methods
     587     * @ticket 41683
     588     * @ticket 56481
     589     *
     590     * @param string $method The HTTP method to use.
     591     */
     592    public function test_get_item_no_permission( $method ) {
    507593        wp_set_current_user( 0 );
    508594        $this->setup_sidebar(
     
    513599        );
    514600
    515         $request  = new WP_REST_Request( 'GET', '/wp/v2/sidebars/sidebar-1' );
     601        $request  = new WP_REST_Request( $method, '/wp/v2/sidebars/sidebar-1' );
    516602        $response = rest_get_server()->dispatch( $request );
    517603        $this->assertErrorResponse( 'rest_cannot_manage_widgets', $response, 401 );
     
    553639
    554640    /**
    555      * @ticket 41683
    556      */
    557     public function test_get_item_wrong_permission_author() {
     641     * @dataProvider data_readable_http_methods
     642     * @ticket 41683
     643     * @ticket 56481
     644     *
     645     * @param string $method The HTTP method to use.
     646     */
     647    public function test_get_item_wrong_permission_author( $method ) {
    558648        wp_set_current_user( self::$author_id );
    559649        $this->setup_sidebar(
     
    564654        );
    565655
    566         $request  = new WP_REST_Request( 'GET', '/wp/v2/sidebars/sidebar-1' );
     656        $request  = new WP_REST_Request( $method, '/wp/v2/sidebars/sidebar-1' );
    567657        $response = rest_get_server()->dispatch( $request );
    568658        $this->assertErrorResponse( 'rest_cannot_manage_widgets', $response, 403 );
  • trunk/tests/phpunit/tests/rest-api/rest-tags-controller.php

    r59458 r59899  
    628628    }
    629629
    630     public function test_get_terms_pagination_headers() {
     630    /**
     631     * @dataProvider data_readable_http_methods
     632     * @ticket 56481
     633     *
     634     * @param string $method HTTP method to use.
     635     */
     636    public function test_get_terms_pagination_headers( $method ) {
    631637        $total_tags  = self::$total_tags;
    632638        $total_pages = (int) ceil( $total_tags / 10 );
    633639
    634640        // Start of the index.
    635         $request  = new WP_REST_Request( 'GET', '/wp/v2/tags' );
     641        $request  = new WP_REST_Request( $method, '/wp/v2/tags' );
    636642        $response = rest_get_server()->dispatch( $request );
    637643        $headers  = $response->get_headers();
     
    15031509        $this->check_taxonomy_term( $tag, $data, $response->get_links() );
    15041510    }
     1511
     1512    /**
     1513     * @dataProvider data_readable_http_methods
     1514     * @ticket 56481
     1515     *
     1516     * @param string $method HTTP method to use.
     1517     */
     1518    public function test_get_items_only_fetches_ids_for_head_requests( $method ) {
     1519        $is_head_request = 'HEAD' === $method;
     1520        $request         = new WP_REST_Request( $method, '/wp/v2/tags' );
     1521
     1522        $filter = new MockAction();
     1523
     1524        add_filter( 'terms_pre_query', array( $filter, 'filter' ), 10, 2 );
     1525
     1526        $response = rest_get_server()->dispatch( $request );
     1527
     1528        $this->assertSame( 200, $response->get_status() );
     1529        if ( $is_head_request ) {
     1530            $this->assertEmpty( $response->get_data() );
     1531        } else {
     1532            $this->assertNotEmpty( $response->get_data() );
     1533        }
     1534
     1535        $args = $filter->get_args();
     1536        $this->assertTrue( isset( $args[0][1] ), 'Query parameters were not captured.' );
     1537        $this->assertInstanceOf( WP_Term_Query::class, $args[0][1], 'Query parameters were not captured.' );
     1538
     1539        /** @var WP_Term_Query $query */
     1540        $query = $args[0][1];
     1541
     1542        if ( $is_head_request ) {
     1543            $this->assertArrayHasKey( 'fields', $query->query_vars, 'The fields parameter is not set in the query vars.' );
     1544            $this->assertSame( 'ids', $query->query_vars['fields'], 'The query must fetch only term IDs.' );
     1545            $this->assertArrayHasKey( 'update_term_meta_cache', $query->query_vars, 'The update_term_meta_cache key is missing in the query vars.' );
     1546            $this->assertFalse( $query->query_vars['update_term_meta_cache'], 'The update_term_meta_cache value should be false for HEAD requests.' );
     1547        } else {
     1548            $this->assertTrue(
     1549                ! array_key_exists( 'fields', $query->query_vars ) || 'ids' !== $query->query_vars['fields'],
     1550                'The fields parameter should not be forced to "ids" for non-HEAD requests.'
     1551            );
     1552            $this->assertArrayHasKey( 'update_term_meta_cache', $query->query_vars, 'The update_term_meta_cache key is missing in the query vars.' );
     1553            $this->assertTrue( $query->query_vars['update_term_meta_cache'], 'The update_term_meta_cache value should be true for HEAD requests.' );
     1554        }
     1555
     1556        if ( ! $is_head_request ) {
     1557            return;
     1558        }
     1559
     1560        global $wpdb;
     1561        $terms_table = preg_quote( $wpdb->terms, '/' );
     1562
     1563        $pattern = '/SELECT\s+t\.term_id.+FROM\s+' . $terms_table . '\s+AS\s+t\s+INNER\s+JOIN/is';
     1564
     1565        // Assert that the SQL query only fetches the term_id column.
     1566        $this->assertMatchesRegularExpression( $pattern, $query->request, 'The SQL query does not match the expected string.' );
     1567    }
     1568
     1569    /**
     1570     * Data provider intended to provide HTTP method names for testing GET and HEAD requests.
     1571     *
     1572     * @return array
     1573     */
     1574    public static function data_readable_http_methods() {
     1575        return array(
     1576            'GET request'  => array( 'GET' ),
     1577            'HEAD request' => array( 'HEAD' ),
     1578        );
     1579    }
     1580
     1581    /**
     1582     * @dataProvider data_readable_http_methods
     1583     * @ticket 56481
     1584     *
     1585     * @param string $method The HTTP method to use.
     1586     */
     1587    public function test_get_item_should_allow_adding_headers_via_filter( string $method ) {
     1588        $tag_id = self::factory()->tag->create();
     1589
     1590        $request = new WP_REST_Request( $method, sprintf( '/wp/v2/tags/%d', $tag_id ) );
     1591
     1592        $hook_name = 'rest_prepare_post_tag';
     1593
     1594        $filter   = new MockAction();
     1595        $callback = array( $filter, 'filter' );
     1596        add_filter( $hook_name, $callback );
     1597        $header_filter = new class() {
     1598            public static function add_custom_header( $response ) {
     1599                $response->header( 'X-Test-Header', 'Test' );
     1600
     1601                return $response;
     1602            }
     1603        };
     1604        add_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     1605        $response = rest_get_server()->dispatch( $request );
     1606        remove_filter( $hook_name, $callback );
     1607        remove_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     1608
     1609        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     1610        $this->assertSame( 1, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' );
     1611        $headers = $response->get_headers();
     1612        $this->assertArrayHasKey( 'X-Test-Header', $headers, 'The "X-Test-Header" header should be present in the response.' );
     1613        $this->assertSame( 'Test', $headers['X-Test-Header'], 'The "X-Test-Header" header value should be equal to "Test".' );
     1614        if ( 'HEAD' !== $method ) {
     1615            return null;
     1616        }
     1617        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     1618    }
    15051619}
  • trunk/tests/phpunit/tests/rest-api/rest-taxonomies-controller.php

    r56746 r59899  
    6161    }
    6262
     63    /**
     64     * @ticket 56481
     65     */
     66    public function test_get_items_with_head_request_should_not_prepare_taxonomy_data() {
     67        $request   = new WP_REST_Request( 'HEAD', '/wp/v2/taxonomies' );
     68        $hook_name = 'rest_prepare_taxonomy';
     69        $filter    = new MockAction();
     70        $callback  = array( $filter, 'filter' );
     71        add_filter( $hook_name, $callback );
     72        $response = rest_get_server()->dispatch( $request );
     73        remove_filter( $hook_name, $callback );
     74        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     75        $this->assertSame( 0, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' );
     76        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     77    }
     78
    6379    public function test_get_items_context_edit() {
    6480        wp_set_current_user( self::$contributor_id );
     
    8096    }
    8197
    82     public function test_get_items_invalid_permission_for_context() {
     98
     99    /**
     100     * @dataProvider data_readable_http_methods
     101     * @ticket 56481
     102     *
     103     * @param string $method HTTP method to use.
     104     */
     105    public function test_get_items_invalid_permission_for_context( $method ) {
    83106        wp_set_current_user( 0 );
    84         $request = new WP_REST_Request( 'GET', '/wp/v2/taxonomies' );
     107        $request = new WP_REST_Request( $method, '/wp/v2/taxonomies' );
    85108        $request->set_param( 'context', 'edit' );
    86109        $response = rest_get_server()->dispatch( $request );
    87110        $this->assertErrorResponse( 'rest_cannot_view', $response, 401 );
     111    }
     112
     113    /**
     114     * Data provider intended to provide HTTP method names for testing GET and HEAD requests.
     115     *
     116     * @return array
     117     */
     118    public static function data_readable_http_methods() {
     119        return array(
     120            'GET request'  => array( 'GET' ),
     121            'HEAD request' => array( 'HEAD' ),
     122        );
    88123    }
    89124
     
    95130    }
    96131
    97     public function test_get_taxonomies_for_invalid_type() {
    98         $request = new WP_REST_Request( 'GET', '/wp/v2/taxonomies' );
     132    /**
     133     * @dataProvider data_readable_http_methods
     134     * @ticket 56481
     135     *
     136     * @param string $method HTTP method to use.
     137     */
     138    public function test_get_taxonomies_for_invalid_type( $method ) {
     139        $request = new WP_REST_Request( $method, '/wp/v2/taxonomies' );
    99140        $request->set_param( 'type', 'wingding' );
    100141        $response = rest_get_server()->dispatch( $request );
    101142        $this->assertSame( 200, $response->get_status() );
     143        if ( 'HEAD' === $method ) {
     144            return null;
     145        }
    102146        $data = $response->get_data();
    103147        $this->assertSame( '{}', json_encode( $data ) );
     
    108152        $response = rest_get_server()->dispatch( $request );
    109153        $this->check_taxonomy_object_response( 'view', $response );
     154    }
     155
     156    /**
     157     * @dataProvider data_readable_http_methods
     158     * @ticket 56481
     159     *
     160     * @param string $method The HTTP method to use.
     161     */
     162    public function test_get_item_should_allow_adding_headers_via_filter( $method ) {
     163        $request   = new WP_REST_Request( 'HEAD', '/wp/v2/taxonomies/category' );
     164        $hook_name = 'rest_prepare_taxonomy';
     165        $filter    = new MockAction();
     166        $callback  = array( $filter, 'filter' );
     167        add_filter( $hook_name, $callback );
     168        $header_filter = new class() {
     169            public static function add_custom_header( $response ) {
     170                $response->header( 'X-Test-Header', 'Test' );
     171
     172                return $response;
     173            }
     174        };
     175        add_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     176        $response = rest_get_server()->dispatch( $request );
     177        remove_filter( $hook_name, $callback );
     178        remove_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     179
     180        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     181        $this->assertSame( 1, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' );
     182        $headers = $response->get_headers();
     183        $this->assertArrayHasKey( 'X-Test-Header', $headers, 'The "X-Test-Header" header should be present in the response.' );
     184        $this->assertSame( 'Test', $headers['X-Test-Header'], 'The "X-Test-Header" header value should be equal to "Test".' );
     185        if ( 'HEAD' !== $method ) {
     186            return null;
     187        }
     188        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
    110189    }
    111190
     
    119198    }
    120199
    121     public function test_get_item_invalid_permission_for_context() {
     200    /**
     201     * @dataProvider data_readable_http_methods
     202     * @ticket 56481
     203     *
     204     * @param string $method HTTP method to use.
     205     */
     206    public function test_get_item_invalid_permission_for_context( $method ) {
    122207        wp_set_current_user( 0 );
    123         $request = new WP_REST_Request( 'GET', '/wp/v2/taxonomies/category' );
     208        $request = new WP_REST_Request( $method, '/wp/v2/taxonomies/category' );
    124209        $request->set_param( 'context', 'edit' );
    125210        $response = rest_get_server()->dispatch( $request );
     
    127212    }
    128213
    129     public function test_get_invalid_taxonomy() {
    130         $request  = new WP_REST_Request( 'GET', '/wp/v2/taxonomies/invalid' );
     214    /**
     215     * @dataProvider data_readable_http_methods
     216     * @ticket 56481
     217     *
     218     * @param string $method HTTP method to use.
     219     */
     220    public function test_get_invalid_taxonomy( $method ) {
     221        $request  = new WP_REST_Request( $method, '/wp/v2/taxonomies/invalid' );
    131222        $response = rest_get_server()->dispatch( $request );
    132223        $this->assertErrorResponse( 'rest_taxonomy_invalid', $response, 404 );
    133224    }
    134225
    135     public function test_get_non_public_taxonomy_not_authenticated() {
     226    /**
     227     * @dataProvider data_readable_http_methods
     228     * @ticket 56481
     229     *
     230     * @param string $method HTTP method to use.
     231     */
     232    public function test_get_non_public_taxonomy_not_authenticated( $method ) {
    136233        register_taxonomy( 'api-private', 'post', array( 'public' => false ) );
    137234
    138         $request  = new WP_REST_Request( 'GET', '/wp/v2/taxonomies/api-private' );
     235        $request  = new WP_REST_Request( $method, '/wp/v2/taxonomies/api-private' );
    139236        $response = rest_get_server()->dispatch( $request );
    140237        $this->assertErrorResponse( 'rest_forbidden', $response, 401 );
    141238    }
    142239
    143     public function test_get_non_public_taxonomy_no_permission() {
     240    /**
     241     * @dataProvider data_readable_http_methods
     242     * @ticket 56481
     243     *
     244     * @param string $method HTTP method to use.
     245     */
     246    public function test_get_non_public_taxonomy_no_permission( $method ) {
    144247        wp_set_current_user( self::$contributor_id );
    145248        register_taxonomy( 'api-private', 'post', array( 'public' => false ) );
    146249
    147         $request  = new WP_REST_Request( 'GET', '/wp/v2/taxonomies/api-private' );
     250        $request  = new WP_REST_Request( $method, '/wp/v2/taxonomies/api-private' );
    148251        $response = rest_get_server()->dispatch( $request );
    149252        $this->assertErrorResponse( 'rest_forbidden', $response, 403 );
  • trunk/tests/phpunit/tests/rest-api/rest-users-controller.php

    r59892 r59899  
    235235    }
    236236
    237     public function test_get_items_with_edit_context() {
    238         wp_set_current_user( self::$user );
    239 
    240         $request = new WP_REST_Request( 'GET', '/wp/v2/users' );
     237    /**
     238     * @dataProvider data_readable_http_methods
     239     * @ticket 56481
     240     *
     241     * @param string $method HTTP method to use.
     242     */
     243    public function test_get_items_with_edit_context( $method ) {
     244        wp_set_current_user( self::$user );
     245
     246        $request = new WP_REST_Request( $method, '/wp/v2/users' );
    241247        $request->set_param( 'context', 'edit' );
    242248        $response = rest_get_server()->dispatch( $request );
    243249
    244         $this->assertSame( 200, $response->get_status() );
     250        $this->assertSame(
     251            200,
     252            $response->get_status(),
     253            sprintf( 'Expected HTTP status code 200 but got %s.', $response->get_status() )
     254        );
     255
     256        if ( 'HEAD' === $method ) {
     257            $this->assertNull( $response->get_data(), 'Expected null response data for HEAD request, but received non-null data.' );
     258            return null;
     259        }
    245260
    246261        $all_data = $response->get_data();
     
    250265    }
    251266
    252     public function test_get_items_with_edit_context_without_permission() {
     267    /**
     268     * Data provider intended to provide HTTP method names for testing GET and HEAD requests.
     269     *
     270     * @return array
     271     */
     272    public static function data_readable_http_methods() {
     273        return array(
     274            'GET request'  => array( 'GET' ),
     275            'HEAD request' => array( 'HEAD' ),
     276        );
     277    }
     278
     279    /**
     280     * @dataProvider data_readable_http_methods
     281     * @ticket 56481
     282     *
     283     * @param string $method HTTP method to use.
     284     */
     285    public function test_get_items_with_edit_context_without_permission( $method ) {
    253286        // Test with a user not logged in.
    254         $request = new WP_REST_Request( 'GET', '/wp/v2/users' );
     287        $request = new WP_REST_Request( $method, '/wp/v2/users' );
    255288        $request->set_param( 'context', 'edit' );
    256289        $response = rest_get_server()->dispatch( $request );
     
    262295        wp_set_current_user( self::$editor );
    263296
    264         $request = new WP_REST_Request( 'GET', '/wp/v2/users' );
     297        $request = new WP_REST_Request( $method, '/wp/v2/users' );
    265298        $request->set_param( 'context', 'edit' );
    266299        $response = rest_get_server()->dispatch( $request );
     
    320353    }
    321354
    322     public function test_get_items_pagination_headers() {
     355    /**
     356     * @dataProvider data_readable_http_methods
     357     * @ticket 56481
     358     *
     359     * @param string $method HTTP method to use.
     360     */
     361    public function test_get_items_pagination_headers( $method ) {
    323362        $total_users = self::$total_users;
    324363        $total_pages = (int) ceil( $total_users / 10 );
     
    327366
    328367        // Start of the index.
    329         $request  = new WP_REST_Request( 'GET', '/wp/v2/users' );
     368        $request  = new WP_REST_Request( $method, '/wp/v2/users' );
    330369        $response = rest_get_server()->dispatch( $request );
    331370        $headers  = $response->get_headers();
     
    345384        ++$total_users;
    346385        ++$total_pages;
    347         $request = new WP_REST_Request( 'GET', '/wp/v2/users' );
     386        $request = new WP_REST_Request( $method, '/wp/v2/users' );
    348387        $request->set_param( 'page', 3 );
    349388        $response = rest_get_server()->dispatch( $request );
     
    367406
    368407        // Last page.
    369         $request = new WP_REST_Request( 'GET', '/wp/v2/users' );
     408        $request = new WP_REST_Request( $method, '/wp/v2/users' );
    370409        $request->set_param( 'page', $total_pages );
    371410        $response = rest_get_server()->dispatch( $request );
     
    383422
    384423        // Out of bounds.
    385         $request = new WP_REST_Request( 'GET', '/wp/v2/users' );
     424        $request = new WP_REST_Request( $method, '/wp/v2/users' );
    386425        $request->set_param( 'page', 100 );
    387426        $response = rest_get_server()->dispatch( $request );
     
    412451    }
    413452
    414     public function test_get_items_page() {
    415         wp_set_current_user( self::$user );
    416 
    417         $request = new WP_REST_Request( 'GET', '/wp/v2/users' );
     453    /**
     454     * @dataProvider data_readable_http_methods
     455     * @ticket 56481
     456     *
     457     * @param string $method HTTP method to use.
     458     */
     459    public function test_get_items_page( $method ) {
     460        wp_set_current_user( self::$user );
     461
     462        $request = new WP_REST_Request( $method, '/wp/v2/users' );
    418463        $request->set_param( 'per_page', 5 );
    419464        $request->set_param( 'page', 2 );
    420465        $response = rest_get_server()->dispatch( $request );
    421         $this->assertCount( 5, $response->get_data() );
     466
     467        if ( 'HEAD' !== $method ) {
     468            $this->assertCount( 5, $response->get_data() );
     469        }
     470
    422471        $prev_link = add_query_arg(
    423472            array(
     
    12661315    }
    12671316
    1268     public function test_get_current_user() {
    1269         wp_set_current_user( self::$user );
    1270 
    1271         $request  = new WP_REST_Request( 'GET', '/wp/v2/users/me' );
     1317    /**
     1318     * @dataProvider data_readable_http_methods
     1319     * @ticket 56481
     1320     *
     1321     * @param string $method HTTP method to use.
     1322     */
     1323    public function test_get_current_user( $method ) {
     1324        wp_set_current_user( self::$user );
     1325
     1326        $request  = new WP_REST_Request( $method, '/wp/v2/users/me' );
    12721327        $response = rest_get_server()->dispatch( $request );
    12731328        $this->assertSame( 200, $response->get_status() );
    1274         $this->check_get_user_response( $response, 'view' );
    1275 
    12761329        $headers = $response->get_headers();
    12771330        $this->assertArrayNotHasKey( 'Location', $headers );
    12781331
     1332        if ( 'HEAD' === $method ) {
     1333            // HEAD responses only contain headers. Bail.
     1334            return null;
     1335        }
     1336        $this->check_get_user_response( $response, 'view' );
    12791337        $links = $response->get_links();
    12801338        $this->assertSame( rest_url( 'wp/v2/users/' . self::$user ), $links['self'][0]['href'] );
     
    31593217    }
    31603218
     3219    /**
     3220     * @dataProvider data_readable_http_methods
     3221     * @ticket 56481
     3222     *
     3223     * @param string $method The HTTP method to use.
     3224     */
     3225    public function test_get_item_should_allow_adding_headers_via_filter( $method ) {
     3226        wp_set_current_user( self::$user );
     3227        $request = new WP_REST_Request( $method, sprintf( '/wp/v2/users/%d', self::$user ) );
     3228
     3229        $hook_name = 'rest_prepare_user';
     3230
     3231        $filter   = new MockAction();
     3232        $callback = array( $filter, 'filter' );
     3233        add_filter( $hook_name, $callback );
     3234        $header_filter = new class() {
     3235            public static function add_custom_header( $response ) {
     3236                $response->header( 'X-Test-Header', 'Test' );
     3237
     3238                return $response;
     3239            }
     3240        };
     3241        add_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     3242        $response = rest_get_server()->dispatch( $request );
     3243        remove_filter( $hook_name, $callback );
     3244        remove_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     3245
     3246        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     3247        $this->assertSame( 1, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' );
     3248        $headers = $response->get_headers();
     3249        $this->assertArrayHasKey( 'X-Test-Header', $headers, 'The "X-Test-Header" header should be present in the response.' );
     3250        $this->assertSame( 'Test', $headers['X-Test-Header'], 'The "X-Test-Header" header value should be equal to "Test".' );
     3251        if ( 'HEAD' !== $method ) {
     3252            return null;
     3253        }
     3254        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     3255    }
     3256
     3257    /**
     3258     * @dataProvider data_readable_http_methods
     3259     * @ticket 56481
     3260     *
     3261     * @param string $method HTTP method to use.
     3262     */
     3263    public function test_get_items_only_fetches_ids_for_head_requests( $method ) {
     3264        $is_head_request = 'HEAD' === $method;
     3265        $request         = new WP_REST_Request( $method, '/wp/v2/users' );
     3266
     3267        $filter = new MockAction();
     3268
     3269        add_filter( 'pre_user_query', array( $filter, 'filter' ), 10, 2 );
     3270
     3271        $response = rest_get_server()->dispatch( $request );
     3272
     3273        $this->assertSame( 200, $response->get_status() );
     3274        if ( $is_head_request ) {
     3275            $this->assertNull( $response->get_data() );
     3276        } else {
     3277            $this->assertNotEmpty( $response->get_data() );
     3278        }
     3279
     3280        $args = $filter->get_args();
     3281        $this->assertTrue( isset( $args[0][0] ), 'Query parameters were not captured.' );
     3282        $this->assertInstanceOf( WP_User_Query::class, $args[0][0], 'Query parameters were not captured.' );
     3283
     3284        /** @var WP_User $query */
     3285        $query = $args[0][0];
     3286
     3287        if ( $is_head_request ) {
     3288            $this->assertArrayHasKey( 'fields', $query->query_vars, 'The fields parameter is not set in the query vars.' );
     3289            $this->assertSame( 'id', $query->query_vars['fields'], 'The query must fetch only user IDs.' );
     3290        } else {
     3291            $this->assertTrue(
     3292                ! array_key_exists( 'fields', $query->query_vars ) || 'id' !== $query->query_vars['fields'],
     3293                'The fields parameter should not be forced to "id" for non-HEAD requests.'
     3294            );
     3295        }
     3296
     3297        if ( ! $is_head_request ) {
     3298            return;
     3299        }
     3300
     3301        global $wpdb;
     3302        $users_table = preg_quote( $wpdb->users, '/' );
     3303        $pattern     = '/SELECT SQL_CALC_FOUND_ROWS wptests_users.ID\n\s+FROM\s+' . $users_table . '/is';
     3304
     3305        // Assert that the SQL query only fetches the id column.
     3306        $this->assertMatchesRegularExpression( $pattern, $query->request, 'The SQL query does not match the expected string.' );
     3307    }
     3308
    31613309    protected function check_user_data( $user, $data, $context, $links ) {
    31623310        $this->assertSame( $user->ID, $data['id'] );
  • trunk/tests/phpunit/tests/rest-api/rest-widget-types-controller.php

    r56559 r59899  
    123123
    124124    /**
     125     * @ticket 56481
     126     */
     127    public function test_get_items_with_head_request_should_not_prepare_widget_types_data() {
     128        wp_set_current_user( self::$admin_id );
     129        $request  = new WP_REST_Request( 'HEAD', '/wp/v2/widget-types' );
     130        $response = rest_get_server()->dispatch( $request );
     131        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     132        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     133    }
     134
     135    /**
    125136     * @ticket 53303
    126137     */
     
    183194
    184195    /**
     196     * @dataProvider data_readable_http_methods
     197     * @ticket 56481
     198     *
     199     * @param string $method The HTTP method to use.
     200     */
     201    public function test_get_item_should_allow_adding_headers_via_filter( $method ) {
     202        $widget_name = 'calendar';
     203        wp_set_current_user( self::$admin_id );
     204        $request = new WP_REST_Request( $method, '/wp/v2/widget-types/' . $widget_name );
     205
     206        $hook_name = 'rest_prepare_widget_type';
     207        $filter    = new MockAction();
     208        $callback  = array( $filter, 'filter' );
     209        add_filter( $hook_name, $callback );
     210        $header_filter = new class() {
     211            public static function add_custom_header( $response ) {
     212                $response->header( 'X-Test-Header', 'Test' );
     213
     214                return $response;
     215            }
     216        };
     217        add_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     218        $response = rest_get_server()->dispatch( $request );
     219        remove_filter( $hook_name, $callback );
     220        remove_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     221
     222        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     223        $this->assertSame( 1, $filter->get_call_count(), 'The "' . $hook_name . '" filter was not called when it should be for GET/HEAD requests.' );
     224        $headers = $response->get_headers();
     225        $this->assertArrayHasKey( 'X-Test-Header', $headers, 'The "X-Test-Header" header should be present in the response.' );
     226        $this->assertSame( 'Test', $headers['X-Test-Header'], 'The "X-Test-Header" header value should be equal to "Test".' );
     227        if ( 'HEAD' !== $method ) {
     228            return null;
     229        }
     230        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     231    }
     232
     233    /**
     234     * Data provider intended to provide HTTP method names for testing GET and HEAD requests.
     235     *
     236     * @return array
     237     */
     238    public static function data_readable_http_methods() {
     239        return array(
     240            'GET request'  => array( 'GET' ),
     241            'HEAD request' => array( 'HEAD' ),
     242        );
     243    }
     244
     245    /**
    185246     * @ticket 41683
    186247     */
     
    201262
    202263    /**
    203      * @ticket 41683
    204      */
    205     public function test_get_widget_invalid_name() {
     264     * @dataProvider data_readable_http_methods
     265     * @ticket 41683
     266     * @ticket 56481
     267     *
     268     * @param string $method HTTP method to use.
     269     */
     270    public function test_get_widget_invalid_name( $method ) {
    206271        $widget_type = 'fake';
    207272        wp_set_current_user( self::$admin_id );
    208         $request  = new WP_REST_Request( 'GET', '/wp/v2/widget-types/' . $widget_type );
     273        $request  = new WP_REST_Request( $method, '/wp/v2/widget-types/' . $widget_type );
    209274        $response = rest_get_server()->dispatch( $request );
    210275
     
    252317
    253318    /**
    254      * @ticket 41683
    255      */
    256     public function test_get_items_wrong_permission() {
     319     * @dataProvider data_readable_http_methods
     320     * @ticket 41683
     321     * @ticket 56481
     322     *
     323     * @param string $method HTTP method to use.
     324     */
     325    public function test_get_items_wrong_permission( $method ) {
    257326        wp_set_current_user( self::$subscriber_id );
    258         $request  = new WP_REST_Request( 'GET', '/wp/v2/widget-types' );
     327        $request  = new WP_REST_Request( $method, '/wp/v2/widget-types' );
    259328        $response = rest_get_server()->dispatch( $request );
    260329        $this->assertErrorResponse( 'rest_cannot_manage_widgets', $response, 403 );
     
    262331
    263332    /**
    264      * @ticket 41683
    265      */
    266     public function test_get_item_wrong_permission() {
     333     * @dataProvider data_readable_http_methods
     334     * @ticket 41683
     335     * @ticket 56481
     336     *
     337     * @param string $method HTTP method to use.
     338     */
     339    public function test_get_item_wrong_permission( $method ) {
    267340        wp_set_current_user( self::$subscriber_id );
    268         $request  = new WP_REST_Request( 'GET', '/wp/v2/widget-types/calendar' );
     341        $request  = new WP_REST_Request( $method, '/wp/v2/widget-types/calendar' );
    269342        $response = rest_get_server()->dispatch( $request );
    270343        $this->assertErrorResponse( 'rest_cannot_manage_widgets', $response, 403 );
     
    272345
    273346    /**
    274      * @ticket 41683
    275      */
    276     public function test_get_items_no_permission() {
     347     * @dataProvider data_readable_http_methods
     348     * @ticket 41683
     349     * @ticket 56481
     350     *
     351     * @param string $method HTTP method to use.
     352     */
     353    public function test_get_items_no_permission( $method ) {
    277354        wp_set_current_user( 0 );
    278         $request  = new WP_REST_Request( 'GET', '/wp/v2/widget-types' );
     355        $request  = new WP_REST_Request( $method, '/wp/v2/widget-types' );
    279356        $response = rest_get_server()->dispatch( $request );
    280357        $this->assertErrorResponse( 'rest_cannot_manage_widgets', $response, 401 );
     
    282359
    283360    /**
    284      * @ticket 41683
    285      */
    286     public function test_get_item_no_permission() {
     361     * @dataProvider data_readable_http_methods
     362     * @ticket 41683
     363     * @ticket 56481
     364     *
     365     * @param string $method HTTP method to use.
     366     */
     367    public function test_get_item_no_permission( $method ) {
    287368        wp_set_current_user( 0 );
    288         $request  = new WP_REST_Request( 'GET', '/wp/v2/widget-types/calendar' );
     369        $request  = new WP_REST_Request( $method, '/wp/v2/widget-types/calendar' );
    289370        $response = rest_get_server()->dispatch( $request );
    290371        $this->assertErrorResponse( 'rest_cannot_manage_widgets', $response, 401 );
  • trunk/tests/phpunit/tests/rest-api/rest-widgets-controller.php

    r58097 r59899  
    229229
    230230    /**
    231      * @ticket 41683
    232      */
    233     public function test_get_items_no_permission() {
     231     * @dataProvider data_readable_http_methods
     232     * @ticket 41683
     233     * @ticket 56481
     234     *
     235     * @param string $method The HTTP method to use.
     236     */
     237    public function test_get_items_no_permission( $method ) {
    234238        wp_set_current_user( 0 );
    235         $request  = new WP_REST_Request( 'GET', '/wp/v2/widgets' );
     239        $request  = new WP_REST_Request( $method, '/wp/v2/widgets' );
    236240        $response = rest_get_server()->dispatch( $request );
    237241        $this->assertErrorResponse( 'rest_cannot_manage_widgets', $response, 401 );
     242    }
     243
     244    /**
     245     * Data provider intended to provide HTTP method names for testing GET and HEAD requests.
     246     *
     247     * @return array
     248     */
     249    public static function data_readable_http_methods() {
     250        return array(
     251            'GET request'  => array( 'GET' ),
     252            'HEAD request' => array( 'HEAD' ),
     253        );
    238254    }
    239255
     
    333349
    334350    /**
    335      * @ticket 41683
    336      */
    337     public function test_get_items_wrong_permission_author() {
     351     * @dataProvider data_readable_http_methods
     352     * @ticket 41683
     353     * @ticket 56481
     354     *
     355     * @param string $method The HTTP method to use.
     356     */
     357    public function test_get_items_wrong_permission_author( $method ) {
    338358        wp_set_current_user( self::$author_id );
    339         $request  = new WP_REST_Request( 'GET', '/wp/v2/widgets' );
     359        $request  = new WP_REST_Request( $method, '/wp/v2/widgets' );
    340360        $response = rest_get_server()->dispatch( $request );
    341361        $this->assertErrorResponse( 'rest_cannot_manage_widgets', $response, 403 );
     
    378398        $request  = new WP_REST_Request( 'GET', '/wp/v2/widgets' );
    379399        $response = rest_get_server()->dispatch( $request );
    380         $data     = $response->get_data();
    381         $data     = $this->remove_links( $data );
     400        remove_filter( 'pre_http_request', array( $this, 'mocked_rss_response' ) );
     401        $data = $response->get_data();
     402        $data = $this->remove_links( $data );
    382403        $this->assertSameSets(
    383404            array(
     
    405426
    406427        $wp_widget_factory->widgets['WP_Widget_RSS']->widget_options['show_instance_in_rest'] = true;
     428    }
     429
     430    /**
     431     * @dataProvider data_readable_http_methods
     432     * @ticket 56481
     433     *
     434     * @param string $method The HTTP method to use.
     435     */
     436    public function test_get_items_with_head_request_should_not_prepare_widget_data( $method ) {
     437        $block_content = '<!-- wp:paragraph --><p>Block test</p><!-- /wp:paragraph -->';
     438
     439        $this->setup_widget(
     440            'rss',
     441            1,
     442            array(
     443                'title' => 'RSS test',
     444                'url'   => 'https://wordpress.org/news/feed',
     445            )
     446        );
     447        $this->setup_widget(
     448            'block',
     449            1,
     450            array(
     451                'content' => $block_content,
     452            )
     453        );
     454        $this->setup_sidebar(
     455            'sidebar-1',
     456            array(
     457                'name' => 'Test sidebar',
     458            ),
     459            array( 'block-1', 'rss-1', 'testwidget' )
     460        );
     461
     462        $request = new WP_REST_Request( 'HEAD', '/wp/v2/widgets' );
     463
     464        $hook_name = 'rest_prepare_post';
     465        $filter    = new MockAction();
     466        $callback  = array( $filter, 'filter' );
     467
     468        add_filter( $hook_name, $callback );
     469        $response = rest_get_server()->dispatch( $request );
     470        remove_filter( $hook_name, $callback );
     471
     472        $this->assertNotWPError( $response );
     473        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     474        $this->assertSame( 0, $filter->get_call_count(), 'The "' . $hook_name . '" filter was called when it should not be for HEAD requests.' );
     475        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
    407476    }
    408477
     
    529598
    530599    /**
    531      * @ticket 41683
    532      */
    533     public function test_get_item_no_permission() {
     600     * @dataProvider data_readable_http_methods
     601     * @ticket 56481
     602     *
     603     * @param string $method The HTTP method to use.
     604     */
     605    public function test_get_item_should_allow_adding_headers_via_filter( $method ) {
     606        $this->setup_widget(
     607            'text',
     608            1,
     609            array(
     610                'text' => 'Custom text test',
     611            )
     612        );
     613        $this->setup_sidebar(
     614            'sidebar-1',
     615            array(
     616                'name' => 'Test sidebar',
     617            ),
     618            array( 'text-1' )
     619        );
     620
     621        $request = new WP_REST_Request( $method, '/wp/v2/widgets/text-1' );
     622
     623        $hook_name = 'rest_prepare_widget';
     624        $filter    = new MockAction();
     625        $callback  = array( $filter, 'filter' );
     626        add_filter( $hook_name, $callback );
     627        $header_filter = new class() {
     628            public static function add_custom_header( $response ) {
     629                $response->header( 'X-Test-Header', 'Test' );
     630
     631                return $response;
     632            }
     633        };
     634        add_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     635        $response = rest_get_server()->dispatch( $request );
     636        remove_filter( $hook_name, $callback );
     637        remove_filter( $hook_name, array( $header_filter, 'add_custom_header' ) );
     638
     639        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     640        $this->assertSame( 1, $filter->get_call_count(), 'The "' . $hook_name . '" filter was not called when it should be for GET/HEAD requests.' );
     641        $headers = $response->get_headers();
     642        $this->assertArrayHasKey( 'X-Test-Header', $headers, 'The "X-Test-Header" header should be present in the response.' );
     643        $this->assertSame( 'Test', $headers['X-Test-Header'], 'The "X-Test-Header" header value should be equal to "Test".' );
     644        if ( 'HEAD' !== $method ) {
     645            return null;
     646        }
     647        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     648    }
     649
     650    /**
     651     * @dataProvider data_readable_http_methods
     652     * @ticket 41683
     653     * @ticket 56481
     654     *
     655     * @param string $method The HTTP method to use.
     656     */
     657    public function test_get_item_no_permission( $method ) {
    534658        wp_set_current_user( 0 );
    535659
     
    549673        );
    550674
    551         $request  = new WP_REST_Request( 'GET', '/wp/v2/widgets/text-1' );
     675        $request  = new WP_REST_Request( $method, '/wp/v2/widgets/text-1' );
    552676        $response = rest_get_server()->dispatch( $request );
    553677        $this->assertErrorResponse( 'rest_cannot_manage_widgets', $response, 401 );
     
    555679
    556680    /**
    557      * @ticket 41683
    558      */
    559     public function test_get_item_wrong_permission_author() {
     681     * @dataProvider data_readable_http_methods
     682     * @ticket 41683
     683     * @ticket 56481
     684     *
     685     * @param string $method The HTTP method to use.
     686     */
     687    public function test_get_item_wrong_permission_author( $method ) {
    560688        wp_set_current_user( self::$author_id );
    561689        $this->setup_widget(
     
    573701        );
    574702
    575         $request  = new WP_REST_Request( 'GET', '/wp/v2/widgets/text-1' );
     703        $request  = new WP_REST_Request( $method, '/wp/v2/widgets/text-1' );
    576704        $response = rest_get_server()->dispatch( $request );
    577705        $this->assertErrorResponse( 'rest_cannot_manage_widgets', $response, 403 );
  • trunk/tests/phpunit/tests/rest-api/wpRestBlockPatternCategoriesController.php

    r56492 r59899  
    125125
    126126    /**
     127     * @ticket 56481
     128     */
     129    public function test_get_items_with_head_request_should_not_prepare_block_pattern_categories_data() {
     130        wp_set_current_user( self::$admin_id );
     131        $request  = new WP_REST_Request( 'HEAD', static::REQUEST_ROUTE );
     132        $response = rest_get_server()->dispatch( $request );
     133        $this->assertSame( 200, $response->get_status(), 'The response status should be 200.' );
     134        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     135    }
     136
     137    /**
    127138     * Verify capability check for unauthorized request (not logged in).
    128139     */
  • trunk/tests/phpunit/tests/rest-api/wpRestTemplateAutosavesController.php

    r59605 r59899  
    3434     */
    3535    const TEMPLATE_PART_POST_TYPE = 'wp_template_part';
     36
     37    /**
     38     * @var string
     39     */
     40    const PARENT_POST_TYPE = 'wp_template';
    3641
    3742    /**
     
    294299
    295300    /**
     301     * @ticket 56481
     302     */
     303    public function test_get_items_should_return_no_response_body_for_head_requests() {
     304        wp_set_current_user( self::$admin_id );
     305        $autosave_post_id = wp_create_post_autosave(
     306            array(
     307                'post_content' => 'Autosave content.',
     308                'post_ID'      => self::$template_post->ID,
     309                'post_type'    => self::PARENT_POST_TYPE,
     310            )
     311        );
     312
     313        $request  = new WP_REST_Request(
     314            'HEAD',
     315            '/wp/v2/templates/' . self::TEST_THEME . '/' . self::TEMPLATE_NAME . '/autosaves'
     316        );
     317        $response = rest_get_server()->dispatch( $request );
     318        $this->assertSame( 200, $response->get_status(), 'Response status is 200.' );
     319        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     320    }
     321
     322    /**
    296323     * Data provider for test_get_items_with_data_provider.
    297324     *
     
    425452            )
    426453        );
     454    }
     455
     456    /**
     457     * @ticket 56481
     458     */
     459    public function test_get_item_should_return_no_response_body_for_head_requests() {
     460        wp_set_current_user( self::$admin_id );
     461
     462        $autosave_post_id = wp_create_post_autosave(
     463            array(
     464                'post_content' => 'Autosave content.',
     465                'post_ID'      => self::$template_post->ID,
     466                'post_type'    => self::PARENT_POST_TYPE,
     467            )
     468        );
     469
     470        $request  = new WP_REST_Request( 'HEAD', '/wp/v2/templates/' . self::TEST_THEME . '/' . self::TEMPLATE_NAME . '/autosaves/' . $autosave_post_id );
     471        $response = rest_get_server()->dispatch( $request );
     472        $this->assertSame( 200, $response->get_status(), 'Response status is 200.' );
     473        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
    427474    }
    428475
  • trunk/tests/phpunit/tests/rest-api/wpRestTemplateRevisionsController.php

    r59630 r59899  
    427427        );
    428428    }
     429    /**
     430     * @ticket 56481
     431     */
     432    public function test_get_items_should_return_no_response_body_for_head_requests() {
     433        wp_set_current_user( self::$admin_id );
     434        $request  = new WP_REST_Request(
     435            'HEAD',
     436            '/wp/v2/templates/' . self::TEST_THEME . '/' . self::TEMPLATE_NAME . '/revisions'
     437        );
     438        $response = rest_get_server()->dispatch( $request );
     439        $this->assertSame( 200, $response->get_status(), 'Response status is 200.' );
     440        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     441    }
    429442
    430443    /**
     
    432445     * @covers WP_REST_Template_Revisions_Controller::get_items_permissions_check
    433446     * @ticket 56922
     447     * @ticket 56481
    434448     *
    435449     * @param string $rest_base   Base part of the REST API endpoint to test.
    436450     * @param string $template_id Template ID to use in the test.
    437      */
    438     public function test_get_items_endpoint_should_return_unauthorized_https_status_code_for_unauthorized_request( $rest_base, $template_id ) {
     451     * @param string $method HTTP method to use.
     452     */
     453    public function test_get_items_endpoint_should_return_unauthorized_https_status_code_for_unauthorized_request( $rest_base, $template_id, $method ) {
    439454        wp_set_current_user( 0 );
    440         $request  = new WP_REST_Request( 'GET', '/wp/v2/' . $rest_base . '/' . $template_id . '/revisions' );
     455        $request  = new WP_REST_Request( $method, '/wp/v2/' . $rest_base . '/' . $template_id . '/revisions' );
    441456        $response = rest_get_server()->dispatch( $request );
    442457        $this->assertErrorResponse( 'rest_cannot_read', $response, WP_Http::UNAUTHORIZED );
     
    450465    public function data_get_items_endpoint_should_return_unauthorized_https_status_code_for_unauthorized_request() {
    451466        return array(
    452             'templates'      => array( 'templates', self::TEST_THEME . '//' . self::TEMPLATE_NAME ),
    453             'template parts' => array( 'template-parts', self::TEST_THEME . '//' . self::TEMPLATE_PART_NAME ),
     467            'templates, GET request'       => array( 'templates', self::TEST_THEME . '//' . self::TEMPLATE_NAME, 'GET' ),
     468            'templates, HEAD request'      => array( 'templates', self::TEST_THEME . '//' . self::TEMPLATE_NAME, 'HEAD' ),
     469            'template parts, GET request'  => array( 'template-parts', self::TEST_THEME . '//' . self::TEMPLATE_PART_NAME, 'GET' ),
     470            'template parts, HEAD request' => array( 'template-parts', self::TEST_THEME . '//' . self::TEMPLATE_PART_NAME, 'HEAD' ),
    454471        );
    455472    }
     
    459476     * @covers WP_REST_Template_Revisions_Controller::get_items_permissions_check
    460477     * @ticket 56922
     478     * @ticket 56481
    461479     *
    462480     * @param string $rest_base   Base part of the REST API endpoint to test.
    463481     * @param string $template_id Template ID to use in the test.
    464      */
    465     public function test_get_items_endpoint_should_return_forbidden_https_status_code_for_users_with_insufficient_permissions( $rest_base, string $template_id ) {
     482     * @param string $method HTTP method to use.
     483     */
     484    public function test_get_items_endpoint_should_return_forbidden_https_status_code_for_users_with_insufficient_permissions( $rest_base, string $template_id, $method ) {
    466485        wp_set_current_user( self::$contributor_id );
    467         $request  = new WP_REST_Request( 'GET', '/wp/v2/' . $rest_base . '/' . $template_id . '/revisions' );
     486        $request  = new WP_REST_Request( $method, '/wp/v2/' . $rest_base . '/' . $template_id . '/revisions' );
    468487        $response = rest_get_server()->dispatch( $request );
    469488        $this->assertErrorResponse( 'rest_cannot_read', $response, WP_Http::FORBIDDEN );
     
    477496    public function data_get_items_endpoint_should_return_forbidden_https_status_code_for_users_with_insufficient_permissions() {
    478497        return array(
    479             'templates'      => array( 'templates', self::TEST_THEME . '//' . self::TEMPLATE_NAME ),
    480             'template parts' => array( 'template-parts', self::TEST_THEME . '//' . self::TEMPLATE_PART_NAME ),
     498            'templates, GET request'       => array( 'templates', self::TEST_THEME . '//' . self::TEMPLATE_NAME, 'GET' ),
     499            'templates, HEAD request'      => array( 'templates', self::TEST_THEME . '//' . self::TEMPLATE_NAME, 'HEAD' ),
     500            'template parts, GET request'  => array( 'template-parts', self::TEST_THEME . '//' . self::TEMPLATE_PART_NAME, 'GET' ),
     501            'template parts, HEAD request' => array( 'template-parts', self::TEST_THEME . '//' . self::TEMPLATE_PART_NAME, 'HEAD' ),
    481502        );
    482503    }
     
    598619
    599620    /**
     621     * @ticket 56481
     622     */
     623    public function test_get_item_should_return_no_response_body_for_head_requests() {
     624        wp_set_current_user( self::$admin_id );
     625        $revisions   = wp_get_post_revisions( self::$template_post, array( 'fields' => 'ids' ) );
     626        $revision_id = array_shift( $revisions );
     627        $request     = new WP_REST_Request( 'HEAD', '/wp/v2/templates/' . self::TEST_THEME . '/' . self::TEMPLATE_NAME . '/revisions/' . $revision_id );
     628        $response    = rest_get_server()->dispatch( $request );
     629        $this->assertSame( 200, $response->get_status(), 'Response status is 200.' );
     630        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     631    }
     632
     633    /**
    600634     * Data provider for test_get_item_with_data_provider.
    601635     *
     
    613647     * @covers WP_REST_Template_Revisions_Controller::get_item
    614648     * @ticket 56922
     649     * @ticket 56481
    615650     *
    616651     * @param string  $parent_post_property_name  A class property name that contains the parent post object.
    617652     * @param string  $rest_base                  Base part of the REST API endpoint to test.
    618      */
    619     public function test_get_item_not_found( $parent_post_property_name, $rest_base ) {
     653     * @param string  $method                     HTTP method to use.
     654     */
     655    public function test_get_item_not_found( $parent_post_property_name, $rest_base, $method ) {
    620656        wp_set_current_user( self::$admin_id );
    621657
     
    625661        $revision_id = array_shift( $revisions );
    626662
    627         $request  = new WP_REST_Request( 'GET', '/wp/v2/' . $rest_base . '/invalid//parent/revisions/' . $revision_id );
     663        $request  = new WP_REST_Request( $method, '/wp/v2/' . $rest_base . '/invalid//parent/revisions/' . $revision_id );
    628664        $response = rest_get_server()->dispatch( $request );
    629665        $this->assertErrorResponse( 'rest_post_invalid_parent', $response, WP_Http::NOT_FOUND );
     
    637673    public function data_get_item_not_found() {
    638674        return array(
    639             'templates'      => array( 'template_post', 'templates' ),
    640             'template parts' => array( 'template_part_post', 'template-parts' ),
     675            'templates, GET request'       => array( 'template_post', 'templates', 'GET' ),
     676            'templates, HEAD request'      => array( 'template_post', 'templates', 'HEAD' ),
     677            'template parts, GET request'  => array( 'template_part_post', 'template-parts', 'GET' ),
     678            'template parts, HEAD request' => array( 'template_part_post', 'template-parts', 'HEAD' ),
    641679        );
    642680    }
     
    646684     * @covers WP_REST_Template_Revisions_Controller::get_item
    647685     * @ticket 59875
     686     * @ticket 56481
    648687     *
    649688     * @param string $parent_post_property_name        A class property name that contains the parent post object.
     
    651690     * @param string $rest_base                        Base part of the REST API endpoint to test.
    652691     * @param string $template_id                      Template ID to use in the test.
    653      */
    654     public function test_get_item_invalid_parent_id( $parent_post_property_name, $actual_parent_post_property_name, $rest_base, $template_id ) {
     692     * @param string $method HTTP method to use.
     693     */
     694    public function test_get_item_invalid_parent_id( $parent_post_property_name, $actual_parent_post_property_name, $rest_base, $template_id, $method ) {
    655695        wp_set_current_user( self::$admin_id );
    656696
     
    660700        $revision_id        = array_shift( $revisions );
    661701
    662         $request = new WP_REST_Request( 'GET', '/wp/v2/' . $rest_base . '/' . $template_id . '/revisions/' . $revision_id );
     702        $request = new WP_REST_Request( $method, '/wp/v2/' . $rest_base . '/' . $template_id . '/revisions/' . $revision_id );
    663703
    664704        $response = rest_get_server()->dispatch( $request );
     
    676716    public function data_get_item_invalid_parent_id() {
    677717        return array(
    678             'templates'      => array(
     718            'templates, GET request'       => array(
    679719                'template_post',
    680720                'template_post_2',
    681721                'templates',
    682722                self::TEST_THEME . '//' . self::TEMPLATE_NAME_2,
    683             ),
    684             'template parts' => array(
     723                'GET',
     724            ),
     725            'templates, HEAD request'      => array(
     726                'template_post',
     727                'template_post_2',
     728                'templates',
     729                self::TEST_THEME . '//' . self::TEMPLATE_NAME_2,
     730                'HEAD',
     731            ),
     732            'template parts, GET request'  => array(
    685733                'template_part_post',
    686734                'template_part_post_2',
    687735                'template-parts',
    688736                self::TEST_THEME . '//' . self::TEMPLATE_PART_NAME_2,
     737                'GET',
     738            ),
     739            'template parts, HEAD request' => array(
     740                'template_part_post',
     741                'template_part_post_2',
     742                'template-parts',
     743                self::TEST_THEME . '//' . self::TEMPLATE_PART_NAME_2,
     744                'HEAD',
    689745            ),
    690746        );
  • trunk/tests/phpunit/tests/rest-api/wpRestTemplatesController.php

    r59201 r59899  
    177177
    178178    /**
     179     * @ticket 56481
     180     *
     181     * @covers WP_REST_Templates_Controller::get_items
     182     */
     183    public function test_get_items_should_return_no_response_body_for_head_requests() {
     184        wp_set_current_user( self::$admin_id );
     185        $request  = new WP_REST_Request( 'HEAD', '/wp/v2/templates' );
     186        $response = rest_get_server()->dispatch( $request );
     187        $this->assertSame( 200, $response->get_status(), 'Response status is 200.' );
     188        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     189    }
     190
     191    /**
    179192     * @covers WP_REST_Templates_Controller::get_items
    180193     */
     
    269282
    270283    /**
     284     * @ticket 56481
     285     *
     286     * @covers WP_REST_Templates_Controller::get_item
     287     * @covers WP_REST_Templates_Controller::prepare_item_for_response
     288     */
     289    public function test_get_item_should_return_no_response_body_for_head_requests() {
     290        wp_set_current_user( self::$admin_id );
     291        $request  = new WP_REST_Request( 'HEAD', '/wp/v2/templates/default//my_template' );
     292        $response = rest_get_server()->dispatch( $request );
     293        $this->assertSame( 200, $response->get_status(), 'Response status is 200.' );
     294        $this->assertNull( $response->get_data(), 'The server should not generate a body in response to a HEAD request.' );
     295    }
     296
     297    /**
    271298     * @covers WP_REST_Templates_Controller::get_item
    272299     */
     
    311338        wp_set_current_user( self::$subscriber_id );
    312339        $request  = new WP_REST_Request( 'GET', '/wp/v2/templates/default//my_template' );
    313         $response = rest_get_server()->dispatch( $request );
    314340        $response = rest_get_server()->dispatch( $request );
    315341        $this->assertErrorResponse( 'rest_cannot_manage_templates', $response, 403 );
Note: See TracChangeset for help on using the changeset viewer.