test(Infrastructure::class) ->assertForbidden(); }); it('loads the .env-derived value when nothing has been saved from the console yet', function () { config()->set('provisioning.dns.zone', 'env-zone.example'); Livewire::actingAs(operator('Owner'), 'operator') ->test(Infrastructure::class) ->assertSet('dnsZone', 'env-zone.example'); }); it('saves every field, and the consumer reads the saved value back', function () { Livewire::actingAs(operator('Owner'), 'operator') ->test(Infrastructure::class) ->set('dnsZone', 'console-zone.example') ->set('wgEndpoint', 'vpn.example:51820') ->set('wgHubPubkey', 'hubpubkey==') ->set('traefikDynamicPath', '/etc/traefik/dynamic') ->set('sshPublicKey', 'ssh-ed25519 AAAAC3 clupilot') ->set('monitoringUrl', 'http://kuma-bridge:8080') ->call('save') ->assertHasNoErrors(); // Not just that Settings holds the value — that the class every real // consumer (HttpHetznerDnsClient, LocalWireguardHub, SshTraefikWriter, // EstablishSshTrust, HttpMonitoringClient) actually calls returns it. expect(ProvisioningSettings::dnsZone())->toBe('console-zone.example') ->and(ProvisioningSettings::wgEndpoint())->toBe('vpn.example:51820') ->and(ProvisioningSettings::wgHubPublicKey())->toBe('hubpubkey==') ->and(ProvisioningSettings::traefikDynamicPath())->toBe('/etc/traefik/dynamic') ->and(ProvisioningSettings::sshPublicKey())->toBe('ssh-ed25519 AAAAC3 clupilot') ->and(ProvisioningSettings::monitoringUrl())->toBe('http://kuma-bridge:8080'); }); it('refuses a monitoring URL that is not a URL', function () { Livewire::actingAs(operator('Owner'), 'operator') ->test(Infrastructure::class) ->set('monitoringUrl', 'not a url') ->call('save') ->assertHasErrors('monitoringUrl'); });