Skip to content

Commit 34219f5

Browse files
bobbyonmagicbobbyiliev
authored andcommitted
test: Add comprehensive password reset flow tests
1 parent f7e10aa commit 34219f5

File tree

1 file changed

+275
-0
lines changed

1 file changed

+275
-0
lines changed
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
<?php
2+
3+
use App\Models\User;
4+
use Illuminate\Auth\Notifications\ResetPassword;
5+
use Illuminate\Support\Facades\Hash;
6+
use Illuminate\Support\Facades\Notification;
7+
use Illuminate\Support\Facades\Password;
8+
use Livewire\Livewire;
9+
10+
beforeEach(function () {
11+
User::query()->delete();
12+
});
13+
14+
/*
15+
|--------------------------------------------------------------------------
16+
| Password Reset Request Page Tests
17+
|--------------------------------------------------------------------------
18+
*/
19+
20+
it('displays the password reset request page', function () {
21+
$response = $this->get(route('auth.password.request'));
22+
23+
$response->assertStatus(200);
24+
});
25+
26+
it('shows validation error for invalid email format on reset request', function () {
27+
Livewire::test('auth.password.reset')
28+
->set('email', 'not-valid-email')
29+
->call('sendResetPasswordLink')
30+
->assertHasErrors(['email' => 'email']);
31+
});
32+
33+
it('shows validation error for empty email on reset request', function () {
34+
Livewire::test('auth.password.reset')
35+
->set('email', '')
36+
->call('sendResetPasswordLink')
37+
->assertHasErrors(['email' => 'required']);
38+
});
39+
40+
it('sends password reset link for valid email', function () {
41+
Notification::fake();
42+
43+
$user = User::factory()->create([
44+
'email' => 'test@example.com',
45+
]);
46+
47+
Livewire::test('auth.password.reset')
48+
->set('email', 'test@example.com')
49+
->call('sendResetPasswordLink')
50+
->assertHasNoErrors()
51+
->assertSet('emailSentMessage', trans(Password::RESET_LINK_SENT));
52+
53+
Notification::assertSentTo($user, ResetPassword::class);
54+
});
55+
56+
it('shows error for non-existent email on reset request', function () {
57+
Notification::fake();
58+
59+
Livewire::test('auth.password.reset')
60+
->set('email', 'nonexistent@example.com')
61+
->call('sendResetPasswordLink')
62+
->assertHasErrors(['email']);
63+
64+
Notification::assertNothingSent();
65+
});
66+
67+
/*
68+
|--------------------------------------------------------------------------
69+
| Password Reset Page (with token) HTTP Tests
70+
| Note: These tests use HTTP requests as Volt components with Folio dynamic
71+
| routes ([token].blade.php) can't be directly tested via Livewire::test()
72+
|--------------------------------------------------------------------------
73+
*/
74+
75+
it('displays the password reset page with valid token', function () {
76+
$user = User::factory()->create([
77+
'email' => 'test@example.com',
78+
]);
79+
80+
$token = Password::broker()->createToken($user);
81+
82+
$response = $this->get(route('password.reset', ['token' => $token, 'email' => $user->email]));
83+
84+
$response->assertStatus(200);
85+
$response->assertSee('test@example.com');
86+
});
87+
88+
it('displays the password reset page with invalid token but still shows form', function () {
89+
$user = User::factory()->create([
90+
'email' => 'test@example.com',
91+
]);
92+
93+
// Page should still load even with invalid token - error shown on submit
94+
$response = $this->get(route('password.reset', ['token' => 'invalid-token', 'email' => $user->email]));
95+
96+
$response->assertStatus(200);
97+
});
98+
99+
it('resets password with valid token using password broker directly', function () {
100+
$user = User::factory()->create([
101+
'email' => 'test@example.com',
102+
'password' => Hash::make('oldpassword'),
103+
]);
104+
105+
$token = Password::broker()->createToken($user);
106+
107+
// Use the password broker directly to test the reset logic
108+
$response = Password::broker()->reset(
109+
[
110+
'token' => $token,
111+
'email' => 'test@example.com',
112+
'password' => 'newpassword123',
113+
],
114+
function ($user, $password) {
115+
$user->password = Hash::make($password);
116+
$user->save();
117+
},
118+
);
119+
120+
expect($response)->toBe(Password::PASSWORD_RESET);
121+
122+
// Verify password was changed
123+
$user->refresh();
124+
$this->assertTrue(Hash::check('newpassword123', $user->password));
125+
});
126+
127+
it('fails to reset password with invalid token using password broker', function () {
128+
$user = User::factory()->create([
129+
'email' => 'test@example.com',
130+
'password' => Hash::make('oldpassword'),
131+
]);
132+
133+
// Use the password broker directly with invalid token
134+
$response = Password::broker()->reset(
135+
[
136+
'token' => 'invalid-token',
137+
'email' => 'test@example.com',
138+
'password' => 'newpassword123',
139+
],
140+
function ($user, $password) {
141+
$user->password = Hash::make($password);
142+
$user->save();
143+
},
144+
);
145+
146+
expect($response)->toBe(Password::INVALID_TOKEN);
147+
148+
// Verify password was NOT changed
149+
$user->refresh();
150+
$this->assertTrue(Hash::check('oldpassword', $user->password));
151+
});
152+
153+
it('fails to reset password with expired token', function () {
154+
$user = User::factory()->create([
155+
'email' => 'test@example.com',
156+
'password' => Hash::make('oldpassword'),
157+
]);
158+
159+
$token = Password::broker()->createToken($user);
160+
161+
// Delete the token to simulate expiration
162+
Password::broker()->deleteToken($user);
163+
164+
// Use the password broker directly
165+
$response = Password::broker()->reset(
166+
[
167+
'token' => $token,
168+
'email' => 'test@example.com',
169+
'password' => 'newpassword123',
170+
],
171+
function ($user, $password) {
172+
$user->password = Hash::make($password);
173+
$user->save();
174+
},
175+
);
176+
177+
expect($response)->toBe(Password::INVALID_TOKEN);
178+
179+
// Verify password was NOT changed
180+
$user->refresh();
181+
$this->assertTrue(Hash::check('oldpassword', $user->password));
182+
});
183+
184+
it('fails to reset password with wrong email', function () {
185+
$user = User::factory()->create([
186+
'email' => 'test@example.com',
187+
'password' => Hash::make('oldpassword'),
188+
]);
189+
190+
$token = Password::broker()->createToken($user);
191+
192+
// Use the password broker directly with wrong email
193+
$response = Password::broker()->reset(
194+
[
195+
'token' => $token,
196+
'email' => 'wrong@example.com',
197+
'password' => 'newpassword123',
198+
],
199+
function ($user, $password) {
200+
$user->password = Hash::make($password);
201+
$user->save();
202+
},
203+
);
204+
205+
expect($response)->toBe(Password::INVALID_USER);
206+
207+
// Verify password was NOT changed
208+
$user->refresh();
209+
$this->assertTrue(Hash::check('oldpassword', $user->password));
210+
});
211+
212+
it('cannot reuse the same token twice', function () {
213+
$user = User::factory()->create([
214+
'email' => 'test@example.com',
215+
'password' => Hash::make('oldpassword'),
216+
]);
217+
218+
$token = Password::broker()->createToken($user);
219+
220+
// First reset should succeed
221+
$response = Password::broker()->reset(
222+
[
223+
'token' => $token,
224+
'email' => 'test@example.com',
225+
'password' => 'newpassword123',
226+
],
227+
function ($user, $password) {
228+
$user->password = Hash::make($password);
229+
$user->save();
230+
},
231+
);
232+
233+
expect($response)->toBe(Password::PASSWORD_RESET);
234+
235+
// Verify password was changed
236+
$user->refresh();
237+
$this->assertTrue(Hash::check('newpassword123', $user->password));
238+
239+
// Second reset with same token should fail
240+
$response = Password::broker()->reset(
241+
[
242+
'token' => $token,
243+
'email' => 'test@example.com',
244+
'password' => 'anotherpassword',
245+
],
246+
function ($user, $password) {
247+
$user->password = Hash::make($password);
248+
$user->save();
249+
},
250+
);
251+
252+
expect($response)->toBe(Password::INVALID_TOKEN);
253+
254+
// Verify password was NOT changed again
255+
$user->refresh();
256+
$this->assertTrue(Hash::check('newpassword123', $user->password));
257+
});
258+
259+
it('can create multiple tokens for different users', function () {
260+
$user1 = User::factory()->create(['email' => 'user1@example.com']);
261+
$user2 = User::factory()->create(['email' => 'user2@example.com']);
262+
263+
$token1 = Password::broker()->createToken($user1);
264+
$token2 = Password::broker()->createToken($user2);
265+
266+
expect($token1)->not->toBe($token2);
267+
268+
// Both tokens should be valid for their respective users
269+
$this->assertTrue(Password::broker()->tokenExists($user1, $token1));
270+
$this->assertTrue(Password::broker()->tokenExists($user2, $token2));
271+
272+
// Tokens should not be interchangeable
273+
$this->assertFalse(Password::broker()->tokenExists($user1, $token2));
274+
$this->assertFalse(Password::broker()->tokenExists($user2, $token1));
275+
});

0 commit comments

Comments
 (0)