protected function RequestTest::requestOpenApiJson in OpenAPI 8
Makes OpenAPI request and checks the response.
Parameters
string $api_module: The API module being tested. Either 'rest' or 'jsonapi'.
array $options: The query options for generating the OpenAPI output.
1 call to RequestTest::requestOpenApiJson()
- RequestTest::testRequests in tests/
src/ Functional/ RequestTest.php - Tests OpenAPI requests.
File
- tests/
src/ Functional/ RequestTest.php, line 211
Class
- RequestTest
- Tests requests OpenAPI routes.
Namespace
Drupal\Tests\openapi\FunctionalCode
protected function requestOpenApiJson($api_module, array $options = []) {
$get_options = [
'query' => [
'_format' => 'json',
'options' => $options,
],
];
$response = $this
->drupalGet("openapi/{$api_module}", $get_options);
$decoded_response = json_decode($response, TRUE);
$this
->assertSession()
->statusCodeEquals(200);
// Test the the first tier schema has the expected keys.
$structure_keys = array_keys(static::EXPECTED_STRUCTURE);
$response_keys = array_keys($decoded_response);
$missing = array_diff($structure_keys, $response_keys);
$this
->assertTrue(empty($missing), 'Schema missing expected key(s): ' . implode(', ', $missing));
// Test that the required info block keys exist in the response.
$structure_info_keys = array_keys(static::EXPECTED_STRUCTURE['info']);
$response_keys = array_keys($decoded_response['info']);
$missing_info = array_diff($structure_info_keys, $response_keys);
$this
->assertTrue(empty($missing_info), 'Schema info missing expected key(s): ' . implode(', ', $missing_info));
// Test that schemes is not empty.
$this
->assertTrue(!empty($decoded_response['schemes']), 'Schema for ' . $api_module . ' should define at least one scheme.');
// Test basePath and host.
$port = parse_url($this->baseUrl, PHP_URL_PORT);
$host = parse_url($this->baseUrl, PHP_URL_HOST) . ($port ? ':' . $port : '');
$this
->assertEquals($host, $decoded_response['host'], 'Schema has invalid host.');
$basePath = $this
->getBasePath();
$response_basePath = $decoded_response['basePath'];
$this
->assertEquals($basePath, substr($response_basePath, 0, strlen($basePath)), 'Schema has invalid basePath.');
$routeBase = $api_module === 'jsonapi' ? 'jsonapi' : '';
$response_routeBase = substr($response_basePath, strlen($basePath));
// Verify that with the subdirectory removed, that the basePath is correct.
$this
->assertEquals($routeBase, ltrim($response_routeBase, '/'), 'Route base path is invalid.');
// Verify that root consumes and produces exists and is not empty.
foreach ([
'consumes',
'produces',
] as $key) {
$this
->assertArrayHasKey($key, $decoded_response, "Schema does not contains a root {$key}");
$this
->assertNotEmpty($decoded_response[$key], "Schema has empty root {$key}");
if (!isset($decoded_response[$key])) {
if ($api_module == 'jsonapi') {
$this
->assertEquals([
'application/vnd.api+json',
], $decoded_response[$key], "{$api_module} root {$key} should only contain application/vnd.api+json");
}
elseif ($api_module == 'rest') {
$rest_mimetypes = [
'application/json',
];
if (isset($options['entity_type_id']) && $options['entity_type_id'] === 'openapi_test_entity') {
$rest_mimetypes[] = 'application/hal+json';
}
$this
->assertEquals($rest_mimetypes, $decoded_response[$key], "{$api_module} root {$key} should only contain " . implode(' and ', $rest_mimetypes));
}
}
}
/*
* Tags for rest schema define 'x-entity-type' to reference the entity type
* associated with the entity. This value should exist in the definitions.
*
* NOTE: Currently not all entity types are provided as definitions. As a
* result, the below test is subject to failure, and has been disabled.
*
* @TODO: #2940397 - Convert x-entity-type to x-definition.
* @TODO: #2940407 - Provide all entity types as definitions.
*/
$tags = $decoded_response['tags'];
if (FALSE) {
$definitions = $decoded_response['definitions'];
foreach ($tags as $tag) {
if (isset($tag['x-entity-type'])) {
$type_id = $tag['x-entity-type'];
$this
->assertTrue(isset($definitions[$type_id]), 'The \'x-entity-type\' ' . $type_id . ' is invalid for ' . $tag['name'] . '.');
}
}
}
// Validate that all security definitions are valid, and have a provider.
$security_definitions = $decoded_response['securityDefinitions'];
$auth_providers = $this->container
->get('authentication_collector')
->getSortedProviders();
$supported_security_types = [
'basic',
'apiKey',
'cookie',
'oauth',
'oauth2',
];
foreach ($security_definitions as $definition_id => $definition) {
if ($definition_id !== 'csrf_token') {
// CSRF Token will never have an auth collector, all others shoud.
$this
->assertTrue(array_key_exists($definition_id, $auth_providers), 'Security definition ' . $definition_id . ' not an auth collector.');
}
$this
->assertTrue(in_array($definition['type'], $supported_security_types), 'Security definition schema ' . $definition_id . ' has invalid type ' . $definition['type']);
}
// Test paths for valid tags, schema, security, and definitions.
$paths =& $decoded_response['paths'];
$tag_names = array_column($tags, 'name');
$all_method_tags = [];
foreach ($paths as $path => &$methods) {
foreach ($methods as $method => &$method_schema) {
// Ensure all tags are defined.
$missing_tags = array_diff($method_schema['tags'], $tag_names);
$all_method_tags = array_merge($all_method_tags, $method_schema['tags']);
$this
->assertTrue(empty($missing_tags), 'Method ' . $method . ' for ' . $path . ' has invalid tag(s): ' . implode(', ', $missing_tags));
// Ensure all request schemes are defined.
if (isset($method_schema['schemes'])) {
$missing_schemas = array_diff($method_schema['schemes'], $decoded_response['schemes']);
$this
->assertTrue(empty($missing_schemas), 'Method ' . $method . ' for ' . $path . ' has invalid scheme(s): ' . implode(', ', $missing_schemas));
}
$response_security_types = array_keys($decoded_response['securityDefinitions']);
if (isset($method_schema['security'])) {
foreach ($method_schema['security'] as $security_definitions) {
$security_types = array_keys($security_definitions);
$missing_security_types = array_diff($security_types, $response_security_types);
$this
->assertTrue(empty($missing_security_types), 'Method ' . $method . ' for ' . $path . ' has invalid security type(s): ' . implode(', ', $missing_security_types) . ' + ' . implode(', ', $security_types) . ' + ' . implode(', ', $response_security_types));
}
}
foreach ([
'consumes',
'produces',
] as $key) {
if (isset($method_schema[$key]) && !empty($method_schema[$key])) {
// Filter out mimetypes that exist in parent.
$method_extra_mimetypes = array_diff($method_schema[$key], $decoded_response[$key]);
$this
->assertEmpty($method_extra_mimetypes, 'Method ' . $method . ' for ' . $path . ' has invalid mime type(s): ' . implode(', ', $method_extra_mimetypes));
if ($api_module == 'rest') {
$rest_mimetypes = [
'application/json',
];
if (isset($options['entity_type_id']) && $options['entity_type_id'] === 'openapi_test_entity') {
$rest_mimetypes[] = 'application/hal+json';
}
$this
->assertEquals($rest_mimetypes, $method_schema[$key], 'Entity type ' . $options['entity_type_id'] . ' should only have REST mimetype(s): ' . implode(', ', $rest_mimetypes));
}
}
}
// Remove all tested properties from method schema.
unset($method_schema['tags']);
unset($method_schema['schemes']);
unset($method_schema['security']);
}
}
$all_method_tags = array_unique($all_method_tags);
asort($all_method_tags);
asort($tag_names);
$this
->assertEquals(array_values($all_method_tags), array_values($tag_names), "Method tags equal tag names");
// Strip response down to only untested properties.
$root_keys = [
'definitions',
'paths',
];
foreach (array_diff(array_keys($decoded_response), $root_keys) as $remove) {
unset($decoded_response[$remove]);
}
// Build file name.
$file_name = __DIR__ . "/../../expectations/{$api_module}";
if ($options) {
$file_name .= "." . implode('.', $options);
}
$file_name .= '.json';
if (static::GENERATE_EXPECTATION_FILES) {
$this
->saveExpectationFile($file_name, $decoded_response);
// Response assertion is not performed when generating expectation
// files.
return;
}
// Load expected value and test remaining schema.
$expected = json_decode(file_get_contents($file_name), TRUE);
$this
->nestedKsort($expected);
$this
->nestedKsort($decoded_response);
// @todo Update the expectations to include path alias stuff, and then
// delete these unset() calls.
unset($decoded_response['definitions']['path_alias--path_alias']);
unset($decoded_response['paths']['/path_alias/path_alias']);
unset($decoded_response['paths']['/path_alias/path_alias/{entity}']);
$this
->assertEquals($expected, $decoded_response, "The response does not match expected file: {$file_name}");
}