Files
einundzwanzig-verein/tests/Feature/Livewire/Association/ProfileTest.php
HolgerHatGarKeineNode eb9285d601 Fix this (vibe-kanban 0064af70)
## Exception Summary

- Class: `TypeError`
- Message: `Cannot assign array to property Livewire\Component@anonymous::$fax of type string`
- Code: `0`
- File: `vendor/livewire/livewire/src/Mechanisms/HandleComponents/HandleComponents.php:555`
- Timestamp: `2026-02-11T11:10:02+00:00`
- Details: This exception was thrown during a HTTP Request.

## HTTP Request

- Method: `POST`
- URL: `https://verein.einundzwanzig.space/livewire-5c4c7b52/update`
- Route: `livewire.update`
- Status code: `500`
- IP address: `206.237.102.28`

### Request Headers

- `content-type: application/json`
- `content-length: 1484`
- `cookie: XSRF-TOKEN=[342 bytes redacted]; einundzwanzig_verein_session=[342 bytes redacted]`
- `sec-fetch-user: ?1`
- `sec-fetch-site: none`
- `sec-fetch-mode: navigate`
- `sec-fetch-dest: document`
- `upgrade-insecure-requests: 1`
- `connection: keep-alive`
- `accept-language: en-US,en;q=0.5`
- `accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8`
- `user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36`
- `accept-encoding: identity`
- `host: verein.einundzwanzig.space`

### Request Payload

```json
{
    "_token": "[40 bytes redacted]",
    "components": [
        {
            "snapshot": "{\"data\": {\"form\": [{\"reason\": \"\", \"check\": false, \"currentPleb\": null}, {\"class\": \"App\\\\Livewire\\\\Forms\\\\ApplicationForm\", \"s\": \"form\"}], \"profileForm\": [{\"email\": \"\", \"nip05Handle\": \"\", \"currentPleb\": null}, {\"class\": \"App\\\\Livewire\\\\Forms\\\\ProfileForm\", \"s\": \"form\"}], \"no\": false, \"showEmail\": true, \"fax\": \"\", \"nip05Verified\": false, \"nip05VerifiedHandle\": null, \"nip05HandleMismatch\": false, \"nip05VerifiedHandles\": [[], {\"s\": \"arr\"}], \"yearsPaid\": [[], {\"s\": \"arr\"}], \"events\": [[], {\"s\": \"arr\"}], \"payments\": null, \"invoiceStatus\": null, \"invoiceStatusLabel\": null, \"invoiceStatusMessage\": null, \"invoiceStatusVariant\": \"info\", \"invoiceExpiresAt\": null, \"invoiceExpiresAtDisplay\": null, \"invoiceExpiresIn\": null, \"amountToPay\": 21000, \"currentYearIsPaid\": false, \"currentPubkey\": null, \"currentPleb\": null, \"qrCode\": null}, \"memo\": {\"id\": \"w62UhlWfdP5hmH873cUk\", \"name\": \"association.profile\", \"path\": \"association/profile\", \"method\": \"GET\", \"release\": \"a-a-a\", \"attributes\": {\"_livewire_component\": \"association.profile\"}, \"children\": [], \"scripts\": [], \"assets\": [], \"errors\": [], \"locale\": \"en\", \"islands\": []}, \"checksum\": \"ef5141c41105681483f6be62494c4717a8d5a43c8c5a07173ea799a2a58d7005\"}",
            "updates": {
                "fax": []
            },
            "calls": []
        }
    ],
    "_nightwatch_files": []
}
```

### Authenticated User

- Not authenticated for this execution.

## Database Queries (before exception)

- Not captured

## Stack Trace (most recent call first)

- [0] Livewire\\Mechanisms\\HandleComponents\\HandleComponents->setComponentPropertyAwareOfTypes()

    at vendor/livewire/livewire/src/Mechanisms/HandleComponents/HandleComponents.php:555

- [1] Livewire\\Mechanisms\\HandleComponents\\HandleComponents->setComponentPropertyAwareOfTypes()

    at vendor/livewire/livewire/src/Mechanisms/HandleComponents/HandleComponents.php:455

- [2] Livewire\\Mechanisms\\HandleComponents\\HandleComponents->updateProperty()

    at vendor/livewire/livewire/src/Mechanisms/HandleComponents/HandleComponents.php:426

