set('admin_access.secrets_key', 'base64:'.base64_encode(random_bytes(32))); }); it('shows the no-key banner for a malformed SECRETS_KEY, not only a missing one', function () { // Codex R15#3, P2: render() feeds 'usable' => $vault->isUsable() to the // view, and the banner is the one place an operator actually sees that // verdict. Before the fix, a malformed (nonempty, wrong-length) key made // isUsable() answer true, so this banner stayed hidden — the page said // credentials could be stored here when they could not. Asserted before // the lock gate specifically because @if (! $usable) sits above @if (! // $unlocked) in the view; a signed-in-but-locked session must still see // it. config()->set('admin_access.secrets_key', 'zu-kurz'); Livewire::actingAs(operator('Owner'), 'operator') ->test(SecretsPage::class) ->assertSee(__('secrets.no_key')); }); it('is not reachable without the capability', function () { // Every operator has console.view. That must not mean "can read the // payment key". Livewire::actingAs(operator('Admin'), 'operator') ->test(SecretsPage::class) ->assertForbidden(); }); it('shows nothing until the password is confirmed', function () { $owner = operator('Owner'); app(SecretVault::class)->put('stripe.secret', 'sk_live_ABCDEFGH9999', $owner); $page = Livewire::actingAs($owner, 'operator')->test(SecretsPage::class); $page->assertSee(__('secrets.locked_title')) // Not even the outline before unlocking. ->assertDontSee('9999') ->assertViewHas('unlocked', false); }); it('refuses every action while locked, not merely hiding the buttons', function () { // A Livewire action is reachable by anyone who can post to /livewire/update. $owner = operator('Owner'); foreach ([['save', 'stripe.secret'], ['forget', 'stripe.secret'], ['test', 'stripe.secret']] as [$action, $arg]) { Livewire::actingAs($owner, 'operator') ->test(SecretsPage::class) ->call($action, $arg) ->assertForbidden(); } }); it('unlocks with the password, then stores and clears the entered value', function () { $owner = operator('Owner'); $page = Livewire::actingAs($owner, 'operator') ->test(SecretsPage::class) ->set('confirmablePassword', 'password') ->call('confirmPassword') ->assertHasNoErrors() ->assertViewHas('unlocked', true); $page->set('entered.stripe_secret', 'sk_live_NEWKEY1234') ->call('save', 'stripe.secret') ->assertHasNoErrors(); expect(app(SecretVault::class)->get('stripe.secret'))->toBe('sk_live_NEWKEY1234') // Out of the component state as soon as it is stored: a Livewire // property travels to the browser and back in the snapshot. ->and($page->get('entered')['stripe_secret'])->toBe(''); }); it('never puts a stored secret into the component snapshot', function () { $owner = operator('Owner'); app(SecretVault::class)->put('stripe.secret', 'sk_live_TOPSECRET42', $owner); $page = Livewire::actingAs($owner, 'operator') ->test(SecretsPage::class) ->set('confirmablePassword', 'password') ->call('confirmPassword'); expect(json_encode($page->snapshot))->not->toContain('sk_live_TOPSECRET42'); }); it('can be locked again without signing out', function () { $owner = operator('Owner'); Livewire::actingAs($owner, 'operator') ->test(SecretsPage::class) ->set('confirmablePassword', 'password') ->call('confirmPassword') ->assertViewHas('unlocked', true) ->call('forgetPasswordConfirmation') ->assertViewHas('unlocked', false); }); it('binds the form to a key Livewire can actually write to', function () { // A dot in a Livewire property path means nesting, so a registry key with a // dot in it would be written to entered['stripe']['secret'] and the value // would never reach the save. expect(Secrets::field('stripe.secret'))->toBe('stripe_secret') ->and(str_contains(Secrets::field('stripe.secret'), '.'))->toBeFalse(); });