get(route('login'))->assertOk()->assertInertia(fn (Assert $page) => $page->component('Auth/Phone')); } public function test_authenticated_user_is_redirected_away_from_guest_routes(): void { $this->actingAs(User::factory()->create())->get(route('login'))->assertRedirect(); } public function test_check_phone_redirects_to_password_when_phone_has_single_level(): void { User::factory()->create(['phone' => '+989120000000', 'level' => UserEnum::Doctor->value]); $response = $this->post(route('checkPhone'), ['prefix' => '+98', 'phone' => '09120000000']); $response->assertRedirect(route('password')); $response->assertSessionHas('login_phone', '+989120000000'); $response->assertSessionHas('login_level', 'doctor'); } public function test_check_phone_redirects_to_portal_when_phone_has_multiple_levels(): void { User::factory()->create(['phone' => '+989120000000', 'level' => UserEnum::Doctor->value]); User::factory()->create(['phone' => '+989120000000', 'level' => UserEnum::Supervisor->value]); $response = $this->post(route('checkPhone'), ['prefix' => '+98', 'phone' => '09120000000']); $response->assertRedirect(route('portal')); $response->assertSessionHas('available_levels'); } public function test_check_phone_redirects_to_registration_for_unknown_phone(): void { $response = $this->post(route('checkPhone'), ['prefix' => '+98', 'phone' => '09120000000']); $response->assertRedirect(route('register.chooseType')); $response->assertSessionHas('register_phone', '+989120000000'); } public function test_check_phone_validates_input(): void { $this->post(route('checkPhone'), ['phone' => 'abc'])->assertSessionHasErrors(['prefix', 'phone']); } public function test_choose_portal_requires_session_state(): void { $this->get(route('portal'))->assertRedirect(route('login')); } public function test_choose_portal_renders_available_levels(): void { $this->withSession(['login_phone' => '+989120000000', 'available_levels' => ['doctor', 'supervisor']]) ->get(route('portal')) ->assertOk() ->assertInertia(fn (Assert $page) => $page->component('Auth/ChoosePortal')->where('availableLevels', ['doctor', 'supervisor'])); } public function test_choose_portal_with_level_stores_it_and_redirects_to_password(): void { $this->withSession(['login_phone' => '+989120000000', 'available_levels' => ['doctor', 'supervisor']]) ->get(route('portal', 'doctor')) ->assertRedirect(route('password')) ->assertSessionHas('login_level', 'doctor'); } public function test_password_form_requires_session_state(): void { $this->get(route('password'))->assertRedirect(route('login')); } public function test_password_form_renders(): void { $this->withSession(['login_phone' => '+989120000000', 'login_level' => 'doctor']) ->get(route('password')) ->assertOk() ->assertInertia(fn (Assert $page) => $page->component('Auth/Password')->where('level', 'doctor')); } public function test_authenticate_logs_in_and_redirects_to_dashboard(): void { $user = User::factory()->create(['phone' => '+989120000000', 'level' => UserEnum::Doctor->value, 'password' => 'secret123']); $this->withSession(['login_phone' => '+989120000000', 'login_level' => 'doctor']) ->post(route('authenticate'), ['password' => 'secret123']) ->assertRedirect(route('dashboard.index')); $this->assertAuthenticatedAs($user); } public function test_authenticate_rejects_wrong_password(): void { User::factory()->create(['phone' => '+989120000000', 'level' => UserEnum::Doctor->value, 'password' => 'secret123']); $this->withSession(['login_phone' => '+989120000000', 'login_level' => 'doctor']) ->from(route('password')) ->post(route('authenticate'), ['password' => 'wrong']) ->assertRedirect(route('password')) ->assertSessionHasErrors('password'); $this->assertGuest(); } public function test_authenticate_without_session_redirects_to_login(): void { $this->post(route('authenticate'), ['password' => 'secret123'])->assertRedirect(route('login')); } public function test_send_otp_stores_code_and_redirects_to_verification(): void { $this->withSession(['login_phone' => '+989120000000']) ->post(route('sendOtp')) ->assertRedirect(route('otpVerification')); $this->assertDatabaseHas('otp_codes', ['phone' => '+989120000000']); } public function test_send_otp_without_phone_redirects_to_login(): void { $this->post(route('sendOtp'))->assertRedirect(route('login')); $this->assertDatabaseCount('otp_codes', 0); } public function test_otp_verification_page_renders_with_phone(): void { $this->withSession(['login_phone' => '+989120000000']) ->get(route('otpVerification')) ->assertOk() ->assertInertia(fn (Assert $page) => $page->component('Auth/OtpVerification')->where('phone', '+989120000000')); } public function test_verify_otp_marks_session_verified_and_redirects_to_reset(): void { OtpCode::factory()->create(['phone' => '+989120000000', 'code' => '1234']); $this->withSession(['login_phone' => '+989120000000']) ->post(route('verifyOtp'), ['otp_code' => '1234']) ->assertRedirect(route('resetPassword')) ->assertSessionHas('otp_verified', true) ->assertSessionHas('verified_phone', '+989120000000'); $this->assertDatabaseCount('otp_codes', 0); } public function test_verify_otp_rejects_invalid_code(): void { OtpCode::factory()->create(['phone' => '+989120000000', 'code' => '1234']); $this->withSession(['login_phone' => '+989120000000']) ->post(route('verifyOtp'), ['otp_code' => '0000']) ->assertSessionHasErrors('otp_code'); } public function test_reset_password_page_requires_verified_session(): void { $this->get(route('resetPassword'))->assertRedirect(route('login')); $this->withSession(['otp_verified' => true, 'verified_phone' => '+989120000000']) ->get(route('resetPassword')) ->assertOk() ->assertInertia(fn (Assert $page) => $page->component('Auth/ResetPassword')); } public function test_update_password_changes_password_and_flushes_session(): void { $user = User::factory()->create(['phone' => '+989120000000', 'level' => UserEnum::Doctor->value]); $this->withSession(['otp_verified' => true, 'verified_phone' => '+989120000000', 'login_level' => 'doctor']) ->post(route('updatePassword'), ['password' => 'newsecret1', 'password_confirmation' => 'newsecret1']) ->assertRedirect(route('login')) ->assertSessionMissing('otp_verified'); $this->assertTrue(Hash::check('newsecret1', $user->fresh()->password)); } public function test_update_password_validates_confirmation_and_length(): void { $this->withSession(['otp_verified' => true, 'verified_phone' => '+989120000000', 'login_level' => 'doctor']) ->post(route('updatePassword'), ['password' => 'short', 'password_confirmation' => 'other']) ->assertSessionHasErrors('password'); } public function test_update_password_without_verified_session_redirects_to_login(): void { $this->post(route('updatePassword'), ['password' => 'newsecret1', 'password_confirmation' => 'newsecret1']) ->assertRedirect(route('login')); } public function test_update_password_for_unknown_user_redirects_to_login_with_error(): void { $this->withSession(['otp_verified' => true, 'verified_phone' => '+989120000000', 'login_level' => 'doctor']) ->post(route('updatePassword'), ['password' => 'newsecret1', 'password_confirmation' => 'newsecret1']) ->assertRedirect(route('login')) ->assertSessionHas('error'); } public function test_register_pages_render_with_session_phone(): void { $session = ['register_phone' => '+989120000000']; $this->withSession($session)->get(route('register.chooseType'))->assertInertia(fn (Assert $page) => $page->component('Auth/ChooseRegisterType')->where('phone', '+989120000000')); $this->withSession($session)->get(route('register.supervisor'))->assertInertia(fn (Assert $page) => $page->component('Auth/RegisterSupervisor')); $this->withSession($session)->get(route('register.veterinarian'))->assertInertia(fn (Assert $page) => $page->component('Auth/RegisterVet')); } public function test_store_registers_user_with_session_phone_and_logs_in(): void { $this->withSession(['register_phone' => '+989120000000']) ->post(route('register.store'), ['phone' => '09999999999', 'national_code' => '0012345678', 'level' => UserEnum::Vet->value]) ->assertRedirect(route('dashboard.index')); $this->assertAuthenticated(); $this->assertDatabaseHas('users', ['phone' => '+989120000000', 'national_code' => '0012345678']); $this->assertDatabaseMissing('users', ['phone' => '09999999999']); } public function test_store_without_session_phone_redirects_to_login(): void { $this->post(route('register.store'), ['phone' => '09120000000', 'national_code' => '0012345678']) ->assertRedirect(route('login')); $this->assertGuest(); $this->assertDatabaseCount('users', 0); } public function test_logout_ends_session(): void { $this->actingAs(User::factory()->create())->get(route('dashboard.logout'))->assertRedirect(route('login')); $this->assertGuest(); } }