key = $key; } public function recommendations(array $countryCodes = [], int $limit = 5): array { return [[ 'hostname' => 'nl.example.invalid', 'address' => '198.51.100.10', 'country' => 'NL', 'public_key' => $this->key, ]]; } public function credentials(string $token): string { $this->token = $token; return $this->key; } } class BootstrapperTestConfiguration extends ManagedConfiguration { private Settings $settings; public array $provisioned = []; public function __construct() { $this->settings = new Settings(); } public function settings(): Settings { return $this->settings; } public function provision(string $privateKey, array $candidate): void { $this->provisioned = [$privateKey, $candidate]; } public function repairGatewayInterface(): bool { return false; } } class BootstrapperTestRunner extends SystemRunner { public array $operations = []; public function applyWireGuard(): void { $this->operations[] = 'wireguard'; } public function applyRouting(): void { $this->operations[] = 'routing'; } public function applyFirewall(): void { $this->operations[] = 'firewall'; } } final class BootstrapperTest extends TestCase { public function testBootstrapQueuesReconciliationAfterApplyingInitialTunnel(): void { $key = base64_encode(str_repeat("\0", 32)); $api = new BootstrapperTestApi($key); $configuration = new BootstrapperTestConfiguration(); $runner = new BootstrapperTestRunner(); $statusPath = sys_get_temp_dir() . '/nordvpn-bootstrap-' . bin2hex(random_bytes(8)) . '.json'; $lockPath = sys_get_temp_dir() . '/nordvpn-bootstrap-' . bin2hex(random_bytes(8)) . '.lock'; $endpointDirectory = sys_get_temp_dir() . '/nordvpn-bootstrap-' . bin2hex(random_bytes(8)); $endpointPath = $endpointDirectory . '/endpoints.txt'; $configuration->settings()->general->exempt_wireguard = '1'; $queued = 0; $result = (new Bootstrapper( $api, $configuration, new StatusStore($statusPath), $runner, new EndpointStore($endpointPath), new ReconciliationLock($lockPath), static function () use (&$queued): void { $queued++; } ))->run('synthetic-token'); self::assertSame('ok', $result['status']); self::assertSame('synthetic-token', $api->token); self::assertSame($key, $configuration->provisioned[0]); self::assertSame(['firewall', 'wireguard', 'routing', 'firewall'], $runner->operations); self::assertSame("198.51.100.10\n", file_get_contents($endpointPath)); self::assertSame('reconciling', $result['tunnel']['state']); self::assertSame(1, $queued); unlink($statusPath); unlink($lockPath); unlink($endpointPath); rmdir($endpointDirectory); } }