- [3] Livewire\\Mechanisms\\HandleComponents\\HandleComponents->updateProperties()

    at vendor/livewire/livewire/src/Mechanisms/HandleComponents/HandleComponents.php:188

- [4] Livewire\\Mechanisms\\HandleComponents\\HandleComponents->update()

    at vendor/livewire/livewire/src/LivewireManager.php:131

- [5] Livewire\\LivewireManager->update()

    at vendor/livewire/volt/src/LivewireManager.php:35

- [6] Livewire\\Volt\\LivewireManager->update()

    at vendor/livewire/livewire/src/Mechanisms/HandleRequests/HandleRequests.php:123

- [7] Livewire\\Mechanisms\\HandleRequests\\HandleRequests->handleUpdate()

    at vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php:46

- [8] Illuminate\\Routing\\ControllerDispatcher->dispatch()

    at vendor/laravel/framework/src/Illuminate/Routing/Route.php:265

- [9] Illuminate\\Routing\\Route->runController()

    at vendor/laravel/framework/src/Illuminate/Routing/Route.php:211

- [10] Illuminate\\Routing\\Route->run()

    at vendor/laravel/framework/src/Illuminate/Routing/Router.php:822

- [11] Illuminate\\Routing\\Router->{closure:Illuminate\\Routing\\Router::runRouteWithinStack():821}()

    at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:180

- [12] Illuminate\\Pipeline\\Pipeline->{closure:Illuminate\\Pipeline\\Pipeline::prepareDestination():178}()

    at vendor/laravel/nightwatch/src/Hooks/RouteMiddleware.php:34

- [13] Laravel\\Nightwatch\\Hooks\\RouteMiddleware->handle()

    at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219

- [14] Illuminate\\Pipeline\\Pipeline->{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}()

    at vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php:50

- [15] Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()

    at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219

- [16] Illuminate\\Pipeline\\Pipeline->{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}()

    at vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php:87

- [17] Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()

    at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219

- [18] Illuminate\\Pipeline\\Pipeline->{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}()

    at vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php:48

- [19] Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle()

    at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219

- [20] Illuminate\\Pipeline\\Pipeline->{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}()

    at vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php:120

- [21] Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()

    at vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php:63

- [22] Illuminate\\Session\\Middleware\\StartSession->handle()

    at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219

- [23] Illuminate\\Pipeline\\Pipeline->{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}()

    at vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php:36

- [24] Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle()

    at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219

- [25] Illuminate\\Pipeline\\Pipeline->{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}()

    at vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php:74

- [26] Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()

    at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219

- [27] Illuminate\\Pipeline\\Pipeline->{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}()

    at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:137

- [28] Illuminate\\Pipeline\\Pipeline->then()

    at vendor/laravel/framework/src/Illuminate/Routing/Router.php:821

- [29] Illuminate\\Routing\\Router->runRouteWithinStack()

    at vendor/laravel/framework/src/Illuminate/Routing/Router.php:800

- [30] Illuminate\\Routing\\Router->runRoute()

    at vendor/laravel/framework/src/Illuminate/Routing/Router.php:764

- [31] Illuminate\\Routing\\Router->dispatchToRoute()

    at vendor/laravel/framework/src/Illuminate/Routing/Router.php:753

- [32] Illuminate\\Routing\\Router->dispatch()

    at vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php:200

- [33] Illuminate\\Foundation\\Http\\Kernel->{closure:Illuminate\\Foundation\\Http\\Kernel::dispatchToRouter():197}()

    at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:180

- [34] Illuminate\\Pipeline\\Pipeline->{closure:Illuminate\\Pipeline\\Pipeline::prepareDestination():178}()

    at vendor/livewire/livewire/src/Features/SupportDisablingBackButtonCache/DisableBackButtonCacheMiddleware.php:19

- [35] Livewire\\Features\\SupportDisablingBackButtonCache\\DisableBackButtonCacheMiddleware->handle()

    at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219

- [36] Illuminate\\Pipeline\\Pipeline->{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}()

    at vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php:21

- [37] Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()

    at vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php:31

- [38] Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()

    at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219

- [39] Illuminate\\Pipeline\\Pipeline->{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}()

    at vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php:21

