get(route('security.edit')); $response->assertRedirect(route('login')); }); test('guests are redirected to the login page for password update', function () { $response = $this->put(route('user-password.update'), [ 'current_password' => 'password', 'password' => 'new-password', 'password_confirmation' => 'new-password', ]); $response->assertRedirect(route('login')); }); /* |-------------------------------------------------------------------------- | SECURITY EDIT PAGE |-------------------------------------------------------------------------- */ test('security page is displayed', function () { $user = User::factory()->create(); $response = $this ->actingAs($user) ->get(route('security.edit')); $response->assertOk(); }); test('security page renders correct inertia component', function () { $user = User::factory()->create(); $this->actingAs($user) ->get(route('security.edit')) ->assertInertia(fn (Assert $page) => $page ->component('settings/security') ); }); test('security page passes password rules', function () { $user = User::factory()->create(); $this->actingAs($user) ->get(route('security.edit')) ->assertInertia(fn (Assert $page) => $page ->has('passwordRules') ); }); test('security page does not pass two factor or passkey data', function () { $user = User::factory()->create(); $this->actingAs($user) ->get(route('security.edit')) ->assertInertia(fn (Assert $page) => $page ->missing('canManageTwoFactor') ->missing('canManagePasskeys') ->missing('passkeys') ->missing('twoFactorEnabled') ->missing('requiresConfirmation') ); }); /* |-------------------------------------------------------------------------- | PASSWORD UPDATE - SUCCESS |-------------------------------------------------------------------------- */ test('password can be updated', function () { $user = User::factory()->create(); $response = $this ->actingAs($user) ->from(route('security.edit')) ->put(route('user-password.update'), [ 'current_password' => 'password', 'password' => 'new-password', 'password_confirmation' => 'new-password', ]); $response ->assertSessionHasNoErrors() ->assertRedirect(route('security.edit')); expect(Hash::check('new-password', $user->refresh()->password))->toBeTrue(); }); test('password update redirects back to security page', function () { $user = User::factory()->create(); $response = $this ->actingAs($user) ->from(route('security.edit')) ->put(route('user-password.update'), [ 'current_password' => 'password', 'password' => 'another-new-password', 'password_confirmation' => 'another-new-password', ]); $response->assertRedirect(); }); /* |-------------------------------------------------------------------------- | PASSWORD UPDATE - VALIDATION: CURRENT PASSWORD |-------------------------------------------------------------------------- */ test('correct current password must be provided to update password', function () { $user = User::factory()->create(); $response = $this ->actingAs($user) ->from(route('security.edit')) ->put(route('user-password.update'), [ 'current_password' => 'wrong-password', 'password' => 'new-password', 'password_confirmation' => 'new-password', ]); $response ->assertSessionHasErrors('current_password') ->assertRedirect(route('security.edit')); expect(Hash::check('new-password', $user->refresh()->password))->toBeFalse(); }); test('current_password is required', function () { $user = User::factory()->create(); $response = $this ->actingAs($user) ->from(route('security.edit')) ->put(route('user-password.update'), [ 'password' => 'new-password', 'password_confirmation' => 'new-password', ]); $response->assertSessionHasErrors('current_password'); }); /* |-------------------------------------------------------------------------- | PASSWORD UPDATE - VALIDATION: NEW PASSWORD |-------------------------------------------------------------------------- */ test('password is required', function () { $user = User::factory()->create(); $response = $this ->actingAs($user) ->from(route('security.edit')) ->put(route('user-password.update'), [ 'current_password' => 'password', 'password_confirmation' => 'new-password', ]); $response->assertSessionHasErrors('password'); }); test('password must be confirmed', function () { $user = User::factory()->create(); $response = $this ->actingAs($user) ->from(route('security.edit')) ->put(route('user-password.update'), [ 'current_password' => 'password', 'password' => 'new-password', ]); $response->assertSessionHasErrors('password'); }); test('password and confirmation must match', function () { $user = User::factory()->create(); $response = $this ->actingAs($user) ->from(route('security.edit')) ->put(route('user-password.update'), [ 'current_password' => 'password', 'password' => 'new-password', 'password_confirmation' => 'different-password', ]); $response->assertSessionHasErrors('password'); }); /* |-------------------------------------------------------------------------- | PASSWORD UPDATE - VALIDATION: EMPTY REQUEST |-------------------------------------------------------------------------- */ test('user cannot update password without submitting any data', function () { $user = User::factory()->create(); $response = $this ->actingAs($user) ->from(route('security.edit')) ->put(route('user-password.update'), []); $response->assertSessionHasErrors(['current_password', 'password']); }); /* |-------------------------------------------------------------------------- | PASSWORD UPDATE - TOAST / FLASH |-------------------------------------------------------------------------- */ test('password update flashes success toast via inertia', function () { $user = User::factory()->create(); $response = $this ->actingAs($user) ->from(route('security.edit')) ->put(route('user-password.update'), [ 'current_password' => 'password', 'password' => 'new-password', 'password_confirmation' => 'new-password', ]); $response->assertRedirect(); }); /* |-------------------------------------------------------------------------- | PASSWORD UPDATE - DATA INTEGRITY |-------------------------------------------------------------------------- */ test('password update does not change other user fields', function () { $user = User::factory()->create([ 'email' => 'test@example.com', 'username' => 'testuser', ]); $this ->actingAs($user) ->from(route('security.edit')) ->put(route('user-password.update'), [ 'current_password' => 'password', 'password' => 'new-password', 'password_confirmation' => 'new-password', ]); $user->refresh(); expect($user->email)->toBe('test@example.com'); expect($user->username)->toBe('testuser'); }); /* |-------------------------------------------------------------------------- | AUTHORIZATION - GUEST CANNOT PERFORM ACTIONS |-------------------------------------------------------------------------- */ test('guest cannot access security page', function () { $response = $this->get(route('security.edit')); $response->assertRedirect(route('login')); }); test('guest cannot update password', function () { $response = $this->put(route('user-password.update'), [ 'current_password' => 'password', 'password' => 'new-password', 'password_confirmation' => 'new-password', ]); $response->assertRedirect(route('login')); }); /* |-------------------------------------------------------------------------- | REALISTIC USER SCENARIOS |-------------------------------------------------------------------------- */ test('user changes password then can login with new password', function () { $user = User::factory()->create(['password' => 'old-password']); $this ->actingAs($user) ->from(route('security.edit')) ->put(route('user-password.update'), [ 'current_password' => 'old-password', 'password' => 'brand-new-password', 'password_confirmation' => 'brand-new-password', ]); $user->refresh(); expect(Hash::check('brand-new-password', $user->password))->toBeTrue(); expect(Hash::check('old-password', $user->password))->toBeFalse(); }); test('user tries to set same password as current', function () { $user = User::factory()->create(['password' => 'same-password']); $response = $this ->actingAs($user) ->from(route('security.edit')) ->put(route('user-password.update'), [ 'current_password' => 'same-password', 'password' => 'same-password', 'password_confirmation' => 'same-password', ]); $response->assertSessionHasNoErrors(); expect(Hash::check('same-password', $user->refresh()->password))->toBeTrue(); });