WordPress.org

Make WordPress Core

Ticket #38412: 38412.3.diff

File 38412.3.diff, 68.7 KB (added by tharsheblows, 5 years ago)

unit tests and patch to comments controller

  • src/wp-includes/capabilities.php

     
    242242        case 'edit_post_meta':
    243243        case 'delete_post_meta':
    244244        case 'add_post_meta':
    245                 $post = get_post( $args[0] );
    246                 if ( ! $post ) {
     245        case 'edit_comment_meta':
     246        case 'delete_comment_meta':
     247        case 'add_comment_meta':
     248        case 'edit_term_meta':
     249        case 'delete_term_meta':
     250        case 'add_term_meta':
     251        case 'edit_user_meta':
     252        case 'delete_user_meta':
     253        case 'add_user_meta':
     254                list( $_, $object_type, $_ ) = explode( '_', $cap );
     255                $object_id = (int) $args[0];
     256
     257                switch ( $object_type ) {
     258                        case 'post':
     259                                $post = get_post( $object_id );
     260                                if ( ! $post ) {
     261                                        break;
     262                                }
     263
     264                                $sub_type = get_post_type( $post );
     265                                break;
     266
     267                        case 'comment':
     268                                $comment = get_comment( $object_id );
     269                                if ( ! $comment ) {
     270                                        break;
     271                                }
     272
     273                                $sub_type = empty( $comment->comment_type ) ? 'comment' : $comment->comment_type;
     274                                break;
     275
     276                        case 'term':
     277                                $term = get_term( $object_id );
     278                                if ( ! $term ) {
     279                                        break;
     280                                }
     281
     282                                $sub_type = $term->taxonomy;
     283                                break;
     284
     285                        case 'user':
     286                                $user = get_user_by( 'id', $object_id );
     287                                if ( ! $user ) {
     288                                        break;
     289                                }
     290
     291                                $sub_type = 'user';
     292                                break;
     293                }
     294                if ( empty( $sub_type ) ) {
    247295                        $caps[] = 'do_not_allow';
    248296                        break;
    249297                }
    250298
    251                 $post_type = get_post_type( $post );
     299                $caps = map_meta_cap( "edit_{$object_type}", $user_id, $object_id );
    252300
    253                 $caps = map_meta_cap( 'edit_post', $user_id, $post->ID );
    254 
    255301                $meta_key = isset( $args[ 1 ] ) ? $args[ 1 ] : false;
    256302
    257                 if ( $meta_key && ( has_filter( "auth_post_meta_{$meta_key}" ) || has_filter( "auth_post_{$post_type}_meta_{$meta_key}" ) ) ) {
     303                $has_filter = has_filter( "auth_{$object_type}_meta_{$meta_key}" ) || has_filter( "auth_{$object_type}_{$sub_type}_meta_{$meta_key}" );
     304                if ( $meta_key && $has_filter ) {
    258305                        /**
    259                          * Filters whether the user is allowed to add post meta to a post.
     306                         * Filters whether the user is allowed to add meta to an object.
    260307                         *
    261                          * The dynamic portion of the hook name, `$meta_key`, refers to the
    262                          * meta key passed to map_meta_cap().
     308                         * The dynamic portions of the hook name `$object_type`, and `$meta_key`, refer
     309                         * to the object type (post, comment, term, user) and meta key passed to
     310                         * map_meta_cap(), respectively.
    263311                         *
    264312                         * @since 3.3.0
    265313                         *
    266                          * @param bool   $allowed  Whether the user can add the post meta. Default false.
    267                          * @param string $meta_key The meta key.
    268                          * @param int    $post_id  Post ID.
    269                          * @param int    $user_id  User ID.
    270                          * @param string $cap      Capability name.
    271                          * @param array  $caps     User capabilities.
     314                         * @param bool   $allowed    Whether the user can add the meta. Default false.
     315                         * @param string $meta_key   The meta key.
     316                         * @param int    $object_id  Object ID.
     317                         * @param int    $user_id    User ID.
     318                         * @param string $cap        Capability name.
     319                         * @param array  $caps       User capabilities.
    272320                         */
    273                         $allowed = apply_filters( "auth_post_meta_{$meta_key}", false, $meta_key, $post->ID, $user_id, $cap, $caps );
     321                        $allowed = apply_filters( "auth_{$object_type}_meta_{$meta_key}", false, $meta_key, $object_id, $user_id, $cap, $caps );
    274322
    275323                        /**
    276324                         * Filters whether the user is allowed to add post meta to a post of a given type.
    277325                         *
    278                          * The dynamic portions of the hook name, `$meta_key` and `$post_type`,
    279                          * refer to the meta key passed to map_meta_cap() and the post type, respectively.
     326                         * The dynamic portions of the hook name, `$meta_key`, `$object_type`,
     327                         * `$sub_type`, refer to the meta key passed to map_meta_cap(), the object type
     328                         * (post, comment, term, user) and the sub-type (post type, comment type,
     329                         * taxonomy), respectively.
    280330                         *
    281331                         * @since 4.6.0
    282332                         *
    283                          * @param bool   $allowed  Whether the user can add the post meta. Default false.
    284                          * @param string $meta_key The meta key.
    285                          * @param int    $post_id  Post ID.
    286                          * @param int    $user_id  User ID.
    287                          * @param string $cap      Capability name.
    288                          * @param array  $caps     User capabilities.
     333                         * @param bool   $allowed    Whether the user can add the post meta. Default false.
     334                         * @param string $meta_key   The meta key.
     335                         * @param int    $object_id  Object ID.
     336                         * @param int    $user_id    User ID.
     337                         * @param string $cap        Capability name.
     338                         * @param array  $caps       User capabilities.
    289339                         */
    290                         $allowed = apply_filters( "auth_post_{$post_type}_meta_{$meta_key}", $allowed, $meta_key, $post->ID, $user_id, $cap, $caps );
     340                        $allowed = apply_filters( "auth_{$object_type}_{$sub_type}_meta_{$meta_key}", $allowed, $meta_key, $object_id, $user_id, $cap, $caps );
    291341
    292342                        if ( ! $allowed )
    293343                                $caps[] = $cap;
    294                 } elseif ( $meta_key && is_protected_meta( $meta_key, 'post' ) ) {
     344                } elseif ( $meta_key && is_protected_meta( $meta_key, $object_type ) ) {
    295345                        $caps[] = $cap;
    296346                }
    297347                break;
  • src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php

     
    624624                        if ( ! $change ) {
    625625                                return new WP_Error( 'rest_comment_failed_edit', __( 'Updating comment status failed.' ), array( 'status' => 500 ) );
    626626                        }
    627                 } else {
     627                } elseif( ! empty( $prepared_args ) ) {
    628628                        if ( is_wp_error( $prepared_args ) ) {
    629629                                return $prepared_args;
    630630                        }
  • src/wp-includes/rest-api/fields/class-wp-rest-meta-fields.php

     
    122122         */
    123123        public function update_value( $request, $object_id ) {
    124124                $fields = $this->get_registered_fields();
    125 
    126125                foreach ( $fields as $name => $args ) {
    127126                        if ( ! array_key_exists( $name, $request ) ) {
    128127                                continue;
     
    159158         * @return bool|WP_Error True if meta field is deleted, WP_Error otherwise.
    160159         */
    161160        protected function delete_meta_value( $object_id, $name ) {
    162                 if ( ! current_user_can( 'delete_post_meta', $object_id, $name ) ) {
     161                $meta_type = $this->get_meta_type();
     162                if ( ! current_user_can( "delete_{$meta_type}_meta", $object_id, $name ) ) {
    163163                        return new WP_Error(
    164164                                'rest_cannot_delete',
    165165                                sprintf( __( 'You do not have permission to edit the %s custom field.' ), $name ),
     
    167167                        );
    168168                }
    169169
    170                 if ( ! delete_metadata( $this->get_meta_type(), $object_id, wp_slash( $name ) ) ) {
     170                if ( ! delete_metadata( $meta_type, $object_id, wp_slash( $name ) ) ) {
    171171                        return new WP_Error(
    172172                                'rest_meta_database_error',
    173173                                __( 'Could not delete meta value from database.' ),
     
    192192         * @return bool|WP_Error True if meta fields are updated, WP_Error otherwise.
    193193         */
    194194        protected function update_multi_meta_value( $object_id, $name, $values ) {
    195                 if ( ! current_user_can( 'edit_post_meta', $object_id, $name ) ) {
     195                $meta_type = $this->get_meta_type();
     196                if ( ! current_user_can( "edit_{$meta_type}_meta", $object_id, $name ) ) {
    196197                        return new WP_Error(
    197198                                'rest_cannot_update',
    198199                                sprintf( __( 'You do not have permission to edit the %s custom field.' ), $name ),
     
    200201                        );
    201202                }
    202203
    203                 $current = get_metadata( $this->get_meta_type(), $object_id, $name, false );
     204                $current = get_metadata( $meta_type, $object_id, $name, false );
    204205
    205206                $to_remove = $current;
    206207                $to_add    = $values;
     
    227228                $to_remove = array_unique( $to_remove );
    228229
    229230                foreach ( $to_remove as $value ) {
    230                         if ( ! delete_metadata( $this->get_meta_type(), $object_id, wp_slash( $name ), wp_slash( $value ) ) ) {
     231                        if ( ! delete_metadata( $meta_type, $object_id, wp_slash( $name ), wp_slash( $value ) ) ) {
    231232                                return new WP_Error(
    232233                                        'rest_meta_database_error',
    233234                                        __( 'Could not update meta value in database.' ),
     
    237238                }
    238239
    239240                foreach ( $to_add as $value ) {
    240                         if ( ! add_metadata( $this->get_meta_type(), $object_id, wp_slash( $name ), wp_slash( $value ) ) ) {
     241                        if ( ! add_metadata( $meta_type, $object_id, wp_slash( $name ), wp_slash( $value ) ) ) {
    241242                                return new WP_Error(
    242243                                        'rest_meta_database_error',
    243244                                        __( 'Could not update meta value in database.' ),
     
    261262         * @return bool|WP_Error True if the meta field was updated, WP_Error otherwise.
    262263         */
    263264        protected function update_meta_value( $object_id, $name, $value ) {
    264                 if ( ! current_user_can( 'edit_post_meta', $object_id, $name ) ) {
     265                $meta_type = $this->get_meta_type();
     266                if ( ! current_user_can(  "edit_{$meta_type}_meta", $object_id, $name ) ) {
    265267                        return new WP_Error(
    266268                                'rest_cannot_update',
    267269                                sprintf( __( 'You do not have permission to edit the %s custom field.' ), $name ),
     
    269271                        );
    270272                }
    271273
    272                 $meta_type  = $this->get_meta_type();
    273274                $meta_key   = wp_slash( $name );
    274275                $meta_value = wp_slash( $value );
    275276
  • tests/phpunit/tests/rest-api/rest-comment-meta-fields.php

     
     1<?php
     2/**
     3 * Unit tests covering WP_REST_Posts meta functionality.
     4 *
     5 * @package WordPress
     6 * @subpackage REST API
     7 */
     8
     9 /**
     10  * @group restapi
     11  */
     12class WP_Test_REST_Comment_Meta_Fields extends WP_Test_REST_TestCase {
     13        protected static $comment_id;
     14        protected static $post_id;
     15
     16        public static function wpSetUpBeforeClass( $factory ) {
     17                self::$post_id = $factory->post->create();
     18                self::$comment_id = $factory->comment->create_post_comments( self::$post_id, 1 )[0];
     19        }
     20
     21        public static function wpTearDownAfterClass() {
     22                wp_delete_comment( self::$comment_id );
     23                wp_delete_post( self::$post_id );
     24        }
     25        public function setUp() {
     26                parent::setUp();
     27
     28                register_meta( 'comment', 'test_single', array(
     29                        'show_in_rest' => true,
     30                        'single' => true,
     31                ));
     32                register_meta( 'comment', 'test_multi', array(
     33                        'show_in_rest' => true,
     34                        'single' => false,
     35                ));
     36                register_meta( 'comment', 'test_bad_auth', array(
     37                        'show_in_rest' => true,
     38                        'single' => true,
     39                        'auth_callback' => '__return_false',
     40                ));
     41                register_meta( 'comment', 'test_bad_auth_multi', array(
     42                        'show_in_rest' => true,
     43                        'single' => false,
     44                        'auth_callback' => '__return_false',
     45                ));
     46                register_meta( 'comment', 'test_no_rest', array() );
     47                register_meta( 'comment', 'test_rest_disabled', array(
     48                        'show_in_rest' => false,
     49                ));
     50                register_meta( 'comment', 'test_custom_schema', array(
     51                        'single' => true,
     52                        'type' => 'integer',
     53                        'show_in_rest' => array(
     54                                'schema' => array(
     55                                        'type' => 'number',
     56                                ),
     57                        ),
     58                ));
     59                register_meta( 'comment', 'test_invalid_type', array(
     60                        'single' => true,
     61                        'type' => false,
     62                        'show_in_rest' => true,
     63                ));
     64
     65                /** @var WP_REST_Server $wp_rest_server */
     66                global $wp_rest_server;
     67                $this->server = $wp_rest_server = new Spy_REST_Server;
     68                do_action( 'rest_api_init' );
     69
     70                self::$post_id = $this->factory->post->create();
     71                self::$comment_id = $this->factory->comment->create_post_comments( self::$post_id, 1 )[0];
     72
     73        }
     74
     75        protected function grant_write_permission() {
     76                // Ensure we have write permission.
     77                $user = $this->factory->user->create( array(
     78                        'role' => 'editor',
     79                ));
     80                wp_set_current_user( $user );
     81        }
     82
     83        public function test_get_value() {
     84                add_comment_meta( self::$comment_id, 'test_single', 'testvalue' );
     85
     86                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/comments/%d', self::$comment_id ) );
     87                $response = $this->server->dispatch( $request );
     88
     89                $this->assertEquals( 200, $response->get_status() );
     90
     91                $data = $response->get_data();
     92                $this->assertArrayHasKey( 'meta', $data );
     93
     94                $meta = (array) $data['meta'];
     95                $this->assertArrayHasKey( 'test_single', $meta );
     96                $this->assertEquals( 'testvalue', $meta['test_single'] );
     97        }
     98
     99        /**
     100         * @depends test_get_value
     101         */
     102        public function test_get_multi_value() {
     103                add_comment_meta( self::$comment_id, 'test_multi', 'value1' );
     104                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/comments/%d', self::$comment_id ) );
     105
     106                $response = $this->server->dispatch( $request );
     107                $this->assertEquals( 200, $response->get_status() );
     108
     109                $data = $response->get_data();
     110                $meta = (array) $data['meta'];
     111                $this->assertArrayHasKey( 'test_multi', $meta );
     112                $this->assertInternalType( 'array', $meta['test_multi'] );
     113                $this->assertContains( 'value1', $meta['test_multi'] );
     114
     115                // Check after an update.
     116                add_comment_meta( self::$comment_id, 'test_multi', 'value2' );
     117
     118                $response = $this->server->dispatch( $request );
     119                $this->assertEquals( 200, $response->get_status() );
     120                $data = $response->get_data();
     121                $meta = (array) $data['meta'];
     122                $this->assertContains( 'value1', $meta['test_multi'] );
     123                $this->assertContains( 'value2', $meta['test_multi'] );
     124        }
     125
     126        /**
     127         * @depends test_get_value
     128         */
     129        public function test_get_unregistered() {
     130                add_comment_meta( self::$comment_id, 'test_unregistered', 'value1' );
     131                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/comments/%d', self::$comment_id ) );
     132
     133                $response = $this->server->dispatch( $request );
     134                $this->assertEquals( 200, $response->get_status() );
     135
     136                $data = $response->get_data();
     137                $meta = (array) $data['meta'];
     138                $this->assertArrayNotHasKey( 'test_unregistered', $meta );
     139        }
     140
     141        /**
     142         * @depends test_get_value
     143         */
     144        public function test_get_registered_no_api_access() {
     145                add_comment_meta( self::$comment_id, 'test_no_rest', 'for_the_wicked' );
     146                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/comments/%d', self::$comment_id ) );
     147
     148                $response = $this->server->dispatch( $request );
     149                $this->assertEquals( 200, $response->get_status() );
     150
     151                $data = $response->get_data();
     152                $meta = (array) $data['meta'];
     153                $this->assertArrayNotHasKey( 'test_no_rest', $meta );
     154        }
     155
     156        /**
     157         * @depends test_get_value
     158         */
     159        public function test_get_registered_api_disabled() {
     160                add_comment_meta( self::$comment_id, 'test_rest_disabled', 'sleepless_nights' );
     161                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/comments/%d', self::$comment_id ) );
     162
     163                $response = $this->server->dispatch( $request );
     164                $this->assertEquals( 200, $response->get_status() );
     165
     166                $data = $response->get_data();
     167                $meta = (array) $data['meta'];
     168                $this->assertArrayNotHasKey( 'test_rest_disabled', $meta );
     169        }
     170
     171        public function test_get_value_types() {
     172                register_meta( 'comment', 'test_string', array(
     173                        'show_in_rest' => true,
     174                        'single' => true,
     175                        'type' => 'string',
     176                ));
     177                register_meta( 'comment', 'test_number', array(
     178                        'show_in_rest' => true,
     179                        'single' => true,
     180                        'type' => 'number',
     181                ));
     182                register_meta( 'comment', 'test_bool', array(
     183                        'show_in_rest' => true,
     184                        'single' => true,
     185                        'type' => 'boolean',
     186                ));
     187
     188                /** @var WP_REST_Server $wp_rest_server */
     189                global $wp_rest_server;
     190                $this->server = $wp_rest_server = new Spy_REST_Server;
     191                do_action( 'rest_api_init' );
     192
     193                add_comment_meta( self::$comment_id, 'test_string', 42 );
     194                add_comment_meta( self::$comment_id, 'test_number', '42' );
     195                add_comment_meta( self::$comment_id, 'test_bool', 1 );
     196
     197                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/comments/%d', self::$comment_id ) );
     198                $response = $this->server->dispatch( $request );
     199                $this->assertEquals( 200, $response->get_status() );
     200
     201                $data = $response->get_data();
     202                $meta = (array) $data['meta'];
     203
     204                $this->assertArrayHasKey( 'test_string', $meta );
     205                $this->assertInternalType( 'string', $meta['test_string'] );
     206                $this->assertSame( '42', $meta['test_string'] );
     207
     208                $this->assertArrayHasKey( 'test_number', $meta );
     209                $this->assertInternalType( 'float', $meta['test_number'] );
     210                $this->assertSame( 42.0, $meta['test_number'] );
     211
     212                $this->assertArrayHasKey( 'test_bool', $meta );
     213                $this->assertInternalType( 'boolean', $meta['test_bool'] );
     214                $this->assertSame( true, $meta['test_bool'] );
     215        }
     216
     217        /**
     218         * @depends test_get_value
     219         */
     220        public function test_set_value() {
     221                // Ensure no data exists currently.
     222                $values = get_comment_meta( self::$comment_id, 'test_single', false );
     223                $this->assertEmpty( $values );
     224
     225                $this->grant_write_permission();
     226
     227                $data = array(
     228                        'meta' => array(
     229                                'test_single' => 'test_value',
     230                        ),
     231                );
     232                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/comments/%d', self::$comment_id ) );
     233                $request->set_body_params( $data );
     234
     235                $response = $this->server->dispatch( $request );
     236                $this->assertEquals( 200, $response->get_status() );
     237
     238                $meta = get_comment_meta( self::$comment_id, 'test_single', false );
     239                $this->assertNotEmpty( $meta );
     240                $this->assertCount( 1, $meta );
     241                $this->assertEquals( 'test_value', $meta[0] );
     242
     243                $data = $response->get_data();
     244                $meta = (array) $data['meta'];
     245                $this->assertArrayHasKey( 'test_single', $meta );
     246                $this->assertEquals( 'test_value', $meta['test_single'] );
     247        }
     248
     249        /**
     250         * @depends test_get_value
     251         */
     252        public function test_set_duplicate_single_value() {
     253                // Start with an existing metakey and value.
     254                $values = update_comment_meta( self::$comment_id, 'test_single', 'test_value' );
     255                $this->assertEquals( 'test_value', get_comment_meta( self::$comment_id, 'test_single', true ) );
     256
     257                $this->grant_write_permission();
     258
     259                $data = array(
     260                        'meta' => array(
     261                                'test_single' => 'test_value',
     262                        ),
     263                );
     264                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/comments/%d', self::$comment_id ) );
     265                $request->set_body_params( $data );
     266
     267                $response = $this->server->dispatch( $request );
     268                $this->assertEquals( 200, $response->get_status() );
     269
     270                $meta = get_comment_meta( self::$comment_id, 'test_single', true );
     271                $this->assertNotEmpty( $meta );
     272                $this->assertEquals( 'test_value', $meta );
     273
     274                $data = $response->get_data();
     275                $meta = (array) $data['meta'];
     276                $this->assertArrayHasKey( 'test_single', $meta );
     277                $this->assertEquals( 'test_value', $meta['test_single'] );
     278        }
     279
     280        /**
     281         * @depends test_set_value
     282         */
     283        public function test_set_value_unauthenticated() {
     284                $data = array(
     285                        'meta' => array(
     286                                'test_single' => 'test_value',
     287                        ),
     288                );
     289
     290                wp_set_current_user( 0 );
     291
     292                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/comments/%d', self::$comment_id ) );
     293                $request->set_body_params( $data );
     294
     295                $response = $this->server->dispatch( $request );
     296                $this->assertErrorResponse( 'rest_cannot_edit', $response, 401 );
     297
     298                // Check that the value wasn't actually updated.
     299                $this->assertEmpty( get_comment_meta( self::$comment_id, 'test_single', false ) );
     300        }
     301
     302        /**
     303         * @depends test_set_value
     304         */
     305        public function test_set_value_blocked() {
     306                $data = array(
     307                        'meta' => array(
     308                                'test_bad_auth' => 'test_value',
     309                        ),
     310                );
     311
     312                $this->grant_write_permission();
     313
     314                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/comments/%d', self::$comment_id ) );
     315                $request->set_body_params( $data );
     316
     317                $response = $this->server->dispatch( $request );
     318                $this->assertErrorResponse( 'rest_cannot_update', $response, 403 );
     319                $this->assertEmpty( get_comment_meta( self::$comment_id, 'test_bad_auth', false ) );
     320        }
     321
     322        /**
     323         * @depends test_set_value
     324         */
     325        public function test_set_value_db_error() {
     326                $data = array(
     327                        'meta' => array(
     328                                'test_single' => 'test_value',
     329                        ),
     330                );
     331
     332                $this->grant_write_permission();
     333
     334                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/comments/%d', self::$comment_id ) );
     335                $request->set_body_params( $data );
     336
     337                /**
     338                 * Disable showing error as the below is going to intentionally
     339                 * trigger a DB error.
     340                 */
     341                global $wpdb;
     342                $wpdb->suppress_errors = true;
     343                add_filter( 'query', array( $this, 'error_insert_query' ) );
     344
     345                $response = $this->server->dispatch( $request );
     346                remove_filter( 'query', array( $this, 'error_insert_query' ) );
     347                $wpdb->show_errors = true;
     348        }
     349
     350        public function test_set_value_multiple() {
     351                // Ensure no data exists currently.
     352                $values = get_comment_meta( self::$comment_id, 'test_multi', false );
     353                $this->assertEmpty( $values );
     354
     355                $this->grant_write_permission();
     356
     357                $data = array(
     358                        'meta' => array(
     359                                'test_multi' => array( 'val1' ),
     360                        ),
     361                );
     362                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/comments/%d', self::$comment_id ) );
     363                $request->set_body_params( $data );
     364
     365                $response = $this->server->dispatch( $request );
     366                $this->assertEquals( 200, $response->get_status() );
     367
     368                $meta = get_comment_meta( self::$comment_id, 'test_multi', false );
     369                $this->assertNotEmpty( $meta );
     370                $this->assertCount( 1, $meta );
     371                $this->assertEquals( 'val1', $meta[0] );
     372
     373                // Add another value.
     374                $data = array(
     375                        'meta' => array(
     376                                'test_multi' => array( 'val1', 'val2' ),
     377                        ),
     378                );
     379                $request->set_body_params( $data );
     380
     381                $response = $this->server->dispatch( $request );
     382                $this->assertEquals( 200, $response->get_status() );
     383
     384                $meta = get_comment_meta( self::$comment_id, 'test_multi', false );
     385                $this->assertNotEmpty( $meta );
     386                $this->assertCount( 2, $meta );
     387                $this->assertContains( 'val1', $meta );
     388                $this->assertContains( 'val2', $meta );
     389        }
     390
     391        /**
     392         * Test removing only one item with duplicate items.
     393         */
     394        public function test_set_value_remove_one() {
     395                add_comment_meta( self::$comment_id, 'test_multi', 'c' );
     396                add_comment_meta( self::$comment_id, 'test_multi', 'n' );
     397                add_comment_meta( self::$comment_id, 'test_multi', 'n' );
     398
     399                $this->grant_write_permission();
     400
     401                $data = array(
     402                        'meta' => array(
     403                                'test_multi' => array( 'c', 'n' ),
     404                        ),
     405                );
     406                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/comments/%d', self::$comment_id ) );
     407                $request->set_body_params( $data );
     408
     409                $response = $this->server->dispatch( $request );
     410                $this->assertEquals( 200, $response->get_status() );
     411
     412                $meta = get_comment_meta( self::$comment_id, 'test_multi', false );
     413                $this->assertNotEmpty( $meta );
     414                $this->assertCount( 2, $meta );
     415                $this->assertContains( 'c', $meta );
     416                $this->assertContains( 'n', $meta );
     417        }
     418
     419        /**
     420         * @depends test_set_value_multiple
     421         */
     422        public function test_set_value_multiple_unauthenticated() {
     423                // Ensure no data exists currently.
     424                $values = get_comment_meta( self::$comment_id, 'test_multi', false );
     425                $this->assertEmpty( $values );
     426
     427                wp_set_current_user( 0 );
     428
     429                $data = array(
     430                        'meta' => array(
     431                                'test_multi' => array( 'val1' ),
     432                        ),
     433                );
     434                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/comments/%d', self::$comment_id ) );
     435                $request->set_body_params( $data );
     436
     437                $response = $this->server->dispatch( $request );
     438                $this->assertErrorResponse( 'rest_cannot_edit', $response, 401 );
     439
     440                $meta = get_comment_meta( self::$comment_id, 'test_multi', false );
     441                $this->assertEmpty( $meta );
     442        }
     443
     444        /**
     445         * @depends test_set_value_multiple
     446         */
     447        public function test_set_value_multiple_blocked() {
     448                $data = array(
     449                        'meta' => array(
     450                                'test_bad_auth_multi' => array( 'test_value' ),
     451                        ),
     452                );
     453
     454                $this->grant_write_permission();
     455
     456                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/comments/%d', self::$comment_id ) );
     457                $request->set_body_params( $data );
     458
     459                $response = $this->server->dispatch( $request );
     460                $this->assertErrorResponse( 'rest_cannot_update', $response, 403 );
     461                $this->assertEmpty( get_comment_meta( self::$comment_id, 'test_bad_auth_multi', false ) );
     462        }
     463
     464        public function test_add_multi_value_db_error() {
     465                // Ensure no data exists currently.
     466                $values = get_comment_meta( self::$comment_id, 'test_multi', false );
     467                $this->assertEmpty( $values );
     468
     469                $this->grant_write_permission();
     470
     471                $data = array(
     472                        'meta' => array(
     473                                'test_multi' => array( 'val1' ),
     474                        ),
     475                );
     476                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/comments/%d', self::$comment_id ) );
     477                $request->set_body_params( $data );
     478
     479                /**
     480                 * Disable showing error as the below is going to intentionally
     481                 * trigger a DB error.
     482                 */
     483                global $wpdb;
     484                $wpdb->suppress_errors = true;
     485                add_filter( 'query', array( $this, 'error_insert_query' ) );
     486
     487                $response = $this->server->dispatch( $request );
     488                remove_filter( 'query', array( $this, 'error_insert_query' ) );
     489                $wpdb->show_errors = true;
     490
     491                $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 );
     492        }
     493
     494        public function test_remove_multi_value_db_error() {
     495                add_comment_meta( self::$comment_id, 'test_multi', 'val1' );
     496                $values = get_comment_meta( self::$comment_id, 'test_multi', false );
     497                $this->assertEquals( array( 'val1' ), $values );
     498
     499                $this->grant_write_permission();
     500
     501                $data = array(
     502                        'meta' => array(
     503                                'test_multi' => array(),
     504                        ),
     505                );
     506                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/comments/%d', self::$comment_id ) );
     507                $request->set_body_params( $data );
     508
     509                /**
     510                 * Disable showing error as the below is going to intentionally
     511                 * trigger a DB error.
     512                 */
     513                global $wpdb;
     514                $wpdb->suppress_errors = true;
     515                add_filter( 'query', array( $this, 'error_delete_query' ) );
     516
     517                $response = $this->server->dispatch( $request );
     518                remove_filter( 'query', array( $this, 'error_delete_query' ) );
     519                $wpdb->show_errors = true;
     520
     521                $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 );
     522        }
     523
     524        public function test_delete_value() {
     525                add_comment_meta( self::$comment_id, 'test_single', 'val1' );
     526                $current = get_comment_meta( self::$comment_id, 'test_single', true );
     527                $this->assertEquals( 'val1', $current );
     528
     529                $this->grant_write_permission();
     530
     531                $data = array(
     532                        'meta' => array(
     533                                'test_single' => null,
     534                        ),
     535                );
     536                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/comments/%d', self::$comment_id ) );
     537                $request->set_body_params( $data );
     538
     539                $response = $this->server->dispatch( $request );
     540                $this->assertEquals( 200, $response->get_status() );
     541
     542                $meta = get_comment_meta( self::$comment_id, 'test_single', false );
     543                $this->assertEmpty( $meta );
     544        }
     545
     546        /**
     547         * @depends test_delete_value
     548         */
     549        public function test_delete_value_blocked() {
     550                add_comment_meta( self::$comment_id, 'test_bad_auth', 'val1' );
     551                $current = get_comment_meta( self::$comment_id, 'test_bad_auth', true );
     552                $this->assertEquals( 'val1', $current );
     553
     554                $this->grant_write_permission();
     555
     556                $data = array(
     557                        'meta' => array(
     558                                'test_bad_auth' => null,
     559                        ),
     560                );
     561                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/comments/%d', self::$comment_id ) );
     562                $request->set_body_params( $data );
     563
     564                $response = $this->server->dispatch( $request );
     565                $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 );
     566
     567                $meta = get_comment_meta( self::$comment_id, 'test_bad_auth', true );
     568                $this->assertEquals( 'val1', $meta );
     569        }
     570
     571        /**
     572         * @depends test_delete_value
     573         */
     574        public function test_delete_value_db_error() {
     575                add_comment_meta( self::$comment_id, 'test_single', 'val1' );
     576                $current = get_comment_meta( self::$comment_id, 'test_single', true );
     577                $this->assertEquals( 'val1', $current );
     578
     579                $this->grant_write_permission();
     580
     581                $data = array(
     582                        'meta' => array(
     583                                'test_single' => null,
     584                        ),
     585                );
     586                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/comments/%d', self::$comment_id ) );
     587                $request->set_body_params( $data );
     588                /**
     589                 * Disable showing error as the below is going to intentionally
     590                 * trigger a DB error.
     591                 */
     592                global $wpdb;
     593                $wpdb->suppress_errors = true;
     594                add_filter( 'query', array( $this, 'error_delete_query' ) );
     595
     596                $response = $this->server->dispatch( $request );
     597                remove_filter( 'query', array( $this, 'error_delete_query' ) );
     598                $wpdb->show_errors = true;
     599
     600                $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 );
     601        }
     602
     603        public function test_get_schema() {
     604                $request = new WP_REST_Request( 'OPTIONS', sprintf( '/wp/v2/comments/%d', self::$comment_id ) );
     605                $response = $this->server->dispatch( $request );
     606
     607                $data = $response->get_data();
     608                $schema = $data['schema'];
     609
     610                $this->assertArrayHasKey( 'meta', $schema['properties'] );
     611                $meta_schema = $schema['properties']['meta']['properties'];
     612
     613                $this->assertArrayHasKey( 'test_single', $meta_schema );
     614                $this->assertEquals( 'string', $meta_schema['test_single']['type'] );
     615
     616                $this->assertArrayHasKey( 'test_multi', $meta_schema );
     617                $this->assertEquals( 'array', $meta_schema['test_multi']['type'] );
     618                $this->assertArrayHasKey( 'items', $meta_schema['test_multi'] );
     619                $this->assertEquals( 'string', $meta_schema['test_multi']['items']['type'] );
     620
     621                $this->assertArrayHasKey( 'test_custom_schema', $meta_schema );
     622                $this->assertEquals( 'number', $meta_schema['test_custom_schema']['type'] );
     623
     624                $this->assertArrayNotHasKey( 'test_no_rest', $meta_schema );
     625                $this->assertArrayNotHasKey( 'test_rest_disabled', $meta_schema );
     626                $this->assertArrayNotHasKey( 'test_invalid_type', $meta_schema );
     627        }
     628
     629        /**
     630         * Internal function used to disable an insert query which
     631         * will trigger a wpdb error for testing purposes.
     632         */
     633        public function error_insert_query( $query ) {
     634                if ( strpos( $query, 'INSERT' ) === 0 ) {
     635                        $query = '],';
     636                }
     637                return $query;
     638        }
     639
     640        /**
     641         * Internal function used to disable an insert query which
     642         * will trigger a wpdb error for testing purposes.
     643         */
     644        public function error_delete_query( $query ) {
     645                if ( strpos( $query, 'DELETE' ) === 0 ) {
     646                        $query = '],';
     647                }
     648                return $query;
     649        }
     650}
  • tests/phpunit/tests/rest-api/rest-term-meta-fields.php

     
     1<?php
     2/**
     3 * Unit tests covering WP_REST_Taxonomies terms meta functionality.
     4 *
     5 * @package WordPress
     6 * @subpackage REST API
     7 */
     8
     9 /**
     10  * @group restapi
     11  */
     12class WP_Test_REST_Term_Meta_Fields extends WP_Test_REST_TestCase {
     13
     14        public function setUp() {
     15                parent::setUp();
     16
     17                register_meta( 'term', 'test_single', array(
     18                        'show_in_rest' => true,
     19                        'single' => true,
     20                ));
     21                register_meta( 'term', 'test_multi', array(
     22                        'show_in_rest' => true,
     23                        'single' => false,
     24                ));
     25                register_meta( 'term', 'test_bad_auth', array(
     26                        'show_in_rest' => true,
     27                        'single' => true,
     28                        'auth_callback' => '__return_false',
     29                ));
     30                register_meta( 'term', 'test_bad_auth_multi', array(
     31                        'show_in_rest' => true,
     32                        'single' => false,
     33                        'auth_callback' => '__return_false',
     34                ));
     35                register_meta( 'term', 'test_no_rest', array() );
     36                register_meta( 'term', 'test_rest_disabled', array(
     37                        'show_in_rest' => false,
     38                ));
     39                register_meta( 'term', 'test_custom_schema', array(
     40                        'single' => true,
     41                        'type' => 'integer',
     42                        'show_in_rest' => array(
     43                                'schema' => array(
     44                                        'type' => 'number',
     45                                ),
     46                        ),
     47                ));
     48                register_meta( 'term', 'test_invalid_type', array(
     49                        'single' => true,
     50                        'type' => false,
     51                        'show_in_rest' => true,
     52                ));
     53
     54                /** @var WP_REST_Server $wp_rest_server */
     55                global $wp_rest_server;
     56                $this->server = $wp_rest_server = new Spy_REST_Server;
     57                do_action( 'rest_api_init' );
     58
     59                register_taxonomy( 'wptests_tax', 'post', array( 'show_in_rest' => true ) );
     60                $controller = new WP_REST_Terms_Controller( 'wptests_tax' );
     61                $controller->register_routes();
     62
     63                $this->term_id = $this->factory->term->create( array( 'taxonomy' => 'wptests_tax' ) );
     64        }
     65
     66        protected function grant_write_permission() {
     67                // Ensure we have write permission.
     68                $user = $this->factory->user->create( array(
     69                        'role' => 'administrator',
     70                ));
     71                wp_set_current_user( $user );
     72        }
     73
     74        public function test_get_value() {
     75                add_term_meta( $this->term_id, 'test_single', 'testvalue' );
     76                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/wptests_tax/%d', $this->term_id ) );
     77
     78                $response = $this->server->dispatch( $request );
     79
     80                $this->assertEquals( 200, $response->get_status() );
     81
     82                $data = $response->get_data();
     83                $this->assertArrayHasKey( 'meta', $data );
     84
     85                $meta = (array) $data['meta'];
     86                $this->assertArrayHasKey( 'test_single', $meta );
     87                $this->assertEquals( 'testvalue', $meta['test_single'] );
     88        }
     89
     90        /**
     91         * @depends test_get_value
     92         */
     93        public function test_get_multi_value() {
     94                add_term_meta( $this->term_id, 'test_multi', 'value1' );
     95                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/wptests_tax/%d', $this->term_id ) );
     96
     97                $response = $this->server->dispatch( $request );
     98                $this->assertEquals( 200, $response->get_status() );
     99
     100                $data = $response->get_data();
     101                $meta = (array) $data['meta'];
     102                $this->assertArrayHasKey( 'test_multi', $meta );
     103                $this->assertInternalType( 'array', $meta['test_multi'] );
     104                $this->assertContains( 'value1', $meta['test_multi'] );
     105
     106                // Check after an update.
     107                add_term_meta( $this->term_id, 'test_multi', 'value2' );
     108
     109                $response = $this->server->dispatch( $request );
     110                $this->assertEquals( 200, $response->get_status() );
     111                $data = $response->get_data();
     112                $meta = (array) $data['meta'];
     113                $this->assertContains( 'value1', $meta['test_multi'] );
     114                $this->assertContains( 'value2', $meta['test_multi'] );
     115        }
     116
     117        /**
     118         * @depends test_get_value
     119         */
     120        public function test_get_unregistered() {
     121                add_term_meta( $this->term_id, 'test_unregistered', 'value1' );
     122                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/wptests_tax/%d', $this->term_id ) );
     123
     124                $response = $this->server->dispatch( $request );
     125                $this->assertEquals( 200, $response->get_status() );
     126
     127                $data = $response->get_data();
     128                $meta = (array) $data['meta'];
     129                $this->assertArrayNotHasKey( 'test_unregistered', $meta );
     130        }
     131
     132        /**
     133         * @depends test_get_value
     134         */
     135        public function test_get_registered_no_api_access() {
     136                add_term_meta( $this->term_id, 'test_no_rest', 'for_the_wicked' );
     137                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/wptests_tax/%d', $this->term_id ) );
     138
     139                $response = $this->server->dispatch( $request );
     140                $this->assertEquals( 200, $response->get_status() );
     141
     142                $data = $response->get_data();
     143                $meta = (array) $data['meta'];
     144                $this->assertArrayNotHasKey( 'test_no_rest', $meta );
     145        }
     146
     147        /**
     148         * @depends test_get_value
     149         */
     150        public function test_get_registered_api_disabled() {
     151                add_term_meta( $this->term_id, 'test_rest_disabled', 'sleepless_nights' );
     152                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/wptests_tax/%d', $this->term_id ) );
     153
     154                $response = $this->server->dispatch( $request );
     155                $this->assertEquals( 200, $response->get_status() );
     156
     157                $data = $response->get_data();
     158                $meta = (array) $data['meta'];
     159                $this->assertArrayNotHasKey( 'test_rest_disabled', $meta );
     160        }
     161
     162        public function test_get_value_types() {
     163                register_meta( 'term', 'test_string', array(
     164                        'show_in_rest' => true,
     165                        'single' => true,
     166                        'type' => 'string',
     167                ));
     168                register_meta( 'term', 'test_number', array(
     169                        'show_in_rest' => true,
     170                        'single' => true,
     171                        'type' => 'number',
     172                ));
     173                register_meta( 'term', 'test_bool', array(
     174                        'show_in_rest' => true,
     175                        'single' => true,
     176                        'type' => 'boolean',
     177                ));
     178
     179                /** @var WP_REST_Server $wp_rest_server */
     180                global $wp_rest_server;
     181                $this->server = $wp_rest_server = new Spy_REST_Server;
     182                do_action( 'rest_api_init' );
     183
     184                add_term_meta( $this->term_id, 'test_string', 42 );
     185                add_term_meta( $this->term_id, 'test_number', '42' );
     186                add_term_meta( $this->term_id, 'test_bool', 1 );
     187
     188                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/wptests_tax/%d', $this->term_id ) );
     189                $response = $this->server->dispatch( $request );
     190                $this->assertEquals( 200, $response->get_status() );
     191
     192                $data = $response->get_data();
     193                $meta = (array) $data['meta'];
     194
     195                $this->assertArrayHasKey( 'test_string', $meta );
     196                $this->assertInternalType( 'string', $meta['test_string'] );
     197                $this->assertSame( '42', $meta['test_string'] );
     198
     199                $this->assertArrayHasKey( 'test_number', $meta );
     200                $this->assertInternalType( 'float', $meta['test_number'] );
     201                $this->assertSame( 42.0, $meta['test_number'] );
     202
     203                $this->assertArrayHasKey( 'test_bool', $meta );
     204                $this->assertInternalType( 'boolean', $meta['test_bool'] );
     205                $this->assertSame( true, $meta['test_bool'] );
     206        }
     207
     208        /**
     209         * @depends test_get_value
     210         */
     211        public function test_set_value() {
     212                // Ensure no data exists currently.
     213                $values = get_term_meta( $this->term_id, 'test_single', false );
     214                $this->assertEmpty( $values );
     215
     216                $this->grant_write_permission();
     217
     218                $data = array(
     219                        'meta' => array(
     220                                'test_single' => 'test_value',
     221                        ),
     222                );
     223                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/wptests_tax/%d', $this->term_id ) );
     224                $request->set_body_params( $data );
     225
     226                $response = $this->server->dispatch( $request );
     227                $this->assertEquals( 200, $response->get_status() );
     228
     229                $meta = get_term_meta( $this->term_id, 'test_single', false );
     230                $this->assertNotEmpty( $meta );
     231                $this->assertCount( 1, $meta );
     232                $this->assertEquals( 'test_value', $meta[0] );
     233
     234                $data = $response->get_data();
     235                $meta = (array) $data['meta'];
     236                $this->assertArrayHasKey( 'test_single', $meta );
     237                $this->assertEquals( 'test_value', $meta['test_single'] );
     238        }
     239
     240        /**
     241         * @depends test_get_value
     242         */
     243        public function test_set_duplicate_single_value() {
     244                // Start with an existing metakey and value.
     245                $values = update_term_meta( $this->term_id, 'test_single', 'test_value' );
     246                $this->assertEquals( 'test_value', get_term_meta( $this->term_id, 'test_single', true ) );
     247
     248                $this->grant_write_permission();
     249
     250                $data = array(
     251                        'meta' => array(
     252                                'test_single' => 'test_value',
     253                        ),
     254                );
     255                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/wptests_tax/%d', $this->term_id ) );
     256                $request->set_body_params( $data );
     257
     258                $response = $this->server->dispatch( $request );
     259                $this->assertEquals( 200, $response->get_status() );
     260
     261                $meta = get_term_meta( $this->term_id, 'test_single', true );
     262                $this->assertNotEmpty( $meta );
     263                $this->assertEquals( 'test_value', $meta );
     264
     265                $data = $response->get_data();
     266                $meta = (array) $data['meta'];
     267                $this->assertArrayHasKey( 'test_single', $meta );
     268                $this->assertEquals( 'test_value', $meta['test_single'] );
     269        }
     270
     271        /**
     272         * @depends test_set_value
     273         */
     274        public function test_set_value_unauthenticated() {
     275                $data = array(
     276                        'meta' => array(
     277                                'test_single' => 'test_value',
     278                        ),
     279                );
     280
     281                wp_set_current_user( 0 );
     282
     283                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/wptests_tax/%d', $this->term_id ) );
     284                $request->set_body_params( $data );
     285
     286                $response = $this->server->dispatch( $request );
     287                $this->assertErrorResponse( 'rest_cannot_update', $response, 401 );
     288
     289                // Check that the value wasn't actually updated.
     290                $this->assertEmpty( get_term_meta( $this->term_id, 'test_single', false ) );
     291        }
     292
     293        /**
     294         * @depends test_set_value
     295         */
     296        public function test_set_value_blocked() {
     297                $data = array(
     298                        'meta' => array(
     299                                'test_bad_auth' => 'test_value',
     300                        ),
     301                );
     302
     303                $this->grant_write_permission();
     304
     305                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/wptests_tax/%d', $this->term_id ) );
     306                $request->set_body_params( $data );
     307
     308                $response = $this->server->dispatch( $request );
     309                $this->assertErrorResponse( 'rest_cannot_update', $response, 403 );
     310                $this->assertEmpty( get_term_meta( $this->term_id, 'test_bad_auth', false ) );
     311        }
     312
     313        /**
     314         * @depends test_set_value
     315         */
     316        public function test_set_value_db_error() {
     317                $data = array(
     318                        'meta' => array(
     319                                'test_single' => 'test_value',
     320                        ),
     321                );
     322
     323                $this->grant_write_permission();
     324
     325                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/wptests_tax/%d', $this->term_id ) );
     326                $request->set_body_params( $data );
     327
     328                /**
     329                 * Disable showing error as the below is going to intentionally
     330                 * trigger a DB error.
     331                 */
     332                global $wpdb;
     333                $wpdb->suppress_errors = true;
     334                add_filter( 'query', array( $this, 'error_insert_query' ) );
     335
     336                $response = $this->server->dispatch( $request );
     337                remove_filter( 'query', array( $this, 'error_insert_query' ) );
     338                $wpdb->show_errors = true;
     339        }
     340
     341        public function test_set_value_multiple() {
     342                // Ensure no data exists currently.
     343                $values = get_term_meta( $this->term_id, 'test_multi', false );
     344                $this->assertEmpty( $values );
     345
     346                $this->grant_write_permission();
     347
     348                $data = array(
     349                        'meta' => array(
     350                                'test_multi' => array( 'val1' ),
     351                        ),
     352                );
     353                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/wptests_tax/%d', $this->term_id ) );
     354                $request->set_body_params( $data );
     355
     356                $response = $this->server->dispatch( $request );
     357                $this->assertEquals( 200, $response->get_status() );
     358
     359                $meta = get_term_meta( $this->term_id, 'test_multi', false );
     360                $this->assertNotEmpty( $meta );
     361                $this->assertCount( 1, $meta );
     362                $this->assertEquals( 'val1', $meta[0] );
     363
     364                // Add another value.
     365                $data = array(
     366                        'meta' => array(
     367                                'test_multi' => array( 'val1', 'val2' ),
     368                        ),
     369                );
     370                $request->set_body_params( $data );
     371
     372                $response = $this->server->dispatch( $request );
     373                $this->assertEquals( 200, $response->get_status() );
     374
     375                $meta = get_term_meta( $this->term_id, 'test_multi', false );
     376                $this->assertNotEmpty( $meta );
     377                $this->assertCount( 2, $meta );
     378                $this->assertContains( 'val1', $meta );
     379                $this->assertContains( 'val2', $meta );
     380        }
     381
     382        /**
     383         * Test removing only one item with duplicate items.
     384         */
     385        public function test_set_value_remove_one() {
     386                add_term_meta( $this->term_id, 'test_multi', 'c' );
     387                add_term_meta( $this->term_id, 'test_multi', 'n' );
     388                add_term_meta( $this->term_id, 'test_multi', 'n' );
     389
     390                $this->grant_write_permission();
     391
     392                $data = array(
     393                        'meta' => array(
     394                                'test_multi' => array( 'c', 'n' ),
     395                        ),
     396                );
     397                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/wptests_tax/%d', $this->term_id ) );
     398                $request->set_body_params( $data );
     399
     400                $response = $this->server->dispatch( $request );
     401                $this->assertEquals( 200, $response->get_status() );
     402
     403                $meta = get_term_meta( $this->term_id, 'test_multi', false );
     404                $this->assertNotEmpty( $meta );
     405                $this->assertCount( 2, $meta );
     406                $this->assertContains( 'c', $meta );
     407                $this->assertContains( 'n', $meta );
     408        }
     409
     410        /**
     411         * @depends test_set_value_multiple
     412         */
     413        public function test_set_value_multiple_unauthenticated() {
     414                // Ensure no data exists currently.
     415                $values = get_term_meta( $this->term_id, 'test_multi', false );
     416                $this->assertEmpty( $values );
     417
     418                wp_set_current_user( 0 );
     419
     420                $data = array(
     421                        'meta' => array(
     422                                'test_multi' => array( 'val1' ),
     423                        ),
     424                );
     425                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/wptests_tax/%d', $this->term_id ) );
     426                $request->set_body_params( $data );
     427
     428                $response = $this->server->dispatch( $request );
     429                $this->assertErrorResponse( 'rest_cannot_update', $response, 401 );
     430
     431                $meta = get_term_meta( $this->term_id, 'test_multi', false );
     432                $this->assertEmpty( $meta );
     433        }
     434
     435        /**
     436         * @depends test_set_value_multiple
     437         */
     438        public function test_set_value_multiple_blocked() {
     439                $data = array(
     440                        'meta' => array(
     441                                'test_bad_auth_multi' => array( 'test_value' ),
     442                        ),
     443                );
     444
     445                $this->grant_write_permission();
     446
     447                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/wptests_tax/%d', $this->term_id ) );
     448                $request->set_body_params( $data );
     449
     450                $response = $this->server->dispatch( $request );
     451                $this->assertErrorResponse( 'rest_cannot_update', $response, 403 );
     452                $this->assertEmpty( get_term_meta( $this->term_id, 'test_bad_auth_multi', false ) );
     453        }
     454
     455        public function test_add_multi_value_db_error() {
     456                // Ensure no data exists currently.
     457                $values = get_term_meta( $this->term_id, 'test_multi', false );
     458                $this->assertEmpty( $values );
     459
     460                $this->grant_write_permission();
     461
     462                $data = array(
     463                        'meta' => array(
     464                                'test_multi' => array( 'val1' ),
     465                        ),
     466                );
     467                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/wptests_tax/%d', $this->term_id ) );
     468                $request->set_body_params( $data );
     469
     470                /**
     471                 * Disable showing error as the below is going to intentionally
     472                 * trigger a DB error.
     473                 */
     474                global $wpdb;
     475                $wpdb->suppress_errors = true;
     476                add_filter( 'query', array( $this, 'error_insert_query' ) );
     477
     478                $response = $this->server->dispatch( $request );
     479                remove_filter( 'query', array( $this, 'error_insert_query' ) );
     480                $wpdb->show_errors = true;
     481
     482                $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 );
     483        }
     484
     485        public function test_remove_multi_value_db_error() {
     486                add_term_meta( $this->term_id, 'test_multi', 'val1' );
     487                $values = get_term_meta( $this->term_id, 'test_multi', false );
     488                $this->assertEquals( array( 'val1' ), $values );
     489
     490                $this->grant_write_permission();
     491
     492                $data = array(
     493                        'meta' => array(
     494                                'test_multi' => array(),
     495                        ),
     496                );
     497                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/wptests_tax/%d', $this->term_id ) );
     498                $request->set_body_params( $data );
     499
     500                /**
     501                 * Disable showing error as the below is going to intentionally
     502                 * trigger a DB error.
     503                 */
     504                global $wpdb;
     505                $wpdb->suppress_errors = true;
     506                add_filter( 'query', array( $this, 'error_delete_query' ) );
     507
     508                $response = $this->server->dispatch( $request );
     509                remove_filter( 'query', array( $this, 'error_delete_query' ) );
     510                $wpdb->show_errors = true;
     511
     512                $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 );
     513        }
     514
     515        public function test_delete_value() {
     516                add_term_meta( $this->term_id, 'test_single', 'val1' );
     517                $current = get_term_meta( $this->term_id, 'test_single', true );
     518                $this->assertEquals( 'val1', $current );
     519
     520                $this->grant_write_permission();
     521
     522                $data = array(
     523                        'meta' => array(
     524                                'test_single' => null,
     525                        ),
     526                );
     527                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/wptests_tax/%d', $this->term_id ) );
     528                $request->set_body_params( $data );
     529
     530                $response = $this->server->dispatch( $request );
     531                $this->assertEquals( 200, $response->get_status() );
     532
     533                $meta = get_term_meta( $this->term_id, 'test_single', false );
     534                $this->assertEmpty( $meta );
     535        }
     536
     537        /**
     538         * @depends test_delete_value
     539         */
     540        public function test_delete_value_blocked() {
     541                add_term_meta( $this->term_id, 'test_bad_auth', 'val1' );
     542                $current = get_term_meta( $this->term_id, 'test_bad_auth', true );
     543                $this->assertEquals( 'val1', $current );
     544
     545                $this->grant_write_permission();
     546
     547                $data = array(
     548                        'meta' => array(
     549                                'test_bad_auth' => null,
     550                        ),
     551                );
     552                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/wptests_tax/%d', $this->term_id ) );
     553                $request->set_body_params( $data );
     554
     555                $response = $this->server->dispatch( $request );
     556                $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 );
     557
     558                $meta = get_term_meta( $this->term_id, 'test_bad_auth', true );
     559                $this->assertEquals( 'val1', $meta );
     560        }
     561
     562        /**
     563         * @depends test_delete_value
     564         */
     565        public function test_delete_value_db_error() {
     566                add_term_meta( $this->term_id, 'test_single', 'val1' );
     567                $current = get_term_meta( $this->term_id, 'test_single', true );
     568                $this->assertEquals( 'val1', $current );
     569
     570                $this->grant_write_permission();
     571
     572                $data = array(
     573                        'meta' => array(
     574                                'test_single' => null,
     575                        ),
     576                );
     577                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/wptests_tax/%d', $this->term_id ) );
     578                $request->set_body_params( $data );
     579                /**
     580                 * Disable showing error as the below is going to intentionally
     581                 * trigger a DB error.
     582                 */
     583                global $wpdb;
     584                $wpdb->suppress_errors = true;
     585                add_filter( 'query', array( $this, 'error_delete_query' ) );
     586
     587                $response = $this->server->dispatch( $request );
     588                remove_filter( 'query', array( $this, 'error_delete_query' ) );
     589                $wpdb->show_errors = true;
     590
     591                $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 );
     592        }
     593
     594        public function test_get_schema() {
     595                $request = new WP_REST_Request( 'OPTIONS', sprintf( '/wp/v2/wptests_tax/%d', $this->term_id ) );
     596                $response = $this->server->dispatch( $request );
     597
     598                $data = $response->get_data();
     599                $schema = $data['schema'];
     600
     601                $this->assertArrayHasKey( 'meta', $schema['properties'] );
     602                $meta_schema = $schema['properties']['meta']['properties'];
     603
     604                $this->assertArrayHasKey( 'test_single', $meta_schema );
     605                $this->assertEquals( 'string', $meta_schema['test_single']['type'] );
     606
     607                $this->assertArrayHasKey( 'test_multi', $meta_schema );
     608                $this->assertEquals( 'array', $meta_schema['test_multi']['type'] );
     609                $this->assertArrayHasKey( 'items', $meta_schema['test_multi'] );
     610                $this->assertEquals( 'string', $meta_schema['test_multi']['items']['type'] );
     611
     612                $this->assertArrayHasKey( 'test_custom_schema', $meta_schema );
     613                $this->assertEquals( 'number', $meta_schema['test_custom_schema']['type'] );
     614
     615                $this->assertArrayNotHasKey( 'test_no_rest', $meta_schema );
     616                $this->assertArrayNotHasKey( 'test_rest_disabled', $meta_schema );
     617                $this->assertArrayNotHasKey( 'test_invalid_type', $meta_schema );
     618        }
     619
     620        /**
     621         * Internal function used to disable an insert query which
     622         * will trigger a wpdb error for testing purposes.
     623         */
     624        public function error_insert_query( $query ) {
     625                if ( strpos( $query, 'INSERT' ) === 0 ) {
     626                        $query = '],';
     627                }
     628                return $query;
     629        }
     630
     631        /**
     632         * Internal function used to disable an insert query which
     633         * will trigger a wpdb error for testing purposes.
     634         */
     635        public function error_delete_query( $query ) {
     636                if ( strpos( $query, 'DELETE' ) === 0 ) {
     637                        $query = '],';
     638                }
     639                return $query;
     640        }
     641}
  • tests/phpunit/tests/rest-api/rest-user-meta-fields.php

     
     1<?php
     2/**
     3 * Unit tests covering WP_REST_Users meta functionality.
     4 *
     5 * @package WordPress
     6 * @subpackage REST API
     7 */
     8
     9 /**
     10  * @group restapi
     11  */
     12class WP_Test_REST_User_Meta_Fields extends WP_Test_REST_TestCase {
     13        protected static $user;
     14
     15        public static function wpSetUpBeforeClass( $factory ) {
     16                self::$user = $factory->user->create( array(
     17                        'role' => 'author',
     18                ) );
     19        }
     20
     21        public static function wpTearDownAfterClass() {
     22                wp_delete_user( self::$user );
     23        }
     24
     25        public function setUp() {
     26                parent::setUp();
     27
     28                register_meta( 'user', 'test_single', array(
     29                        'show_in_rest' => true,
     30                        'single' => true,
     31                ));
     32                register_meta( 'user', 'test_multi', array(
     33                        'show_in_rest' => true,
     34                        'single' => false,
     35                ));
     36                register_meta( 'user', 'test_bad_auth', array(
     37                        'show_in_rest' => true,
     38                        'single' => true,
     39                        'auth_callback' => '__return_false',
     40                ));
     41                register_meta( 'user', 'test_bad_auth_multi', array(
     42                        'show_in_rest' => true,
     43                        'single' => false,
     44                        'auth_callback' => '__return_false',
     45                ));
     46                register_meta( 'user', 'test_no_rest', array() );
     47                register_meta( 'user', 'test_rest_disabled', array(
     48                        'show_in_rest' => false,
     49                ));
     50                register_meta( 'user', 'test_custom_schema', array(
     51                        'single' => true,
     52                        'type' => 'integer',
     53                        'show_in_rest' => array(
     54                                'schema' => array(
     55                                        'type' => 'number',
     56                                ),
     57                        ),
     58                ));
     59                register_meta( 'user', 'test_invalid_type', array(
     60                        'single' => true,
     61                        'type' => false,
     62                        'show_in_rest' => true,
     63                ));
     64
     65                /** @var WP_REST_Server $wp_rest_server */
     66                global $wp_rest_server;
     67                $this->server = $wp_rest_server = new Spy_REST_Server;
     68                do_action( 'rest_api_init' );
     69
     70        }
     71
     72        protected function grant_write_permission() {
     73                // Ensure we have write permission.
     74                $user = $this->factory->user->create( array(
     75                        'role' => 'administrator',
     76                ));
     77                wp_set_current_user( $user );
     78        }
     79
     80        public function test_get_value() {
     81                add_user_meta( self::$user, 'test_single', 'testvalue' );
     82                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/users/%d', self::$user ) );
     83
     84                $this->grant_write_permission();
     85
     86                $response = $this->server->dispatch( $request );
     87
     88                $this->assertEquals( 200, $response->get_status() );
     89
     90                $data = $response->get_data();
     91                $this->assertArrayHasKey( 'meta', $data );
     92
     93                $meta = (array) $data['meta'];
     94                $this->assertArrayHasKey( 'test_single', $meta );
     95                $this->assertEquals( 'testvalue', $meta['test_single'] );
     96        }
     97
     98        /**
     99         * @depends test_get_value
     100         */
     101        public function test_get_multi_value() {
     102                add_user_meta( self::$user, 'test_multi', 'value1' );
     103                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/users/%d', self::$user ) );
     104
     105                $this->grant_write_permission();
     106
     107                $response = $this->server->dispatch( $request );
     108                $this->assertEquals( 200, $response->get_status() );
     109
     110                $data = $response->get_data();
     111                $meta = (array) $data['meta'];
     112                $this->assertArrayHasKey( 'test_multi', $meta );
     113                $this->assertInternalType( 'array', $meta['test_multi'] );
     114                $this->assertContains( 'value1', $meta['test_multi'] );
     115
     116                // Check after an update.
     117                add_user_meta( self::$user, 'test_multi', 'value2' );
     118
     119                $response = $this->server->dispatch( $request );
     120                $this->assertEquals( 200, $response->get_status() );
     121                $data = $response->get_data();
     122                $meta = (array) $data['meta'];
     123                $this->assertContains( 'value1', $meta['test_multi'] );
     124                $this->assertContains( 'value2', $meta['test_multi'] );
     125        }
     126
     127        /**
     128         * @depends test_get_value
     129         */
     130        public function test_get_unregistered() {
     131                add_user_meta( self::$user, 'test_unregistered', 'value1' );
     132                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/users/%d', self::$user ) );
     133
     134                $this->grant_write_permission();
     135
     136                $response = $this->server->dispatch( $request );
     137                $this->assertEquals( 200, $response->get_status() );
     138
     139                $data = $response->get_data();
     140                $meta = (array) $data['meta'];
     141                $this->assertArrayNotHasKey( 'test_unregistered', $meta );
     142        }
     143
     144        /**
     145         * @depends test_get_value
     146         */
     147        public function test_get_registered_no_api_access() {
     148                add_user_meta( self::$user, 'test_no_rest', 'for_the_wicked' );
     149                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/users/%d', self::$user ) );
     150
     151                $this->grant_write_permission();
     152
     153                $response = $this->server->dispatch( $request );
     154                $this->assertEquals( 200, $response->get_status() );
     155
     156                $data = $response->get_data();
     157                $meta = (array) $data['meta'];
     158                $this->assertArrayNotHasKey( 'test_no_rest', $meta );
     159        }
     160
     161        /**
     162         * @depends test_get_value
     163         */
     164        public function test_get_registered_api_disabled() {
     165                add_user_meta( self::$user, 'test_rest_disabled', 'sleepless_nights' );
     166                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/users/%d', self::$user ) );
     167
     168                $this->grant_write_permission();
     169               
     170                $response = $this->server->dispatch( $request );
     171                $this->assertEquals( 200, $response->get_status() );
     172
     173                $data = $response->get_data();
     174                $meta = (array) $data['meta'];
     175                $this->assertArrayNotHasKey( 'test_rest_disabled', $meta );
     176        }
     177
     178        public function test_get_value_types() {
     179                register_meta( 'user', 'test_string', array(
     180                        'show_in_rest' => true,
     181                        'single' => true,
     182                        'type' => 'string',
     183                ));
     184                register_meta( 'user', 'test_number', array(
     185                        'show_in_rest' => true,
     186                        'single' => true,
     187                        'type' => 'number',
     188                ));
     189                register_meta( 'user', 'test_bool', array(
     190                        'show_in_rest' => true,
     191                        'single' => true,
     192                        'type' => 'boolean',
     193                ));
     194
     195                /** @var WP_REST_Server $wp_rest_server */
     196                global $wp_rest_server;
     197                $this->server = $wp_rest_server = new Spy_REST_Server;
     198                do_action( 'rest_api_init' );
     199
     200                add_user_meta( self::$user, 'test_string', 42 );
     201                add_user_meta( self::$user, 'test_number', '42' );
     202                add_user_meta( self::$user, 'test_bool', 1 );
     203
     204                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/users/%d', self::$user ) );
     205
     206                $this->grant_write_permission();
     207
     208                $response = $this->server->dispatch( $request );
     209                $this->assertEquals( 200, $response->get_status() );
     210
     211                $data = $response->get_data();
     212                $meta = (array) $data['meta'];
     213
     214                $this->assertArrayHasKey( 'test_string', $meta );
     215                $this->assertInternalType( 'string', $meta['test_string'] );
     216                $this->assertSame( '42', $meta['test_string'] );
     217
     218                $this->assertArrayHasKey( 'test_number', $meta );
     219                $this->assertInternalType( 'float', $meta['test_number'] );
     220                $this->assertSame( 42.0, $meta['test_number'] );
     221
     222                $this->assertArrayHasKey( 'test_bool', $meta );
     223                $this->assertInternalType( 'boolean', $meta['test_bool'] );
     224                $this->assertSame( true, $meta['test_bool'] );
     225        }
     226
     227        /**
     228         * @depends test_get_value
     229         */
     230        public function test_set_value() {
     231                // Ensure no data exists currently.
     232                $values = get_user_meta( self::$user, 'test_single', false );
     233                $this->assertEmpty( $values );
     234
     235                $this->grant_write_permission();
     236
     237                $data = array(
     238                        'meta' => array(
     239                                'test_single' => 'test_value',
     240                        ),
     241                );
     242                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/users/%d', self::$user ) );
     243                $request->set_body_params( $data );
     244
     245                $response = $this->server->dispatch( $request );
     246                $this->assertEquals( 200, $response->get_status() );
     247
     248                $meta = get_user_meta( self::$user, 'test_single', false );
     249                $this->assertNotEmpty( $meta );
     250                $this->assertCount( 1, $meta );
     251                $this->assertEquals( 'test_value', $meta[0] );
     252
     253                $data = $response->get_data();
     254                $meta = (array) $data['meta'];
     255                $this->assertArrayHasKey( 'test_single', $meta );
     256                $this->assertEquals( 'test_value', $meta['test_single'] );
     257        }
     258
     259        /**
     260         * @depends test_get_value
     261         */
     262        public function test_set_duplicate_single_value() {
     263                // Start with an existing metakey and value.
     264                $values = update_user_meta( self::$user, 'test_single', 'test_value' );
     265                $this->assertEquals( 'test_value', get_user_meta( self::$user, 'test_single', true ) );
     266
     267                $this->grant_write_permission();
     268
     269                $data = array(
     270                        'meta' => array(
     271                                'test_single' => 'test_value',
     272                        ),
     273                );
     274                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/users/%d', self::$user ) );
     275                $request->set_body_params( $data );
     276
     277                $response = $this->server->dispatch( $request );
     278                $this->assertEquals( 200, $response->get_status() );
     279
     280                $meta = get_user_meta( self::$user, 'test_single', true );
     281                $this->assertNotEmpty( $meta );
     282                $this->assertEquals( 'test_value', $meta );
     283
     284                $data = $response->get_data();
     285                $meta = (array) $data['meta'];
     286                $this->assertArrayHasKey( 'test_single', $meta );
     287                $this->assertEquals( 'test_value', $meta['test_single'] );
     288        }
     289
     290        /**
     291         * @depends test_set_value
     292         */
     293        public function test_set_value_unauthenticated() {
     294                $data = array(
     295                        'meta' => array(
     296                                'test_single' => 'test_value',
     297                        ),
     298                );
     299
     300                wp_set_current_user( 0 );
     301
     302                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/users/%d', self::$user ) );
     303                $request->set_body_params( $data );
     304
     305                $response = $this->server->dispatch( $request );
     306                $this->assertErrorResponse( 'rest_cannot_edit', $response, 401 );
     307
     308                // Check that the value wasn't actually updated.
     309                $this->assertEmpty( get_user_meta( self::$user, 'test_single', false ) );
     310        }
     311
     312        /**
     313         * @depends test_set_value
     314         */
     315        public function test_set_value_blocked() {
     316                $data = array(
     317                        'meta' => array(
     318                                'test_bad_auth' => 'test_value',
     319                        ),
     320                );
     321
     322                $this->grant_write_permission();
     323
     324                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/users/%d', self::$user ) );
     325                $request->set_body_params( $data );
     326
     327                $response = $this->server->dispatch( $request );
     328                $this->assertErrorResponse( 'rest_cannot_update', $response, 403 );
     329                $this->assertEmpty( get_user_meta( self::$user, 'test_bad_auth', false ) );
     330        }
     331
     332        /**
     333         * @depends test_set_value
     334         */
     335        public function test_set_value_db_error() {
     336                $data = array(
     337                        'meta' => array(
     338                                'test_single' => 'test_value',
     339                        ),
     340                );
     341
     342                $this->grant_write_permission();
     343
     344                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/users/%d', self::$user ) );
     345                $request->set_body_params( $data );
     346
     347                /**
     348                 * Disable showing error as the below is going to intentionally
     349                 * trigger a DB error.
     350                 */
     351                global $wpdb;
     352                $wpdb->suppress_errors = true;
     353                add_filter( 'query', array( $this, 'error_insert_query' ) );
     354
     355                $response = $this->server->dispatch( $request );
     356                remove_filter( 'query', array( $this, 'error_insert_query' ) );
     357                $wpdb->show_errors = true;
     358        }
     359
     360        public function test_set_value_multiple() {
     361                // Ensure no data exists currently.
     362                $values = get_user_meta( self::$user, 'test_multi', false );
     363                $this->assertEmpty( $values );
     364
     365                $this->grant_write_permission();
     366
     367                $data = array(
     368                        'meta' => array(
     369                                'test_multi' => array( 'val1' ),
     370                        ),
     371                );
     372                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/users/%d', self::$user ) );
     373                $request->set_body_params( $data );
     374
     375                $response = $this->server->dispatch( $request );
     376                $this->assertEquals( 200, $response->get_status() );
     377
     378                $meta = get_user_meta( self::$user, 'test_multi', false );
     379                $this->assertNotEmpty( $meta );
     380                $this->assertCount( 1, $meta );
     381                $this->assertEquals( 'val1', $meta[0] );
     382
     383                // Add another value.
     384                $data = array(
     385                        'meta' => array(
     386                                'test_multi' => array( 'val1', 'val2' ),
     387                        ),
     388                );
     389                $request->set_body_params( $data );
     390
     391                $response = $this->server->dispatch( $request );
     392                $this->assertEquals( 200, $response->get_status() );
     393
     394                $meta = get_user_meta( self::$user, 'test_multi', false );
     395                $this->assertNotEmpty( $meta );
     396                $this->assertCount( 2, $meta );
     397                $this->assertContains( 'val1', $meta );
     398                $this->assertContains( 'val2', $meta );
     399        }
     400
     401        /**
     402         * Test removing only one item with duplicate items.
     403         */
     404        public function test_set_value_remove_one() {
     405                add_user_meta( self::$user, 'test_multi', 'c' );
     406                add_user_meta( self::$user, 'test_multi', 'n' );
     407                add_user_meta( self::$user, 'test_multi', 'n' );
     408
     409                $this->grant_write_permission();
     410
     411                $data = array(
     412                        'meta' => array(
     413                                'test_multi' => array( 'c', 'n' ),
     414                        ),
     415                );
     416                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/users/%d', self::$user ) );
     417                $request->set_body_params( $data );
     418
     419                $response = $this->server->dispatch( $request );
     420                $this->assertEquals( 200, $response->get_status() );
     421
     422                $meta = get_user_meta( self::$user, 'test_multi', false );
     423                $this->assertNotEmpty( $meta );
     424                $this->assertCount( 2, $meta );
     425                $this->assertContains( 'c', $meta );
     426                $this->assertContains( 'n', $meta );
     427        }
     428
     429        /**
     430         * @depends test_set_value_multiple
     431         */
     432        public function test_set_value_multiple_unauthenticated() {
     433                // Ensure no data exists currently.
     434                $values = get_user_meta( self::$user, 'test_multi', false );
     435                $this->assertEmpty( $values );
     436
     437                wp_set_current_user( 0 );
     438
     439                $data = array(
     440                        'meta' => array(
     441                                'test_multi' => array( 'val1' ),
     442                        ),
     443                );
     444                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/users/%d', self::$user ) );
     445                $request->set_body_params( $data );
     446
     447                $response = $this->server->dispatch( $request );
     448                $this->assertErrorResponse( 'rest_cannot_edit', $response, 401 );
     449
     450                $meta = get_user_meta( self::$user, 'test_multi', false );
     451                $this->assertEmpty( $meta );
     452        }
     453
     454        /**
     455         * @depends test_set_value_multiple
     456         */
     457        public function test_set_value_multiple_blocked() {
     458                $data = array(
     459                        'meta' => array(
     460                                'test_bad_auth_multi' => array( 'test_value' ),
     461                        ),
     462                );
     463
     464                $this->grant_write_permission();
     465
     466                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/users/%d', self::$user ) );
     467                $request->set_body_params( $data );
     468
     469                $response = $this->server->dispatch( $request );
     470                $this->assertErrorResponse( 'rest_cannot_update', $response, 403 );
     471                $this->assertEmpty( get_user_meta( self::$user, 'test_bad_auth_multi', false ) );
     472        }
     473
     474        public function test_add_multi_value_db_error() {
     475                // Ensure no data exists currently.
     476                $values = get_user_meta( self::$user, 'test_multi', false );
     477                $this->assertEmpty( $values );
     478
     479                $this->grant_write_permission();
     480
     481                $data = array(
     482                        'meta' => array(
     483                                'test_multi' => array( 'val1' ),
     484                        ),
     485                );
     486                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/users/%d', self::$user ) );
     487                $request->set_body_params( $data );
     488
     489                /**
     490                 * Disable showing error as the below is going to intentionally
     491                 * trigger a DB error.
     492                 */
     493                global $wpdb;
     494                $wpdb->suppress_errors = true;
     495                add_filter( 'query', array( $this, 'error_insert_query' ) );
     496
     497                $response = $this->server->dispatch( $request );
     498                remove_filter( 'query', array( $this, 'error_insert_query' ) );
     499                $wpdb->show_errors = true;
     500
     501                $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 );
     502        }
     503
     504        public function test_remove_multi_value_db_error() {
     505                add_user_meta( self::$user, 'test_multi', 'val1' );
     506                $values = get_user_meta( self::$user, 'test_multi', false );
     507                $this->assertEquals( array( 'val1' ), $values );
     508
     509                $this->grant_write_permission();
     510
     511                $data = array(
     512                        'meta' => array(
     513                                'test_multi' => array(),
     514                        ),
     515                );
     516                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/users/%d', self::$user ) );
     517                $request->set_body_params( $data );
     518
     519                /**
     520                 * Disable showing error as the below is going to intentionally
     521                 * trigger a DB error.
     522                 */
     523                global $wpdb;
     524                $wpdb->suppress_errors = true;
     525                add_filter( 'query', array( $this, 'error_delete_query' ) );
     526
     527                $response = $this->server->dispatch( $request );
     528                remove_filter( 'query', array( $this, 'error_delete_query' ) );
     529                $wpdb->show_errors = true;
     530
     531                $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 );
     532        }
     533
     534        public function test_delete_value() {
     535                add_user_meta( self::$user, 'test_single', 'val1' );
     536                $current = get_user_meta( self::$user, 'test_single', true );
     537                $this->assertEquals( 'val1', $current );
     538
     539                $this->grant_write_permission();
     540
     541                $data = array(
     542                        'meta' => array(
     543                                'test_single' => null,
     544                        ),
     545                );
     546                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/users/%d', self::$user ) );
     547                $request->set_body_params( $data );
     548
     549                $response = $this->server->dispatch( $request );
     550                $this->assertEquals( 200, $response->get_status() );
     551
     552                $meta = get_user_meta( self::$user, 'test_single', false );
     553                $this->assertEmpty( $meta );
     554        }
     555
     556        /**
     557         * @depends test_delete_value
     558         */
     559        public function test_delete_value_blocked() {
     560                add_user_meta( self::$user, 'test_bad_auth', 'val1' );
     561                $current = get_user_meta( self::$user, 'test_bad_auth', true );
     562                $this->assertEquals( 'val1', $current );
     563
     564                $this->grant_write_permission();
     565
     566                $data = array(
     567                        'meta' => array(
     568                                'test_bad_auth' => null,
     569                        ),
     570                );
     571                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/users/%d', self::$user ) );
     572                $request->set_body_params( $data );
     573
     574                $response = $this->server->dispatch( $request );
     575                $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 );
     576
     577                $meta = get_user_meta( self::$user, 'test_bad_auth', true );
     578                $this->assertEquals( 'val1', $meta );
     579        }
     580
     581        /**
     582         * @depends test_delete_value
     583         */
     584        public function test_delete_value_db_error() {
     585                add_user_meta( self::$user, 'test_single', 'val1' );
     586                $current = get_user_meta( self::$user, 'test_single', true );
     587                $this->assertEquals( 'val1', $current );
     588
     589                $this->grant_write_permission();
     590
     591                $data = array(
     592                        'meta' => array(
     593                                'test_single' => null,
     594                        ),
     595                );
     596                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/users/%d', self::$user ) );
     597                $request->set_body_params( $data );
     598                /**
     599                 * Disable showing error as the below is going to intentionally
     600                 * trigger a DB error.
     601                 */
     602                global $wpdb;
     603                $wpdb->suppress_errors = true;
     604                add_filter( 'query', array( $this, 'error_delete_query' ) );
     605
     606                $response = $this->server->dispatch( $request );
     607                remove_filter( 'query', array( $this, 'error_delete_query' ) );
     608                $wpdb->show_errors = true;
     609
     610                $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 );
     611        }
     612
     613        public function test_get_schema() {
     614                $request = new WP_REST_Request( 'OPTIONS', sprintf( '/wp/v2/users/%d', self::$user ) );
     615                $response = $this->server->dispatch( $request );
     616
     617                $data = $response->get_data();
     618                $schema = $data['schema'];
     619
     620                $this->assertArrayHasKey( 'meta', $schema['properties'] );
     621                $meta_schema = $schema['properties']['meta']['properties'];
     622
     623                $this->assertArrayHasKey( 'test_single', $meta_schema );
     624                $this->assertEquals( 'string', $meta_schema['test_single']['type'] );
     625
     626                $this->assertArrayHasKey( 'test_multi', $meta_schema );
     627                $this->assertEquals( 'array', $meta_schema['test_multi']['type'] );
     628                $this->assertArrayHasKey( 'items', $meta_schema['test_multi'] );
     629                $this->assertEquals( 'string', $meta_schema['test_multi']['items']['type'] );
     630
     631                $this->assertArrayHasKey( 'test_custom_schema', $meta_schema );
     632                $this->assertEquals( 'number', $meta_schema['test_custom_schema']['type'] );
     633
     634                $this->assertArrayNotHasKey( 'test_no_rest', $meta_schema );
     635                $this->assertArrayNotHasKey( 'test_rest_disabled', $meta_schema );
     636                $this->assertArrayNotHasKey( 'test_invalid_type', $meta_schema );
     637        }
     638
     639        /**
     640         * Internal function used to disable an insert query which
     641         * will trigger a wpdb error for testing purposes.
     642         */
     643        public function error_insert_query( $query ) {
     644                if ( strpos( $query, 'INSERT' ) === 0 ) {
     645                        $query = '],';
     646                }
     647                return $query;
     648        }
     649
     650        /**
     651         * Internal function used to disable an insert query which
     652         * will trigger a wpdb error for testing purposes.
     653         */
     654        public function error_delete_query( $query ) {
     655                if ( strpos( $query, 'DELETE' ) === 0 ) {
     656                        $query = '],';
     657                }
     658                return $query;
     659        }
     660}