From b505b8578a577709ea2fe5b5611cc566e9e08eb3 Mon Sep 17 00:00:00 2001 From: Dominik Prabucki Date: Wed, 21 Aug 2024 12:26:13 +0200 Subject: [PATCH] Add admin roles Add IsAdminMiddleware Add super admin and admin to the seeder Add test dashboard for admin and user Update tests that check role permission Update routes --- .../Controllers/AdminDashboardController.php | 16 +++ .../AuthenticateSessionController.php | 6 +- app/Http/Controllers/ContestController.php | 5 + .../Controllers/RegisterUserController.php | 2 - app/Http/Middleware/IsAdminMiddleware.php | 20 ++++ app/Models/User.php | 14 ++- app/Policies/QuizPolicy.php | 4 + app/Role.php | 12 ++ database/factories/UserFactory.php | 13 ++- ...4_08_21_072609_add_role_to_users_table.php | 24 ++++ database/seeders/DatabaseSeeder.php | 3 +- resources/js/Pages/Auth/Admin/Dashboard.vue | 13 +++ resources/js/Pages/Dashboard.vue | 11 ++ resources/js/components/Common/Searchbar.vue | 1 + routes/web.php | 10 +- tests/Feature/AnswerTest.php | 109 +++++++++++++----- tests/Feature/AuthenticateSessionTest.php | 2 +- tests/Feature/QuestionTest.php | 85 ++++++++++---- tests/Feature/QuizTest.php | 87 ++++++++++---- tests/Feature/ResetPasswordTest.php | 2 +- 20 files changed, 351 insertions(+), 88 deletions(-) create mode 100644 app/Http/Controllers/AdminDashboardController.php create mode 100644 app/Http/Middleware/IsAdminMiddleware.php create mode 100644 app/Role.php create mode 100644 database/migrations/2024_08_21_072609_add_role_to_users_table.php create mode 100644 resources/js/Pages/Auth/Admin/Dashboard.vue create mode 100644 resources/js/Pages/Dashboard.vue diff --git a/app/Http/Controllers/AdminDashboardController.php b/app/Http/Controllers/AdminDashboardController.php new file mode 100644 index 00000000..6ebdf585 --- /dev/null +++ b/app/Http/Controllers/AdminDashboardController.php @@ -0,0 +1,16 @@ +attempt($credentials)) { $request->session()->regenerate(); - return Redirect::route("home")->with("success"); + if (auth()->user()->isAdmin()) { + return Redirect::route("admin.dashboard")->with("success", "Welcome to the Admin Dashboard"); + } + + return Redirect::route("dashboard")->with("success"); } throw ValidationException::withMessages([ diff --git a/app/Http/Controllers/ContestController.php b/app/Http/Controllers/ContestController.php index 50793353..df030400 100644 --- a/app/Http/Controllers/ContestController.php +++ b/app/Http/Controllers/ContestController.php @@ -17,4 +17,9 @@ public function index(): Response return Inertia::render("Home", ["schools" => SchoolResource::collection($schools)]); } + + public function create(): Response + { + return Inertia::render("Dashboard"); + } } diff --git a/app/Http/Controllers/RegisterUserController.php b/app/Http/Controllers/RegisterUserController.php index 7c3dd6be..b8dff544 100644 --- a/app/Http/Controllers/RegisterUserController.php +++ b/app/Http/Controllers/RegisterUserController.php @@ -8,7 +8,6 @@ use App\Models\User; use Illuminate\Auth\Events\Registered; use Illuminate\Http\RedirectResponse; -use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Redirect; @@ -23,7 +22,6 @@ public function store(RegisterUserRequest $request): RedirectResponse $user->password = Hash::make($request->password); $user->save(); event(new Registered($user)); - Auth::login($user); } return Redirect::route("home"); diff --git a/app/Http/Middleware/IsAdminMiddleware.php b/app/Http/Middleware/IsAdminMiddleware.php new file mode 100644 index 00000000..5fbc2b29 --- /dev/null +++ b/app/Http/Middleware/IsAdminMiddleware.php @@ -0,0 +1,20 @@ +user()->role === Role::USER, Response::HTTP_FORBIDDEN); + + return $next($request); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 37b579a5..e5a9894e 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -4,6 +4,7 @@ namespace App\Models; +use App\Role; use Carbon\Carbon; use Illuminate\Contracts\Auth\CanResetPassword; use Illuminate\Contracts\Auth\MustVerifyEmail; @@ -20,9 +21,9 @@ * @property string $password * @property Carbon $email_verified_at * @property int $school_id + * @property Role $role * @property Carbon $created_at * @property Carbon $updated_at - * * @property School $school */ class User extends Authenticatable implements MustVerifyEmail, CanResetPassword @@ -45,11 +46,22 @@ public function school(): BelongsTo return $this->belongsTo(School::class); } + public function isAdmin(): bool + { + return $this->role === Role::ADMIN || $this->role === Role::SUPER_ADMIN; + } + + public function isSuperAdmin(): bool + { + return $this->role === Role::SUPER_ADMIN; + } + protected function casts(): array { return [ "email_verified_at" => "datetime", "password" => "hashed", + "role" => Role::class, ]; } } diff --git a/app/Policies/QuizPolicy.php b/app/Policies/QuizPolicy.php index 4b110bf1..e4d5aea7 100644 --- a/app/Policies/QuizPolicy.php +++ b/app/Policies/QuizPolicy.php @@ -16,6 +16,10 @@ public function update(User $user, Quiz $quiz): bool public function delete(User $user, Quiz $quiz): bool { + if ($user->isSuperAdmin()) { + return true; + } + return !$quiz->isLocked; } diff --git a/app/Role.php b/app/Role.php new file mode 100644 index 00000000..0d11f581 --- /dev/null +++ b/app/Role.php @@ -0,0 +1,12 @@ + Hash::make("password"), "remember_token" => Str::random(10), "school_id" => School::factory(), + "role" => Role::USER->value, ]; } - public function unverified(): static + public function admin(): static { return $this->state(fn(array $attributes): array => [ - "email_verified_at" => null, + "role" => Role::ADMIN->value, + ]); + } + + public function superAdmin(): static + { + return $this->state(fn(array $attributes): array => [ + "role" => Role::SUPER_ADMIN->value, ]); } } diff --git a/database/migrations/2024_08_21_072609_add_role_to_users_table.php b/database/migrations/2024_08_21_072609_add_role_to_users_table.php new file mode 100644 index 00000000..62c89df9 --- /dev/null +++ b/database/migrations/2024_08_21_072609_add_role_to_users_table.php @@ -0,0 +1,24 @@ +unsignedTinyInteger("role")->default(Role::USER->value); + }); + } + + public function down(): void + { + Schema::table("users", function (Blueprint $table): void { + $table->dropColumn("role"); + }); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 16466def..0c75990e 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -14,7 +14,8 @@ class DatabaseSeeder extends Seeder { public function run(): void { - User::factory()->create(); + User::factory()->superAdmin()->create(); + User::factory()->admin()->create(); Quiz::factory()->create(); Answer::factory()->create(); AnswerRecord::factory()->create(); diff --git a/resources/js/Pages/Auth/Admin/Dashboard.vue b/resources/js/Pages/Auth/Admin/Dashboard.vue new file mode 100644 index 00000000..a4002cca --- /dev/null +++ b/resources/js/Pages/Auth/Admin/Dashboard.vue @@ -0,0 +1,13 @@ + + + + + diff --git a/resources/js/Pages/Dashboard.vue b/resources/js/Pages/Dashboard.vue new file mode 100644 index 00000000..f5bbbce9 --- /dev/null +++ b/resources/js/Pages/Dashboard.vue @@ -0,0 +1,11 @@ + + + + + diff --git a/resources/js/components/Common/Searchbar.vue b/resources/js/components/Common/Searchbar.vue index 16fd09a6..b2ba147f 100644 --- a/resources/js/components/Common/Searchbar.vue +++ b/resources/js/components/Common/Searchbar.vue @@ -30,6 +30,7 @@ const onOptionClick = (school:School)=>{ name("password.reset"); }); +Route::middleware("auth")->group(function (): void { + Route::get("/dashboard", [ContestController::class, "create"])->name("dashboard"); +}); + Route::post("/auth/password/reset", [PasswordResetLinkController::class, "resetStore"])->name("password.update"); -Route::group(["prefix" => "admin"], function (): void { +Route::group(["prefix" => "admin", "middleware" => ["auth", IsAdminMiddleware::class]], function (): void { Route::get("/quizzes", [QuizController::class, "index"])->name("admin.quizzes.index"); Route::post("/quizzes", [QuizController::class, "store"])->name("admin.quizzes.store"); Route::get("/quizzes/{quiz}", [QuizController::class, "show"])->name("admin.quizzes.show"); @@ -55,6 +61,8 @@ Route::post("/answers/{answer}/clone/{question}", [QuestionAnswerController::class, "clone"])->can("clone,answer,question")->name("admin.answers.clone"); Route::post("/answers/{answer}/correct", [QuestionAnswerController::class, "markAsCorrect"])->can("update,answer")->name("admin.answers.correct"); Route::post("/answers/{answer}/invalid", [QuestionAnswerController::class, "markAsInvalid"])->can("update,answer")->name("admin.answers.invalid"); + + Route::get("/dashboard", [AdminDashboardController::class, "index"])->name("admin.dashboard"); }); Route::post("/quizzes/{quiz}/start", [QuizController::class, "createSubmission"])->middleware(EnsureQuizIsNotAlreadyStarted::class)->can("submit,quiz")->name("quizzes.start"); diff --git a/tests/Feature/AnswerTest.php b/tests/Feature/AnswerTest.php index 4a0b4f92..c82664d6 100644 --- a/tests/Feature/AnswerTest.php +++ b/tests/Feature/AnswerTest.php @@ -6,6 +6,7 @@ use App\Models\Answer; use App\Models\Question; +use App\Models\Quiz; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Inertia\Testing\AssertableInertia as Assert; @@ -21,10 +22,10 @@ protected function setUp(): void { parent::setUp(); - $this->user = User::factory()->create(); + $this->user = User::factory()->admin()->create(); } - public function testUserCanViewQuestionAnswers(): void + public function testAdminCanViewQuestionAnswers(): void { $question = Question::factory()->create(); Answer::factory()->count(10)->create(["question_id" => $question->id]); @@ -40,13 +41,13 @@ public function testUserCanViewQuestionAnswers(): void ); } - public function testUserCannotViewAnswersOfQuestionThatNotExisted(): void + public function testAdminCannotViewAnswersOfQuestionThatNotExisted(): void { $this->actingAs($this->user)->get(route("admin.answers.index", 1)) ->assertStatus(404); } - public function testUserCanViewSingleAnswer(): void + public function testAdminCanViewSingleAnswer(): void { $answer = Answer::factory()->create(); @@ -61,7 +62,7 @@ public function testUserCanViewSingleAnswer(): void ); } - public function testUserCanViewLockedAnswer(): void + public function testAdminCanViewLockedAnswer(): void { $answer = Answer::factory()->locked()->create(); @@ -77,13 +78,13 @@ public function testUserCanViewLockedAnswer(): void ); } - public function testUserCannotViewAnswerThatNotExisted(): void + public function testAdminCannotViewAnswerThatNotExisted(): void { $this->actingAs($this->user)->get(route("admin.answers.show", 1)) ->assertStatus(404); } - public function testUserCanCreateAnswer(): void + public function testAdminCanCreateAnswer(): void { $question = Question::factory()->create(); @@ -98,7 +99,7 @@ public function testUserCanCreateAnswer(): void ]); } - public function testUserCanCreateMultipleAnswers(): void + public function testAdminCanCreateMultipleAnswers(): void { $question = Question::factory()->create(); @@ -120,7 +121,7 @@ public function testUserCanCreateMultipleAnswers(): void $this->assertDatabaseHas("answers", ["text" => "Example answer 2"]); } - public function testUserCannotCreateAnswerToQuestionThatNotExisted(): void + public function testAdminCannotCreateAnswerToQuestionThatNotExisted(): void { $this->actingAs($this->user) ->from("/quizzes") @@ -132,7 +133,7 @@ public function testUserCannotCreateAnswerToQuestionThatNotExisted(): void ]); } - public function testUserCannotCreateAnswerToQuestionThatIsLocked(): void + public function testAdminCannotCreateAnswerToQuestionThatIsLocked(): void { $question = Question::factory()->locked()->create(); @@ -146,7 +147,7 @@ public function testUserCannotCreateAnswerToQuestionThatIsLocked(): void ]); } - public function testUserCannotCreateInvalidAnswer(): void + public function testAdminCannotCreateInvalidAnswer(): void { $question = Question::factory()->create(); @@ -162,7 +163,7 @@ public function testUserCannotCreateInvalidAnswer(): void $this->assertDatabaseCount("answers", 0); } - public function testUserCanEditAnswer(): void + public function testAdminCanEditAnswer(): void { $answer = Answer::factory()->create(["text" => "Old answer"]); @@ -174,7 +175,7 @@ public function testUserCanEditAnswer(): void $this->assertDatabaseHas("answers", ["text" => "New answer"]); } - public function testUserCannotEditAnswerThatNotExisted(): void + public function testAdminCannotEditAnswerThatNotExisted(): void { $this->actingAs($this->user) ->from("/quizzes") @@ -182,7 +183,7 @@ public function testUserCannotEditAnswerThatNotExisted(): void ->assertStatus(404); } - public function testUserCannotMakeInvalidEdit(): void + public function testAdminCannotMakeInvalidEdit(): void { $answer = Answer::factory()->create(["text" => "Old answer"]); @@ -198,7 +199,7 @@ public function testUserCannotMakeInvalidEdit(): void $this->assertDatabaseHas("answers", ["text" => "Old answer"]); } - public function testUserCannotEditLockedAnswer(): void + public function testAdminCannotEditLockedAnswer(): void { $answer = Answer::factory()->locked()->create(["text" => "Old answer"]); @@ -210,7 +211,7 @@ public function testUserCannotEditLockedAnswer(): void $this->assertDatabaseHas("answers", ["text" => "Old answer"]); } - public function testUserCanDeleteAnswer(): void + public function testAdminCanDeleteAnswer(): void { $answer = Answer::factory()->create(["text" => "answer"]); @@ -222,7 +223,7 @@ public function testUserCanDeleteAnswer(): void $this->assertDatabaseMissing("answers", ["text" => "answer"]); } - public function testUserCannotDeleteLockedAnswer(): void + public function testAdminCannotDeleteLockedAnswer(): void { $answer = Answer::factory()->locked()->create(["text" => "answer"]); @@ -234,7 +235,7 @@ public function testUserCannotDeleteLockedAnswer(): void $this->assertDatabaseHas("answers", ["text" => "answer"]); } - public function testUserCannotDeleteAnswerThatNotExisted(): void + public function testAdminCannotDeleteAnswerThatNotExisted(): void { $this->actingAs($this->user) ->from("/quizzes") @@ -242,7 +243,7 @@ public function testUserCannotDeleteAnswerThatNotExisted(): void ->assertStatus(404); } - public function testUserCanMarkAnswerAsCorrect(): void + public function testAdminCanMarkAnswerAsCorrect(): void { $answer = Answer::factory()->create(["text" => "answer"]); @@ -254,7 +255,7 @@ public function testUserCanMarkAnswerAsCorrect(): void $this->assertDatabaseHas("questions", ["correct_answer_id" => $answer->id]); } - public function testUserCanChangeCorrectAnswer(): void + public function testAdminCanChangeCorrectAnswer(): void { $question = Question::factory()->create(); $answerA = Answer::factory()->create(["text" => "answer A", "question_id" => $question->id]); @@ -273,7 +274,7 @@ public function testUserCanChangeCorrectAnswer(): void $this->assertDatabaseHas("questions", ["correct_answer_id" => $answerB->id]); } - public function testUserCanDeleteCorrectAnswer(): void + public function testAdminCanDeleteCorrectAnswer(): void { $question = Question::factory()->create(); $answer = Answer::factory()->create(["text" => "answer", "question_id" => $question->id]); @@ -291,7 +292,7 @@ public function testUserCanDeleteCorrectAnswer(): void $this->assertDatabaseHas("questions", ["correct_answer_id" => null]); } - public function testUserCannotChangeCorrectAnswerInLockedQuestion(): void + public function testAdminCannotChangeCorrectAnswerInLockedQuestion(): void { $question = Question::factory()->locked()->create(); $answerA = Answer::factory()->create(["text" => "answer A", "question_id" => $question->id]); @@ -310,7 +311,7 @@ public function testUserCannotChangeCorrectAnswerInLockedQuestion(): void $this->assertDatabaseHas("questions", ["correct_answer_id" => $answerA->id]); } - public function testUserCanChangeCorrectAnswerToInvalid(): void + public function testAdminCanChangeCorrectAnswerToInvalid(): void { $question = Question::factory()->create(); $answer = Answer::factory()->create(["text" => "answer", "question_id" => $question->id]); @@ -328,7 +329,7 @@ public function testUserCanChangeCorrectAnswerToInvalid(): void $this->assertDatabaseHas("questions", ["correct_answer_id" => $answer->id]); } - public function testUserCannotChangeCorrectAnswerToInvalidInLockedQuestion(): void + public function testAdminCannotChangeCorrectAnswerToInvalidInLockedQuestion(): void { $question = Question::factory()->locked()->create(); $answer = Answer::factory()->create(["text" => "answer", "question_id" => $question->id]); @@ -346,7 +347,7 @@ public function testUserCannotChangeCorrectAnswerToInvalidInLockedQuestion(): vo $this->assertDatabaseHas("questions", ["correct_answer_id" => $answer->id]); } - public function testUserCanCopyAnswer(): void + public function testAdminCanCopyAnswer(): void { $questionA = Question::factory()->create(); $questionB = Question::factory()->create(); @@ -362,7 +363,7 @@ public function testUserCanCopyAnswer(): void $this->assertDatabaseHas("answers", ["question_id" => $questionB->id]); } - public function testUserCanCopyLockedAnswer(): void + public function testAdminCanCopyLockedAnswer(): void { $questionA = Question::factory()->locked()->create(); $questionB = Question::factory()->create(); @@ -378,7 +379,7 @@ public function testUserCanCopyLockedAnswer(): void $this->assertDatabaseHas("answers", ["question_id" => $questionB->id]); } - public function testUserCannotCopyAnswerToLockedQuestion(): void + public function testAdminCannotCopyAnswerToLockedQuestion(): void { $questionA = Question::factory()->create(); $questionB = Question::factory()->locked()->create(); @@ -394,7 +395,7 @@ public function testUserCannotCopyAnswerToLockedQuestion(): void $this->assertDatabaseHas("answers", ["question_id" => $questionA->id]); } - public function testUserCanCopyCorrectAnswer(): void + public function testAdminCanCopyCorrectAnswer(): void { $questionA = Question::factory()->create(); $questionB = Question::factory()->create(); @@ -416,7 +417,7 @@ public function testUserCanCopyCorrectAnswer(): void $this->assertDatabaseHas("questions", ["id" => $questionB->id, "correct_answer_id" => null]); } - public function testUserCannotCopyAnswerThatNotExisted(): void + public function testAdminCannotCopyAnswerThatNotExisted(): void { $question = Question::factory()->create(); @@ -426,7 +427,7 @@ public function testUserCannotCopyAnswerThatNotExisted(): void ->assertStatus(404); } - public function testUserCannotCopyAnswerToQuestionThatNotExisted(): void + public function testAdminCannotCopyAnswerToQuestionThatNotExisted(): void { $answer = Answer::factory()->create(); @@ -435,4 +436,52 @@ public function testUserCannotCopyAnswerToQuestionThatNotExisted(): void ->post(route("admin.answers.clone", ["answer" => $answer->id, "question" => 2])) ->assertStatus(404); } + + public function testUserCannotAccessToCrud(): void + { + $user = User::factory()->create(); + $quiz = Quiz::factory()->create(); + $question = Question::factory()->create(["quiz_id" => $quiz->id]); + $answer = Answer::factory()->create(["question_id" => $question->id]); + + $this->actingAs($user) + ->from("/") + ->get(route("admin.answers.index", $question->id)) + ->assertStatus(403); + + $this->actingAs($user) + ->from("/") + ->post(route("admin.answers.store", $question->id), ["text" => "New answer"]) + ->assertStatus(403); + + $this->actingAs($user) + ->from("/") + ->get(route("admin.answers.show", $answer->id)) + ->assertStatus(403); + + $this->actingAs($user) + ->from("/") + ->patch(route("admin.answers.update", $answer->id), ["text" => "Updated question"]) + ->assertStatus(403); + + $this->actingAs($user) + ->from("/") + ->post(route("admin.answers.clone", ["answer" => $answer->id, "question" => $question->id])) + ->assertStatus(403); + + $this->actingAs($user) + ->from("/") + ->delete(route("admin.answers.destroy", $answer->id)) + ->assertStatus(403); + + $this->actingAs($user) + ->from("/") + ->post(route("admin.answers.correct", $answer->id)) + ->assertStatus(403); + + $this->actingAs($user) + ->from("/") + ->post(route("admin.answers.invalid", $answer->id)) + ->assertStatus(403); + } } diff --git a/tests/Feature/AuthenticateSessionTest.php b/tests/Feature/AuthenticateSessionTest.php index 86a62bef..c00349bc 100644 --- a/tests/Feature/AuthenticateSessionTest.php +++ b/tests/Feature/AuthenticateSessionTest.php @@ -19,7 +19,7 @@ public function testUserCanLogin(): void $this->post("/auth/login", [ "email" => "test@example.com", "password" => "goodPassword", - ])->assertRedirect("/"); + ])->assertRedirect("/dashboard"); } public function testUserCanNotLoginWithWrongPassword(): void diff --git a/tests/Feature/QuestionTest.php b/tests/Feature/QuestionTest.php index 3eb614b3..059589eb 100644 --- a/tests/Feature/QuestionTest.php +++ b/tests/Feature/QuestionTest.php @@ -22,10 +22,10 @@ protected function setUp(): void { parent::setUp(); - $this->user = User::factory()->create(); + $this->user = User::factory()->admin()->create(); } - public function testUserCanViewQuizQuestions(): void + public function testAdminCanViewQuizQuestions(): void { $quiz = Quiz::factory()->create(); $question = Question::factory()->create(["quiz_id" => $quiz->id]); @@ -45,13 +45,13 @@ public function testUserCanViewQuizQuestions(): void ); } - public function testUserCannotViewQuestionsOfQuizThatNotExisted(): void + public function testAdminCannotViewQuestionsOfQuizThatNotExisted(): void { $this->actingAs($this->user)->get(route("admin.questions.index", 1)) ->assertStatus(404); } - public function testUserCanViewSingleQuestion(): void + public function testAdminCanViewSingleQuestion(): void { $question = Question::factory()->create(); @@ -66,7 +66,7 @@ public function testUserCanViewSingleQuestion(): void ); } - public function testUserCanViewLockedQuestion(): void + public function testAdminCanViewLockedQuestion(): void { $question = Question::factory()->locked()->create(); @@ -82,13 +82,13 @@ public function testUserCanViewLockedQuestion(): void ); } - public function testUserCannotViewQuestionThatNotExisted(): void + public function testAdminCannotViewQuestionThatNotExisted(): void { $this->actingAs($this->user)->get(route("admin.questions.show", 1)) ->assertStatus(404); } - public function testUserCanCreateQuestion(): void + public function testAdminCanCreateQuestion(): void { $quiz = Quiz::factory()->create(); @@ -103,7 +103,7 @@ public function testUserCanCreateQuestion(): void ]); } - public function testUserCanCreateMultipleQuestions(): void + public function testAdminCanCreateMultipleQuestions(): void { $quiz = Quiz::factory()->create(); @@ -125,7 +125,7 @@ public function testUserCanCreateMultipleQuestions(): void $this->assertDatabaseHas("questions", ["text" => "Example question 2"]); } - public function testUserCannotCreateQuestionToQuizThatNotExisted(): void + public function testAdminCannotCreateQuestionToQuizThatNotExisted(): void { $this->actingAs($this->user) ->from("/") @@ -137,7 +137,7 @@ public function testUserCannotCreateQuestionToQuizThatNotExisted(): void ]); } - public function testUserCannotCreateQuestionToQuizThatIsLocked(): void + public function testAdminCannotCreateQuestionToQuizThatIsLocked(): void { $quiz = Quiz::factory()->locked()->create(); @@ -151,7 +151,7 @@ public function testUserCannotCreateQuestionToQuizThatIsLocked(): void ]); } - public function testUserCannotCreateInvalidQuestion(): void + public function testAdminCannotCreateInvalidQuestion(): void { $quiz = Quiz::factory()->create(); @@ -167,7 +167,7 @@ public function testUserCannotCreateInvalidQuestion(): void $this->assertDatabaseCount("questions", 0); } - public function testUserCanEditQuestion(): void + public function testAdminCanEditQuestion(): void { $question = Question::factory()->create(["text" => "Old questions"]); @@ -179,7 +179,7 @@ public function testUserCanEditQuestion(): void $this->assertDatabaseHas("questions", ["text" => "New question"]); } - public function testUserCannotEditQuestionThatNotExisted(): void + public function testAdminCannotEditQuestionThatNotExisted(): void { $this->actingAs($this->user) ->from("/") @@ -187,7 +187,7 @@ public function testUserCannotEditQuestionThatNotExisted(): void ->assertStatus(404); } - public function testUserCannotMakeInvalidEdit(): void + public function testAdminCannotMakeInvalidEdit(): void { $question = Question::factory()->create(["text" => "Old questions"]); @@ -203,7 +203,7 @@ public function testUserCannotMakeInvalidEdit(): void $this->assertDatabaseHas("questions", ["text" => "Old questions"]); } - public function testUserCannotEditLockedQuestion(): void + public function testAdminCannotEditLockedQuestion(): void { $question = Question::factory()->locked()->create(["text" => "Old question"]); @@ -215,7 +215,7 @@ public function testUserCannotEditLockedQuestion(): void $this->assertDatabaseHas("questions", ["text" => "Old question"]); } - public function testUserCanDeleteQuestion(): void + public function testAdminCanDeleteQuestion(): void { $question = Question::factory()->create(["text" => "question"]); Answer::factory()->create(["question_id" => $question->id]); @@ -235,7 +235,7 @@ public function testUserCanDeleteQuestion(): void $this->assertDatabaseCount("answers", 0); } - public function testUserCannotDeleteLockedQuestion(): void + public function testAdminCannotDeleteLockedQuestion(): void { $question = Question::factory()->locked()->create(["text" => "question"]); @@ -247,7 +247,7 @@ public function testUserCannotDeleteLockedQuestion(): void $this->assertDatabaseHas("questions", ["text" => "question"]); } - public function testUserCannotDeleteQuestionThatNotExisted(): void + public function testAdminCannotDeleteQuestionThatNotExisted(): void { $this->actingAs($this->user) ->from("/") @@ -255,7 +255,7 @@ public function testUserCannotDeleteQuestionThatNotExisted(): void ->assertStatus(404); } - public function testUserCanCopyQuestion(): void + public function testAdminCanCopyQuestion(): void { $quizA = Quiz::factory()->create(); $quizB = Quiz::factory()->create(); @@ -274,7 +274,7 @@ public function testUserCanCopyQuestion(): void $this->assertDatabaseCount("answers", 20); } - public function testUserCanCopyLockedQuestion(): void + public function testAdminCanCopyLockedQuestion(): void { $quizA = Quiz::factory()->locked()->create(); $quizB = Quiz::factory()->create(); @@ -290,7 +290,7 @@ public function testUserCanCopyLockedQuestion(): void $this->assertDatabaseHas("questions", ["quiz_id" => $quizB->id]); } - public function testUserCannotCopyAnswerToLockedQuestion(): void + public function testAdminCannotCopyAnswerToLockedQuestion(): void { $quizA = Quiz::factory()->create(); $quizB = Quiz::factory()->locked()->create(); @@ -306,7 +306,7 @@ public function testUserCannotCopyAnswerToLockedQuestion(): void $this->assertDatabaseHas("questions", ["quiz_id" => $quizA->id]); } - public function testUserCanCopyQuestionWithCorrectAnswer(): void + public function testAdminCanCopyQuestionWithCorrectAnswer(): void { $quizA = Quiz::factory()->create(); $quizB = Quiz::factory()->create(); @@ -326,7 +326,7 @@ public function testUserCanCopyQuestionWithCorrectAnswer(): void $this->assertNotEquals($quizA->questions[0]->correctAnswer->id, $quizB->questions[0]->correctAnswer->id); } - public function testUserCannotCopyQuestionThatNotExisted(): void + public function testAdminCannotCopyQuestionThatNotExisted(): void { $quiz = Question::factory()->create(); @@ -336,7 +336,7 @@ public function testUserCannotCopyQuestionThatNotExisted(): void ->assertStatus(404); } - public function testUserCannotCopyAnswerToQuestionThatNotExisted(): void + public function testAdminCannotCopyAnswerToQuestionThatNotExisted(): void { $question = Question::factory()->create(); @@ -345,4 +345,41 @@ public function testUserCannotCopyAnswerToQuestionThatNotExisted(): void ->post(route("admin.questions.clone", ["question" => $question->id, "quiz" => 2])) ->assertStatus(404); } + + public function testUserCannotAccessToCrud(): void + { + $user = User::factory()->create(); + $quiz = Quiz::factory()->create(); + $question = Question::factory()->create(["quiz_id" => $quiz->id]); + + $this->actingAs($user) + ->from("/") + ->get(route("admin.questions.index", $quiz->id)) + ->assertStatus(403); + + $this->actingAs($user) + ->from("/") + ->post(route("admin.questions.store", $quiz->id), ["text" => "New question"]) + ->assertStatus(403); + + $this->actingAs($user) + ->from("/") + ->get(route("admin.questions.show", $question->id)) + ->assertStatus(403); + + $this->actingAs($user) + ->from("/") + ->patch(route("admin.questions.update", $question->id), ["text" => "Updated question"]) + ->assertStatus(403); + + $this->actingAs($user) + ->from("/") + ->post(route("admin.questions.clone", ["question" => $question->id, "quiz" => $quiz->id])) + ->assertStatus(403); + + $this->actingAs($user) + ->from("/") + ->delete(route("admin.questions.destroy", $question->id)) + ->assertStatus(403); + } } diff --git a/tests/Feature/QuizTest.php b/tests/Feature/QuizTest.php index 09147abc..d917059e 100644 --- a/tests/Feature/QuizTest.php +++ b/tests/Feature/QuizTest.php @@ -24,7 +24,7 @@ protected function setUp(): void { parent::setUp(); Carbon::setTestNow(Carbon::create(2024, 1, 1, 10)); - $this->user = User::factory()->create(); + $this->user = User::factory()->admin()->create(); } protected function tearDown(): void @@ -33,7 +33,7 @@ protected function tearDown(): void Carbon::setTestNow(); } - public function testUserCanViewQuizzes(): void + public function testAdminCanViewQuizzes(): void { $quizzes = Quiz::factory()->count(2)->create(); Question::factory()->count(5)->create(["quiz_id" => $quizzes[0]->id]); @@ -53,13 +53,13 @@ public function testUserCanViewQuizzes(): void ); } - public function testUserCannotViewQuizThatNotExisted(): void + public function testAdminCannotViewQuizThatNotExisted(): void { $this->actingAs($this->user)->get(route("admin.quizzes.show", 1)) ->assertStatus(404); } - public function testUserCanViewSingleQuiz(): void + public function testAdminCanViewSingleQuiz(): void { $quiz = Quiz::factory()->create(); @@ -74,7 +74,7 @@ public function testUserCanViewSingleQuiz(): void ); } - public function testUserCanViewLockedQuiz(): void + public function testAdminCanViewLockedQuiz(): void { $quiz = Quiz::factory()->locked()->create(); @@ -90,7 +90,7 @@ public function testUserCanViewLockedQuiz(): void ); } - public function testUserCanCreateQuiz(): void + public function testAdminCanCreateQuiz(): void { $this->actingAs($this->user) ->from("/") @@ -103,7 +103,7 @@ public function testUserCanCreateQuiz(): void ]); } - public function testUserCanCreateQuizWithoutDate(): void + public function testAdminCanCreateQuizWithoutDate(): void { Carbon::setTestNow(Carbon::create(2024, 1, 1, 10)); @@ -118,7 +118,7 @@ public function testUserCanCreateQuizWithoutDate(): void ]); } - public function testUserCanCreateMultipleQuizzes(): void + public function testAdminCanCreateMultipleQuizzes(): void { $this->actingAs($this->user) ->from("/") @@ -138,7 +138,7 @@ public function testUserCanCreateMultipleQuizzes(): void $this->assertDatabaseHas("quizzes", ["name" => "Example quiz 2"]); } - public function testUserCannotCreateInvalidQuiz(): void + public function testAdminCannotCreateInvalidQuiz(): void { $this->actingAs($this->user) ->from("/") @@ -168,7 +168,7 @@ public function testUserCannotCreateInvalidQuiz(): void $this->assertDatabaseCount("quizzes", 0); } - public function testUserCanEditQuiz(): void + public function testAdminCanEditQuiz(): void { $quiz = Quiz::factory()->create(["name" => "Old quiz", "scheduled_at" => "2024-02-10 11:40:00"]); @@ -180,7 +180,7 @@ public function testUserCanEditQuiz(): void $this->assertDatabaseHas("quizzes", ["name" => "New quiz", "scheduled_at" => "2024-03-10 12:15:00", "duration" => 7200]); } - public function testUserCannotEditQuizThatNotExisted(): void + public function testAdminCannotEditQuizThatNotExisted(): void { $this->actingAs($this->user) ->from("/") @@ -188,7 +188,7 @@ public function testUserCannotEditQuizThatNotExisted(): void ->assertStatus(404); } - public function testUserCannotMakeInvalidEdit(): void + public function testAdminCannotMakeInvalidEdit(): void { $quiz = Quiz::factory()->create(["name" => "Old quiz"]); @@ -220,7 +220,7 @@ public function testUserCannotMakeInvalidEdit(): void $this->assertDatabaseHas("quizzes", ["name" => "Old quiz"]); } - public function testUserCannotEditLockedQuiz(): void + public function testAdminCannotEditLockedQuiz(): void { $quiz = Quiz::factory()->locked()->create(["name" => "Old quiz"]); @@ -232,7 +232,7 @@ public function testUserCannotEditLockedQuiz(): void $this->assertDatabaseHas("quizzes", ["name" => "Old quiz"]); } - public function testUserCanDeleteQuiz(): void + public function testAdminCanDeleteQuiz(): void { $quiz = Quiz::factory()->create(["name" => "quiz"]); $question = Question::factory()->create(["quiz_id" => $quiz->id]); @@ -253,7 +253,7 @@ public function testUserCanDeleteQuiz(): void $this->assertDatabaseCount("answers", 0); } - public function testUserCannotDeleteLockedQuiz(): void + public function testAdminCannotDeleteLockedQuiz(): void { $quiz = Quiz::factory()->locked()->create(["name" => "quiz"]); @@ -265,7 +265,7 @@ public function testUserCannotDeleteLockedQuiz(): void $this->assertDatabaseHas("quizzes", ["name" => "quiz"]); } - public function testUserCannotDeleteQuestionThatNotExisted(): void + public function testAdminCannotDeleteQuestionThatNotExisted(): void { $this->actingAs($this->user) ->from("/") @@ -273,7 +273,7 @@ public function testUserCannotDeleteQuestionThatNotExisted(): void ->assertStatus(404); } - public function testUserCanCopyQuiz(): void + public function testAdminCanCopyQuiz(): void { $quiz = Quiz::factory()->create(); $questions = Question::factory()->count(2)->create(["quiz_id" => $quiz->id]); @@ -295,7 +295,7 @@ public function testUserCanCopyQuiz(): void $this->assertDatabaseCount("answers", 40); } - public function testUserCanCopyLockedQuiz(): void + public function testAdminCanCopyLockedQuiz(): void { $quiz = Quiz::factory()->locked()->create(); @@ -309,7 +309,7 @@ public function testUserCanCopyLockedQuiz(): void $this->assertDatabaseCount("quizzes", 2); } - public function testUserCannotCopyQuizThatNotExisted(): void + public function testAdminCannotCopyQuizThatNotExisted(): void { $this->actingAs($this->user) ->from("/") @@ -320,13 +320,14 @@ public function testUserCannotCopyQuizThatNotExisted(): void public function testUserCanStartQuiz(): void { $quiz = Quiz::factory()->locked()->create(); + $user = User::factory()->create(); - $response = $this->actingAs($this->user) + $response = $this->actingAs($user) ->from("/") ->post(route("quizzes.start", $quiz->id)); $submission = QuizSubmission::query()->where([ - "user_id" => $this->user->id, + "user_id" => $user->id, "quiz_id" => $quiz->id, ])->firstOrFail(); @@ -335,9 +336,10 @@ public function testUserCanStartQuiz(): void public function testUserCannotStartAlreadyStartedQuiz(): void { - $submission = QuizSubmission::factory()->create(["user_id" => $this->user->id]); + $user = User::factory()->create(); + $submission = QuizSubmission::factory()->create(["user_id" => $user->id]); - $this->actingAs($this->user) + $this->actingAs($user) ->from("/") ->post(route("quizzes.start", $submission->quiz->id)) ->assertRedirect(route("submissions.show", $submission->id)); @@ -348,12 +350,49 @@ public function testUserCannotStartAlreadyStartedQuiz(): void public function testUserCannotStartUnlockedQuiz(): void { $quiz = Quiz::factory()->create(); + $user = User::factory()->create(); - $this->actingAs($this->user) + $this->actingAs($user) ->from("/") ->post(route("quizzes.start", $quiz->id)) ->assertStatus(403); $this->assertDatabaseCount("quiz_submissions", 0); } + + public function testUserCannotAccessToCrud(): void + { + $user = User::factory()->create(); + $quiz = Quiz::factory()->create(); + + $this->actingAs($user) + ->from("/") + ->get(route("admin.quizzes.index")) + ->assertStatus(403); + + $this->actingAs($user) + ->from("/") + ->post(route("admin.quizzes.store")) + ->assertStatus(403); + + $this->actingAs($user) + ->from("/") + ->get(route("admin.quizzes.show", $quiz->id)) + ->assertStatus(403); + + $this->actingAs($user) + ->from("/") + ->patch(route("admin.quizzes.update", $quiz->id)) + ->assertStatus(403); + + $this->actingAs($user) + ->from("/") + ->post(route("admin.quizzes.clone", $quiz->id)) + ->assertStatus(403); + + $this->actingAs($user) + ->from("/") + ->delete(route("admin.quizzes.destroy", $quiz->id)) + ->assertStatus(403); + } } diff --git a/tests/Feature/ResetPasswordTest.php b/tests/Feature/ResetPasswordTest.php index 3b0af68b..9088b673 100644 --- a/tests/Feature/ResetPasswordTest.php +++ b/tests/Feature/ResetPasswordTest.php @@ -45,7 +45,7 @@ public function testUserCanResetPasswordWithValidToken(): void "password" => "newPassword", ]); - $loginResponse->assertRedirect("/"); + $loginResponse->assertRedirect("/dashboard"); $this->assertAuthenticated(); Event::assertDispatched(PasswordReset::class); }