Make WordPress Core

Changeset 56797


Ignore:
Timestamp:
10/06/2023 05:01:48 PM (3 years ago)
Author:
flixos90
Message:

Options, Meta APIs: Add test coverage for update_network_option() comparison of new and existing value.

Having those tests in trunk already will help ensure potential future fixes to this logic maintain backward compatibility.

Props mukesh27, spacedmonkey.
See #59360.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/option/networkOption.php

    r54637 r56797  
    229229                $this->assertSame( $num_queries_pre_update, get_num_queries() );
    230230        }
     231
     232        /**
     233         * Tests that update_network_option() triggers one additional query and returns true
     234         * for some loosely equal old and new values when the old value is retrieved from the cache.
     235         *
     236         * The additional query is triggered to update the value in the database.
     237         *
     238         * If the old value is false, the additional queries are triggered to:
     239         * 1. get the old value from the database via get_network_option() -> get_option().
     240         * 2. (Single Site only) get the old value from the database via update_network_option() -> update_option() -> get_option().
     241         * 3. update the value in the database via update_network_options() -> update_option().
     242         *
     243         * @ticket 59360
     244         *
     245         * @covers ::update_network_option
     246         *
     247         * @dataProvider data_loosely_equal_values_that_should_update
     248         *
     249         * @param mixed $old_value The old value.
     250         * @param mixed $new_value The new value to try to set.
     251         */
     252        public function test_update_network_option_should_update_some_loosely_equal_values_from_cache( $old_value, $new_value ) {
     253                add_network_option( null, 'foo', $old_value );
     254
     255                $num_queries = get_num_queries();
     256
     257                // Comparison will happen against value cached during add_network_option() above.
     258                $updated = update_network_option( null, 'foo', $new_value );
     259
     260                $expected_queries = 1;
     261
     262                if ( false === $old_value ) {
     263                        $expected_queries = is_multisite() ? 2 : 3;
     264                }
     265
     266                $this->assertSame( $expected_queries, get_num_queries() - $num_queries, "The number of queries should have increased by $expected_queries." );
     267                $this->assertTrue( $updated, 'update_network_option() should have returned true.' );
     268        }
     269
     270        /**
     271         * Tests that update_network_option() triggers two additional queries and returns true
     272         * for some loosely equal old and new values when the old value is retrieved from the database.
     273         *
     274         * The two additional queries are triggered to:
     275         * 1. retrieve the old value from the database, as the option does not exist in the cache.
     276         * 2. update the value in the database.
     277         *
     278         * On Single Site, if the old value is false, the four additional queries are triggered to:
     279         * 1. get the old value from the database via get_network_option() -> get_option().
     280         * 2. get the alloptions cache via get_network_option() -> get_option().
     281         * 3. get the old value from the database via update_network_option() -> update_option() -> get_option().
     282         * 4. update the value in the database via update_network_options() -> update_option().
     283         *
     284         * @ticket 59360
     285         *
     286         * @covers ::update_network_option
     287         *
     288         * @dataProvider data_loosely_equal_values_that_should_update
     289         *
     290         * @param mixed $old_value The old value.
     291         * @param mixed $new_value The new value to try to set.
     292         */
     293        public function test_update_network_option_should_update_some_loosely_equal_values_from_db( $old_value, $new_value ) {
     294                add_network_option( null, 'foo', $old_value );
     295
     296                $num_queries = get_num_queries();
     297
     298                // Delete cache.
     299                $network_cache_key = get_current_network_id() . ':foo';
     300                wp_cache_delete( $network_cache_key, 'site-options' );
     301                wp_cache_delete( 'alloptions', 'options' );
     302
     303                $updated = update_network_option( null, 'foo', $new_value );
     304
     305                $expected_queries = false === $old_value && ! is_multisite() ? 4 : 2;
     306
     307                $this->assertSame( $expected_queries, get_num_queries() - $num_queries, "The number of queries should have increased by $expected_queries." );
     308                $this->assertTrue( $updated, 'update_network_option() should have returned true.' );
     309        }
     310
     311        /**
     312         * Tests that update_network_option() triggers one additional query and returns true
     313         * for some loosely equal old and new values when the old value is retrieved from a refreshed cache.
     314         *
     315         * The additional query is triggered to update the value in the database.
     316         *
     317         * If the old value is false, the additional queries are triggered to:
     318         * 1. get the old value from the database via get_network_option() -> get_option().
     319         * 2. get the old value from the database via update_network_option() -> update_option() -> get_option().
     320         * 3. update the value in the database via update_network_options() -> update_option().
     321         *
     322         * @ticket 59360
     323         *
     324         * @covers ::update_network_option
     325         *
     326         * @dataProvider data_loosely_equal_values_that_should_update
     327         *
     328         * @param mixed $old_value The old value.
     329         * @param mixed $new_value The new value to try to set.
     330         */
     331        public function test_update_network_option_should_update_some_loosely_equal_values_from_refreshed_cache( $old_value, $new_value ) {
     332                add_network_option( null, 'foo', $old_value );
     333
     334                // Delete and refresh cache from DB.
     335                wp_cache_delete( 'alloptions', 'options' );
     336                wp_load_alloptions();
     337
     338                $num_queries = get_num_queries();
     339                $updated     = update_network_option( null, 'foo', $new_value );
     340
     341                $expected_queries = 1;
     342
     343                if ( false === $old_value ) {
     344                        $expected_queries = is_multisite() ? 2 : 3;
     345                }
     346
     347                $this->assertSame( $expected_queries, get_num_queries() - $num_queries, "The number of queries should have increased by $expected_queries." );
     348                $this->assertTrue( $updated, 'update_network_option() should have returned true.' );
     349        }
     350
     351        /**
     352         * Data provider.
     353         *
     354         * @return array
     355         */
     356        public function data_loosely_equal_values_that_should_update() {
     357                return array(
     358                        // Falsey values.
     359                        '(string) "0" to false'       => array( '0', false ),
     360                        'empty string to (int) 0'     => array( '', 0 ),
     361                        'empty string to (float) 0.0' => array( '', 0.0 ),
     362                        '(int) 0 to empty string'     => array( 0, '' ),
     363                        '(int) 0 to false'            => array( 0, false ),
     364                        '(float) 0.0 to empty string' => array( 0.0, '' ),
     365                        '(float) 0.0 to false'        => array( 0.0, false ),
     366                        'false to (string) "0"'       => array( false, '0' ),
     367                        'false to (int) 0'            => array( false, 0 ),
     368                        'false to (float) 0.0'        => array( false, 0.0 ),
     369
     370                        // Non-scalar values.
     371                        'false to array()'            => array( false, array() ),
     372                        '(string) "false" to array()' => array( 'false', array() ),
     373                        'empty string to array()'     => array( '', array() ),
     374                        '(int 0) to array()'          => array( 0, array() ),
     375                        '(string) "0" to array()'     => array( '0', array() ),
     376                        '(string) "false" to null'    => array( 'false', null ),
     377                        '(int) 0 to null'             => array( 0, null ),
     378                        '(string) "0" to null'        => array( '0', null ),
     379                        'array() to false'            => array( array(), false ),
     380                        'array() to (string) "false"' => array( array(), 'false' ),
     381                        'array() to empty string'     => array( array(), '' ),
     382                        'array() to (int) 0'          => array( array(), 0 ),
     383                        'array() to (string) "0"'     => array( array(), '0' ),
     384                        'array() to null'             => array( array(), null ),
     385                );
     386        }
     387
     388        /**
     389         * Tests that update_network_option() triggers no additional queries and returns false
     390         * for some values when the old value is retrieved from the cache.
     391         *
     392         * @ticket 59360
     393         *
     394         * @covers ::update_network_option
     395         *
     396         * @dataProvider data_loosely_equal_values_that_should_not_update
     397         * @dataProvider data_strictly_equal_values
     398         *
     399         * @param mixed $old_value The old value.
     400         * @param mixed $new_value The new value to try to set.
     401         */
     402        public function test_update_option_should_not_update_some_values_from_cache( $old_value, $new_value ) {
     403                add_network_option( null, 'foo', $old_value );
     404
     405                $num_queries = get_num_queries();
     406
     407                // Comparison will happen against value cached during add_option() above.
     408                $updated = update_network_option( null, 'foo', $new_value );
     409
     410                $expected_queries = $old_value === $new_value || ! is_multisite() ? 0 : 1;
     411                $this->assertSame( $expected_queries, get_num_queries() - $num_queries, "The number of queries should have increased by $expected_queries." );
     412
     413                $this->assertFalse( $updated, 'update_network_option() should have returned false.' );
     414        }
     415
     416        /**
     417         * Tests that update_network_option() triggers one additional query and returns false
     418         * for some values when the old value is retrieved from the database.
     419         *
     420         * The additional query is triggered to retrieve the old value from the database.
     421         *
     422         * @ticket 59360
     423         *
     424         * @covers ::update_network_option
     425         *
     426         * @dataProvider data_loosely_equal_values_that_should_not_update
     427         * @dataProvider data_strictly_equal_values
     428         *
     429         * @param mixed $old_value The old value.
     430         * @param mixed $new_value The new value to try to set.
     431         */
     432        public function test_update_network_option_should_not_update_some_values_from_db( $old_value, $new_value ) {
     433                add_network_option( null, 'foo', $old_value );
     434
     435                $num_queries = get_num_queries();
     436
     437                // Delete cache.
     438                $network_cache_key = get_current_network_id() . ':foo';
     439                wp_cache_delete( $network_cache_key, 'site-options' );
     440                wp_cache_delete( 'alloptions', 'options' );
     441
     442                $updated = update_network_option( null, 'foo', $new_value );
     443
     444                // Mimic the behavior of the database by casting the old value to string.
     445                if ( is_scalar( $old_value ) ) {
     446                        $old_value = (string) $old_value;
     447                }
     448
     449                $expected_queries = $old_value === $new_value || ! is_multisite() ? 1 : 2;
     450                $this->assertSame( $expected_queries, get_num_queries() - $num_queries, "The number of queries should have increased by $expected_queries." );
     451
     452                $this->assertFalse( $updated, 'update_network_option() should have returned false.' );
     453        }
     454
     455        /**
     456         * Tests that update_network_option() triggers no additional queries and returns false
     457         * for some values when the old value is retrieved from a refreshed cache.
     458         *
     459         * @ticket 59360
     460         *
     461         * @covers ::update_network_option
     462         *
     463         * @dataProvider data_loosely_equal_values_that_should_not_update
     464         * @dataProvider data_strictly_equal_values
     465         *
     466         * @param mixed $old_value The old value.
     467         * @param mixed $new_value The new value to try to set.
     468         */
     469        public function test_update_network_option_should_not_update_some_values_from_refreshed_cache( $old_value, $new_value ) {
     470                add_network_option( null, 'foo', $old_value );
     471
     472                // Delete and refresh cache from DB.
     473                wp_cache_delete( 'alloptions', 'options' );
     474                wp_load_alloptions();
     475
     476                $num_queries = get_num_queries();
     477                $updated     = update_network_option( null, 'foo', $new_value );
     478
     479                /*
     480                 * Strictly equal old and new values will cause an early return
     481                 * with no additional queries.
     482                 */
     483                $expected_queries = $old_value === $new_value || ! is_multisite() ? 0 : 1;
     484                $this->assertSame( $expected_queries, get_num_queries() - $num_queries, "The number of queries should have increased by $expected_queries." );
     485
     486                $this->assertFalse( $updated, 'update_network_option() should have returned false.' );
     487        }
     488
     489        /**
     490         * Data provider.
     491         *
     492         * @return array[]
     493         */
     494        public function data_loosely_equal_values_that_should_not_update() {
     495                return array(
     496                        // Truthy values.
     497                        '(string) "1" to (int) 1'     => array( '1', 1 ),
     498                        '(string) "1" to (float) 1.0' => array( '1', 1.0 ),
     499                        '(string) "1" to true'        => array( '1', true ),
     500                        '(int) 1 to (string) "1"'     => array( 1, '1' ),
     501                        '1 to (float) 1.0'            => array( 1, 1.0 ),
     502                        '(int) 1 to true'             => array( 1, true ),
     503                        '(float) 1.0 to (string) "1"' => array( 1.0, '1' ),
     504                        '(float) 1.0 to (int) 1'      => array( 1.0, 1 ),
     505                        '1.0 to true'                 => array( 1.0, true ),
     506                        'true to (string) "1"'        => array( true, '1' ),
     507                        'true to 1'                   => array( true, 1 ),
     508                        'true to (float) 1.0'         => array( true, 1.0 ),
     509
     510                        // Falsey values.
     511                        '(string) "0" to (int) 0'     => array( '0', 0 ),
     512                        '(string) "0" to (float) 0.0' => array( '0', 0.0 ),
     513                        '(int) 0 to (string) "0"'     => array( 0, '0' ),
     514                        '(int) 0 to (float) 0.0'      => array( 0, 0.0 ),
     515                        '(float) 0.0 to (string) "0"' => array( 0.0, '0' ),
     516                        '(float) 0.0 to (int) 0'      => array( 0.0, 0 ),
     517                        'empty string to false'       => array( '', false ),
     518
     519                        /*
     520                         * null as an initial value behaves differently by triggering
     521                         * a query, so it is not included in these datasets.
     522                         *
     523                         * See data_stored_as_empty_string() and its related test.
     524                         */
     525                );
     526        }
     527
     528        /**
     529         * Data provider.
     530         *
     531         * @return array
     532         */
     533        public function data_strictly_equal_values() {
     534                $obj = new stdClass();
     535
     536                return array(
     537                        // Truthy values.
     538                        '(string) "1"'       => array( '1', '1' ),
     539                        '(int) 1'            => array( 1, 1 ),
     540                        '(float) 1.0'        => array( 1.0, 1.0 ),
     541                        'true'               => array( true, true ),
     542                        'string with spaces' => array( ' ', ' ' ),
     543                        'non-empty array'    => array( array( 'false' ), array( 'false' ) ),
     544                        'object'             => array( $obj, $obj ),
     545
     546                        // Falsey values.
     547                        '(string) "0"'       => array( '0', '0' ),
     548                        'empty string'       => array( '', '' ),
     549                        '(int) 0'            => array( 0, 0 ),
     550                        '(float) 0.0'        => array( 0.0, 0.0 ),
     551                        'empty array'        => array( array(), array() ),
     552
     553                        /*
     554                         * false and null are not included in these datasets
     555                         * because false is the default value, which triggers
     556                         * a call to add_network_option().
     557                         *
     558                         * See data_stored_as_empty_string() and its related test.
     559                         */
     560                );
     561        }
     562
     563        /**
     564         * Tests that update_network_option() handles a null new value when the new value
     565         * is retrieved from the cache.
     566         *
     567         * On Single Site, this will result in no additional queries as
     568         * the option_value database field is not nullable.
     569         *
     570         * On Multisite, this will result in one additional query as
     571         * the meta_value database field is nullable.
     572         *
     573         * @ticket 59360
     574         *
     575         * @covers ::update_network_option
     576         */
     577        public function test_update_option_should_handle_a_null_new_value_from_cache() {
     578                add_network_option( null, 'foo', '' );
     579
     580                $num_queries = get_num_queries();
     581
     582                // Comparison will happen against value cached during add_option() above.
     583                $updated = update_network_option( null, 'foo', null );
     584
     585                $expected_queries = is_multisite() ? 1 : 0;
     586                $this->assertSame( $expected_queries, get_num_queries() - $num_queries, "The number of queries should have increased by $expected_queries." );
     587
     588                if ( is_multisite() ) {
     589                        $this->assertTrue( $updated, 'update_network_option() should have returned true.' );
     590                } else {
     591                        $this->assertFalse( $updated, 'update_network_option() should have returned false.' );
     592                }
     593        }
     594
     595        /**
     596         * Tests that update_network_option() handles a null new value when the new value
     597         * is retrieved from the database.
     598         *
     599         * On Single Site, this will result in only 1 additional query as
     600         * the option_value database field is not nullable.
     601         *
     602         * On Multisite, this will result in two additional queries as
     603         * the meta_value database field is nullable.
     604         *
     605         * @ticket 59360
     606         *
     607         * @covers ::update_network_option
     608         */
     609        public function test_update_option_should_handle_a_null_new_value_from_db() {
     610                add_network_option( null, 'foo', '' );
     611
     612                $num_queries = get_num_queries();
     613
     614                // Delete cache.
     615                $network_cache_key = get_current_network_id() . ':foo';
     616                wp_cache_delete( $network_cache_key, 'site-options' );
     617                wp_cache_delete( 'alloptions', 'options' );
     618
     619                $updated = update_network_option( null, 'foo', null );
     620
     621                $expected_queries = is_multisite() ? 2 : 1;
     622                $this->assertSame( $expected_queries, get_num_queries() - $num_queries, "The number of queries should have increased by $expected_queries." );
     623
     624                if ( is_multisite() ) {
     625                        $this->assertTrue( $updated, 'update_network_option() should have returned true.' );
     626                } else {
     627                        $this->assertFalse( $updated, 'update_network_option() should have returned false.' );
     628                }
     629        }
     630
     631        /**
     632         * Tests that update_network_option() handles a null new value when the new value
     633         * is retrieved from a refreshed cache.
     634         *
     635         * On Single Site, this will result in no additional queries as
     636         * the option_value database field is not nullable.
     637         *
     638         * On Multisite, this will result in one additional query as
     639         * the meta_value database field is nullable.
     640         *
     641         * @ticket 59360
     642         *
     643         * @covers ::update_network_option
     644         */
     645        public function test_update_option_should_handle_a_null_new_value_from_refreshed_cache() {
     646                add_network_option( null, 'foo', '' );
     647
     648                // Delete and refresh cache from DB.
     649                wp_cache_delete( 'alloptions', 'options' );
     650                wp_load_alloptions();
     651
     652                $num_queries = get_num_queries();
     653                $updated     = update_network_option( null, 'foo', null );
     654
     655                $expected_queries = is_multisite() ? 1 : 0;
     656                $this->assertSame( $expected_queries, get_num_queries() - $num_queries, "The number of queries should have increased by $expected_queries." );
     657
     658                if ( is_multisite() ) {
     659                        $this->assertTrue( $updated, 'update_network_option() should have returned true.' );
     660                } else {
     661                        $this->assertFalse( $updated, 'update_network_option() should have returned false.' );
     662                }
     663        }
     664
     665        /**
     666         * Tests that update_network_option() adds a non-existent option when the new value
     667         * is stored as an empty string and false is the default value for the option.
     668         *
     669         * @ticket 59360
     670         *
     671         * @dataProvider data_stored_as_empty_string
     672         *
     673         * @param mixed $new_value A value that casts to an empty string.
     674         */
     675        public function test_update_network_option_should_add_network_option_when_the_new_value_is_stored_as_an_empty_string_and_matches_default_value_false( $new_value ) {
     676                global $wpdb;
     677
     678                if ( is_multisite() ) {
     679                        $this->markTestSkipped( 'This test should only run on Single Site.' );
     680                }
     681
     682                $this->assertTrue( update_network_option( null, 'foo', $new_value ), 'update_network_option() should have returned true.' );
     683
     684                $actual = $wpdb->get_row( "SELECT option_value FROM $wpdb->options WHERE option_name = 'foo' LIMIT 1" );
     685
     686                $this->assertIsObject( $actual, 'The option was not added to the database.' );
     687                $this->assertObjectHasProperty( 'option_value', $actual, 'The "option_value" property was not included.' );
     688                $this->assertSame( '', $actual->option_value, 'The value was not stored as an empty string.' );
     689        }
     690
     691        /**
     692         * Data provider.
     693         *
     694         * @return array[]
     695         */
     696        public function data_stored_as_empty_string() {
     697                return array(
     698                        'empty string' => array( '' ),
     699                        'null'         => array( null ),
     700                );
     701        }
    231702}
Note: See TracChangeset for help on using the changeset viewer.