When running json api tests content-type header is ignored?
I'm using this library to create my API, and I added a 'login' custom route - this route isn't a resource one so I added a separate controller for it
JsonApi::register('v1')->routes(static function (Api $api) {
/**
* Custom route that uses SessionController
*
* Used for 'log in' purposes (bearer token generation), and
* 'log out' purposes (revoking bearer token).
*/
$api->post('login', [SessionController::class, 'login'])
->name('login')
->middleware('throttle:10,1');
// Other routes are registered here.
});
I have a login() method in my SessionController (which extends the JsonApiController). When I test it in postman, everything works.
I wrote a test for it
<?php
namespace Tests\Feature\Api;
use Carbon\Carbon;
use Laravel\Passport\Passport;
use Tests\UsersTestCase;
use App\User;
class LoginTest extends UsersTestCase
{
/**
* @var string
*/
protected $loginRoute = 'api/v1/login';
/**
* @var array
*/
protected $headers = [
'Accept' => 'application/vnd.api+json',
'Content-Type' => 'application/vnd.api+json'
];
/**
* @return void
*/
public function setUp(): void
{
parent::setUp();
$this->artisan('passport:install');
}
public function testApprovedUserCanLogIn()
{
// Set up user.
$password = 'asd81ikj3$adfg2.';
$user = factory(User::class)->create([
'approved_at' => Carbon::now()->format('Y-m-d H:i:s'),
'password' => bcrypt($password),
]);
$user->assignRole('admin');
$data = [
'email' => $user->email,
'password' => $password,
'remember_me' => true,
];
Passport::actingAs($user);
$response = $this
->withHeaders($this->headers)
->postJson($this->loginRoute, $data, $this->headers);
$response
->assertStatus(200)
->assertJsonStructure([
'type',
'attributes' => [
'access_token',
'token_type',
'expires_at',
]
]);
}
}
And when I run it, I get
{#2411
+"errors": array:1 [
0 => {#2539
+"status": "415"
+"title": "Unsupported Media Type"
+"detail": "The request entity has a media type which the server or resource does not support."
}
]
}
So I dug a bit deeper into Laravel's postJson method and it turns out that there are headers sent in this way:
/**
* Call the given URI with a JSON request.
*
* @param string $method
* @param string $uri
* @param array $data
* @param array $headers
* @return \Illuminate\Foundation\Testing\TestResponse
*/
public function json($method, $uri, array $data = [], array $headers = [])
{
$files = $this->extractFilesFromDataArray($data);
$content = json_encode($data);
$headers = array_merge([
'CONTENT_LENGTH' => mb_strlen($content, '8bit'),
'CONTENT_TYPE' => 'application/json',
'Accept' => 'application/json',
], $headers);
error_log(print_r($headers, true));
return $this->call(
$method, $uri, [], [], $files, $this->transformHeadersToServerVars($headers), $content
);
}
So it looks like my Content-Type header is ignored completely in favor of CONTENT_TYPE. When I tried setting CONTENT_TYPE in my test I got an error
testing.ERROR: Method Illuminate\Auth\RequestGuard::attempt does not exist. {"userId":1,"exception":"[object] (BadMethodCallException(code: 0): Method Illuminate\Auth\RequestGuard::attempt does not exist. at /Users/infinum-denis/my-test-api/vendor/laravel/framework/src/Illuminate/Support/Traits/Macroable.php:103) [stacktrace]
So I'm a bit lost here.
Are the tests forcing application/json content type? How can I overwrite it? Because the error from the test happens only if I don't specify the application/vnd.api+json content type during my API calls (even in postman).
Please or to participate in this conversation.