diff --git a/app/Actions/GetOoklaSpeedtestServers.php b/app/Actions/GetOoklaSpeedtestServers.php index 54f33bc10..7a85d087e 100644 --- a/app/Actions/GetOoklaSpeedtestServers.php +++ b/app/Actions/GetOoklaSpeedtestServers.php @@ -35,13 +35,14 @@ public function handle(): array /** * Fetch the raw Ookla server array from the Ookla API. */ - public static function fetch(): array + public static function fetch(?string $search = null): array { - $query = [ + $query = array_filter([ 'engine' => 'js', 'https_functional' => true, 'limit' => 20, - ]; + 'search' => $search, + ]); try { $response = Http::retry(3, 250) @@ -58,6 +59,28 @@ public static function fetch(): array } } + /** + * For UI search: return the ID, Sponsor, and Name matching the query. + */ + public static function search(string $query): array + { + if (strlen($query) < 3) { + return []; + } + + $servers = self::fetch($query); + + if (empty($servers) || ! is_array($servers) || (isset($servers[0]) && ! is_array($servers[0]))) { + return []; + } + + return collect($servers)->mapWithKeys(function (array $item) { + return [ + $item['id'] => ($item['sponsor'] ?? 'Unknown').' ('.($item['name'] ?? 'Unknown').', '.$item['id'].')', + ]; + })->toArray(); + } + /** * For API: return array of structured server objects */ diff --git a/app/Livewire/Topbar/Actions.php b/app/Livewire/Topbar/Actions.php index 9077724c8..7d4386f6d 100644 --- a/app/Livewire/Topbar/Actions.php +++ b/app/Livewire/Topbar/Actions.php @@ -48,6 +48,8 @@ public function speedtestAction(): Action __('results.closest_servers') => GetOoklaSpeedtestServers::run(), ]); }) + ->getSearchResultsUsing(fn (string $search): array => GetOoklaSpeedtestServers::search($search)) + ->getOptionLabelUsing(fn (int|string $value): string => (string) $value) ->searchable(), ]) ->action(function (array $data) { diff --git a/composer.json b/composer.json index 20436421a..7f891e20e 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,6 @@ "filament/filament": "^5.2", "filament/spatie-laravel-settings-plugin": "^5.2", "influxdata/influxdb-client-php": "^3.8", - "laravel-notification-channels/telegram": "^6.0", "laravel/framework": "^12.50.0", "laravel/prompts": "^0.3.11", "laravel/sanctum": "^4.3.0", diff --git a/composer.lock b/composer.lock index 3437076e4..1cfd979d1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1cce055c0f3fe0da6e6b110f9ce6c3e9", + "content-hash": "30d545596d328a0b0fd2d5f839b7c8ad", "packages": [ { "name": "anourvalar/eloquent-serialize", diff --git a/lang/en/results.php b/lang/en/results.php index 8b625d37a..a2b547339 100644 --- a/lang/en/results.php +++ b/lang/en/results.php @@ -70,7 +70,7 @@ // Run Speedtest Action 'speedtest' => 'Speedtest', 'select_server' => 'Select Server', - 'select_server_helper' => 'Leave empty to run the speedtest without specifying a server. Blocked servers will be skipped.', + 'select_server_helper' => 'Leave empty to run the speedtest without specifying a server. Type to search for a server by name or location. Blocked servers will be skipped.', 'manual_servers' => 'Manual servers', 'closest_servers' => 'Closest servers', 'run_speedtest' => 'Run Speedtest', diff --git a/tests/Feature/GetOoklaSpeedtestServersTest.php b/tests/Feature/GetOoklaSpeedtestServersTest.php new file mode 100644 index 000000000..f8ef35f19 --- /dev/null +++ b/tests/Feature/GetOoklaSpeedtestServersTest.php @@ -0,0 +1,91 @@ +toBe([]); + Http::assertNothingSent(); + }); + + test('returns formatted servers matching the search query', function () { + Http::fake([ + 'www.speedtest.net/api/js/servers*' => Http::response([ + ['id' => 12345, 'sponsor' => 'Acme ISP', 'name' => 'London', 'host' => 'acme.example.com', 'country' => 'UK'], + ['id' => 67890, 'sponsor' => 'BetaNet', 'name' => 'Manchester', 'host' => 'beta.example.com', 'country' => 'UK'], + ]), + ]); + + $result = GetOoklaSpeedtestServers::search('London'); + + expect($result)->toBe([ + 12345 => 'Acme ISP (London, 12345)', + 67890 => 'BetaNet (Manchester, 67890)', + ]); + }); + + test('sends the search term to the Ookla API', function () { + Http::fake([ + 'www.speedtest.net/api/js/servers*' => Http::response([]), + ]); + + GetOoklaSpeedtestServers::search('Paris'); + + Http::assertSent(function ($request) { + return str_contains($request->url(), 'search=Paris'); + }); + }); + + test('returns empty array when API response is not a list of servers', function () { + Http::fake([ + 'www.speedtest.net/api/js/servers*' => Http::response(['⚠️ error message']), + ]); + + $result = GetOoklaSpeedtestServers::search('London'); + + expect($result)->toBe([]); + }); + + test('returns empty array when API returns empty list', function () { + Http::fake([ + 'www.speedtest.net/api/js/servers*' => Http::response([]), + ]); + + $result = GetOoklaSpeedtestServers::search('Nowhere'); + + expect($result)->toBe([]); + }); + }); + + describe('fetch', function () { + test('passes search parameter to API when provided', function () { + Http::fake([ + 'www.speedtest.net/api/js/servers*' => Http::response([]), + ]); + + GetOoklaSpeedtestServers::fetch('Berlin'); + + Http::assertSent(function ($request) { + return str_contains($request->url(), 'search=Berlin'); + }); + }); + + test('does not pass search parameter when null', function () { + Http::fake([ + 'www.speedtest.net/api/js/servers*' => Http::response([]), + ]); + + GetOoklaSpeedtestServers::fetch(); + + Http::assertSent(function ($request) { + return ! str_contains($request->url(), 'search='); + }); + }); + }); +});