package updater import ( "context" "io" "net/http" "net/netip" "strings" "testing" "github.com/qdm12/gluetun/internal/constants/vpn" "github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/provider/common" "github.com/stretchr/testify/assert" ) func Test_Updater_FetchServers(t *testing.T) { t.Parallel() testCases := map[string]struct { // Inputs minServers int // From API responseStatus int responseBody string // Output servers []models.Server errWrapped error errMessage string }{ "http_response_error": { responseStatus: http.StatusNoContent, errWrapped: common.ErrHTTPStatusCodeNotOK, errMessage: "fetching API: HTTP status code not OK: 204 No Content", }, "success_field_false": { responseStatus: http.StatusOK, responseBody: `{"success": false}`, errWrapped: ErrResponseSuccessFalse, errMessage: "response success field is false", }, "validation_failed": { responseStatus: http.StatusOK, responseBody: `{ "success": true, "datacenters": [ { "city": "Vienna", "servers": [ {} ] } ] }`, errWrapped: ErrCountryNameNotSet, errMessage: "validating data center 1 of 1: data center Vienna: country name is not set", }, "not_enough_servers": { minServers: 3, responseStatus: http.StatusOK, responseBody: `{ "success": true, "datacenters": [ { "city": "Vienna", "country_name": "Austria", "servers": [ { "ip": "37.120.212.227", "ptr": "vpn44.prd.vienna.ovpn.com", "online": true, "public_key": "r83LIc0Q2F8s3dY9x5y17Yz8wTADJc7giW1t5eSmoXc=", "wireguard_ports": [9929], "multihop_openvpn_port": 20044, "multihop_wireguard_port": 30044 } ] } ] }`, errWrapped: common.ErrNotEnoughServers, errMessage: "not enough servers found: 2 and expected at least 3", }, "success": { minServers: 2, responseBody: `{ "success": true, "datacenters": [ { "slug": "vienna", "city": "Vienna", "country": "AT", "country_name": "Austria", "pools": [ "pool-1.prd.at.vienna.ovpn.com" ], "ping_address": "37.120.212.227", "servers": [ { "ip": "37.120.212.227", "ptr": "vpn44.prd.vienna.ovpn.com", "name": "VPN44 - Vienna", "online": true, "load": 8, "public_key": "r83LIc0Q2F8s3dY9x5y17Yz8wTADJc7giW1t5eSmoXc=", "public_key_ipv4": "wFbSRyjSXBmkjJodlqz7DoYn3WNDPYFUIXyIUS2QU2A=", "wireguard_ports": [ 9929 ], "multihop_openvpn_port": 20044, "multihop_wireguard_port": 30044 }, { "ip": "37.120.212.228", "ptr": "vpn45.prd.vienna.ovpn.com", "online": false, "public_key": "r93LIc0Q2F8s3dY9x5y17Yz8wTADJc7giW1t5eSmoXc=", "wireguard_ports": [9929], "multihop_openvpn_port": 20045, "multihop_wireguard_port": 30045 } ] } ] }`, responseStatus: http.StatusOK, servers: []models.Server{ { Country: "Austria", City: "Vienna", Hostname: "vpn44.prd.vienna.ovpn.com", IPs: []netip.Addr{netip.MustParseAddr("37.120.212.227")}, VPN: vpn.OpenVPN, UDP: true, TCP: true, }, { Country: "Austria", City: "Vienna", Hostname: "vpn44.prd.vienna.ovpn.com", IPs: []netip.Addr{netip.MustParseAddr("37.120.212.227")}, VPN: vpn.Wireguard, WgPubKey: "r83LIc0Q2F8s3dY9x5y17Yz8wTADJc7giW1t5eSmoXc=", }, }, }, } for name, testCase := range testCases { t.Run(name, func(t *testing.T) { t.Parallel() ctx := context.Background() client := &http.Client{ Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) { assert.Equal(t, http.MethodGet, r.Method) assert.Equal(t, r.URL.String(), "https://www.ovpn.com/v2/api/client/entry") return &http.Response{ StatusCode: testCase.responseStatus, Status: http.StatusText(testCase.responseStatus), Body: io.NopCloser(strings.NewReader(testCase.responseBody)), }, nil }), } updater := &Updater{ client: client, } servers, err := updater.FetchServers(ctx, testCase.minServers) assert.Equal(t, testCase.servers, servers) assert.ErrorIs(t, err, testCase.errWrapped) if testCase.errWrapped != nil { assert.EqualError(t, err, testCase.errMessage) } }) } }