public function ResourceTestBase::testGetIndividual in JSON:API 8
Same name and namespace in other branches
- 8.2 tests/src/Functional/ResourceTestBase.php \Drupal\Tests\jsonapi\Functional\ResourceTestBase::testGetIndividual()
Tests GETting an individual resource, plus edge cases to ensure good DX.
6 calls to ResourceTestBase::testGetIndividual()
- BlockContentTest::testGetIndividual in tests/
src/ Functional/ BlockContentTest.php - Tests GETting an individual resource, plus edge cases to ensure good DX.
- BlockContentTest::testPostIndividual in tests/
src/ Functional/ BlockContentTest.php - Tests POSTing an individual resource, plus edge cases to ensure good DX.
- EntityFormDisplayTest::testGetIndividual in tests/
src/ Functional/ EntityFormDisplayTest.php - Tests GETting an individual resource, plus edge cases to ensure good DX.
- EntityViewDisplayTest::testGetIndividual in tests/
src/ Functional/ EntityViewDisplayTest.php - Tests GETting an individual resource, plus edge cases to ensure good DX.
- FileTest::testGetIndividual in tests/
src/ Functional/ FileTest.php - Tests GETting an individual resource, plus edge cases to ensure good DX.
7 methods override ResourceTestBase::testGetIndividual()
- BlockContentTest::testGetIndividual in tests/
src/ Functional/ BlockContentTest.php - Tests GETting an individual resource, plus edge cases to ensure good DX.
- EntityFormDisplayTest::testGetIndividual in tests/
src/ Functional/ EntityFormDisplayTest.php - Tests GETting an individual resource, plus edge cases to ensure good DX.
- EntityViewDisplayTest::testGetIndividual in tests/
src/ Functional/ EntityViewDisplayTest.php - Tests GETting an individual resource, plus edge cases to ensure good DX.
- FileTest::testGetIndividual in tests/
src/ Functional/ FileTest.php - Tests GETting an individual resource, plus edge cases to ensure good DX.
- ItemTest::testGetIndividual in tests/
src/ Functional/ ItemTest.php - Tests GETting an individual resource, plus edge cases to ensure good DX.
File
- tests/
src/ Functional/ ResourceTestBase.php, line 893
Class
- ResourceTestBase
- Subclass this for every JSON API resource type.
Namespace
Drupal\Tests\jsonapi\FunctionalCode
public function testGetIndividual() {
// The URL and Guzzle request options that will be used in this test. The
// request options will be modified/expanded throughout this test:
// - to first test all mistakes a developer might make, and assert that the
// error responses provide a good DX
// - to eventually result in a well-formed request that succeeds.
// @todo Remove line below in favor of commented line in https://www.drupal.org/project/jsonapi/issues/2878463.
$url = Url::fromRoute(sprintf('jsonapi.%s.individual', static::$resourceTypeName), [
static::$entityTypeId => $this->entity
->uuid(),
]);
/* $url = $this->entity->toUrl('jsonapi'); */
$request_options = [];
$request_options[RequestOptions::HEADERS]['Accept'] = 'application/vnd.api+json';
$request_options = NestedArray::mergeDeep($request_options, $this
->getAuthenticationRequestOptions());
// DX: 403 when unauthorized.
$response = $this
->request('GET', $url, $request_options);
$reason = $this
->getExpectedUnauthorizedAccessMessage('GET');
$message = trim("The current user is not allowed to GET the selected resource. {$reason}");
$this
->assertResourceErrorResponse(403, $message, $response, '/data');
$this
->assertArrayNotHasKey('Link', $response
->getHeaders());
$this
->setUpAuthorization('GET');
// Set body despite that being nonsensical: should be ignored.
$request_options[RequestOptions::BODY] = Json::encode($this
->getExpectedDocument());
// 200 for well-formed HEAD request.
$response = $this
->request('HEAD', $url, $request_options);
$this
->assertResourceResponse(200, NULL, $response, $this
->getExpectedCacheTags(), $this
->getExpectedCacheContexts(), FALSE, 'MISS');
$head_headers = $response
->getHeaders();
// 200 for well-formed GET request. Page Cache hit because of HEAD request.
// Same for Dynamic Page Cache hit.
$response = $this
->request('GET', $url, $request_options);
$this
->assertResourceResponse(200, $this
->getExpectedDocument(), $response, $this
->getExpectedCacheTags(), $this
->getExpectedCacheContexts(), FALSE, 'HIT');
// Assert that Dynamic Page Cache did not store a ResourceResponse object,
// which needs serialization after every cache hit. Instead, it should
// contain a flattened response. Otherwise performance suffers.
// @see \Drupal\jsonapi\EventSubscriber\ResourceResponseSubscriber::flattenResponse()
$cache_items = $this->container
->get('database')
->query("SELECT cid, data FROM {cache_dynamic_page_cache} WHERE cid LIKE :pattern", [
':pattern' => '%[route]=jsonapi.%',
])
->fetchAllAssoc('cid');
$this
->assertTrue(count($cache_items) >= 2);
$found_cache_redirect = FALSE;
$found_cached_200_response = FALSE;
$other_cached_responses_are_4xx = TRUE;
foreach ($cache_items as $cid => $cache_item) {
$cached_data = unserialize($cache_item->data);
if (!isset($cached_data['#cache_redirect'])) {
$cached_response = $cached_data['#response'];
if ($cached_response
->getStatusCode() === 200) {
$found_cached_200_response = TRUE;
}
elseif (!$cached_response
->isClientError()) {
$other_cached_responses_are_4xx = FALSE;
}
$this
->assertNotInstanceOf(ResourceResponse::class, $cached_response);
$this
->assertInstanceOf(CacheableResponseInterface::class, $cached_response);
}
else {
$found_cache_redirect = TRUE;
}
}
$this
->assertTrue($found_cache_redirect);
$this
->assertTrue($found_cached_200_response);
$this
->assertTrue($other_cached_responses_are_4xx);
// Not only assert the normalization, also assert deserialization of the
// response results in the expected object.
$unserialized = $this->serializer
->deserialize((string) $response
->getBody(), JsonApiDocumentTopLevel::class, 'api_json', [
'target_entity' => static::$entityTypeId,
'resource_type' => $this->container
->get('jsonapi.resource_type.repository')
->getByTypeName(static::$resourceTypeName),
]);
$this
->assertSame($unserialized
->uuid(), $this->entity
->uuid());
$get_headers = $response
->getHeaders();
// Verify that the GET and HEAD responses are the same. The only difference
// is that there's no body. For this reason the 'Transfer-Encoding' and
// 'Vary' headers are also added to the list of headers to ignore, as they
// may be added to GET requests, depending on web server configuration. They
// are usually 'Transfer-Encoding: chunked' and 'Vary: Accept-Encoding'.
$ignored_headers = [
'Date',
'Content-Length',
'X-Drupal-Cache',
'X-Drupal-Dynamic-Cache',
'Transfer-Encoding',
'Vary',
];
$header_cleaner = function ($headers) use ($ignored_headers) {
foreach ($headers as $header => $value) {
if (strpos($header, 'X-Drupal-Assertion-') === 0 || in_array($header, $ignored_headers)) {
unset($headers[$header]);
}
}
return $headers;
};
$get_headers = $header_cleaner($get_headers);
$head_headers = $header_cleaner($head_headers);
$this
->assertSame($get_headers, $head_headers);
// @todo Uncomment this in https://www.drupal.org/project/jsonapi/issues/2929932.
// @codingStandardsIgnoreStart
/*
// BC: serialization_update_8401().
// Only run this for fieldable entities. It doesn't make sense for config
// entities as config values always use the raw values (as per the config
// schema), returned directly from the ConfigEntityNormalizer, which
// doesn't deal with fields individually.
if ($this->entity instanceof FieldableEntityInterface) {
// Test the BC settings for timestamp values.
$this->config('serialization.settings')->set('bc_timestamp_normalizer_unix', TRUE)->save(TRUE);
// Rebuild the container so new config is reflected in the addition of the
// TimestampItemNormalizer.
$this->rebuildAll();
$response = $this->request('GET', $url, $request_options);
$this->assertResourceResponse(200, FALSE, $response, $this->getExpectedCacheTags(), $this->getExpectedCacheContexts(), static::$auth ? FALSE : 'MISS', 'MISS');
// This ensures the BC layer for bc_timestamp_normalizer_unix works as
// expected. This method should be using
// ::formatExpectedTimestampValue() to generate the timestamp value. This
// will take into account the above config setting.
$expected = $this->getExpectedNormalizedEntity();
// Config entities are not affected.
// @see \Drupal\serialization\Normalizer\ConfigEntityNormalizer::normalize()
static::recursiveKsort($expected);
$actual = Json::decode((string) $response->getBody());
static::recursiveKsort($actual);
$this->assertSame($expected, $actual);
// Reset the config value and rebuild.
$this->config('serialization.settings')->set('bc_timestamp_normalizer_unix', FALSE)->save(TRUE);
$this->rebuildAll();
}
*/
// @codingStandardsIgnoreEnd
// Feature: Sparse fieldsets.
$this
->doTestSparseFieldSets($url, $request_options);
// Feature: Included.
$this
->doTestIncluded($url, $request_options);
// DX: 404 when GETting non-existing entity.
$random_uuid = \Drupal::service('uuid')
->generate();
$url = Url::fromRoute(sprintf('jsonapi.%s.individual', static::$resourceTypeName), [
static::$entityTypeId => $random_uuid,
]);
$response = $this
->request('GET', $url, $request_options);
$message_url = clone $url;
$path = str_replace($random_uuid, '{' . static::$entityTypeId . '}', $message_url
->setAbsolute()
->setOptions([
'base_url' => '',
'query' => [],
])
->toString());
$message = 'The "' . static::$entityTypeId . '" parameter was not converted for the path "' . $path . '" (route name: "jsonapi.' . static::$resourceTypeName . '.individual")';
$this
->assertResourceErrorResponse(404, $message, $response);
// DX: when Accept request header is missing, still 404, but HTML response.
unset($request_options[RequestOptions::HEADERS]['Accept']);
$response = $this
->request('GET', $url, $request_options);
$this
->assertSame(404, $response
->getStatusCode());
$this
->assertSame([
'text/html; charset=UTF-8',
], $response
->getHeader('Content-Type'));
}