public function ResourceTestBase::testCollection in Drupal 9
Same name and namespace in other branches
- 8 core/modules/jsonapi/tests/src/Functional/ResourceTestBase.php \Drupal\Tests\jsonapi\Functional\ResourceTestBase::testCollection()
Tests GETting a collection of resources.
3 methods override ResourceTestBase::testCollection()
- FileUploadTest::testCollection in core/
modules/ jsonapi/ tests/ src/ Functional/ FileUploadTest.php - @requires module irrelevant_for_this_test
- ItemTest::testCollection in core/
modules/ jsonapi/ tests/ src/ Functional/ ItemTest.php - Tests GETting a collection of resources.
- MessageTest::testCollection in core/
modules/ jsonapi/ tests/ src/ Functional/ MessageTest.php - Tests GETting a collection of resources.
File
- core/
modules/ jsonapi/ tests/ src/ Functional/ ResourceTestBase.php, line 1067
Class
- ResourceTestBase
- Subclass this for every JSON:API resource type.
Namespace
Drupal\Tests\jsonapi\FunctionalCode
public function testCollection() {
$entity_collection = $this
->getData();
assert(count($entity_collection) > 1, 'A collection must have more that one entity in it.');
$collection_url = Url::fromRoute(sprintf('jsonapi.%s.collection', static::$resourceTypeName))
->setAbsolute(TRUE);
$request_options = [];
$request_options[RequestOptions::HEADERS]['Accept'] = 'application/vnd.api+json';
$request_options = NestedArray::mergeDeep($request_options, $this
->getAuthenticationRequestOptions());
// This asserts that collections will work without a sort, added by default
// below, without actually asserting the content of the response.
$expected_response = $this
->getExpectedCollectionResponse($entity_collection, $collection_url
->toString(), $request_options);
$expected_cacheability = $expected_response
->getCacheableMetadata();
$response = $this
->request('HEAD', $collection_url, $request_options);
// MISS or UNCACHEABLE depends on the collection data. It must not be HIT.
$dynamic_cache = $expected_cacheability
->getCacheMaxAge() === 0 ? 'UNCACHEABLE' : 'MISS';
$this
->assertResourceResponse(200, NULL, $response, $expected_cacheability
->getCacheTags(), $expected_cacheability
->getCacheContexts(), FALSE, $dynamic_cache);
// Different databases have different sort orders, so a sort is required so
// test expectations do not need to vary per database.
$default_sort = [
'sort' => 'drupal_internal__' . $this->entity
->getEntityType()
->getKey('id'),
];
$collection_url
->setOption('query', $default_sort);
// 200 for collections, even when all entities are inaccessible. Access is
// on a per-entity basis, which is handled by
// self::getExpectedCollectionResponse().
$expected_response = $this
->getExpectedCollectionResponse($entity_collection, $collection_url
->toString(), $request_options);
$expected_cacheability = $expected_response
->getCacheableMetadata();
$expected_document = $expected_response
->getResponseData();
$response = $this
->request('GET', $collection_url, $request_options);
$this
->assertResourceResponse(200, $expected_document, $response, $expected_cacheability
->getCacheTags(), $expected_cacheability
->getCacheContexts(), FALSE, $dynamic_cache);
$this
->setUpAuthorization('GET');
// 200 for well-formed HEAD request.
$expected_response = $this
->getExpectedCollectionResponse($entity_collection, $collection_url
->toString(), $request_options);
$expected_cacheability = $expected_response
->getCacheableMetadata();
$response = $this
->request('HEAD', $collection_url, $request_options);
$this
->assertResourceResponse(200, NULL, $response, $expected_cacheability
->getCacheTags(), $expected_cacheability
->getCacheContexts(), FALSE, $dynamic_cache);
// 200 for well-formed GET request.
$expected_response = $this
->getExpectedCollectionResponse($entity_collection, $collection_url
->toString(), $request_options);
$expected_cacheability = $expected_response
->getCacheableMetadata();
$expected_document = $expected_response
->getResponseData();
$response = $this
->request('GET', $collection_url, $request_options);
// Dynamic Page Cache HIT unless the HEAD request was UNCACHEABLE.
$dynamic_cache = $dynamic_cache === 'UNCACHEABLE' ? 'UNCACHEABLE' : 'HIT';
$this
->assertResourceResponse(200, $expected_document, $response, $expected_cacheability
->getCacheTags(), $expected_cacheability
->getCacheContexts(), FALSE, $dynamic_cache);
if ($this->entity instanceof FieldableEntityInterface) {
// 403 for filtering on an unauthorized field on the base resource type.
$unauthorized_filter_url = clone $collection_url;
$unauthorized_filter_url
->setOption('query', [
'filter' => [
'related_author_id' => [
'operator' => '<>',
'path' => 'field_jsonapi_test_entity_ref.status',
'value' => 'does_not@matter.com',
],
],
]);
$response = $this
->request('GET', $unauthorized_filter_url, $request_options);
$expected_error_message = "The current user is not authorized to filter by the `field_jsonapi_test_entity_ref` field, given in the path `field_jsonapi_test_entity_ref`. The 'field_jsonapi_test_entity_ref view access' permission is required.";
$expected_cache_tags = [
'4xx-response',
'http_response',
];
$expected_cache_contexts = [
'url.query_args:filter',
'url.query_args:sort',
'url.site',
'user.permissions',
];
$this
->assertResourceErrorResponse(403, $expected_error_message, $unauthorized_filter_url, $response, FALSE, $expected_cache_tags, $expected_cache_contexts, FALSE, 'MISS');
$this
->grantPermissionsToTestedRole([
'field_jsonapi_test_entity_ref view access',
]);
// 403 for filtering on an unauthorized field on a related resource type.
$response = $this
->request('GET', $unauthorized_filter_url, $request_options);
$expected_error_message = "The current user is not authorized to filter by the `status` field, given in the path `field_jsonapi_test_entity_ref.entity:user.status`.";
$this
->assertResourceErrorResponse(403, $expected_error_message, $unauthorized_filter_url, $response, FALSE, $expected_cache_tags, $expected_cache_contexts, FALSE, 'MISS');
}
// Remove an entity from the collection, then filter it out.
$filtered_entity_collection = $entity_collection;
$removed = array_shift($filtered_entity_collection);
$filtered_collection_url = clone $collection_url;
$entity_collection_filter = [
'filter' => [
'ids' => [
'condition' => [
'operator' => '<>',
'path' => 'id',
'value' => $removed
->uuid(),
],
],
],
];
$filtered_collection_url
->setOption('query', $entity_collection_filter + $default_sort);
$expected_response = $this
->getExpectedCollectionResponse($filtered_entity_collection, $filtered_collection_url
->toString(), $request_options, NULL, TRUE);
$expected_cacheability = $expected_response
->getCacheableMetadata();
$expected_document = $expected_response
->getResponseData();
$response = $this
->request('GET', $filtered_collection_url, $request_options);
// MISS or UNCACHEABLE depends on the collection data. It must not be HIT.
$dynamic_cache = $expected_cacheability
->getCacheMaxAge() === 0 || !empty(array_intersect([
'user',
'session',
], $expected_cacheability
->getCacheContexts())) ? 'UNCACHEABLE' : 'MISS';
$this
->assertResourceResponse(200, $expected_document, $response, $expected_cacheability
->getCacheTags(), $expected_cacheability
->getCacheContexts(), FALSE, $dynamic_cache);
// Filtered collection with includes.
$relationship_field_names = array_reduce($filtered_entity_collection, function ($relationship_field_names, $entity) {
return array_unique(array_merge($relationship_field_names, $this
->getRelationshipFieldNames($entity)));
}, []);
$include = [
'include' => implode(',', $relationship_field_names),
];
$filtered_collection_include_url = clone $collection_url;
$filtered_collection_include_url
->setOption('query', $entity_collection_filter + $include + $default_sort);
$expected_response = $this
->getExpectedCollectionResponse($filtered_entity_collection, $filtered_collection_include_url
->toString(), $request_options, $relationship_field_names, TRUE);
$expected_cacheability = $expected_response
->getCacheableMetadata();
$expected_cacheability
->setCacheTags(array_values(array_diff($expected_cacheability
->getCacheTags(), [
'4xx-response',
])));
$expected_document = $expected_response
->getResponseData();
$response = $this
->request('GET', $filtered_collection_include_url, $request_options);
// MISS or UNCACHEABLE depends on the included data. It must not be HIT.
$dynamic_cache = $expected_cacheability
->getCacheMaxAge() === 0 || !empty(array_intersect([
'user',
'session',
], $expected_cacheability
->getCacheContexts())) ? 'UNCACHEABLE' : 'MISS';
$this
->assertResourceResponse(200, $expected_document, $response, $expected_cacheability
->getCacheTags(), $expected_cacheability
->getCacheContexts(), FALSE, $dynamic_cache);
// If the response should vary by a user's authorizations, grant permissions
// for the included resources and execute another request.
$permission_related_cache_contexts = [
'user',
'user.permissions',
'user.roles',
];
if (!empty($relationship_field_names) && !empty(array_intersect($expected_cacheability
->getCacheContexts(), $permission_related_cache_contexts))) {
$applicable_permissions = array_intersect_key(static::getIncludePermissions(), array_flip($relationship_field_names));
$flattened_permissions = array_unique(array_reduce($applicable_permissions, 'array_merge', []));
$this
->grantPermissionsToTestedRole($flattened_permissions);
$expected_response = $this
->getExpectedCollectionResponse($filtered_entity_collection, $filtered_collection_include_url
->toString(), $request_options, $relationship_field_names, TRUE);
$expected_cacheability = $expected_response
->getCacheableMetadata();
$expected_document = $expected_response
->getResponseData();
$response = $this
->request('GET', $filtered_collection_include_url, $request_options);
$requires_include_only_permissions = !empty($flattened_permissions);
$uncacheable = $expected_cacheability
->getCacheMaxAge() === 0 || !empty(array_intersect([
'user',
'session',
], $expected_cacheability
->getCacheContexts()));
$dynamic_cache = !$uncacheable ? $requires_include_only_permissions ? 'MISS' : 'HIT' : 'UNCACHEABLE';
$this
->assertResourceResponse(200, $expected_document, $response, $expected_cacheability
->getCacheTags(), $expected_cacheability
->getCacheContexts(), FALSE, $dynamic_cache);
}
// Sorted collection with includes.
$sorted_entity_collection = $entity_collection;
uasort($sorted_entity_collection, function (EntityInterface $a, EntityInterface $b) {
// Sort by ID in reverse order.
return strcmp($b
->uuid(), $a
->uuid());
});
$sorted_collection_include_url = clone $collection_url;
$sorted_collection_include_url
->setOption('query', $include + [
'sort' => "-id",
]);
$expected_response = $this
->getExpectedCollectionResponse($sorted_entity_collection, $sorted_collection_include_url
->toString(), $request_options, $relationship_field_names);
$expected_cacheability = $expected_response
->getCacheableMetadata();
$expected_cacheability
->setCacheTags(array_values(array_diff($expected_cacheability
->getCacheTags(), [
'4xx-response',
])));
$expected_document = $expected_response
->getResponseData();
$response = $this
->request('GET', $sorted_collection_include_url, $request_options);
// MISS or UNCACHEABLE depends on the included data. It must not be HIT.
$dynamic_cache = $expected_cacheability
->getCacheMaxAge() === 0 ? 'UNCACHEABLE' : 'MISS';
$this
->assertResourceResponse(200, $expected_document, $response, $expected_cacheability
->getCacheTags(), $expected_cacheability
->getCacheContexts(), FALSE, $dynamic_cache);
}