diff --git a/tests/Feature/Admin/OperatorTwoFactorEnrollmentTest.php b/tests/Feature/Admin/OperatorTwoFactorEnrollmentTest.php index 0bd144d..2ca6e36 100644 --- a/tests/Feature/Admin/OperatorTwoFactorEnrollmentTest.php +++ b/tests/Feature/Admin/OperatorTwoFactorEnrollmentTest.php @@ -36,7 +36,7 @@ it('refuses every two-factor action without a recent password confirmation', fun // onDisableConfirmed is the R23 forwarding listener ConfirmDisableTwoFactor // reaches — it must lead straight into the same guard as disableTwoFactor, // not around it. - foreach (['enableTwoFactor', 'confirmTwoFactor', 'disableTwoFactor', 'regenerateRecoveryCodes', 'onDisableConfirmed'] as $action) { + foreach (['enableTwoFactor', 'cancelSetup', 'confirmTwoFactor', 'disableTwoFactor', 'regenerateRecoveryCodes', 'onDisableConfirmed'] as $action) { Livewire::actingAs($op, 'operator') ->test(TwoFactorSetup::class) ->call($action) @@ -216,3 +216,23 @@ it('sends the operator to sign in instead of a 500 when the session has ended', $page->call('$refresh')->assertRedirect(route('admin.login')); }); + +it('lets an operator back out of a half-finished setup, taking the secret with it', function () { + // Before cancelSetup() existed, the only way out of the QR step was to + // navigate away — which left an unconfirmed secret on the account that + // nothing on the console ever showed. + $op = operator('Support'); + + $page = Livewire::actingAs($op, 'operator') + ->test(TwoFactorSetup::class) + ->set('confirmablePassword', 'password') + ->call('confirmPassword') + ->call('enableTwoFactor'); + + expect($op->refresh()->two_factor_secret)->not->toBeNull(); + + $page->call('cancelSetup'); + + expect($op->refresh()->two_factor_secret)->toBeNull() + ->and($op->two_factor_confirmed_at)->toBeNull(); +});