subSeconds($seconds), rxBytes: 100, txBytes: 100, ); } it('reports not_configured for a host with no wg_pubkey at all', function () { $host = Host::factory()->create(['wg_pubkey' => null]); expect($host->vpnState(null, hubReachable: true))->toBe('not_configured'); }); it('reports connected for a fresh handshake', function () { $host = Host::factory()->create(['wg_pubkey' => 'HOSTPUB=']); $snapshot = snapshotAgedSeconds('HOSTPUB=', 30); expect($host->vpnState($snapshot, hubReachable: true))->toBe('connected'); }); it('reports silent for a stale handshake', function () { $host = Host::factory()->create(['wg_pubkey' => 'HOSTPUB=']); $snapshot = snapshotAgedSeconds('HOSTPUB=', 60 * 60 * 24 * 2); // two days expect($host->vpnState($snapshot, hubReachable: true))->toBe('silent'); }); it('reports silent for a configured peer that never handshaked', function () { $host = Host::factory()->create(['wg_pubkey' => 'HOSTPUB=']); $snapshot = new PeerSnapshot('HOSTPUB=', null, '10.66.0.9/32', null, 0, 0); expect($host->vpnState($snapshot, hubReachable: true))->toBe('silent'); }); it('reports unknown when the hub could not be asked, even for a configured host', function () { $host = Host::factory()->create(['wg_pubkey' => 'HOSTPUB=']); expect($host->vpnState(null, hubReachable: false))->toBe('unknown'); }); it('never reports not_configured as unknown or vice versa regardless of hub reachability', function () { $unconfigured = Host::factory()->create(['wg_pubkey' => null]); expect($unconfigured->vpnState(null, hubReachable: false))->toBe('not_configured'); }); /** * Pins the exact threshold boundary — this is the comparison the mutation * pass targets (`<=` flipped to `<`, or the constant changed) to prove the * test actually fails when a stale handshake would otherwise read as connected. */ it('treats a handshake older than the freshness threshold as silent, not connected', function () { $host = Host::factory()->create(['wg_pubkey' => 'HOSTPUB=']); // Frozen clock: a wall-clock version of this test is one CPU hiccup away // from putting "now" a millisecond past the boundary it means to sit at. // travelTo() freezes both Carbon and CarbonImmutable's now(). $now = CarbonImmutable::parse('2026-07-28 12:00:00'); $this->travelTo($now); $atThreshold = new PeerSnapshot('HOSTPUB=', null, '10.66.0.9/32', $now->subSeconds(Host::VPN_HANDSHAKE_FRESH_SECONDS), 0, 0); $pastThreshold = new PeerSnapshot('HOSTPUB=', null, '10.66.0.9/32', $now->subSeconds(Host::VPN_HANDSHAKE_FRESH_SECONDS + 1), 0, 0); expect($host->vpnState($atThreshold, hubReachable: true))->toBe('connected') ->and($host->vpnState($pastThreshold, hubReachable: true))->toBe('silent'); $this->travelBack(); }); it('renders the three VPN states distinctly in the hosts list', function () { $hub = new FakeWireguardHub; app()->instance(WireguardHub::class, $hub); $unconfigured = Host::factory()->active()->create(['name' => 'pve-none-1', 'wg_pubkey' => null]); $connected = Host::factory()->active()->create(['name' => 'pve-conn-1', 'wg_pubkey' => 'CONNPUB=']); $silent = Host::factory()->active()->create(['name' => 'pve-silent-1', 'wg_pubkey' => 'SILENTPUB=']); $hub->addPeer('CONNPUB=', '10.66.0.10'); $hub->addPeer('SILENTPUB=', '10.66.0.11'); $hub->snapshots['CONNPUB='] = snapshotAgedSeconds('CONNPUB=', 20); $hub->snapshots['SILENTPUB='] = snapshotAgedSeconds('SILENTPUB=', 60 * 60 * 24 * 3); $response = $this->actingAs(admin(), 'operator')->get(route('admin.hosts')); $response->assertOk() ->assertSeeInOrder([$unconfigured->name, __('hosts.vpn.not_configured')]) ->assertSeeInOrder([$connected->name, __('hosts.vpn.connected')]) // "Still · vor 3 Tagen" (or English "Silent · 3 days ago") — the fixed // word proves the state, the diffForHumans() tail proves "how long". ->assertSee(__('hosts.vpn.silent')); }); it('shows unknown, not a false connected or silent, when the hub is unreachable', function () { $hub = new FakeWireguardHub; $hub->failPeers = true; app()->instance(WireguardHub::class, $hub); $host = Host::factory()->active()->create(['name' => 'pve-unreachable-1', 'wg_pubkey' => 'HOSTPUB=']); $this->actingAs(admin(), 'operator')->get(route('admin.hosts')) ->assertOk() ->assertSeeInOrder([$host->name, __('hosts.vpn.unknown')]); }); it('calls the hub once for the whole list, not once per host', function () { $hub = new FakeWireguardHub; app()->instance(WireguardHub::class, $hub); foreach (range(1, 5) as $i) { Host::factory()->active()->create(['wg_pubkey' => 'PUB'.$i.'=']); } Livewire::actingAs(admin(), 'operator')->test(Hosts::class); expect($hub->peersCalls)->toBe(1); });