We'll learn to test AdonisJS Authentication with Bouncer for actions like deleting a post. We'll cover happy paths where authorization is granted and sad paths where authorization is denied and the action is forbidden.
Next, let's talk about authorization, which differs from authentication. Authentication deals with determining who you are, while authorization deals with determining what you can do.
So, in addition to testing things that require an authenticated user, we also want to test what that authenticated user is allowed to do within our application. For the most part, our PostsController is just a mock; however, at the bottom, there is a real destroy handler mapped to a model and route. This destroy method also performs authorization via Bouncer that ensures only administrators or the post owner can delete the post.
That is the authorization check we'll be testing here today. So, first, let's start with our happy paths, which will be:
It should allow a post owner to delete their post
It should allow an administrator to delete a post
Let's start with a post owner, and we can add this to our posts spec.
test("allow a post owner to delete their post", async ({ assert, client, route,}) => { // create a post with an owner const post = await PostFactory.with("user").create(); const response = await client .delete(route("posts.destroy", { id: post.id })) .withCsrfToken() .loginAs(post.user) // <- login as the owner .redirects(0); // confirm expected response & flash message response.assertStatus(302); response.assertFlashMessage("success", "Your post was deleted"); // confirm post was actually deleted const deletedPost = await Post.find(post.id); assert.isNull(deletedPost);});
Copied!
Next, we can test with an admin.
test("allow an admin to delete a post", async ({ assert, client, route }) => { // create a post with an owner const post = await PostFactory.with("user").create(); // create an admin (note: you can also use states like we did with our password reset) const admin = await UserFactory.merge({ roleId: Roles.ADMIN }).create(); const response = await client .delete(route("posts.destroy", { id: post.id })) .withCsrfToken() .loginAs(admin) // <- login as the admin .redirects(0); // confirm expected response & flash message response.assertStatus(302); response.assertFlashMessage("success", "Your post was deleted"); // confirm post was actually deleted const deletedPost = await Post.find(post.id); assert.isNull(deletedPost);});
Copied!
Fantastic! Now, we're left with our sad authorization path. For this, we'll want to test that it should prevent a non-owner or admin from deleting a post.
test("prevent a non owner or admin from deleting a post", async ({ assert, client, route,}) => { // create a post with an owner const post = await PostFactory.with("user").create(); // create another non-admin user const user = await UserFactory.create(); const response = await client .delete(route("posts.destroy", { id: post.id })) .withCsrfToken() .loginAs(user) // <- login as that other user .redirects(0); // confirm request was forbidden response.assertForbidden(); response.assertTextIncludes("Access denied"); // confirm post still exists const deletedPost = await Post.findOrFail(post.id); assert.equal(deletedPost.id, post.id);});