2026-08-07 01:01:58 +09:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
use OPNsense\NordVPN\NordApi;
|
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
|
|
|
|
|
|
final class NordApiTest extends TestCase
|
|
|
|
|
{
|
|
|
|
|
private string $key;
|
|
|
|
|
|
|
|
|
|
protected function setUp(): void
|
|
|
|
|
{
|
|
|
|
|
$this->key = base64_encode(str_repeat("\0", 32));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testCredentialsUseNordVpnAccessTokenAuthorization(): void
|
|
|
|
|
{
|
|
|
|
|
$api = new NordApi(function ($path, $query, $authorization): array {
|
|
|
|
|
$this->assertSame('/v1/users/services/credentials', $path);
|
|
|
|
|
$this->assertSame([], $query);
|
|
|
|
|
$this->assertSame('Bearer token:synthetic-token', $authorization);
|
|
|
|
|
return ['nordlynx_private_key' => $this->key];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$this->assertSame($this->key, $api->credentials('synthetic-token'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testRecommendationsApplyCountryAllowlist(): void
|
|
|
|
|
{
|
|
|
|
|
$requests = [];
|
|
|
|
|
$api = new NordApi(function ($path, $query) use (&$requests): array {
|
|
|
|
|
$requests[] = [$path, $query];
|
|
|
|
|
if ($path === '/v1/servers/countries') {
|
|
|
|
|
return [['id' => 99, 'code' => 'nl', 'name' => 'Netherlands']];
|
|
|
|
|
}
|
|
|
|
|
return [[
|
|
|
|
|
'hostname' => 'nl.example.invalid',
|
|
|
|
|
'station' => '198.51.100.10',
|
|
|
|
|
'locations' => [['country' => ['code' => 'nl']]],
|
|
|
|
|
'technologies' => [[
|
|
|
|
|
'id' => 35,
|
|
|
|
|
'pivot' => ['status' => 'online'],
|
|
|
|
|
'metadata' => [['name' => 'public_key', 'value' => $this->key]],
|
|
|
|
|
]],
|
|
|
|
|
]];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$servers = $api->recommendations(['NL']);
|
|
|
|
|
|
|
|
|
|
$this->assertCount(1, $servers);
|
|
|
|
|
$this->assertSame('NL', $servers[0]['country']);
|
|
|
|
|
$this->assertSame(99, $requests[1][1]['filters[country_id]']);
|
|
|
|
|
$this->assertSame(35, $requests[1][1]['filters[servers_technologies]']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testEmptyCountryAllowlistQueriesRecommendationsDirectly(): void
|
|
|
|
|
{
|
|
|
|
|
$api = new NordApi(static function ($path): array {
|
|
|
|
|
if ($path === '/v1/servers/countries') {
|
|
|
|
|
throw new RuntimeException('countries should not be queried');
|
|
|
|
|
}
|
|
|
|
|
return [];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$this->assertSame([], $api->recommendations([]));
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-07 02:30:26 +09:00
|
|
|
public function testRecommendationsRequestEverySelectedCountry(): void
|
|
|
|
|
{
|
|
|
|
|
$requests = [];
|
|
|
|
|
$api = new NordApi(function ($path, $query) use (&$requests): array {
|
|
|
|
|
$requests[] = [$path, $query];
|
|
|
|
|
if ($path === '/v1/servers/countries') {
|
|
|
|
|
return [
|
|
|
|
|
['id' => 1, 'code' => 'nl', 'name' => 'Netherlands'],
|
|
|
|
|
['id' => 2, 'code' => 'kr', 'name' => 'Korea'],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
$country = $query['filters[country_id]'] === 1 ? 'NL' : 'KR';
|
|
|
|
|
return [[
|
|
|
|
|
'hostname' => strtolower($country) . '.example.invalid',
|
|
|
|
|
'station' => $country === 'NL' ? '198.51.100.10' : '198.51.100.11',
|
|
|
|
|
'locations' => [['country' => ['code' => $country]]],
|
|
|
|
|
'technologies' => [[
|
|
|
|
|
'id' => 35,
|
|
|
|
|
'pivot' => ['status' => 'online'],
|
|
|
|
|
'metadata' => [['name' => 'public_key', 'value' => $this->key]],
|
|
|
|
|
]],
|
|
|
|
|
]];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$servers = $api->recommendations(['NL', 'KR'], 1);
|
|
|
|
|
|
|
|
|
|
self::assertSame(1, $requests[1][1]['filters[country_id]']);
|
|
|
|
|
self::assertSame(2, $requests[2][1]['filters[country_id]']);
|
|
|
|
|
self::assertSame(['NL', 'KR'], array_column($servers, 'country'));
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-07 01:01:58 +09:00
|
|
|
public function testEndpointDiscoveryPaginatesCompleteNordLynxAddressSet(): void
|
|
|
|
|
{
|
|
|
|
|
$requests = [];
|
|
|
|
|
$api = new NordApi(static function ($path, $query) use (&$requests): array {
|
|
|
|
|
$requests[] = [$path, $query];
|
|
|
|
|
if ($query['offset'] === 0) {
|
|
|
|
|
return array_map(
|
|
|
|
|
static fn($index) => ['station' => sprintf('198.51.%d.%d', intdiv($index, 250), $index % 250 + 1)],
|
2026-08-07 02:30:26 +09:00
|
|
|
range(0, 999)
|
2026-08-07 01:01:58 +09:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return [['station' => '203.0.113.10'], ['station' => 'invalid']];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$addresses = $api->endpointAddresses();
|
|
|
|
|
|
2026-08-07 02:30:26 +09:00
|
|
|
$this->assertCount(1001, $addresses);
|
2026-08-07 01:01:58 +09:00
|
|
|
$this->assertSame('/v1/servers', $requests[0][0]);
|
2026-08-07 02:30:26 +09:00
|
|
|
$this->assertSame(1000, $requests[0][1]['limit']);
|
|
|
|
|
$this->assertSame(1000, $requests[1][1]['offset']);
|
2026-08-07 01:01:58 +09:00
|
|
|
$this->assertSame(35, $requests[0][1]['filters[servers_technologies]']);
|
|
|
|
|
}
|
|
|
|
|
}
|