- [40] Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()

    at vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php:51

- [41] Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()

    at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219

- [42] Illuminate\\Pipeline\\Pipeline->{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}()

    at vendor/laravel/framework/src/Illuminate/Http/Middleware/ValidatePostSize.php:27

- [43] Illuminate\\Http\\Middleware\\ValidatePostSize->handle()

    at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219

- [44] Illuminate\\Pipeline\\Pipeline->{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}()

    at vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php:109

- [45] Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()

    at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219

- [46] Illuminate\\Pipeline\\Pipeline->{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}()

    at vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php:48

- [47] Illuminate\\Http\\Middleware\\HandleCors->handle()

    at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219

- [48] Illuminate\\Pipeline\\Pipeline->{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}()

    at vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php:58

- [49] Illuminate\\Http\\Middleware\\TrustProxies->handle()

    at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219

- [50] Illuminate\\Pipeline\\Pipeline->{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}()

    at vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/InvokeDeferredCallbacks.php:22

- [51] Illuminate\\Foundation\\Http\\Middleware\\InvokeDeferredCallbacks->handle()

    at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219

- [52] Illuminate\\Pipeline\\Pipeline->{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}()

    at vendor/laravel/framework/src/Illuminate/Http/Middleware/ValidatePathEncoding.php:26

- [53] Illuminate\\Http\\Middleware\\ValidatePathEncoding->handle()

    at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219

- [54] Illuminate\\Pipeline\\Pipeline->{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}()

    at vendor/laravel/nightwatch/src/Hooks/GlobalMiddleware.php:53

- [55] Laravel\\Nightwatch\\Hooks\\GlobalMiddleware->handle()

    at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219

- [56] Illuminate\\Pipeline\\Pipeline->{closure:{closure:Illuminate\\Pipeline\\Pipeline::carry():194}:195}()

    at vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:137

- [57] Illuminate\\Pipeline\\Pipeline->then()

    at vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php:175

- [58] Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()

    at vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php:144

- [59] Illuminate\\Foundation\\Http\\Kernel->handle()

    at vendor/laravel/framework/src/Illuminate/Foundation/Application.php:1220

- [60] Illuminate\\Foundation\\Application->handleRequest()

    at public/index.php:17

## Code Context

- Not captured

## Occurrence Statistics

- First seen: `2026-02-02T01:38:48+00:00`
- Last seen: `2026-02-11T11:10:02+00:00`
- Occurrences (last 24 hours): `3`
- Occurrences (last 7 days): `3`
- Users affected: `0`
2026-02-11 14:37:05 +01:00

325 lines
10 KiB
PHP

