From f6f8e3f0c5672228716fb0db7fbf8343284fec84 Mon Sep 17 00:00:00 2001 From: ramon Date: Fri, 10 Nov 2023 16:12:20 +1100 Subject: [PATCH 1/8] Adding some tests to check whether get_item can return a revision whose parent does not match the `parent` route fragment. --- .../rest-api/rest-revisions-controller.php | 205 +++++++++++------- 1 file changed, 129 insertions(+), 76 deletions(-) diff --git a/tests/phpunit/tests/rest-api/rest-revisions-controller.php b/tests/phpunit/tests/rest-api/rest-revisions-controller.php index 74cd040d85f68..c29847702e509 100644 --- a/tests/phpunit/tests/rest-api/rest-revisions-controller.php +++ b/tests/phpunit/tests/rest-api/rest-revisions-controller.php @@ -8,24 +8,29 @@ * @group restapi */ class WP_Test_REST_Revisions_Controller extends WP_Test_REST_Controller_Testcase { - protected static $post_id; + protected static $post_id_1; + protected static $post_id_2; protected static $page_id; protected static $editor_id; protected static $contributor_id; private $total_revisions; - private $revisions; - private $revision_1; - private $revision_id1; - private $revision_2; - private $revision_id2; - private $revision_3; - private $revision_id3; + private $revision_1_1; + private $revision_1_1_id; + private $revision_1_2; + private $revision_1_2_id; + private $revision_1_3; + private $revision_1_3_id; + private $revision_2_1; + private $revision_2_1_id; + private $revision_2_2; + private $revision_2_2_id; public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { - self::$post_id = $factory->post->create(); - self::$page_id = $factory->post->create( array( 'post_type' => 'page' ) ); + self::$post_id_1 = $factory->post->create(); + self::$post_id_2 = $factory->post->create(); + self::$page_id = $factory->post->create( array( 'post_type' => 'page' ) ); self::$editor_id = $factory->user->create( array( @@ -42,19 +47,31 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { wp_update_post( array( 'post_content' => 'This content is better.', - 'ID' => self::$post_id, + 'ID' => self::$post_id_1, ) ); wp_update_post( array( 'post_content' => 'This content is marvelous.', - 'ID' => self::$post_id, + 'ID' => self::$post_id_1, ) ); wp_update_post( array( 'post_content' => 'This content is fantastic.', - 'ID' => self::$post_id, + 'ID' => self::$post_id_1, + ) + ); + wp_update_post( + array( + 'post_content' => 'A second post.', + 'ID' => self::$post_id_2, + ) + ); + wp_update_post( + array( + 'post_content' => 'A second post. How prolific.', + 'ID' => self::$post_id_2, ) ); wp_set_current_user( 0 ); @@ -62,7 +79,8 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { public static function wpTearDownAfterClass() { // Also deletes revisions. - wp_delete_post( self::$post_id, true ); + wp_delete_post( self::$post_id_1, true ); + wp_delete_post( self::$post_id_2, true ); wp_delete_post( self::$page_id, true ); self::delete_user( self::$editor_id ); @@ -72,15 +90,22 @@ public static function wpTearDownAfterClass() { public function set_up() { parent::set_up(); - $revisions = wp_get_post_revisions( self::$post_id ); + // Set first post revision vars. + $revisions = wp_get_post_revisions( self::$post_id_1 ); $this->total_revisions = count( $revisions ); - $this->revisions = $revisions; - $this->revision_1 = array_pop( $revisions ); - $this->revision_id1 = $this->revision_1->ID; - $this->revision_2 = array_pop( $revisions ); - $this->revision_id2 = $this->revision_2->ID; - $this->revision_3 = array_pop( $revisions ); - $this->revision_id3 = $this->revision_3->ID; + $this->revision_1_1 = array_pop( $revisions ); + $this->revision_1_1_id = $this->revision_1_1->ID; + $this->revision_1_2 = array_pop( $revisions ); + $this->revision_1_2_id = $this->revision_1_2->ID; + $this->revision_1_3 = array_pop( $revisions ); + $this->revision_1_3_id = $this->revision_1_3->ID; + + // Set second post revision vars. + $revisions = wp_get_post_revisions( self::$post_id_2 ); + $this->revision_2_1 = array_pop( $revisions ); + $this->revision_2_1_id = $this->revision_2_1->ID; + $this->revision_2_2 = array_pop( $revisions ); + $this->revision_2_2_id = $this->revision_2_2->ID; } public function _filter_map_meta_cap_remove_no_allow_revisions( $caps, $cap, $user_id, $args ) { @@ -108,13 +133,13 @@ public function test_register_routes() { public function test_context_param() { // Collection. - $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/revisions' ); + $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] ); $this->assertSameSets( array( 'view', 'edit', 'embed' ), $data['endpoints'][0]['args']['context']['enum'] ); // Single. - $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_1->ID ); + $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1->ID ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] ); @@ -123,26 +148,26 @@ public function test_context_param() { public function test_get_items() { wp_set_current_user( self::$editor_id ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $this->assertSame( 200, $response->get_status() ); $this->assertCount( $this->total_revisions, $data ); // Reverse chronology. - $this->assertSame( $this->revision_id3, $data[0]['id'] ); - $this->check_get_revision_response( $data[0], $this->revision_3 ); + $this->assertSame( $this->revision_1_3_id, $data[0]['id'] ); + $this->check_get_revision_response( $data[0], $this->revision_1_3 ); - $this->assertSame( $this->revision_id2, $data[1]['id'] ); - $this->check_get_revision_response( $data[1], $this->revision_2 ); + $this->assertSame( $this->revision_1_2_id, $data[1]['id'] ); + $this->check_get_revision_response( $data[1], $this->revision_1_2 ); - $this->assertSame( $this->revision_id1, $data[2]['id'] ); - $this->check_get_revision_response( $data[2], $this->revision_1 ); + $this->assertSame( $this->revision_1_1_id, $data[2]['id'] ); + $this->check_get_revision_response( $data[2], $this->revision_1_1 ); } public function test_get_items_no_permission() { wp_set_current_user( 0 ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_cannot_read', $response, 401 ); @@ -167,10 +192,10 @@ public function test_get_items_invalid_parent_post_type() { public function test_get_item() { wp_set_current_user( self::$editor_id ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); - $this->check_get_revision_response( $response, $this->revision_1 ); + $this->check_get_revision_response( $response, $this->revision_1_1 ); $fields = array( 'author', 'date', @@ -193,7 +218,7 @@ public function test_get_item() { public function test_get_item_embed_context() { wp_set_current_user( self::$editor_id ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); $request->set_param( 'context', 'embed' ); $response = rest_get_server()->dispatch( $request ); $fields = array( @@ -211,7 +236,7 @@ public function test_get_item_embed_context() { public function test_get_item_no_permission() { wp_set_current_user( 0 ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_cannot_read', $response, 401 ); @@ -222,25 +247,53 @@ public function test_get_item_no_permission() { public function test_get_item_missing_parent() { wp_set_current_user( self::$editor_id ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/revisions/' . $this->revision_id1 ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/revisions/' . $this->revision_1_1_id ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 ); } public function test_get_item_invalid_parent_post_type() { wp_set_current_user( self::$editor_id ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$page_id . '/revisions/' . $this->revision_id1 ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$page_id . '/revisions/' . $this->revision_1_1_id ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 ); + } + + public function test_get_item_valid_parent_id() { + wp_set_current_user( self::$editor_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $this->assertSame( self::$post_id_1, $data['parent'] ); + } + + public function test_get_item_invalid_parent_id() { + wp_set_current_user( self::$editor_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_2_1_id ); $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + // Temporary - testing a failing state here. + if ( ! empty( $data['parent'] ) ) { + $revision = get_post( $data['id'] ); + $this->assertSame( 'revision', $revision->post_type, 'This test should fail.' ); + $parent = get_post( $data['parent'] ); + $this->assertSame( self::$post_id_1, $parent->ID, 'This test will fail but should not if there is a valid response.' ); + } + + $this->assertEmpty( $data['id'], 'The response should be empty.' ); + $this->assertEmpty( $data['parent'], 'The response should be empty.' ); + $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 ); } public function test_delete_item() { wp_set_current_user( self::$editor_id ); - $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); + $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); $request->set_param( 'force', true ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 ); - $this->assertNotNull( get_post( $this->revision_id1 ) ); + $this->assertNotNull( get_post( $this->revision_1_1_id ) ); } /** @@ -248,11 +301,11 @@ public function test_delete_item() { */ public function test_delete_item_parent_check() { wp_set_current_user( self::$contributor_id ); - $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); + $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); $request->set_param( 'force', true ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 ); - $this->assertNotNull( get_post( $this->revision_id1 ) ); + $this->assertNotNull( get_post( $this->revision_1_1_id ) ); } /** @@ -261,11 +314,11 @@ public function test_delete_item_parent_check() { public function test_delete_item_remove_do_not_allow() { wp_set_current_user( self::$editor_id ); add_filter( 'map_meta_cap', array( $this, '_filter_map_meta_cap_remove_no_allow_revisions' ), 10, 4 ); - $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); + $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); $request->set_param( 'force', true ); $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); - $this->assertNull( get_post( $this->revision_id1 ) ); + $this->assertNull( get_post( $this->revision_1_1_id ) ); } /** @@ -273,11 +326,11 @@ public function test_delete_item_remove_do_not_allow() { */ public function test_delete_item_cannot_delete_parent() { wp_set_current_user( self::$editor_id ); - $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); + $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); $request->set_param( 'force', true ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 ); - $this->assertNotNull( get_post( $this->revision_id1 ) ); + $this->assertNotNull( get_post( $this->revision_1_1_id ) ); } /** @@ -287,7 +340,7 @@ public function test_delete_item_cannot_delete_parent() { public function test_delete_item_no_trash() { wp_set_current_user( self::$editor_id ); add_filter( 'map_meta_cap', array( $this, '_filter_map_meta_cap_remove_no_allow_revisions' ), 10, 4 ); - $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); + $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501 ); @@ -296,31 +349,31 @@ public function test_delete_item_no_trash() { $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501 ); // Ensure the revision still exists. - $this->assertNotNull( get_post( $this->revision_id1 ) ); + $this->assertNotNull( get_post( $this->revision_1_1_id ) ); } public function test_delete_item_no_permission() { wp_set_current_user( self::$contributor_id ); - $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); + $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 ); } public function test_prepare_item() { wp_set_current_user( self::$editor_id ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); - $this->check_get_revision_response( $response, $this->revision_1 ); + $this->check_get_revision_response( $response, $this->revision_1_1 ); } public function test_prepare_item_limit_fields() { wp_set_current_user( self::$editor_id ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); $endpoint = new WP_REST_Revisions_Controller( 'post' ); $request->set_param( 'context', 'edit' ); $request->set_param( '_fields', 'id,slug' ); - $revision = get_post( $this->revision_id1 ); + $revision = get_post( $this->revision_1_1_id ); $response = $endpoint->prepare_item_for_response( $revision, $request ); $this->assertSame( array( @@ -332,7 +385,7 @@ public function test_prepare_item_limit_fields() { } public function test_get_item_schema() { - $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/revisions' ); + $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; @@ -353,13 +406,13 @@ public function test_get_item_schema() { } public function test_create_item() { - $request = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/revisions' ); + $request = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_no_route', $response, 404 ); } public function test_update_item() { - $request = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); + $request = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_no_route', $response, 404 ); } @@ -383,7 +436,7 @@ public function test_get_additional_field_registration() { ) ); - $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/revisions' ); + $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); @@ -393,7 +446,7 @@ public function test_get_additional_field_registration() { wp_set_current_user( 1 ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); $response = rest_get_server()->dispatch( $request ); $this->assertArrayHasKey( 'my_custom_int', $response->data ); @@ -450,14 +503,14 @@ protected function check_get_revision_response( $response, $revision ) { public function test_get_item_sets_up_postdata() { wp_set_current_user( self::$editor_id ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); rest_get_server()->dispatch( $request ); $post = get_post(); $parent_post_id = wp_is_post_revision( $post->ID ); - $this->assertSame( $post->ID, $this->revision_id1 ); - $this->assertSame( $parent_post_id, self::$post_id ); + $this->assertSame( $post->ID, $this->revision_1_1_id ); + $this->assertSame( $parent_post_id, self::$post_id_1 ); } /** @@ -468,7 +521,7 @@ public function test_get_item_sets_up_postdata() { public function test_get_items_pagination_header_of_the_first_page() { wp_set_current_user( self::$editor_id ); - $rest_route = '/wp/v2/posts/' . self::$post_id . '/revisions'; + $rest_route = '/wp/v2/posts/' . self::$post_id_1 . '/revisions'; $per_page = 2; $total_pages = (int) ceil( $this->total_revisions / $per_page ); $page = 1; // First page. @@ -503,7 +556,7 @@ public function test_get_items_pagination_header_of_the_first_page() { public function test_get_items_pagination_header_of_the_last_page() { wp_set_current_user( self::$editor_id ); - $rest_route = '/wp/v2/posts/' . self::$post_id . '/revisions'; + $rest_route = '/wp/v2/posts/' . self::$post_id_1 . '/revisions'; $per_page = 2; $total_pages = (int) ceil( $this->total_revisions / $per_page ); $page = 2; // Last page. @@ -541,7 +594,7 @@ public function test_get_items_invalid_per_page_should_error() { $expected_error = 'rest_invalid_param'; $expected_status = 400; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); $request->set_param( 'per_page', $per_page ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( $expected_error, $response, $expected_status ); @@ -561,7 +614,7 @@ public function test_get_items_out_of_bounds_page_should_error() { $expected_error = 'rest_revision_invalid_page_number'; $expected_status = 400; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); $request->set_query_params( array( 'per_page' => $per_page, @@ -585,7 +638,7 @@ public function test_get_items_invalid_max_pages_should_error() { $expected_error = 'rest_revision_invalid_page_number'; $expected_status = 400; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); $request->set_query_params( array( 'per_page' => $per_page, @@ -608,7 +661,7 @@ public function test_get_items_search_query() { $expected_count = 1; $expected_content = 'This content is better.'; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); $request->set_param( 'search', $search_string ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); @@ -626,7 +679,7 @@ public function test_get_items_default_query_should_fetch_all_revisons() { $expected_count = $this->total_revisions; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); $response = rest_get_server()->dispatch( $request ); $this->assertCount( $expected_count, $response->get_data() ); } @@ -642,7 +695,7 @@ public function test_get_items_offset_should_not_work_without_per_page() { $offset = 1; $expected_count = $this->total_revisions; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); $request->set_param( 'offset', $offset ); $response = rest_get_server()->dispatch( $request ); $this->assertCount( $expected_count, $response->get_data() ); @@ -660,7 +713,7 @@ public function test_get_items_offset_should_work_with_per_page() { $offset = 1; $expected_count = 2; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); $request->set_query_params( array( 'offset' => $offset, @@ -684,7 +737,7 @@ public function test_get_items_offset_should_take_priority_over_page() { $page = 1; $expected_count = 2; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); $request->set_query_params( array( 'offset' => $offset, @@ -709,7 +762,7 @@ public function test_get_items_total_revisions_offset_should_return_empty_data() $expected_error = 'rest_revision_invalid_offset_number'; $expected_status = 400; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); $request->set_query_params( array( 'offset' => $offset, @@ -733,7 +786,7 @@ public function test_get_items_out_of_bound_offset_should_error() { $expected_error = 'rest_revision_invalid_offset_number'; $expected_status = 400; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); $request->set_query_params( array( 'offset' => $offset, @@ -757,7 +810,7 @@ public function test_get_items_impossible_high_number_offset_should_error() { $expected_error = 'rest_revision_invalid_offset_number'; $expected_status = 400; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); $request->set_query_params( array( 'offset' => $offset, @@ -781,7 +834,7 @@ public function test_get_items_invalid_offset_should_error() { $expected_error = 'rest_invalid_param'; $expected_status = 400; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); $request->set_query_params( array( 'offset' => $offset, @@ -806,7 +859,7 @@ public function test_get_items_out_of_bounds_page_should_not_error_if_offset() { $page = $total_pages + 1; // Out of bound page. $expected_count = 2; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); $request->set_query_params( array( 'offset' => 1, From 6fdc982f0f84083e759f4922982256e31113b16e Mon Sep 17 00:00:00 2001 From: ramon Date: Mon, 13 Nov 2023 12:39:13 +1100 Subject: [PATCH 2/8] Add a condition to get_item that checks the revision parent id against the supplied parent id. If it doesn't match, return a 404 Updated test coverage Update error message in class-wp-rest-revisions-controller.php --- .../class-wp-rest-revisions-controller.php | 10 + .../rest-api/rest-revisions-controller.php | 182 +++++++++--------- 2 files changed, 97 insertions(+), 95 deletions(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php index 5501c190c13ee..08a8479678002 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php @@ -387,6 +387,7 @@ public function get_item_permissions_check( $request ) { * Retrieves one revision from the collection. * * @since 4.7.0 + * @since 6.5.0 Added a condition to check that parent id matches revision parent id. * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. @@ -402,6 +403,15 @@ public function get_item( $request ) { return $revision; } + if ( $parent->ID !== $revision->post_parent ) { + return new WP_Error( + 'rest_post_invalid_parent', + /* translators: %s: A post revision id. */ + sprintf( __( 'Parent post does not have a revision with id of "%s"' ), $request['id'] ), + array( 'status' => 404 ) + ); + } + $response = $this->prepare_item_for_response( $revision, $request ); return rest_ensure_response( $response ); } diff --git a/tests/phpunit/tests/rest-api/rest-revisions-controller.php b/tests/phpunit/tests/rest-api/rest-revisions-controller.php index c29847702e509..e3c3618f49538 100644 --- a/tests/phpunit/tests/rest-api/rest-revisions-controller.php +++ b/tests/phpunit/tests/rest-api/rest-revisions-controller.php @@ -8,7 +8,7 @@ * @group restapi */ class WP_Test_REST_Revisions_Controller extends WP_Test_REST_Controller_Testcase { - protected static $post_id_1; + protected static $post_id; protected static $post_id_2; protected static $page_id; @@ -16,19 +16,18 @@ class WP_Test_REST_Revisions_Controller extends WP_Test_REST_Controller_Testcase protected static $contributor_id; private $total_revisions; - private $revision_1_1; - private $revision_1_1_id; - private $revision_1_2; - private $revision_1_2_id; - private $revision_1_3; - private $revision_1_3_id; + private $revisions; + private $revision_1; + private $revision_id1; + private $revision_2; + private $revision_id2; + private $revision_3; + private $revision_id3; private $revision_2_1; private $revision_2_1_id; - private $revision_2_2; - private $revision_2_2_id; public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { - self::$post_id_1 = $factory->post->create(); + self::$post_id = $factory->post->create(); self::$post_id_2 = $factory->post->create(); self::$page_id = $factory->post->create( array( 'post_type' => 'page' ) ); @@ -47,19 +46,19 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { wp_update_post( array( 'post_content' => 'This content is better.', - 'ID' => self::$post_id_1, + 'ID' => self::$post_id, ) ); wp_update_post( array( 'post_content' => 'This content is marvelous.', - 'ID' => self::$post_id_1, + 'ID' => self::$post_id, ) ); wp_update_post( array( 'post_content' => 'This content is fantastic.', - 'ID' => self::$post_id_1, + 'ID' => self::$post_id, ) ); wp_update_post( @@ -79,7 +78,7 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { public static function wpTearDownAfterClass() { // Also deletes revisions. - wp_delete_post( self::$post_id_1, true ); + wp_delete_post( self::$post_id, true ); wp_delete_post( self::$post_id_2, true ); wp_delete_post( self::$page_id, true ); @@ -91,21 +90,20 @@ public function set_up() { parent::set_up(); // Set first post revision vars. - $revisions = wp_get_post_revisions( self::$post_id_1 ); + $revisions = wp_get_post_revisions( self::$post_id ); $this->total_revisions = count( $revisions ); - $this->revision_1_1 = array_pop( $revisions ); - $this->revision_1_1_id = $this->revision_1_1->ID; - $this->revision_1_2 = array_pop( $revisions ); - $this->revision_1_2_id = $this->revision_1_2->ID; - $this->revision_1_3 = array_pop( $revisions ); - $this->revision_1_3_id = $this->revision_1_3->ID; + $this->revisions = $revisions; + $this->revision_1 = array_pop( $revisions ); + $this->revision_id1 = $this->revision_1->ID; + $this->revision_2 = array_pop( $revisions ); + $this->revision_id2 = $this->revision_2->ID; + $this->revision_3 = array_pop( $revisions ); + $this->revision_id3 = $this->revision_3->ID; // Set second post revision vars. $revisions = wp_get_post_revisions( self::$post_id_2 ); - $this->revision_2_1 = array_pop( $revisions ); - $this->revision_2_1_id = $this->revision_2_1->ID; - $this->revision_2_2 = array_pop( $revisions ); - $this->revision_2_2_id = $this->revision_2_2->ID; + $post_2_revision = array_pop( $revisions ); + $this->revision_2_1_id = $post_2_revision->ID; } public function _filter_map_meta_cap_remove_no_allow_revisions( $caps, $cap, $user_id, $args ) { @@ -133,13 +131,13 @@ public function test_register_routes() { public function test_context_param() { // Collection. - $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); + $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/revisions' ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] ); $this->assertSameSets( array( 'view', 'edit', 'embed' ), $data['endpoints'][0]['args']['context']['enum'] ); // Single. - $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1->ID ); + $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_1->ID ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] ); @@ -148,26 +146,26 @@ public function test_context_param() { public function test_get_items() { wp_set_current_user( self::$editor_id ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $this->assertSame( 200, $response->get_status() ); $this->assertCount( $this->total_revisions, $data ); // Reverse chronology. - $this->assertSame( $this->revision_1_3_id, $data[0]['id'] ); - $this->check_get_revision_response( $data[0], $this->revision_1_3 ); + $this->assertSame( $this->revision_id3, $data[0]['id'] ); + $this->check_get_revision_response( $data[0], $this->revision_3 ); - $this->assertSame( $this->revision_1_2_id, $data[1]['id'] ); - $this->check_get_revision_response( $data[1], $this->revision_1_2 ); + $this->assertSame( $this->revision_id2, $data[1]['id'] ); + $this->check_get_revision_response( $data[1], $this->revision_2 ); - $this->assertSame( $this->revision_1_1_id, $data[2]['id'] ); - $this->check_get_revision_response( $data[2], $this->revision_1_1 ); + $this->assertSame( $this->revision_id1, $data[2]['id'] ); + $this->check_get_revision_response( $data[2], $this->revision_1 ); } public function test_get_items_no_permission() { wp_set_current_user( 0 ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_cannot_read', $response, 401 ); @@ -192,10 +190,10 @@ public function test_get_items_invalid_parent_post_type() { public function test_get_item() { wp_set_current_user( self::$editor_id ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); - $this->check_get_revision_response( $response, $this->revision_1_1 ); + $this->check_get_revision_response( $response, $this->revision_1 ); $fields = array( 'author', 'date', @@ -218,7 +216,7 @@ public function test_get_item() { public function test_get_item_embed_context() { wp_set_current_user( self::$editor_id ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); $request->set_param( 'context', 'embed' ); $response = rest_get_server()->dispatch( $request ); $fields = array( @@ -236,7 +234,7 @@ public function test_get_item_embed_context() { public function test_get_item_no_permission() { wp_set_current_user( 0 ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_cannot_read', $response, 401 ); @@ -247,53 +245,47 @@ public function test_get_item_no_permission() { public function test_get_item_missing_parent() { wp_set_current_user( self::$editor_id ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/revisions/' . $this->revision_1_1_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER . '/revisions/' . $this->revision_id1 ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 ); } public function test_get_item_invalid_parent_post_type() { wp_set_current_user( self::$editor_id ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$page_id . '/revisions/' . $this->revision_1_1_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$page_id . '/revisions/' . $this->revision_id1 ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 ); } + /** + * @ticket 59875 + */ public function test_get_item_valid_parent_id() { wp_set_current_user( self::$editor_id ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); - $this->assertSame( self::$post_id_1, $data['parent'] ); + $this->assertSame( self::$post_id, $data['parent'], "The returned revision's id should match the parent id." ); + $this->check_get_revision_response( $response, $this->revision_1 ); } + /** + * @ticket 59875 + */ public function test_get_item_invalid_parent_id() { wp_set_current_user( self::$editor_id ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_2_1_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_2_1_id ); $response = rest_get_server()->dispatch( $request ); - $data = $response->get_data(); - - // Temporary - testing a failing state here. - if ( ! empty( $data['parent'] ) ) { - $revision = get_post( $data['id'] ); - $this->assertSame( 'revision', $revision->post_type, 'This test should fail.' ); - $parent = get_post( $data['parent'] ); - $this->assertSame( self::$post_id_1, $parent->ID, 'This test will fail but should not if there is a valid response.' ); - } - - $this->assertEmpty( $data['id'], 'The response should be empty.' ); - $this->assertEmpty( $data['parent'], 'The response should be empty.' ); - $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 ); } public function test_delete_item() { wp_set_current_user( self::$editor_id ); - $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); + $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); $request->set_param( 'force', true ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 ); - $this->assertNotNull( get_post( $this->revision_1_1_id ) ); + $this->assertNotNull( get_post( $this->revision_id1 ) ); } /** @@ -301,11 +293,11 @@ public function test_delete_item() { */ public function test_delete_item_parent_check() { wp_set_current_user( self::$contributor_id ); - $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); + $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); $request->set_param( 'force', true ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 ); - $this->assertNotNull( get_post( $this->revision_1_1_id ) ); + $this->assertNotNull( get_post( $this->revision_id1 ) ); } /** @@ -314,11 +306,11 @@ public function test_delete_item_parent_check() { public function test_delete_item_remove_do_not_allow() { wp_set_current_user( self::$editor_id ); add_filter( 'map_meta_cap', array( $this, '_filter_map_meta_cap_remove_no_allow_revisions' ), 10, 4 ); - $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); + $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); $request->set_param( 'force', true ); $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); - $this->assertNull( get_post( $this->revision_1_1_id ) ); + $this->assertNull( get_post( $this->revision_id1 ) ); } /** @@ -326,11 +318,11 @@ public function test_delete_item_remove_do_not_allow() { */ public function test_delete_item_cannot_delete_parent() { wp_set_current_user( self::$editor_id ); - $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); + $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); $request->set_param( 'force', true ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 ); - $this->assertNotNull( get_post( $this->revision_1_1_id ) ); + $this->assertNotNull( get_post( $this->revision_id1 ) ); } /** @@ -340,7 +332,7 @@ public function test_delete_item_cannot_delete_parent() { public function test_delete_item_no_trash() { wp_set_current_user( self::$editor_id ); add_filter( 'map_meta_cap', array( $this, '_filter_map_meta_cap_remove_no_allow_revisions' ), 10, 4 ); - $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); + $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501 ); @@ -349,31 +341,31 @@ public function test_delete_item_no_trash() { $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501 ); // Ensure the revision still exists. - $this->assertNotNull( get_post( $this->revision_1_1_id ) ); + $this->assertNotNull( get_post( $this->revision_id1 ) ); } public function test_delete_item_no_permission() { wp_set_current_user( self::$contributor_id ); - $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); + $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 ); } public function test_prepare_item() { wp_set_current_user( self::$editor_id ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); - $this->check_get_revision_response( $response, $this->revision_1_1 ); + $this->check_get_revision_response( $response, $this->revision_1 ); } public function test_prepare_item_limit_fields() { wp_set_current_user( self::$editor_id ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); $endpoint = new WP_REST_Revisions_Controller( 'post' ); $request->set_param( 'context', 'edit' ); $request->set_param( '_fields', 'id,slug' ); - $revision = get_post( $this->revision_1_1_id ); + $revision = get_post( $this->revision_id1 ); $response = $endpoint->prepare_item_for_response( $revision, $request ); $this->assertSame( array( @@ -385,7 +377,7 @@ public function test_prepare_item_limit_fields() { } public function test_get_item_schema() { - $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); + $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/revisions' ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; @@ -406,13 +398,13 @@ public function test_get_item_schema() { } public function test_create_item() { - $request = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); + $request = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/revisions' ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_no_route', $response, 404 ); } public function test_update_item() { - $request = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); + $request = new WP_REST_Request( 'POST', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_no_route', $response, 404 ); } @@ -436,7 +428,7 @@ public function test_get_additional_field_registration() { ) ); - $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); + $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts/' . self::$post_id . '/revisions' ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); @@ -446,7 +438,7 @@ public function test_get_additional_field_registration() { wp_set_current_user( 1 ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); $response = rest_get_server()->dispatch( $request ); $this->assertArrayHasKey( 'my_custom_int', $response->data ); @@ -503,14 +495,14 @@ protected function check_get_revision_response( $response, $revision ) { public function test_get_item_sets_up_postdata() { wp_set_current_user( self::$editor_id ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions/' . $this->revision_1_1_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 ); rest_get_server()->dispatch( $request ); $post = get_post(); $parent_post_id = wp_is_post_revision( $post->ID ); - $this->assertSame( $post->ID, $this->revision_1_1_id ); - $this->assertSame( $parent_post_id, self::$post_id_1 ); + $this->assertSame( $post->ID, $this->revision_id1 ); + $this->assertSame( $parent_post_id, self::$post_id ); } /** @@ -521,7 +513,7 @@ public function test_get_item_sets_up_postdata() { public function test_get_items_pagination_header_of_the_first_page() { wp_set_current_user( self::$editor_id ); - $rest_route = '/wp/v2/posts/' . self::$post_id_1 . '/revisions'; + $rest_route = '/wp/v2/posts/' . self::$post_id . '/revisions'; $per_page = 2; $total_pages = (int) ceil( $this->total_revisions / $per_page ); $page = 1; // First page. @@ -556,7 +548,7 @@ public function test_get_items_pagination_header_of_the_first_page() { public function test_get_items_pagination_header_of_the_last_page() { wp_set_current_user( self::$editor_id ); - $rest_route = '/wp/v2/posts/' . self::$post_id_1 . '/revisions'; + $rest_route = '/wp/v2/posts/' . self::$post_id . '/revisions'; $per_page = 2; $total_pages = (int) ceil( $this->total_revisions / $per_page ); $page = 2; // Last page. @@ -594,7 +586,7 @@ public function test_get_items_invalid_per_page_should_error() { $expected_error = 'rest_invalid_param'; $expected_status = 400; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); $request->set_param( 'per_page', $per_page ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( $expected_error, $response, $expected_status ); @@ -614,7 +606,7 @@ public function test_get_items_out_of_bounds_page_should_error() { $expected_error = 'rest_revision_invalid_page_number'; $expected_status = 400; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); $request->set_query_params( array( 'per_page' => $per_page, @@ -638,7 +630,7 @@ public function test_get_items_invalid_max_pages_should_error() { $expected_error = 'rest_revision_invalid_page_number'; $expected_status = 400; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); $request->set_query_params( array( 'per_page' => $per_page, @@ -661,7 +653,7 @@ public function test_get_items_search_query() { $expected_count = 1; $expected_content = 'This content is better.'; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); $request->set_param( 'search', $search_string ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); @@ -679,7 +671,7 @@ public function test_get_items_default_query_should_fetch_all_revisons() { $expected_count = $this->total_revisions; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); $response = rest_get_server()->dispatch( $request ); $this->assertCount( $expected_count, $response->get_data() ); } @@ -695,7 +687,7 @@ public function test_get_items_offset_should_not_work_without_per_page() { $offset = 1; $expected_count = $this->total_revisions; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); $request->set_param( 'offset', $offset ); $response = rest_get_server()->dispatch( $request ); $this->assertCount( $expected_count, $response->get_data() ); @@ -713,7 +705,7 @@ public function test_get_items_offset_should_work_with_per_page() { $offset = 1; $expected_count = 2; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); $request->set_query_params( array( 'offset' => $offset, @@ -737,7 +729,7 @@ public function test_get_items_offset_should_take_priority_over_page() { $page = 1; $expected_count = 2; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); $request->set_query_params( array( 'offset' => $offset, @@ -762,7 +754,7 @@ public function test_get_items_total_revisions_offset_should_return_empty_data() $expected_error = 'rest_revision_invalid_offset_number'; $expected_status = 400; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); $request->set_query_params( array( 'offset' => $offset, @@ -786,7 +778,7 @@ public function test_get_items_out_of_bound_offset_should_error() { $expected_error = 'rest_revision_invalid_offset_number'; $expected_status = 400; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); $request->set_query_params( array( 'offset' => $offset, @@ -810,7 +802,7 @@ public function test_get_items_impossible_high_number_offset_should_error() { $expected_error = 'rest_revision_invalid_offset_number'; $expected_status = 400; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); $request->set_query_params( array( 'offset' => $offset, @@ -834,7 +826,7 @@ public function test_get_items_invalid_offset_should_error() { $expected_error = 'rest_invalid_param'; $expected_status = 400; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); $request->set_query_params( array( 'offset' => $offset, @@ -859,7 +851,7 @@ public function test_get_items_out_of_bounds_page_should_not_error_if_offset() { $page = $total_pages + 1; // Out of bound page. $expected_count = 2; - $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id_1 . '/revisions' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions' ); $request->set_query_params( array( 'offset' => 1, From 83ddc64594e6831acf81e2cbff41558889c8c838 Mon Sep 17 00:00:00 2001 From: ramon Date: Tue, 14 Nov 2023 10:12:33 +1100 Subject: [PATCH 3/8] Coerce types --- .../rest-api/endpoints/class-wp-rest-revisions-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php index 08a8479678002..9351caa7bc214 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php @@ -403,7 +403,7 @@ public function get_item( $request ) { return $revision; } - if ( $parent->ID !== $revision->post_parent ) { + if ( (int) $parent->ID !== (int) $revision->post_parent ) { return new WP_Error( 'rest_post_invalid_parent', /* translators: %s: A post revision id. */ From f8bf0daa1acfa68f2b421cfd6149fb9acccd41c6 Mon Sep 17 00:00:00 2001 From: ramon Date: Tue, 14 Nov 2023 10:14:15 +1100 Subject: [PATCH 4/8] Remove unused var in test --- tests/phpunit/tests/rest-api/rest-revisions-controller.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/phpunit/tests/rest-api/rest-revisions-controller.php b/tests/phpunit/tests/rest-api/rest-revisions-controller.php index e3c3618f49538..ca7794127847f 100644 --- a/tests/phpunit/tests/rest-api/rest-revisions-controller.php +++ b/tests/phpunit/tests/rest-api/rest-revisions-controller.php @@ -23,7 +23,6 @@ class WP_Test_REST_Revisions_Controller extends WP_Test_REST_Controller_Testcase private $revision_id2; private $revision_3; private $revision_id3; - private $revision_2_1; private $revision_2_1_id; public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { From 6d489eb3cf0fa0abefccb457a80259300b75a2d3 Mon Sep 17 00:00:00 2001 From: Ramon Date: Mon, 20 Nov 2023 07:17:33 +1100 Subject: [PATCH 5/8] Apply suggestions from code review string replacement type signifier %d Co-authored-by: Daniel Bachhuber --- .../rest-api/endpoints/class-wp-rest-revisions-controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php index 9351caa7bc214..605a4e984763e 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php @@ -406,8 +406,8 @@ public function get_item( $request ) { if ( (int) $parent->ID !== (int) $revision->post_parent ) { return new WP_Error( 'rest_post_invalid_parent', - /* translators: %s: A post revision id. */ - sprintf( __( 'Parent post does not have a revision with id of "%s"' ), $request['id'] ), + /* translators: %d: A post revision id. */ + sprintf( __( 'Parent post does not have a revision with id of "%d"' ), $request['id'] ), array( 'status' => 404 ) ); } From ff84ef6abe80fddb999fa76456bee552791612de Mon Sep 17 00:00:00 2001 From: ramon Date: Wed, 20 Dec 2023 14:01:11 +1100 Subject: [PATCH 6/8] Changed duped error code Adding parent id revision parent_id mismatch invalid test to the templates revisions controller tests Remove specific mention of "post" to discern between other post types, e.g., "template" --- .../class-wp-rest-revisions-controller.php | 4 +- .../rest-api/rest-revisions-controller.php | 2 +- .../wpRestTemplateRevisionsController.php | 48 +++++++++++++++++++ 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php index 605a4e984763e..ba8986d7f1af6 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php @@ -405,9 +405,9 @@ public function get_item( $request ) { if ( (int) $parent->ID !== (int) $revision->post_parent ) { return new WP_Error( - 'rest_post_invalid_parent', + 'rest_revision_parent_id_mismatch', /* translators: %d: A post revision id. */ - sprintf( __( 'Parent post does not have a revision with id of "%d"' ), $request['id'] ), + sprintf( __( 'Parent does not have a revision with id of "%d"' ), $request['id'] ), array( 'status' => 404 ) ); } diff --git a/tests/phpunit/tests/rest-api/rest-revisions-controller.php b/tests/phpunit/tests/rest-api/rest-revisions-controller.php index ca7794127847f..00dacbf36bb72 100644 --- a/tests/phpunit/tests/rest-api/rest-revisions-controller.php +++ b/tests/phpunit/tests/rest-api/rest-revisions-controller.php @@ -275,7 +275,7 @@ public function test_get_item_invalid_parent_id() { wp_set_current_user( self::$editor_id ); $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_2_1_id ); $response = rest_get_server()->dispatch( $request ); - $this->assertErrorResponse( 'rest_post_invalid_parent', $response, 404 ); + $this->assertErrorResponse( 'rest_revision_parent_id_mismatch', $response, 404 ); } public function test_delete_item() { diff --git a/tests/phpunit/tests/rest-api/wpRestTemplateRevisionsController.php b/tests/phpunit/tests/rest-api/wpRestTemplateRevisionsController.php index 91370b21c726b..253819253f4a4 100644 --- a/tests/phpunit/tests/rest-api/wpRestTemplateRevisionsController.php +++ b/tests/phpunit/tests/rest-api/wpRestTemplateRevisionsController.php @@ -19,6 +19,11 @@ class Tests_REST_wpRestTemplateRevisionsController extends WP_Test_REST_Controll */ const TEMPLATE_NAME = 'my_template'; + /** + * @var string + */ + const TEMPLATE_NAME_2 = 'my_template_2'; + /** * @var string */ @@ -51,6 +56,15 @@ class Tests_REST_wpRestTemplateRevisionsController extends WP_Test_REST_Controll */ private static $template_post; + /** + * Template post. + * + * @since 6.5.0 + * + * @var WP_Post + */ + private static $template_post_2; + /** * @var array */ @@ -123,6 +137,26 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { 'post_content' => 'Content revision #5', ) ); + + // Create a new template post to test the get_item method. + self::$template_post_2 = $factory->post->create_and_get( + array( + 'post_type' => self::PARENT_POST_TYPE, + 'post_name' => self::TEMPLATE_NAME_2, + 'post_title' => 'My Template 2', + 'post_content' => 'Content 2', + 'post_excerpt' => 'Description of my template 2', + 'tax_input' => array( + 'wp_theme' => array( + self::TEST_THEME, + ), + ), + ) + ); + wp_set_post_terms( self::$template_post_2->ID, self::TEST_THEME, 'wp_theme' ); + + var_dump( self::$template_post->ID ); + var_dump( self::$template_post_2->ID ); } /** @@ -336,6 +370,20 @@ public function test_get_item_not_found() { $this->assertErrorResponse( 'rest_post_invalid_parent', $response, WP_Http::NOT_FOUND ); } + /** + * @ticket 59875 + */ + public function test_get_item_invalid_parent_id() { + wp_set_current_user( self::$admin_id ); + $revisions = wp_get_post_revisions( self::$template_post, array( 'fields' => 'ids' ) ); + $revision_id = array_shift( $revisions ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/templates/' . self::TEST_THEME . '/' . self::TEMPLATE_NAME_2 . '/revisions/' . $revision_id ); + + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_revision_parent_id_mismatch', $response, 404 ); + } + /** * @covers WP_REST_Template_Revisions_Controller::prepare_item_for_response * @ticket 56922 From c5f48bd7b45b1f971495d297d651ba11b7d97b19 Mon Sep 17 00:00:00 2001 From: ramon Date: Thu, 21 Dec 2023 14:18:58 +1100 Subject: [PATCH 7/8] Genericize error message and create a test to ensure the correct parent ID is returned in the error message. --- .../rest-api/endpoints/class-wp-rest-revisions-controller.php | 2 +- tests/phpunit/tests/rest-api/rest-revisions-controller.php | 3 +++ .../tests/rest-api/wpRestTemplateRevisionsController.php | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php index ba8986d7f1af6..9c829b98f2e9d 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php @@ -407,7 +407,7 @@ public function get_item( $request ) { return new WP_Error( 'rest_revision_parent_id_mismatch', /* translators: %d: A post revision id. */ - sprintf( __( 'Parent does not have a revision with id of "%d"' ), $request['id'] ), + sprintf( __( 'The revision does not belong to the specified parent with id of "%d"' ), $parent->ID ), array( 'status' => 404 ) ); } diff --git a/tests/phpunit/tests/rest-api/rest-revisions-controller.php b/tests/phpunit/tests/rest-api/rest-revisions-controller.php index 00dacbf36bb72..08138055062f3 100644 --- a/tests/phpunit/tests/rest-api/rest-revisions-controller.php +++ b/tests/phpunit/tests/rest-api/rest-revisions-controller.php @@ -276,6 +276,9 @@ public function test_get_item_invalid_parent_id() { $request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_2_1_id ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_revision_parent_id_mismatch', $response, 404 ); + + $expected_message = 'The revision does not belong to the specified parent with id of "' . self::$post_id . '"'; + $this->assertSame( $expected_message, $response->as_error()->get_error_messages()[0], 'The message must contain the correct parent ID.' ); } public function test_delete_item() { diff --git a/tests/phpunit/tests/rest-api/wpRestTemplateRevisionsController.php b/tests/phpunit/tests/rest-api/wpRestTemplateRevisionsController.php index 253819253f4a4..62d36abc4a110 100644 --- a/tests/phpunit/tests/rest-api/wpRestTemplateRevisionsController.php +++ b/tests/phpunit/tests/rest-api/wpRestTemplateRevisionsController.php @@ -382,6 +382,9 @@ public function test_get_item_invalid_parent_id() { $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_revision_parent_id_mismatch', $response, 404 ); + + $expected_message = 'The revision does not belong to the specified parent with id of "' . self::$template_post_2->ID . '"'; + $this->assertSame( $expected_message, $response->as_error()->get_error_messages()[0], 'The message must contain the correct parent ID.' ); } /** From 139f6ca709eac90a15ac6595a93d37cc476b9e53 Mon Sep 17 00:00:00 2001 From: ramon Date: Thu, 21 Dec 2023 14:20:50 +1100 Subject: [PATCH 8/8] Remove specific mention of "post" to discern between other post types, e.g., "template" --- .../rest-api/endpoints/class-wp-rest-revisions-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php index 9c829b98f2e9d..415333661f935 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php @@ -406,7 +406,7 @@ public function get_item( $request ) { if ( (int) $parent->ID !== (int) $revision->post_parent ) { return new WP_Error( 'rest_revision_parent_id_mismatch', - /* translators: %d: A post revision id. */ + /* translators: %d: A post id. */ sprintf( __( 'The revision does not belong to the specified parent with id of "%d"' ), $parent->ID ), array( 'status' => 404 ) );