CluPilotCloud/tests/Feature/Auth/OperatorLoginTest.php

125 lines
4.8 KiB
PHP

<?php
use App\Livewire\Auth\OperatorLogin;
use App\Models\Operator;
use App\Models\User;
use App\Support\AdminArea;
use Illuminate\Support\Facades\Auth;
use Livewire\Livewire;
it('offers no way to register — an operator account is not self-served', function () {
$this->get(route('admin.login'))->assertOk()
->assertDontSee(route('register'))
->assertDontSee(__('auth.sign_up'));
});
it('sends an already signed-in operator back to the console instead of the portal', function () {
// Codex + whole-branch review (M1), independently: guest:operator's
// authenticated-user redirect had no console-aware override, so it fell
// to Laravel's own default — the named 'dashboard' route, the PORTAL.
// The web guard is usually unauthenticated there, so an operator
// revisiting admin.login bounced straight to the portal's OWN login. In
// exclusive mode it is worse: dashboard is not a console route at all,
// so RestrictAdminHost 404s it on the console host.
$operator = Operator::factory()->role('Owner')->create();
$this->actingAs($operator, 'operator')
->get(route('admin.login'))
->assertRedirect(route('admin.overview'));
});
it('signs an operator in on the operator guard', function () {
$operator = Operator::factory()->role('Owner')->create(['password' => 'richtig-langes-pw']);
Livewire::test(OperatorLogin::class)
->set('email', $operator->email)
->set('password', 'richtig-langes-pw')
->call('authenticate')
->assertRedirect(AdminArea::home());
expect(Auth::guard('operator')->check())->toBeTrue()
->and(Auth::guard('web')->check())->toBeFalse();
});
it('refuses a portal account at the console door', function () {
$user = User::factory()->create(['email' => 'kunde@clupilot.test', 'password' => 'kunden-passwort']);
Livewire::test(OperatorLogin::class)
->set('email', 'kunde@clupilot.test')
->set('password', 'kunden-passwort')
->call('authenticate')
->assertHasErrors('email');
expect(Auth::guard('operator')->check())->toBeFalse();
});
it('refuses a disabled operator', function () {
$operator = Operator::factory()->role('Owner')->create([
'password' => 'richtig-langes-pw', 'disabled_at' => now(),
]);
Livewire::test(OperatorLogin::class)
->set('email', $operator->email)
->set('password', 'richtig-langes-pw')
->call('authenticate')
->assertHasErrors('email');
expect(Auth::guard('operator')->check())->toBeFalse();
});
it('sends an operator with confirmed two-factor to the challenge instead of the console', function () {
$operator = Operator::factory()->role('Owner')->create([
'password' => 'richtig-langes-pw',
'two_factor_secret' => encrypt('JBSWY3DPEHPK3PXP'),
'two_factor_confirmed_at' => now(),
]);
Livewire::test(OperatorLogin::class)
->set('email', $operator->email)
->set('password', 'richtig-langes-pw')
->call('authenticate')
->assertRedirect(route('admin.two-factor'));
// Not signed in yet — the challenge is not decoration.
expect(Auth::guard('operator')->check())->toBeFalse();
});
it('throttles repeated failures', function () {
$operator = Operator::factory()->role('Owner')->create(['password' => 'richtig-langes-pw']);
$component = Livewire::test(OperatorLogin::class)->set('email', $operator->email)->set('password', 'falsch');
foreach (range(1, 5) as $ignored) {
$component->call('authenticate');
}
$component->call('authenticate')->assertHasErrors('email');
expect(session()->has('login.id'))->toBeFalse();
});
it('refuses to complete a login for an operator who no longer qualifies', function () {
// completeLogin() is the one place both the direct exit and the
// two-factor challenge complete a sign-in — re-checked here so a
// revocation in the window between the password step and a pending
// two-factor code cannot slip through the challenge, which only ever
// confirmed the row still existed.
$disabled = Operator::factory()->role('Owner')->create(['disabled_at' => now()]);
$roleless = Operator::factory()->create(); // no role synced at all
expect(OperatorLogin::completeLogin($disabled, false))->toBeFalse()
->and(Auth::guard('operator')->check())->toBeFalse();
expect(OperatorLogin::completeLogin($roleless, false))->toBeFalse()
->and(Auth::guard('operator')->check())->toBeFalse();
});
it('records when the operator last signed in', function () {
$operator = Operator::factory()->role('Owner')->create(['password' => 'richtig-langes-pw']);
Livewire::test(OperatorLogin::class)
->set('email', $operator->email)->set('password', 'richtig-langes-pw')
->call('authenticate');
expect($operator->fresh()->last_login_at)->not->toBeNull();
});