Validation named the fields and stored nothing. A person on another team must not open this invoice. The test signs them in, requests the invoice, and expects 403. A second test signs in someone on the owning team and expects 200. Those are two behaviours.
Test-driven Laravel · Lesson 2
Authorisation
Video
01 Assert the status the policy produces
Build the invoice for one team. Act as a user whose current team is a different one.
it('hides an invoice from another team', function () {
$invoice = Invoice::factory()->create();
$other = User::factory()->create();
$this->actingAs($other)
->get('/invoices/'.$invoice->id)
->assertForbidden();
});assertForbidden() is the 403 from the policy. assertNotFound() is a different choice: it hides the fact that the invoice exists. Pick the one the route actually returns, and assert that.
The policies article is the decision between a policy and a gate, with the team check written out.
Policies vs gates: choosing the right authorisation tool.
Do not only call $user->can('view', $invoice) and stop. That misses a route that forgot to authorise. The HTTP test is the one that would catch it.
02 The owning team is allowed in
The forbidden test is only half the policy. A member of the invoice’s team should get the page. If both tests return 403, the policy is denying everyone. If both return 200, the team check is missing.
it('shows an invoice to its own team', function () {
$invoice = Invoice::factory()->create();
$this->actingAs($invoice->team->owner)
->get('/invoices/'.$invoice->id)
->assertOk();
});Use the owner the factory already attached. Creating a second user and setting current_team_id by hand repeats the arrange the factory was for. assertOk() is enough here. The forbidden test already proved the other team cannot see the same URL.
Update and delete are separate abilities, so they are separate tests. A policy that allows view and denies update will not be caught by a GET. Post to the update route as the other team and assertForbidden() again. The owning team’s update test asserts the redirect and the changed column. One status code cannot stand in for both.
Recap
You can prove another team is forbidden and the owning team is allowed.
assertForbidden()is the policy's 403.assertNotFound()is a different product choice.- A test that only calls
can()misses a route that forgot to authorise. - If both tests are 403, the policy denies everyone. If both are 200, the team check is missing.
Check yourself · 3 questions
Question 1 of 3Only the other-team test exists, and it gets 403. The owner also gets 403. What do you not know yet?