<?php
use App\Enums\AssociationStatus;
use App\Models\EinundzwanzigPleb;
use App\Support\NostrAuth;
use Illuminate\Support\Facades\Http;
use Livewire\Livewire;
it('rejects non-string values for the fax field', function () {
Livewire::test('association.profile')
->set('fax', [])
->assertStatus(422);
});
it('rejects non-string values for the email field', function () {
Livewire::test('association.profile')
->set('profileForm.email', [])
->assertStatus(422);
});
it('rejects non-string values for the nip05Handle field', function () {
Livewire::test('association.profile')
->set('profileForm.nip05Handle', [])
->assertStatus(422);
});
it('handles nostr login correctly', function () {
$pleb = EinundzwanzigPleb::factory()->create();
Livewire::test('association.profile')
->call('handleNostrLoggedIn', $pleb->pubkey)
->assertSet('currentPubkey', $pleb->pubkey)
->assertSet('currentPleb.pubkey', $pleb->pubkey);
});
it('handles nostr login for active member and initializes payment state', function () {
$pleb = EinundzwanzigPleb::factory()->active()->create();
expect($pleb->paymentEvents()->count())->toBe(0);
Livewire::test('association.profile')
->call('handleNostrLoggedIn', $pleb->pubkey)
->assertSet('currentPubkey', $pleb->pubkey)
->assertSet('currentPleb.pubkey', $pleb->pubkey)
->assertSet('amountToPay', config('app.env') === 'production' ? 21000 : 1);
expect($pleb->paymentEvents()->count())->toBeGreaterThan(0);
});
it('handles nostr logout correctly', function () {
$pleb = EinundzwanzigPleb::factory()->create();
Livewire::test('association.profile')
->call('handleNostrLoggedIn', $pleb->pubkey)
->call('handleNostrLoggedOut')
->assertSet('currentPubkey', null)
->assertSet('currentPleb', null);
});
it('can save email address', function () {
$pleb = EinundzwanzigPleb::factory()->create();
NostrAuth::login($pleb->pubkey);
Livewire::test('association.profile')
->set('profileForm.email', 'test@example.com')
->call('saveEmail')
->assertHasNoErrors();
expect($pleb->fresh()->email)->toBe('test@example.com');
});
it('validates email format', function () {
$pleb = EinundzwanzigPleb::factory()->create();
NostrAuth::login($pleb->pubkey);
Livewire::test('association.profile')
->set('profileForm.email', 'invalid-email')
->call('saveEmail')
->assertHasErrors(['profileForm.email']);
});
it('can save nip05 handle', function () {
$pleb = EinundzwanzigPleb::factory()->active()->create();
NostrAuth::login($pleb->pubkey);
Livewire::test('association.profile')
->set('profileForm.nip05Handle', 'test-handle')
->call('saveNip05Handle')
->assertHasNoErrors();
expect($pleb->fresh()->nip05_handle)->toBe('test-handle');
});
it('validates nip05 handle format', function () {
$pleb = EinundzwanzigPleb::factory()->active()->create();
NostrAuth::login($pleb->pubkey);
Livewire::test('association.profile')
->set('profileForm.nip05Handle', 'invalid handle with spaces')
->call('saveNip05Handle')
->assertHasErrors(['profileForm.nip05Handle']);
});
it('validates nip05 handle uniqueness', function () {
$pleb1 = EinundzwanzigPleb::factory()->active()->create([
'nip05_handle' => 'taken-handle',
]);
$pleb2 = EinundzwanzigPleb::factory()->active()->create();
NostrAuth::login($pleb2->pubkey);
Livewire::test('association.profile')
->set('profileForm.nip05Handle', 'taken-handle')
->call('saveNip05Handle')
->assertHasErrors(['profileForm.nip05Handle']);
});
it('can save null nip05 handle', function () {
$pleb = EinundzwanzigPleb::factory()->active()->create([
'nip05_handle' => 'old-handle',
]);
NostrAuth::login($pleb->pubkey);
Livewire::test('association.profile')
->set('profileForm.nip05Handle', null)
->call('saveNip05Handle')
->assertHasNoErrors();
expect($pleb->fresh()->nip05_handle)->toBeNull();
});
it('can update no email preference', function () {
$pleb = EinundzwanzigPleb::factory()->create();
NostrAuth::login($pleb->pubkey);
Livewire::test('association.profile')
->set('no', true)
->assertSet('showEmail', false);
expect($pleb->fresh()->no_email)->toBeTrue();
});
it('can save membership application', function () {
$pleb = EinundzwanzigPleb::factory()->create([
'association_status' => AssociationStatus::DEFAULT,
]);
NostrAuth::login($pleb->pubkey);
Livewire::test('association.profile')
->set('form.check', true)
->call('save', AssociationStatus::PASSIVE->value)
->assertHasNoErrors();
expect($pleb->fresh()->association_status)->toBe(AssociationStatus::PASSIVE);
});
it('creates payment event when pleb becomes active', function () {
$pleb = EinundzwanzigPleb::factory()->active()->create();
NostrAuth::login($pleb->pubkey);
Livewire::test('association.profile')
->assertSet('amountToPay', config('app.env') === 'production' ? 21000 : 1);
expect($pleb->paymentEvents()->count())->toBeGreaterThan(0);
});
it('displays paid status for current year', function () {
$pleb = EinundzwanzigPleb::factory()->active()->withPaidCurrentYear()->create();
NostrAuth::login($pleb->pubkey);
Livewire::test('association.profile')
->call('listenForPayment')
->assertSet('currentYearIsPaid', true);
});
it('can initiate payment', function () {
Http::fake([
'pay.einundzwanzig.space/*' => Http::response([
'id' => 'invoice123',
'checkoutLink' => 'https://pay.einundzwanzig.space/checkout/invoice123',
], 200),
]);
$pleb = EinundzwanzigPleb::factory()->active()->create();
NostrAuth::login($pleb->pubkey);
$response = Livewire::test('association.profile')
->call('pay', 'test-comment');
$response->assertRedirect();
});
it('removes expired invoices so a fresh payment event is available', function () {
$pleb = EinundzwanzigPleb::factory()->active()->create();
$pleb->paymentEvents()->create([
'year' => date('Y'),
'amount' => 21000,
'event_id' => 'event-old',
'btc_pay_invoice' => 'invoice-old',
]);
Http::fake([
'https://pay.einundzwanzig.space/*' => Http::response([
'id' => 'invoice-old',
'status' => 'Expired',
'expirationTime' => now()->subMinutes(5)->toIso8601String(),
'monitoringExpiration' => now()->toIso8601String(),
], 200),
]);
NostrAuth::login($pleb->pubkey);
Livewire::test('association.profile')
->assertSet('invoiceStatus', 'Expired')
->assertSet('invoiceStatusVariant', 'warning');
$pleb->refresh();
expect($pleb->paymentEvents()->count())->toBe(1);
expect($pleb->paymentEvents()->first()->btc_pay_invoice)->toBeNull();
});
it('shows invoice status details including remaining validity', function () {
$pleb = EinundzwanzigPleb::factory()->active()->create();
$pleb->paymentEvents()->create([
'year' => date('Y'),
'amount' => 21000,
'event_id' => 'event-status',
'btc_pay_invoice' => 'invoice-new',
]);
Http::fake([
'https://pay.einundzwanzig.space/*' => Http::response([
'id' => 'invoice-new',
'status' => 'New',
'expirationTime' => now()->addMinutes(30)->toIso8601String(),
'monitoringExpiration' => now()->addHours(2)->toIso8601String(),
], 200),
]);
NostrAuth::login($pleb->pubkey);
$component = Livewire::test('association.profile')
->call('listenForPayment')
->assertSet('invoiceStatus', 'New')
->assertSet('invoiceStatusVariant', 'info');
expect($component->get('invoiceExpiresAt'))->not->toBeNull();
expect($component->get('invoiceExpiresIn'))->not->toBeNull();
});
it('handles settled invoice with numeric expiration timestamps', function () {
$pleb = EinundzwanzigPleb::factory()->active()->create();
$pleb->paymentEvents()->create([
'year' => date('Y'),
'amount' => 21000,
'event_id' => 'event-real',
'btc_pay_invoice' => 'invoice-real',
]);
Http::fake([
'https://pay.einundzwanzig.space/*' => Http::response([
'id' => 'invoice-real',
'status' => 'Settled',
'additionalStatus' => 'None',
'monitoringExpiration' => now()->addDay()->timestamp,
'expirationTime' => now()->addHour()->timestamp,
'createdTime' => now()->subDay()->timestamp,
'amount' => '21000',
'paidAmount' => '21000',
], 200),
]);
NostrAuth::login($pleb->pubkey);
Livewire::test('association.profile')
->call('listenForPayment')
->assertSet('invoiceStatus', 'Settled')
->assertSet('invoiceStatusVariant', 'success')
->assertSet('currentYearIsPaid', true)
->assertSet('invoiceStatusMessage', 'Zahlung bestätigt. Danke!');
});
it('does not show stale settled status when invoice check fails', function () {
$pleb = EinundzwanzigPleb::factory()->active()->create();
$pleb->paymentEvents()->create([
'year' => date('Y'),
'amount' => 21000,
'event_id' => 'event-fail',
'btc_pay_invoice' => 'invoice-fail',
'paid' => true,
]);
Http::fake([
'https://pay.einundzwanzig.space/*' => Http::response([], 500),
]);
NostrAuth::login($pleb->pubkey);
// With API failure, the component should show error status regardless of previous state
// Locked properties prevent client-side tampering, so we verify the API failure handling directly
Livewire::test('association.profile')
->call('listenForPayment')
->assertSet('invoiceStatus', null)
->assertSet('invoiceStatusLabel', 'Status unbekannt')
->assertSet('invoiceStatusVariant', 'danger')
->assertSet('invoiceStatusMessage', 'Die Rechnung konnte nicht überprüft werden. Bitte versuche es später erneut.')
->assertSet('currentYearIsPaid', true);
});