.../EntityResource/HalEntityNormalizationTrait.php | 38 +++++++++++++++++ .../EntityResource/EntityResourceTestBase.php | 48 ++++++++++++++++++++-- .../rest/tests/src/Functional/ResourceTestBase.php | 16 ++++++++ 3 files changed, 98 insertions(+), 4 deletions(-) diff --git a/core/modules/hal/tests/src/Functional/EntityResource/HalEntityNormalizationTrait.php b/core/modules/hal/tests/src/Functional/EntityResource/HalEntityNormalizationTrait.php index 317110b..daec553 100644 --- a/core/modules/hal/tests/src/Functional/EntityResource/HalEntityNormalizationTrait.php +++ b/core/modules/hal/tests/src/Functional/EntityResource/HalEntityNormalizationTrait.php @@ -6,6 +6,7 @@ use Drupal\Core\Field\EntityReferenceFieldItemListInterface; use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Url; +use GuzzleHttp\RequestOptions; /** * Trait for EntityResourceTestBase subclasses testing formats using HAL normalization @@ -87,4 +88,41 @@ protected function removeFieldsFromNormalization(array $normalization, $field_na return array_diff_key($normalization, array_flip($field_names)); } + /** + * {@inheritdoc} + */ + protected function assertNormalizationEdgeCases($method, Url $url, array $request_options) { + // \Drupal\serialization\Normalizer\EntityNormalizer::denormalize(): entity + // types with bundles MUST send their bundle field to be denormalizable. + if ($this->entity->getEntityType()->hasKey('bundle')) { + $normalization = $this->getNormalizedPostEntity(); + + // @todo Uncomment this in https://www.drupal.org/node/2824827. +/* + $normalization['_links']['type'] = Url::fromUri('base:rest/type/' . static::$entityTypeId . '/bad_bundle_name'); + $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format); + + // DX: 400 when incorrect entity type bundle is specified. + $response = $this->request($method, $url, $request_options); + // @todo use this commented line instead of the 3 lines thereafter once https://www.drupal.org/node/2813853 lands. +// $this->assertResourceErrorResponse(400, 'The type link relation must be specified.', $response); + $this->assertSame(400, $response->getStatusCode()); + $this->assertSame([static::$mimeType], $response->getHeader('Content-Type')); + $this->assertSame($this->serializer->encode(['error' => 'The type link relation must be specified.'], static::$format), (string) $response->getBody()); +*/ + + unset($normalization['_links']['type']); + $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format); + + + // DX: 400 when no entity type bundle is specified. + $response = $this->request($method, $url, $request_options); + // @todo use this commented line instead of the 3 lines thereafter once https://www.drupal.org/node/2813853 lands. +// $this->assertResourceErrorResponse(400, 'The type link relation must be specified.', $response); + $this->assertSame(400, $response->getStatusCode()); + $this->assertSame([static::$mimeType], $response->getHeader('Content-Type')); + $this->assertSame($this->serializer->encode(['error' => 'The type link relation must be specified.'], static::$format), (string) $response->getBody()); + } + } + } diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php index 4315577..9b7b275 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php @@ -594,8 +594,9 @@ public function testPost() { $request_options[RequestOptions::BODY] = $parseable_valid_request_body; - // Before sending a well-formed request, allow the authentication provider's - // edge cases to also be tested. + // Before sending a well-formed request, allow the normalization and + // authentication provider edge cases to also be tested. + $this->assertNormalizationEdgeCases('POST', $url, $request_options); $this->assertAuthenticationEdgeCases('POST', $url, $request_options); // 201 for well-formed request. @@ -786,8 +787,9 @@ public function testPatch() { $request_options[RequestOptions::BODY] = $parseable_valid_request_body; - // Before sending a well-formed request, allow the authentication provider's - // edge cases to also be tested. + // Before sending a well-formed request, allow the normalization and + // authentication provider edge cases to also be tested. + $this->assertNormalizationEdgeCases('PATCH', $url, $request_options); $this->assertAuthenticationEdgeCases('PATCH', $url, $request_options); @@ -924,6 +926,44 @@ public function testDelete() { $this->assertFalse($response->hasHeader('X-Drupal-Cache')); } + /** + * {@inheritdoc} + */ + protected function assertNormalizationEdgeCases($method, Url $url, array $request_options) { + // \Drupal\serialization\Normalizer\EntityNormalizer::denormalize(): entity + // types with bundles MUST send their bundle field to be denormalizable. + $entity_type = $this->entity->getEntityType(); + if ($entity_type->hasKey('bundle') && $entity_type->getBundleEntityType()) { + $bundle_field_name = $this->entity->getEntityType()->getKey('bundle'); + $normalization = $this->getNormalizedPostEntity(); + + + $normalization[$bundle_field_name] = 'bad_bundle_name'; + $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format); + + + // DX: 400 when incorrect entity type bundle is specified. + $response = $this->request($method, $url, $request_options); + // @todo use this commented line instead of the 3 lines thereafter once https://www.drupal.org/node/2813853 lands. +// $this->assertResourceErrorResponse(400, '"bad_bundle_name" is not a valid bundle type for denormalization.', $response); + $this->assertSame(400, $response->getStatusCode()); + $this->assertSame([static::$mimeType], $response->getHeader('Content-Type')); + $this->assertSame($this->serializer->encode(['error' => '"bad_bundle_name" is not a valid bundle type for denormalization.'], static::$format), (string) $response->getBody()); + + + unset($normalization[$bundle_field_name]); + $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format); + + + // DX: 400 when no entity type bundle is specified. + $response = $this->request($method, $url, $request_options); + // @todo use this commented line instead of the 3 lines thereafter once https://www.drupal.org/node/2813853 lands. +// $this->assertResourceErrorResponse(400, 'A string must be provided as a bundle value.', $response); + $this->assertSame(400, $response->getStatusCode()); + $this->assertSame([static::$mimeType], $response->getHeader('Content-Type')); + $this->assertSame($this->serializer->encode(['error' => 'A string must be provided as a bundle value.'], static::$format), (string) $response->getBody()); + } + } /** * Gets an entity resource's GET/PATCH/DELETE URL. diff --git a/core/modules/rest/tests/src/Functional/ResourceTestBase.php b/core/modules/rest/tests/src/Functional/ResourceTestBase.php index 191b9e2..ef18ba3 100644 --- a/core/modules/rest/tests/src/Functional/ResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/ResourceTestBase.php @@ -190,6 +190,22 @@ protected function provisionResource($resource_type, $formats = [], $authenticat abstract protected function assertResponseWhenMissingAuthentication(ResponseInterface $response); /** + * Asserts normalization-specific edge cases. + * + * (Should be called before sending a well-formed request.) + * + * @see \GuzzleHttp\ClientInterface::request() + * + * @param string $method + * HTTP method. + * @param \Drupal\Core\Url $url + * URL to request. + * @param array $request_options + * Request options to apply. + */ + abstract protected function assertNormalizationEdgeCases($method, Url $url, array $request_options); + + /** * Asserts authentication provider-specific edge cases. * * (Should be called before sending a well-formed request.)