Prove ConfirmsPassword resolves its guard, not the ambient default
actingAs()/Livewire::actingAs() call Auth::shouldUse(), which made every existing test for this trait pass identically whether confirmPassword() asked for the right guard explicitly or just read whichever guard happened to be default — reverting the explicit guard() call to a bare auth()->user() passed the full suite. This test signs the operator in without actingAs and deliberately decouples the ambient default guard from their session before calling confirmPassword(), so only the explicit resolution can succeed.feat/operator-identity
parent
8f8a0ad4f7
commit
73dec7f685
|
|
@ -3,6 +3,7 @@
|
|||
use App\Livewire\Admin\Secrets;
|
||||
use App\Models\Operator;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Livewire\Livewire;
|
||||
|
||||
beforeEach(fn () => config()->set('admin_access.secrets_key', 'base64:'.base64_encode(random_bytes(32))));
|
||||
|
|
@ -38,3 +39,50 @@ it('still confirms a portal password on the web guard', function () {
|
|||
->call('confirmPassword')
|
||||
->assertHasNoErrors();
|
||||
});
|
||||
|
||||
it('resolves the operator explicitly by guard, not by whichever guard happens to be the ambient default', function () {
|
||||
// actingAs()/Livewire::actingAs() both call Auth::shouldUse($guard) as a
|
||||
// side effect, which makes the named guard the DEFAULT guard — and under
|
||||
// that, auth()->user() and auth()->guard('operator')->user() resolve
|
||||
// identically, so every OTHER test in this file passes just the same
|
||||
// whether confirmPassword() asks for the right guard explicitly or just
|
||||
// reads the ambient default. That is not a hypothetical: reverting the
|
||||
// trait's guard(...)->user() call back to a bare auth()->user() passes
|
||||
// the entire 834-test suite, this file included — the gap this test
|
||||
// closes, by making that distinction observable.
|
||||
//
|
||||
// Signing in with Auth::guard('operator')->login() alone (no actingAs)
|
||||
// does NOT reach this test: it proves confirmationGuard is genuinely
|
||||
// consulted, but Secrets::mount()'s own $this->authorize('secrets.manage')
|
||||
// ALSO reads the ambient default guard (Laravel's Gate resolves the
|
||||
// current user the same bare way) — so mount() itself 403s before
|
||||
// confirmPassword() is ever reached, on the very account this test means
|
||||
// to prove works. Confirmed directly: Auth::guard('web')->check() is
|
||||
// false, Auth::guard('operator')->check() is true, and still
|
||||
// Gate::check('secrets.manage') is false in that state.
|
||||
//
|
||||
// So: mount through actingAs() as usual — authorize() runs exactly once,
|
||||
// while shouldUse('operator') is still in effect from actingAs, and
|
||||
// legitimately passes. mount() does not run again on a later ->call().
|
||||
// Only THEN, before touching confirmPassword() specifically, undo the
|
||||
// masking by hand: Auth::shouldUse('web') switches the ambient default
|
||||
// back to 'web' — genuinely empty, not merely relabelled — while the
|
||||
// operator's own session on the 'operator' guard is untouched (setUser()
|
||||
// on a guard does not depend on which guard is "default"). That is
|
||||
// exactly the real shape of a console request: a genuine operator sign-in
|
||||
// (App\Livewire\Auth\OperatorLogin::completeLogin()) never touches the
|
||||
// web guard, default or not.
|
||||
$operator = Operator::factory()->role('Owner')->create(['password' => 'operator-passwort']);
|
||||
|
||||
$page = Livewire::actingAs($operator, 'operator')->test(Secrets::class);
|
||||
|
||||
Auth::shouldUse('web');
|
||||
|
||||
expect(Auth::guard('web')->check())->toBeFalse()
|
||||
->and(Auth::guard('operator')->check())->toBeTrue()
|
||||
->and(auth()->check())->toBeFalse();
|
||||
|
||||
$page->set('confirmablePassword', 'operator-passwort')
|
||||
->call('confirmPassword')
|
||||
->assertHasNoErrors();
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue