create(['is_active' => true]); LivewireVolt::test('auth.login') ->set('magicEmail', $user->email) ->call('sendMagicLink') ->assertHasNoErrors(); Mail::assertSent(MagicLoginLink::class, function (MagicLoginLink $mail) use ($user) { return $mail->user->is($user) && str_contains($mail->loginUrl, '/magic-login/'); }); $magicLink = MagicLink::query()->firstOrFail(); expect($magicLink->user_id)->toBe($user->id); expect($magicLink->token_hash)->toHaveLength(64); }); test('the magic link modal validates its own email field and closes on success', function () { Mail::fake(); $user = User::factory()->create(['is_active' => true]); // Leere Eingabe → Validierungsfehler, keine Mail. LivewireVolt::test('auth.login') ->set('magicEmail', '') ->call('sendMagicLink') ->assertHasErrors(['magicEmail']); Mail::assertNothingSent(); // Gültige Eingabe → Feld wird geleert, Schließen-Event wird ausgelöst. LivewireVolt::test('auth.login') ->set('magicEmail', $user->email) ->call('sendMagicLink') ->assertHasNoErrors() ->assertSet('magicEmail', '') ->assertDispatched('magic-link-sent'); }); test('magic link requests are rate limited per email', function () { /** @var TestCase $this */ Mail::fake(); $user = User::factory()->create(['is_active' => true]); foreach (range(1, 3) as $ignored) { LivewireVolt::test('auth.login') ->set('magicEmail', $user->email) ->call('sendMagicLink') ->assertHasNoErrors(); } LivewireVolt::test('auth.login') ->set('magicEmail', $user->email) ->call('sendMagicLink') ->assertHasErrors(['magicEmail']); Mail::assertSent(MagicLoginLink::class, 3); }); test('admin can login with a valid magic link and lands on admin dashboard', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); Mail::fake(); $user = User::factory()->create(['is_active' => true]); $user->assignRole('admin'); LivewireVolt::test('auth.login') ->set('magicEmail', $user->email) ->call('sendMagicLink'); $sentMail = null; Mail::assertSent(MagicLoginLink::class, function (MagicLoginLink $mail) use (&$sentMail) { $sentMail = $mail; return true; }); expect($sentMail)->not->toBeNull(); /** @var MagicLoginLink $sentMail */ $this->get($sentMail->loginUrl) ->assertRedirect(route('dashboard', absolute: false)); $this->assertAuthenticatedAs($user); expect(MagicLink::query()->firstOrFail()->consumed_at)->not->toBeNull(); $user->refresh(); expect($user->last_login_at)->not->toBeNull(); expect($user->last_login_ip)->toBe('127.0.0.1'); }); test('customer is redirected to me dashboard after magic link login', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); Mail::fake(); $customer = User::factory()->create(['is_active' => true]); $customer->assignRole('customer'); LivewireVolt::test('auth.login') ->set('magicEmail', $customer->email) ->call('sendMagicLink'); $sentMail = null; Mail::assertSent(MagicLoginLink::class, function (MagicLoginLink $mail) use (&$sentMail) { $sentMail = $mail; return true; }); /** @var MagicLoginLink $sentMail */ $this->get($sentMail->loginUrl) ->assertRedirect(route('me.dashboard', absolute: false)); $this->assertAuthenticatedAs($customer); }); test('a stale intended admin url does not send a customer into the 403 admin area', function () { /** @var TestCase $this */ $this->seed(RolesAndPermissionsSeeder::class); $customer = User::factory()->create(['is_active' => true]); $customer->assignRole('customer'); $plainToken = 'intended-token'; MagicLink::query()->create([ 'user_id' => $customer->id, 'token_hash' => hash('sha256', $plainToken), 'purpose' => 'login', 'expires_at' => now()->addMinutes(5), ]); // Als Gast zuvor /dashboard besucht → intended-URL liegt in der Session. $this->withSession(['url.intended' => route('dashboard')]); $this->get(route('magic-links.consume', ['token' => $plainToken])) ->assertRedirect(route('me.dashboard', absolute: false)); $this->assertAuthenticatedAs($customer); }); test('expired magic link can not be used', function () { /** @var TestCase $this */ $user = User::factory()->create(['is_active' => true]); $plainToken = 'expired-token'; MagicLink::query()->create([ 'user_id' => $user->id, 'token_hash' => hash('sha256', $plainToken), 'purpose' => 'login', 'expires_at' => now()->subMinute(), ]); $this->get(route('magic-links.consume', ['token' => $plainToken])) ->assertRedirect(route('login', absolute: false)); $this->assertGuest(); }); test('consumed magic link can not be reused', function () { /** @var TestCase $this */ $user = User::factory()->create(['is_active' => true]); $plainToken = 'consumed-token'; MagicLink::query()->create([ 'user_id' => $user->id, 'token_hash' => hash('sha256', $plainToken), 'purpose' => 'login', 'expires_at' => now()->addMinutes(5), 'consumed_at' => now()->subMinute(), ]); $this->get(route('magic-links.consume', ['token' => $plainToken])) ->assertRedirect(route('login', absolute: false)); $this->assertGuest(); });