Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion src/Symfony/Bundle/Test/ApiTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,62 @@ protected static function createClient(array $kernelOptions = [], array $default
throw new \LogicException('You cannot create the client used in functional tests if the "framework.test" config is not set to true.');
}

$client->setDefaultOptions($defaultOptions);
$client->setDefaultOptions(self::withDefaultContentType($defaultOptions));

self::getHttpClient($client);
self::getClient($client->getKernelBrowser());

return $client;
}

/**
* Symfony's HttpClient adds "Content-Type: application/json" automatically when the "json" option is used.
* On a default API Platform project, "json" is not part of the configured formats, so such requests fail
* with 415 Unsupported Media Type. To keep tests working out of the box in that scenario, default the
* Content-Type to the first configured API Platform format.
*
* The default is only applied when "application/json" is NOT one of the configured mime types: when it is
* (e.g. a project that explicitly enables the "json" format, or any GraphQL endpoint that accepts
* "application/json" regardless of the API Platform formats), Symfony's implicit "application/json"
* header already produces a valid request, and overriding it would break per-request "json" usage.
*/
private static function withDefaultContentType(array $defaultOptions): array
{
$headers = $defaultOptions['headers'] ?? [];
foreach (array_keys($headers) as $name) {
if (\is_string($name) && 0 === strcasecmp($name, 'content-type')) {
return $defaultOptions;
}
}

$container = self::getContainer();
if (!$container->hasParameter('api_platform.formats')) {
return $defaultOptions;
}

$formats = $container->getParameter('api_platform.formats');
if (!\is_array($formats) || !$formats) {
return $defaultOptions;
}

foreach ($formats as $mimeTypes) {
if (\is_array($mimeTypes) && \in_array('application/json', $mimeTypes, true)) {
return $defaultOptions;
}
}

$firstFormat = reset($formats);
$mimeType = \is_array($firstFormat) ? ($firstFormat[0] ?? null) : null;
if (!\is_string($mimeType) || '' === $mimeType) {
return $defaultOptions;
}

$headers['content-type'] = $mimeType;
$defaultOptions['headers'] = $headers;

return $defaultOptions;
}

/**
* Finds the IRI of a resource item matching the resource class and the specified criteria.
*/
Expand Down
22 changes: 22 additions & 0 deletions tests/Symfony/Bundle/Test/ApiTestCaseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,28 @@ public function testMissingMethod(): void
$this->assertResponseStatusCodeSame(404);
}

public function testJsonOptionContentTypePreservedWhenApplicationJsonConfigured(): void
{
// The test fixtures configure "application/json" among the API Platform formats, so Symfony's
// implicit "Content-Type: application/json" header (set by the "json" option) must not be overridden.
// Overriding it would break callers that legitimately rely on application/json (e.g. /graphql).
$client = self::createClient();
$client->request('POST', '/something/that/does/not/exist/ever', ['json' => ['name' => 'Kevin']]);

$this->assertSame('application/json', $client->getKernelBrowser()->getRequest()->headers->get('Content-Type'));
}

public function testExplicitContentTypeIsPreserved(): void
{
$client = self::createClient();
$client->request('POST', '/something/that/does/not/exist/ever', [
'headers' => ['content-type' => 'application/json'],
'body' => '{"name":"Kevin"}',
]);

$this->assertSame('application/json', $client->getKernelBrowser()->getRequest()->headers->get('Content-Type'));
}

public function testDoNotRebootKernelOnCreateClient(): void
{
self::$alwaysBootKernel = false;
Expand Down
Loading