'203.0.113.10']]; }); $store = new EndpointStore($path); $this->assertTrue($store->refreshIfStale($api)); $this->assertTrue($store->available()); $this->assertSame("203.0.113.10\n", file_get_contents($path)); $this->assertFalse($store->refreshIfStale($api)); $this->assertSame(1, $requests); unlink($path); rmdir($directory); } public function testRfc1918WarningFollowsDestinationSelectorSemantics(): void { $inspector = new EnrollmentInspector([ 'private' => ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'], 'public' => ['203.0.113.0/24'], 'nested' => ['private'], ]); $this->assertTrue($inspector->includesPrivateDestinations('any', false)); $this->assertTrue($inspector->includesPrivateDestinations('nested', false)); $this->assertFalse($inspector->includesPrivateDestinations('public', false)); $this->assertFalse($inspector->includesPrivateDestinations('2001:db8::/32', false)); $this->assertFalse($inspector->includesPrivateDestinations('private', true)); $this->assertTrue($inspector->includesPrivateDestinations('10.0.0.0/8', true)); } public function testStatusWritesAtomicallyAndRetainsDefaults(): void { $path = sys_get_temp_dir() . '/nordvpn-status-' . bin2hex(random_bytes(8)) . '.json'; $store = new StatusStore($path); $this->assertFalse($store->read()['available']); $store->write(['available' => true, 'state' => 'available']); $status = $store->read(); $this->assertTrue($status['available']); $this->assertGreaterThan(0, $status['updated_at']); unlink($path); } public function testReconciliationLockRejectsConcurrentOperations(): void { $path = sys_get_temp_dir() . '/nordvpn-lock-' . bin2hex(random_bytes(8)); $first = new ReconciliationLock($path); $second = new ReconciliationLock($path); $first->acquire(); try { $second->acquire(); $this->fail('A concurrent operation acquired the lock.'); } catch (RuntimeException $exception) { $this->assertSame('A NordVPN operation is already running.', $exception->getMessage()); } finally { $first->release(); } $second->acquire(); $second->release(); unlink($path); } }