tests/src/Functional/ResourceTestBase.php | 76 ++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/tests/src/Functional/ResourceTestBase.php b/tests/src/Functional/ResourceTestBase.php index 0ee4050..bd7b94b 100644 --- a/tests/src/Functional/ResourceTestBase.php +++ b/tests/src/Functional/ResourceTestBase.php @@ -36,7 +36,7 @@ abstract class ResourceTestBase extends BrowserTestBase { /** * {@inheritdoc} */ - protected static $modules = ['jsonapi', 'basic_auth']; + protected static $modules = ['jsonapi', 'basic_auth', 'rest_test']; /** * The tested entity type. @@ -162,7 +162,6 @@ abstract class ResourceTestBase extends BrowserTestBase { \Drupal::service('jsonapi.resource_type.repository')->clearCachedDefinitions(); \Drupal::service('router.builder')->rebuild(); - /* if ($this->entity instanceof FieldableEntityInterface) { // Add access-protected field. FieldStorageConfig::create([ @@ -210,7 +209,6 @@ abstract class ResourceTestBase extends BrowserTestBase { $this->entity->save(); } } - */ } /** @@ -744,8 +742,7 @@ abstract class ResourceTestBase extends BrowserTestBase { $parseable_invalid_request_body_missing_type = $this->serializer->encode($this->makeNormalizationViolateJsonApiSpec($this->getNormalizedPostEntity(), 'type'), 'api_json'); $parseable_invalid_request_body = $this->serializer->encode($this->makeNormalizationInvalid($this->getNormalizedPostEntity()), 'api_json'); $parseable_invalid_request_body_2 = Json::encode(NestedArray::mergeDeep(['data' => ['id' => $this->randomMachineName(129)]], $this->getNormalizedPostEntity())); - // @todo … -// $parseable_invalid_request_body_3 = $this->serializer->encode($this->getNormalizedPostEntity() + ['field_rest_test' => [['value' => $this->randomString()]]], 'api_json'); + $parseable_invalid_request_body_3 = Json::encode(NestedArray::mergeDeep(['data' => ['attributes' => ['field_rest_test' => $this->randomString()]]], $this->getNormalizedPostEntity())); // The URL and Guzzle request options that will be used in this test. The // request options will be modified/expanded throughout this test: @@ -940,11 +937,11 @@ abstract class ResourceTestBase extends BrowserTestBase { $parseable_valid_request_body = $this->serializer->encode($this->getNormalizedPatchEntity(), 'api_json'); $parseable_valid_request_body_2 = $this->serializer->encode($this->getNormalizedPatchEntity(), 'api_json'); $parseable_invalid_request_body = $this->serializer->encode($this->makeNormalizationInvalid($this->getNormalizedPatchEntity()), 'api_json'); -// $parseable_invalid_request_body_2 = $this->serializer->encode($this->getNormalizedPatchEntity() + ['field_rest_test' => [['value' => $this->randomString()]]], 'api_json'); + $parseable_invalid_request_body_2 = Json::encode(NestedArray::mergeDeep(['data' => ['attributes' => ['field_rest_test' => $this->randomString()]]], $this->getNormalizedPatchEntity())); // The 'field_rest_test' field does not allow 'view' access, so does not end // up in the normalization. Even when we explicitly add it the normalization // that we send in the body of a PATCH request, it is considered invalid. -// $parseable_invalid_request_body_3 = $this->serializer->encode($this->getNormalizedPatchEntity() + ['field_rest_test' => $this->entity->get('field_rest_test')->getValue()], 'api_json'); + $parseable_invalid_request_body_3 = Json::encode(NestedArray::mergeDeep(['data' => ['attributes' => ['field_rest_test' => $this->entity->get('field_rest_test')->getValue()]]], $this->getNormalizedPatchEntity())); // The URL and Guzzle request options that will be used in this test. The // request options will be modified/expanded throughout this test: @@ -1033,12 +1030,30 @@ abstract class ResourceTestBase extends BrowserTestBase { $this->assertResourceResponse(422, json_encode($expected), $response); // $this->assertResourceErrorResponse(422, "$label_field: $label_field_capitalized: this field cannot hold more than 1 values.", $response, '/data/attributes/' . $label_field); -/* $request_options[RequestOptions::BODY] = $parseable_invalid_request_body_2; // DX: 403 when entity contains field without 'edit' access. $response = $this->request('PATCH', $url, $request_options); - $this->assertResourceErrorResponse(403, "Access denied on updating field 'field_rest_test'.", $response); + // @todo Remove $expected + assertResourceResponse() in favor of the commented line below once https://www.drupal.org/project/jsonapi/issues/2943176 lands. + $expected = [ + 'errors' => [ + [ + 'title' => 'Forbidden', + 'status' => 403, + 'detail' => "The current user is not allowed to PATCH the selected field (field_rest_test).", + 'links' => [ + 'info' => HttpExceptionNormalizer::getInfoUrl(403), + ], + 'code' => 0, + 'id' => '/' . static::$resourceTypeName . '/' . $this->entity->uuid(), + 'source' => [ + 'pointer' => '/data/attributes/field_rest_test', + ], + ], + ], + ]; + $this->assertResourceResponse(403, Json::encode($expected), $response); + // $this->assertResourceErrorResponse(403, "The current user is not allowed to PATCH the selected field (field_rest_test).", $response, '/data/attributes/field_rest_test'); $request_options[RequestOptions::BODY] = $parseable_invalid_request_body_3; @@ -1046,8 +1061,26 @@ abstract class ResourceTestBase extends BrowserTestBase { // when the value for that field matches the current value. This is allowed // in principle, but leads to information disclosure. $response = $this->request('PATCH', $url, $request_options); - $this->assertResourceErrorResponse(403, "Access denied on updating field 'field_rest_test'.", $response); -*/ + // @todo Remove $expected + assertResourceResponse() in favor of the commented line below once https://www.drupal.org/project/jsonapi/issues/2943176 lands. + $expected = [ + 'errors' => [ + [ + 'title' => 'Forbidden', + 'status' => 403, + 'detail' => "The current user is not allowed to PATCH the selected field (field_rest_test).", + 'links' => [ + 'info' => HttpExceptionNormalizer::getInfoUrl(403), + ], + 'code' => 0, + 'id' => '/' . static::$resourceTypeName . '/' . $this->entity->uuid(), + 'source' => [ + 'pointer' => '/data/attributes/field_rest_test', + ], + ], + ], + ]; + $this->assertResourceResponse(403, Json::encode($expected), $response); + // $this->assertResourceErrorResponse(403, "The current user is not allowed to PATCH the selected field (field_rest_test).", $response, '/data/attributes/field_rest_test'); // DX: 403 when sending PATCH request with updated read-only fields. list($modified_entity, $original_values) = static::getModifiedEntityForPatchTesting($this->entity); @@ -1063,7 +1096,9 @@ abstract class ResourceTestBase extends BrowserTestBase { [ 'title' => 'Forbidden', 'status' => 403, - 'detail' => "The current user is not allowed to PATCH the selected field (" . $patch_protected_field_name . ")." . ($reason !== NULL ? ' ' . $reason : ''), + // @todo Remove this line in favor of the commented line once https://www.drupal.org/project/drupal/issues/2938053 lands. + 'detail' => "The current user is not allowed to PATCH the selected field (" . $patch_protected_field_name . ").", + //'detail' => "The current user is not allowed to PATCH the selected field (" . $patch_protected_field_name . ")." . ($reason !== NULL ? ' ' . $reason : ''), 'links' => [ 'info' => HttpExceptionNormalizer::getInfoUrl(403), ], @@ -1124,7 +1159,7 @@ abstract class ResourceTestBase extends BrowserTestBase { $this->assertArraySubset(static::castToString($field_normalization), $updated_entity->get($field_name)->getValue(), TRUE); } } -/* + // Ensure that fields do not get deleted if they're not present in the PATCH // request. Test this using the configurable field that we added, but which // is not sent in the PATCH request. @@ -1132,22 +1167,21 @@ abstract class ResourceTestBase extends BrowserTestBase { // Multi-value field: remove item 0. Then item 1 becomes item 0. $normalization_multi_value_tests = $this->getNormalizedPatchEntity(); - $normalization_multi_value_tests['field_rest_test_multivalue'] = $this->entity->get('field_rest_test_multivalue')->getValue(); + $normalization_multi_value_tests['data']['attributes']['field_rest_test_multivalue'] = $this->entity->get('field_rest_test_multivalue')->getValue(); $normalization_remove_item = $normalization_multi_value_tests; - unset($normalization_remove_item['field_rest_test_multivalue'][0]); - $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization_remove_item, static::$format); + unset($normalization_remove_item['data']['attributes']['field_rest_test_multivalue'][0]); + $request_options[RequestOptions::BODY] = Json::encode($normalization_remove_item, 'api_json'); $response = $this->request('PATCH', $url, $request_options); - $this->assertResourceResponse(200, FALSE, $response); + $this->assertResourceResponse(200, FALSE, $response, ['http_response', static::$entityTypeId . ':' . $this->entity->id()], $this->getExpectedCacheContexts()); $this->assertSame([0 => ['value' => 'Two']], $this->entityStorage->loadUnchanged($this->entity->id())->get('field_rest_test_multivalue')->getValue()); // Multi-value field: add one item before the existing one, and one after. $normalization_add_items = $normalization_multi_value_tests; - $normalization_add_items['field_rest_test_multivalue'][2] = ['value' => 'Three']; - $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization_add_items, static::$format); + $normalization_add_items['data']['attributes']['field_rest_test_multivalue'][2] = ['value' => 'Three']; + $request_options[RequestOptions::BODY] = Json::encode($normalization_add_items); $response = $this->request('PATCH', $url, $request_options); - $this->assertResourceResponse(200, FALSE, $response); + $this->assertResourceResponse(200, FALSE, $response, ['http_response', static::$entityTypeId . ':' . $this->entity->id()], $this->getExpectedCacheContexts()); $this->assertSame([0 => ['value' => 'One'], 1 => ['value' => 'Two'], 2 => ['value' => 'Three']], $this->entityStorage->loadUnchanged($this->entity->id())->get('field_rest_test_multivalue')->getValue()); -*/ } /**