From c44e1d6fe41368cb24bc62aaa4d74245fa76a0cf Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Mon, 26 Apr 2021 00:58:14 +0000 Subject: [PATCH] Build/Tests tools: Improve `get_bookmark()` test coverage. Props hellofromtonya, peterwilsoncc. See #52988. git-svn-id: https://develop.svn.wordpress.org/trunk@50789 602fd350-edb4-49c9-b593-d223f7449a82 --- tests/phpunit/tests/bookmark/getBookmark.php | 398 +++++++++++++++++++ 1 file changed, 398 insertions(+) create mode 100644 tests/phpunit/tests/bookmark/getBookmark.php diff --git a/tests/phpunit/tests/bookmark/getBookmark.php b/tests/phpunit/tests/bookmark/getBookmark.php new file mode 100644 index 0000000000..1c546ba932 --- /dev/null +++ b/tests/phpunit/tests/bookmark/getBookmark.php @@ -0,0 +1,398 @@ +bookmark->create_and_get(); + // Delete the bookmark that was cached when the factory invoked get_bookmark(). + wp_cache_delete( self::$bookmark->link_id, 'bookmark' ); + } + + /** + * Delete the bookmark before existing the test class. + */ + public static function wpTearDownAfterClass() { + wp_delete_link( self::$bookmark->link_id ); + } + + /** + * Reset globals after each test. + */ + public function tearDown() { + unset( $GLOBALS['link'] ); + parent::tearDown(); + } + + /** + * Path 1A: Given empty bookmark and global link exists. + * + * @dataProvider data_when_empty_bookmark + */ + public function test_should_return_global_link_in_requested_output_format( $args ) { + $GLOBALS['link'] = self::$bookmark; + $args = $this->init_func_args( $args, 0 ); + $actual_bookmark = get_bookmark( ...$args ); + + $expected = $this->maybe_format_expected_data( $args, $GLOBALS['link'] ); + + $this->assertArrayHasKey( 'link', $GLOBALS ); + $this->assertSame( $expected, $actual_bookmark ); + // Should bypass the cache. + $this->assertFalse( wp_cache_get( self::$bookmark->link_id, 'bookmark' ) ); + } + + /** + * Path 1B: Given empty bookmark and global link does not exist. + * + * @dataProvider data_when_empty_bookmark + */ + public function test_should_return_null( $args ) { + $args = $this->init_func_args( $args, 0 ); + + // Run the function and test results. + $actual_bookmark = get_bookmark( ...$args ); + + $this->assertArrayNotHasKey( 'link', $GLOBALS ); + $this->assertNull( $actual_bookmark ); + $this->assertFalse( wp_cache_get( self::$bookmark->link_id, 'bookmark' ) ); + } + + /** + * Path 1 data provider, i.e. when given empty bookmark. + */ + public function data_when_empty_bookmark() { + return array( + // Unhappy path. + 'with bookmark type mismatch' => array( + array( + 'bookmark' => '', + ), + ), + 'with invalid output' => array( + array( + 'bookmark' => 0, + 'output' => 'invalid', + ), + ), + 'with bookmark type mismatch and invalid output' => array( + array( + 'bookmark' => null, + 'output' => 'invalid', + ), + ), + // Happy path. + 'with defaults' => array( + array( + 'bookmark' => 0, + ), + ), + 'with non-default output' => array( + array( + 'bookmark' => 0, + 'output' => ARRAY_A, + ), + ), + 'with non-default filter' => array( + array( + 'bookmark' => 0, + 'filter' => 'display', + ), + ), + 'with non-default output and filter' => array( + array( + 'bookmark' => 0, + 'output' => ARRAY_N, + 'filter' => 'display', + ), + ), + ); + } + + /** + * Path 2: Bookmark instance is given. + * + * @dataProvider data_when_instance_bookmark + */ + public function test_should_cache_bookmark_when_given_instance( $args ) { + $args = $this->init_func_args( $args ); + $bookmark = $args[0]; + $expected = $this->maybe_format_expected_data( $args, $bookmark ); + + // Check the cache does not exist before the test. + $this->assertFalse( wp_cache_get( $bookmark->link_id, 'bookmark' ) ); + + // Run the function and test results. + $actual_bookmark = get_bookmark( ...$args ); + + $this->assertSame( $expected, $actual_bookmark ); + + // Check the bookmark was cached. + $actual_cache = wp_cache_get( $bookmark->link_id, 'bookmark' ); + $this->assertEquals( $bookmark, $actual_cache ); + } + + /** + * Path 2 data provider, i.e. when bookmark instance is given. + */ + public function data_when_instance_bookmark() { + return array( + // Unhappy path. + 'with incomplete bookmark data' => array( + array( + 'bookmark' => (object) array( + 'link_id' => '100', + ), + ), + ), + 'with invalid output' => array( + array( + 'output' => 'invalid', + ), + ), + 'with invalid filter' => array( + array( + 'filter' => 'invalid', + ), + ), + // Happy path. + 'with defaults' => array( + array(), + ), + 'with non-default output' => array( + array( + 'output' => ARRAY_A, + ), + ), + 'with non-default filter' => array( + array( + 'filter' => 'display', + ), + ), + 'with non-default output and filter' => array( + array( + 'output' => ARRAY_N, + 'filter' => 'display', + ), + ), + ); + } + + /** + * Path 3A: Uses the global link when exists and the given bookmark link ID matches the global link. + * + * @dataProvider data_when_else + * + * @param array $args Function argument list. + */ + public function test_should_return_global_when_else( $args ) { + $args = $this->init_func_args( $args, self::$bookmark->link_id ); + $GLOBALS['link'] = self::$bookmark; + $expected = $this->maybe_format_expected_data( $args, $GLOBALS['link'] ); + + // Run the function and test results. + $actual_bookmark = get_bookmark( ...$args ); + + $this->assertSame( $expected, $actual_bookmark ); + $this->assertFalse( wp_cache_get( self::$bookmark->link_id, 'bookmark' ) ); + } + + /** + * Path 3B: Pulls from cache when given existing bookmark link ID. + * + * @dataProvider data_when_else + * + * @param array $args Function argument list. + */ + public function test_should_return_cached_bookmark_when_given_existing_link_id( $args ) { + // Cache the bookmark instance to setup the test. + wp_cache_add( self::$bookmark->link_id, self::$bookmark, 'bookmark' ); + $args = $this->init_func_args( $args, self::$bookmark->link_id ); + $expected = $this->maybe_format_expected_data( $args, self::$bookmark ); + + // Run the function and test results. + $actual_bookmark = get_bookmark( ...$args ); + + /* + * For non-array output type, use assetEquals. Why? The object pulled from cache will have the same + * property values but will be a different object than the expected object. + */ + if ( is_object( $expected ) ) { + $this->assertEquals( $expected, $actual_bookmark ); + } else { + $this->assertSameSets( $expected, $actual_bookmark ); + } + + // Check the bookmark was cached. + $actual_cache = wp_cache_get( self::$bookmark->link_id, 'bookmark' ); + $this->assertEquals( self::$bookmark, $actual_cache ); + } + + /** + * Path 3C: Attempts to pull non-existent bookmark from database. + * + * @dataProvider data_when_else + * + * @param array $args Function argument list. + */ + public function test_should_return_null_when_bookmark_not_in_database( $args ) { + $bookmark_link_id = self::$bookmark->link_id * 100; + $args = $this->init_func_args( $args, $bookmark_link_id ); + + // Validate it will run path 6. + $this->assertFalse( wp_cache_get( $bookmark_link_id, 'bookmark' ) ); + $this->assertArrayNotHasKey( 'link', $GLOBALS ); + global $wpdb; + $db_actual = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->links WHERE link_id = %d LIMIT 1", $bookmark_link_id ) ); + $this->assertNull( $db_actual ); + + // Run the function and test results. + $actual_bookmark = get_bookmark( ...$args ); + + $this->assertNull( $actual_bookmark ); + $this->assertFalse( wp_cache_get( $bookmark_link_id, 'bookmark' ) ); + } + + /** + * Path 3D: Pulls existing bookmark from the database. + * + * @dataProvider data_when_else + * + * @param array $args Function argument list. + */ + public function test_should_return_existing_bookmark_from_database( $args ) { + $args = $this->init_func_args( $args, self::$bookmark->link_id ); + $expected = $this->maybe_format_expected_data( $args, self::$bookmark ); + + // Validate it will run path 5. + $this->assertFalse( wp_cache_get( self::$bookmark->link_id, 'bookmark' ) ); + $this->assertArrayNotHasKey( 'link', $GLOBALS ); + + // Run the function and test results. + $actual_bookmark = get_bookmark( ...$args ); + + /* + * For non-array output type, use assetEquals. Why? The object pulled from the database will have the same + * property values but will be a different object than the expected object. + */ + if ( is_object( $expected ) ) { + $this->assertEquals( $expected, $actual_bookmark ); + } else { + $this->assertSameSets( $expected, $actual_bookmark ); + } + + // Check the bookmark was cached. + $actual_cache = wp_cache_get( self::$bookmark->link_id, 'bookmark' ); + $this->assertEquals( self::$bookmark, $actual_cache ); + } + + /** + * Path 3's data provider which covers the "else" branch, i.e. when the bookmark argument is not empty and + * not an object. + */ + public function data_when_else() { + return array( + // Unhappy path. + 'with invalid output' => array( + array( + 'output' => 'invalid', + ), + ), + 'with invalid filter' => array( + array( + 'filter' => 'invalid', + ), + ), + // Happy path. + 'with defaults' => array( + array(), + ), + 'with non-default output' => array( + array( + 'output' => ARRAY_A, + ), + ), + 'with non-default filter' => array( + array( + 'filter' => 'display', + ), + ), + 'with non-default output and filter' => array( + array( + 'output' => ARRAY_N, + 'filter' => 'display', + ), + ), + ); + } + + /** + * Initialize the get_bookmark's function arguments to match the order of the function's signature and + * reduce code in the tests. + * + * @param array $args Function argument list. + * @param int|stdClass $bookmark Optional. Bookmark's cache key or instance. + * @return array Ordered argument list. + */ + private function init_func_args( array $args, $bookmark = null ) { + // The defaults sets the order to match the function's arguments as well as setting the default values. + $defaults = array( + 'bookmark' => self::$bookmark, + 'output' => OBJECT, + 'filter' => 'raw', + ); + $args = array_merge( $defaults, $args ); + + // When given a bookmark, use it. + if ( ! is_null( $bookmark ) ) { + $args['bookmark'] = $bookmark; + } + + /* + * Strip out the keys. Why? The splat operator (...) does not work with associative arrays, + * except for in PHP 8 where the keys are named arguments. + */ + return array_values( $args ); + } + + /** + * Maybe format the bookmark's expected data. + * + * @param array $args Function argument list. + * @param int|stdClass|null $bookmark Optional. Bookmark's cache key or instance. + * @return array|stdClass bookmark's data. + */ + private function maybe_format_expected_data( array $args, $bookmark = null ) { + if ( is_null( $bookmark ) ) { + $bookmark = self::$bookmark; + } + + switch ( $args[1] ) { + case ARRAY_A: + case ARRAY_N: + $expected = get_object_vars( $bookmark ); + + if ( ARRAY_N === $args[1] ) { + $expected = array_values( $expected ); + } + + return $expected; + default: + return $bookmark; + } + } +}