Make WordPress Core

Changeset 59899


Ignore:
Timestamp:
03/02/2025 10:05:08 PM (19 months 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.