Reputation: 14271
Good night, I'm having a little trouble here. I'm doing TDD for the first time so I don't know the root of this behavior. This post seems long but mainly because I've copied the mayority of the related code.
Btw, I'm using Laravel as a backend to make an app.
I'm testing that a User
with the role admin
can create 'Facility' objects but the rest of the users can't. Something really basic.
When testing the endpoint it let users with the role user
(the regular one) to create the object. But when I tried to test it using Postman it worked as it should be: blocking the request.
To manage acl I'm using Laratrust package (it works well, already test it).
routes/api.php // this already has the middlewares: auth & api
Route::post('api/v1/facilities', 'FacilityController@store');
App\Htpp\Controllers\FacilityController.php
use App\Http\Requests\Facility\CreateFacilityRequest;
use App\Http\Resources\FacilityResource;
use App\Repositories\FacilityRepository;
use App\Services\ImageService;
use Illuminate\Http\Response;
// some code
/**
* Create a new Facility
*
* @param CreateFacilityRequest $request
* @return \Illuminate\Http\JsonResponse
*/
public function store(CreateFacilityRequest $request)
{
$data = $request->only(['name']);
$file = $request->file('image');
$data['image'] = ImageService::storeAs('images/facilities', $file, 'friendly', $data['name']);
$facility = $this->facilityRepository->create($data);
return response()->json([
"message" => "The facility has been added.",
"data" => new FacilityResource($facility)
], Response::HTTP_CREATED);
}
App\Http\Requests\Facility\CreateFacilityRequest.php
class CreateFacilityRequest extends FormRequest {
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->user()->can('create-facilities');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => 'required|string|unique:facilities,name',
'image' => 'required|image'
];
}
}
Finally, this is my test:
test\Feature\FacilityTest.php
/**
* @test
* Test for: a Facility can be registered only by an admin.
*/
public function a_facility_can_be_registered_only_by_an_admin()
{
/** Given a correct information for a facility */
\Storage::fake('public');
$data = ["name" => "Facility A", 'image' => UploadedFile::fake()->image('facility-a.jpg') ];
/** When the request is made by an admin */
$admin = $this->createUser([], 'admin');
$response = $this->apiAs($admin, 'POST','/api/v1/facilities', $data);
/** Then the facility should be registered */
$response->assertStatus(Response::HTTP_CREATED); // 201
/** When the request is made by somebody else */
$data = ["name" => "Facility B", 'image' =>UploadedFile::fake()->image('facility-b.jpg') ];
$regular_user = $this->createUser([], 'user');
$response = $this->apiAs($regular_user, 'POST','/api/v1/facilities', $data);
/** Then the request should be declined */
$this->assertTrue($regular_user->hasRole('user'));
$this->assertFalse($regular_user->can('create-facilities'));
$response->assertStatus(Response::HTTP_FORBIDDEN); // 403
\Storage::disk('facilities')->assertMissing('facility-b.jpg');
}
All the assertions are confirmed except this one:
$response->assertStatus(Response::HTTP_FORBIDDEN); // 403
The output is this:
Expected status code 403 but received 201.
Failed asserting that false is true.
Time: 447 ms, Memory: 20.00MB
FAILURES! Tests: 1, Assertions: 4, Failures: 1.
Process finished with exit code 1
When I dd($response->json())
it return the regular json of a successfull call. But in Postman it returns the correct one:
{
"message" : "Unauthorized" // with status code 403
}
protected function apiAs()
protected function apiAs($user, $method, $uri, array $data = [], array $headers = []) : TestResponse
{
$headers = array_merge([
'Authorization' => 'Bearer '.\JWTAuth::fromUser($user),
'Accept' => 'application/json'
], $headers);
return $this->api($method, $uri, $data, $headers);
}
protected function api($method, $uri, array $data = [], array $headers = [])
{
return $this->json($method, $uri, $data, $headers);
}
Upvotes: 1
Views: 811
Reputation: 2334
This is because you're using jwt-auth whose apiAs()
method caches the user's identifier associated to the response on this line:
'Authorization' => 'Bearer '.\JWTAuth::fromUser($user),
This causes your $response
variable, the second time you're grabbing the API as a regular user, to return the first cached data for the admin.
I would separate these assertions into separate tests as well as your naming is currently off with a_facility_can_be_registered_only_by_an_admin()
. a_facility_cannot_be_registered_by_a_user_that_is_not_an_admin()
may be an applicable name and get around this caching.
Upvotes: 2