CluPilotCloud/tests/Feature/Auth/PasswordResetTest.php

158 lines
6.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
use App\Livewire\Auth\ForgotPassword;
use App\Livewire\Auth\ResetPassword;
use App\Mail\ResetPasswordMail;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Password;
use Livewire\Livewire;
/**
* Getting back in after forgetting the password.
*
* There was no way to. Fortify's resetPasswords feature was switched off, so
* there was no link on the sign-in form, no page and no route — a customer who
* forgot their password was locked out of their own cloud until somebody
* opened a shell. On a product whose selling point is that you can ring
* somebody, that is a support call a week and an embarrassing one.
*/
it('offers the way back on the sign-in form', function () {
$this->get(route('login'))
->assertOk()
->assertSee(__('auth.forgot_link'))
->assertSee(route('password.request'), false);
});
it('sends a link in this products own design', function () {
Mail::fake();
$user = User::factory()->create(['email' => 'kunde@example.test']);
Livewire::test(ForgotPassword::class)
->set('email', $user->email)
->call('send')
->assertHasNoErrors();
// Not the framework's default MailMessage: somebody who has just been
// locked out is exactly the person a phishing mail is aimed at, and a
// message that looks nothing like the rest of our post is one they cannot
// check against the others.
Mail::assertQueued(ResetPasswordMail::class, fn ($mail) => $mail->hasTo($user->email));
});
it('answers the same whether the address is known or not', function () {
// Otherwise the form is a way of finding out who is a customer, which is
// the first thing anybody targeting a business does.
Mail::fake();
$known = Livewire::test(ForgotPassword::class)->set('email', User::factory()->create()->email)->call('send');
$unknown = Livewire::test(ForgotPassword::class)->set('email', 'niemand@example.test')->call('send');
$known->assertSet('sent', true)->assertHasNoErrors();
$unknown->assertSet('sent', true)->assertHasNoErrors();
Mail::assertQueuedCount(1);
});
it('sets the new password and kills every other session', function () {
$user = User::factory()->create(['password' => Hash::make('das-alte-passwort')]);
$before = $user->remember_token;
$token = Password::broker()->createToken($user);
Livewire::test(ResetPassword::class, ['token' => $token])
->set('email', $user->email)
->set('password', 'ein-langes-neues-passwort')
->set('password_confirmation', 'ein-langes-neues-passwort')
->call('save')
->assertHasNoErrors()
->assertSet('done', true);
$user->refresh();
expect(Hash::check('ein-langes-neues-passwort', $user->password))->toBeTrue()
// Whoever knew the old password may still be signed in somewhere. A
// reset that leaves them there has fixed nothing.
->and($user->remember_token)->not->toBe($before);
});
it('does not sign the visitor in', function () {
// The link arrived by email. A mailbox somebody else can read would
// otherwise be a session somebody else gets.
$user = User::factory()->create();
$token = Password::broker()->createToken($user);
Livewire::test(ResetPassword::class, ['token' => $token])
->set('email', $user->email)
->set('password', 'ein-langes-neues-passwort')
->set('password_confirmation', 'ein-langes-neues-passwort')
->call('save');
expect(auth()->check())->toBeFalse();
});
it('refuses a token that is not this users', function () {
$mine = User::factory()->create();
$theirs = User::factory()->create();
Livewire::test(ResetPassword::class, ['token' => Password::broker()->createToken($theirs)])
->set('email', $mine->email)
->set('password', 'ein-langes-neues-passwort')
->set('password_confirmation', 'ein-langes-neues-passwort')
->call('save')
->assertHasErrors('email')
->assertSet('done', false);
});
it('spends the token, so a forwarded link cannot be used twice', function () {
$user = User::factory()->create();
$token = Password::broker()->createToken($user);
$reset = fn () => Livewire::test(ResetPassword::class, ['token' => $token])
->set('email', $user->email)
->set('password', 'ein-langes-neues-passwort')
->set('password_confirmation', 'ein-langes-neues-passwort')
->call('save');
$reset()->assertSet('done', true);
$reset()->assertSet('done', false)->assertHasErrors('email');
});
it('insists on a password long enough to be worth resetting to', function () {
$user = User::factory()->create();
Livewire::test(ResetPassword::class, ['token' => Password::broker()->createToken($user)])
->set('email', $user->email)
->set('password', 'kurz')
->set('password_confirmation', 'kurz')
->call('save')
->assertHasErrors('password');
});
it('renders the reset mail, which it could not do at all', function () {
// The view was missing its closing </x-mail.layout>. The component's
// contents were never terminated, the layout's own @if ran off the end of
// the file, and the mail raised a Blade syntax error instead of rendering —
// so the link never reached anybody who asked for one. Every test until now
// asserted that the mail was QUEUED, which it faithfully was.
$user = App\Models\User::factory()->create(['name' => 'Bea Berger']);
$html = (new App\Mail\ResetPasswordMail($user, 'https://example.test/reset/abc', 60))
->render();
expect($html)->toContain('https://example.test/reset/abc')
->toContain(__('reset_password.action'));
});
it('closes every mail layout it opens', function () {
// One unterminated component is a mail that cannot be sent, and the failure
// only shows at render time — long after the test that queued it went green.
foreach (glob(resource_path('views/mail/*.blade.php')) as $file) {
$body = file_get_contents($file);
expect(substr_count($body, '<x-mail.layout'))
->toBe(substr_count($body, '</x-mail.layout>'), basename($file));
}
});