chore(all): provider to servers map in allServers

- Simplify formatting CLI
- Simplify updater code
- Simplify filter choices for config validation
- Simplify all servers deep copying
- Custom JSON marshaling methods for `AllServers`
- Simplify provider constructor switch
- Simplify storage merging
- Simplify storage reading and extraction
- Simplify updating code
This commit is contained in:
Quentin McGaw
2022-05-27 00:59:47 +00:00
parent 5ffe8555ba
commit bd0868d764
22 changed files with 854 additions and 1295 deletions

View File

@@ -11,14 +11,14 @@ import (
var _ Flusher = (*Storage)(nil)
type Flusher interface {
FlushToFile(allServers models.AllServers) error
FlushToFile(allServers *models.AllServers) error
}
func (s *Storage) FlushToFile(allServers models.AllServers) error {
func (s *Storage) FlushToFile(allServers *models.AllServers) error {
return flushToFile(s.filepath, allServers)
}
func flushToFile(path string, servers models.AllServers) error {
func flushToFile(path string, servers *models.AllServers) error {
dirPath := filepath.Dir(path)
if err := os.MkdirAll(dirPath, 0644); err != nil {
return err

View File

@@ -3,6 +3,8 @@ package storage
import (
"testing"
"github.com/qdm12/gluetun/internal/constants/providers"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -12,5 +14,13 @@ func Test_parseHardcodedServers(t *testing.T) {
servers, err := parseHardcodedServers()
require.NoError(t, err)
require.NotEmpty(t, len(servers.Cyberghost.Servers))
// all providers minus custom
allProviders := providers.All()
require.Equal(t, len(allProviders), len(servers.ProviderToServers))
for _, provider := range allProviders {
servers, ok := servers.ProviderToServers[provider]
assert.Truef(t, ok, "for provider %s", provider)
assert.NotEmptyf(t, servers, "for provider %s", provider)
}
}

View File

@@ -28,29 +28,20 @@ func (s *Storage) logTimeDiff(provider string, persistedUnix, hardcodedUnix int6
}
func (s *Storage) mergeServers(hardcoded, persisted models.AllServers) models.AllServers {
return models.AllServers{
Version: hardcoded.Version,
Cyberghost: s.mergeProviderServers(providers.Cyberghost, hardcoded.Cyberghost, persisted.Cyberghost),
Expressvpn: s.mergeProviderServers(providers.Expressvpn, hardcoded.Expressvpn, persisted.Expressvpn),
Fastestvpn: s.mergeProviderServers(providers.Fastestvpn, hardcoded.Fastestvpn, persisted.Fastestvpn),
HideMyAss: s.mergeProviderServers(providers.HideMyAss, hardcoded.HideMyAss, persisted.HideMyAss),
Ipvanish: s.mergeProviderServers(providers.Ipvanish, hardcoded.Ipvanish, persisted.Ipvanish),
Ivpn: s.mergeProviderServers(providers.Ivpn, hardcoded.Ivpn, persisted.Ivpn),
Mullvad: s.mergeProviderServers(providers.Mullvad, hardcoded.Mullvad, persisted.Mullvad),
Nordvpn: s.mergeProviderServers(providers.Nordvpn, hardcoded.Nordvpn, persisted.Nordvpn),
Perfectprivacy: s.mergeProviderServers(providers.Perfectprivacy, hardcoded.Perfectprivacy, persisted.Perfectprivacy),
Privado: s.mergeProviderServers(providers.Privado, hardcoded.Privado, persisted.Privado),
Pia: s.mergeProviderServers(providers.PrivateInternetAccess, hardcoded.Pia, persisted.Pia),
Privatevpn: s.mergeProviderServers(providers.Privatevpn, hardcoded.Privatevpn, persisted.Privatevpn),
Protonvpn: s.mergeProviderServers(providers.Protonvpn, hardcoded.Protonvpn, persisted.Protonvpn),
Purevpn: s.mergeProviderServers(providers.Purevpn, hardcoded.Purevpn, persisted.Purevpn),
Surfshark: s.mergeProviderServers(providers.Surfshark, hardcoded.Surfshark, persisted.Surfshark),
Torguard: s.mergeProviderServers(providers.Torguard, hardcoded.Torguard, persisted.Torguard),
VPNUnlimited: s.mergeProviderServers(providers.VPNUnlimited, hardcoded.VPNUnlimited, persisted.VPNUnlimited),
Vyprvpn: s.mergeProviderServers(providers.Vyprvpn, hardcoded.Vyprvpn, persisted.Vyprvpn),
Wevpn: s.mergeProviderServers(providers.Wevpn, hardcoded.Wevpn, persisted.Wevpn),
Windscribe: s.mergeProviderServers(providers.Windscribe, hardcoded.Windscribe, persisted.Windscribe),
allProviders := providers.All()
merged := models.AllServers{
Version: hardcoded.Version,
ProviderToServers: make(map[string]models.Servers, len(allProviders)),
}
for _, provider := range allProviders {
hardcodedServers := hardcoded.ProviderToServers[provider]
persistedServers := persisted.ProviderToServers[provider]
merged.ProviderToServers[provider] = s.mergeProviderServers(provider,
hardcodedServers, persistedServers)
}
return merged
}
func (s *Storage) mergeProviderServers(provider string,

View File

@@ -38,172 +38,39 @@ func (s *Storage) readFromFile(filepath string, hardcoded models.AllServers) (
func (s *Storage) extractServersFromBytes(b []byte, hardcoded models.AllServers) (
servers models.AllServers, err error) {
var versions allVersions
if err := json.Unmarshal(b, &versions); err != nil {
return servers, fmt.Errorf("cannot decode versions: %w", err)
}
var rawMessages allJSONRawMessages
rawMessages := make(map[string]json.RawMessage)
if err := json.Unmarshal(b, &rawMessages); err != nil {
return servers, fmt.Errorf("cannot decode servers: %w", err)
}
type element struct {
provider string
hardcoded models.Servers
serverVersion serverVersion
rawMessage json.RawMessage
target *models.Servers
}
elements := []element{
{
provider: providers.Cyberghost,
hardcoded: hardcoded.Cyberghost,
serverVersion: versions.Cyberghost,
rawMessage: rawMessages.Cyberghost,
target: &servers.Cyberghost,
},
{
provider: providers.Expressvpn,
hardcoded: hardcoded.Expressvpn,
serverVersion: versions.Expressvpn,
rawMessage: rawMessages.Expressvpn,
target: &servers.Expressvpn,
},
{
provider: providers.Fastestvpn,
hardcoded: hardcoded.Fastestvpn,
serverVersion: versions.Fastestvpn,
rawMessage: rawMessages.Fastestvpn,
target: &servers.Fastestvpn,
},
{
provider: providers.HideMyAss,
hardcoded: hardcoded.HideMyAss,
serverVersion: versions.HideMyAss,
rawMessage: rawMessages.HideMyAss,
target: &servers.HideMyAss,
},
{
provider: providers.Ipvanish,
hardcoded: hardcoded.Ipvanish,
serverVersion: versions.Ipvanish,
rawMessage: rawMessages.Ipvanish,
target: &servers.Ipvanish,
},
{
provider: providers.Ivpn,
hardcoded: hardcoded.Ivpn,
serverVersion: versions.Ivpn,
rawMessage: rawMessages.Ivpn,
target: &servers.Ivpn,
},
{
provider: providers.Mullvad,
hardcoded: hardcoded.Mullvad,
serverVersion: versions.Mullvad,
rawMessage: rawMessages.Mullvad,
target: &servers.Mullvad,
},
{
provider: providers.Nordvpn,
hardcoded: hardcoded.Nordvpn,
serverVersion: versions.Nordvpn,
rawMessage: rawMessages.Nordvpn,
target: &servers.Nordvpn,
},
{
provider: providers.Perfectprivacy,
hardcoded: hardcoded.Perfectprivacy,
serverVersion: versions.Perfectprivacy,
rawMessage: rawMessages.Perfectprivacy,
target: &servers.Perfectprivacy,
},
{
provider: providers.Privado,
hardcoded: hardcoded.Privado,
serverVersion: versions.Privado,
rawMessage: rawMessages.Privado,
target: &servers.Privado,
},
{
provider: providers.PrivateInternetAccess,
hardcoded: hardcoded.Pia,
serverVersion: versions.Pia,
rawMessage: rawMessages.Pia,
target: &servers.Pia,
},
{
provider: providers.Privatevpn,
hardcoded: hardcoded.Privatevpn,
serverVersion: versions.Privatevpn,
rawMessage: rawMessages.Privatevpn,
target: &servers.Privatevpn,
},
{
provider: providers.Protonvpn,
hardcoded: hardcoded.Protonvpn,
serverVersion: versions.Protonvpn,
rawMessage: rawMessages.Protonvpn,
target: &servers.Protonvpn,
},
{
provider: providers.Purevpn,
hardcoded: hardcoded.Purevpn,
serverVersion: versions.Purevpn,
rawMessage: rawMessages.Purevpn,
target: &servers.Purevpn,
},
{
provider: providers.Surfshark,
hardcoded: hardcoded.Surfshark,
serverVersion: versions.Surfshark,
rawMessage: rawMessages.Surfshark,
target: &servers.Surfshark,
},
{
provider: providers.Torguard,
hardcoded: hardcoded.Torguard,
serverVersion: versions.Torguard,
rawMessage: rawMessages.Torguard,
target: &servers.Torguard,
},
{
provider: providers.VPNUnlimited,
hardcoded: hardcoded.VPNUnlimited,
serverVersion: versions.VPNUnlimited,
rawMessage: rawMessages.VPNUnlimited,
target: &servers.VPNUnlimited,
},
{
provider: providers.Vyprvpn,
hardcoded: hardcoded.Vyprvpn,
serverVersion: versions.Vyprvpn,
rawMessage: rawMessages.Vyprvpn,
target: &servers.Vyprvpn,
},
{
provider: providers.Wevpn,
hardcoded: hardcoded.Wevpn,
serverVersion: versions.Wevpn,
rawMessage: rawMessages.Wevpn,
target: &servers.Wevpn,
},
{
provider: providers.Windscribe,
hardcoded: hardcoded.Windscribe,
serverVersion: versions.Windscribe,
rawMessage: rawMessages.Windscribe,
target: &servers.Windscribe,
},
}
// Note schema version is at map key "version" as number
for _, element := range elements {
*element.target, err = s.readServers(element.provider,
element.hardcoded, element.serverVersion, element.rawMessage)
if err != nil {
return servers, err
allProviders := providers.All()
servers.ProviderToServers = make(map[string]models.Servers, len(allProviders))
for _, provider := range allProviders {
hardcoded, ok := hardcoded.ProviderToServers[provider]
if !ok {
panic(fmt.Sprintf("provider %s not found in hardcoded servers map", provider))
}
rawMessage, ok := rawMessages[provider]
if !ok {
// If the provider is not found in the data bytes, just don't set it in
// the providers map. That way the hardcoded servers will override them.
// This is user provided and could come from different sources in the
// future (e.g. a file or API request).
continue
}
mergedServers, versionsMatch, err := s.readServers(provider, hardcoded, rawMessage)
if err != nil {
return models.AllServers{}, err
} else if !versionsMatch {
// mergedServers is the empty struct in this case, so don't set the key
// in the providerToServers map.
continue
}
servers.ProviderToServers[provider] = mergedServers
}
return servers, nil
@@ -214,73 +81,20 @@ var (
)
func (s *Storage) readServers(provider string, hardcoded models.Servers,
serverVersion serverVersion, rawMessage json.RawMessage) (
servers models.Servers, err error) {
rawMessage json.RawMessage) (servers models.Servers, versionsMatch bool, err error) {
provider = strings.Title(provider)
if hardcoded.Version != serverVersion.Version {
s.logVersionDiff(provider, hardcoded.Version, serverVersion.Version)
return servers, nil
}
err = json.Unmarshal(rawMessage, &servers)
var persistedServers models.Servers
err = json.Unmarshal(rawMessage, &persistedServers)
if err != nil {
return servers, fmt.Errorf("%w: %s: %s", errDecodeProvider, provider, err)
return servers, false, fmt.Errorf("%w: %s: %s", errDecodeProvider, provider, err)
}
return servers, nil
}
versionsMatch = hardcoded.Version == persistedServers.Version
if !versionsMatch {
s.logVersionDiff(provider, hardcoded.Version, persistedServers.Version)
return servers, versionsMatch, nil
}
// allVersions is a subset of models.AllServers structure used to track
// versions to avoid unmarshaling errors.
type allVersions struct {
Version uint16 `json:"version"` // used for migration of the top level scheme
Cyberghost serverVersion `json:"cyberghost"`
Expressvpn serverVersion `json:"expressvpn"`
Fastestvpn serverVersion `json:"fastestvpn"`
HideMyAss serverVersion `json:"hidemyass"`
Ipvanish serverVersion `json:"ipvanish"`
Ivpn serverVersion `json:"ivpn"`
Mullvad serverVersion `json:"mullvad"`
Nordvpn serverVersion `json:"nordvpn"`
Perfectprivacy serverVersion `json:"perfect privacy"`
Privado serverVersion `json:"privado"`
Pia serverVersion `json:"private internet access"`
Privatevpn serverVersion `json:"privatevpn"`
Protonvpn serverVersion `json:"protonvpn"`
Purevpn serverVersion `json:"purevpn"`
Surfshark serverVersion `json:"surfshark"`
Torguard serverVersion `json:"torguard"`
VPNUnlimited serverVersion `json:"vpn unlimited"`
Vyprvpn serverVersion `json:"vyprvpn"`
Wevpn serverVersion `json:"wevpn"`
Windscribe serverVersion `json:"windscribe"`
}
type serverVersion struct {
Version uint16 `json:"version"`
}
// allJSONRawMessages is to delay decoding of each provider servers.
type allJSONRawMessages struct {
Version uint16 `json:"version"` // used for migration of the top level scheme
Cyberghost json.RawMessage `json:"cyberghost"`
Expressvpn json.RawMessage `json:"expressvpn"`
Fastestvpn json.RawMessage `json:"fastestvpn"`
HideMyAss json.RawMessage `json:"hidemyass"`
Ipvanish json.RawMessage `json:"ipvanish"`
Ivpn json.RawMessage `json:"ivpn"`
Mullvad json.RawMessage `json:"mullvad"`
Nordvpn json.RawMessage `json:"nordvpn"`
Perfectprivacy json.RawMessage `json:"perfect privacy"`
Privado json.RawMessage `json:"privado"`
Pia json.RawMessage `json:"private internet access"`
Privatevpn json.RawMessage `json:"privatevpn"`
Protonvpn json.RawMessage `json:"protonvpn"`
Purevpn json.RawMessage `json:"purevpn"`
Surfshark json.RawMessage `json:"surfshark"`
Torguard json.RawMessage `json:"torguard"`
VPNUnlimited json.RawMessage `json:"vpn unlimited"`
Vyprvpn json.RawMessage `json:"vyprvpn"`
Wevpn json.RawMessage `json:"wevpn"`
Windscribe json.RawMessage `json:"windscribe"`
return persistedServers, versionsMatch, nil
}

View File

@@ -1,80 +1,89 @@
package storage
import (
"errors"
"fmt"
"testing"
gomock "github.com/golang/mock/gomock"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants/providers"
"github.com/qdm12/gluetun/internal/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func populateProviders(allProviderVersion uint16, allProviderTimestamp int64,
servers models.AllServers) models.AllServers {
allProviders := providers.All()
if servers.ProviderToServers == nil {
servers.ProviderToServers = make(map[string]models.Servers, len(allProviders)-1)
}
for _, provider := range allProviders {
_, has := servers.ProviderToServers[provider]
if has {
continue
}
servers.ProviderToServers[provider] = models.Servers{
Version: allProviderVersion,
Timestamp: allProviderTimestamp,
}
}
return servers
}
func Test_extractServersFromBytes(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
b []byte
hardcoded models.AllServers
logged []string
persisted models.AllServers
err error
b []byte
hardcoded models.AllServers
logged []string
persisted models.AllServers
errMessage string
}{
"no data": {
err: errors.New("cannot decode versions: unexpected end of JSON input"),
"bad JSON": {
b: []byte("garbage"),
errMessage: "cannot decode servers: invalid character 'g' looking for beginning of value",
},
"empty JSON": {
b: []byte("{}"),
err: errors.New("cannot decode servers for provider: Cyberghost: unexpected end of JSON input"),
"bad provider JSON": {
b: []byte(`{"cyberghost": "garbage"}`),
hardcoded: populateProviders(1, 0, models.AllServers{}),
errMessage: "cannot decode servers for provider: Cyberghost: " +
"json: cannot unmarshal string into Go value of type models.Servers",
},
"different versions": {
b: []byte(`{}`),
hardcoded: models.AllServers{
Cyberghost: models.Servers{Version: 1},
Expressvpn: models.Servers{Version: 1},
Fastestvpn: models.Servers{Version: 1},
HideMyAss: models.Servers{Version: 1},
Ipvanish: models.Servers{Version: 1},
Ivpn: models.Servers{Version: 1},
Mullvad: models.Servers{Version: 1},
Nordvpn: models.Servers{Version: 1},
Perfectprivacy: models.Servers{Version: 1},
Privado: models.Servers{Version: 1},
Pia: models.Servers{Version: 1},
Privatevpn: models.Servers{Version: 1},
Protonvpn: models.Servers{Version: 1},
Purevpn: models.Servers{Version: 1},
Surfshark: models.Servers{Version: 1},
Torguard: models.Servers{Version: 1},
VPNUnlimited: models.Servers{Version: 1},
Vyprvpn: models.Servers{Version: 1},
Wevpn: models.Servers{Version: 1},
Windscribe: models.Servers{Version: 1},
},
logged: []string{
"Cyberghost servers from file discarded because they have version 0 and hardcoded servers have version 1",
"Expressvpn servers from file discarded because they have version 0 and hardcoded servers have version 1",
"Fastestvpn servers from file discarded because they have version 0 and hardcoded servers have version 1",
"Hidemyass servers from file discarded because they have version 0 and hardcoded servers have version 1",
"Ipvanish servers from file discarded because they have version 0 and hardcoded servers have version 1",
"Ivpn servers from file discarded because they have version 0 and hardcoded servers have version 1",
"Mullvad servers from file discarded because they have version 0 and hardcoded servers have version 1",
"Nordvpn servers from file discarded because they have version 0 and hardcoded servers have version 1",
"Perfect Privacy servers from file discarded because they have version 0 and hardcoded servers have version 1",
"Privado servers from file discarded because they have version 0 and hardcoded servers have version 1",
"Private Internet Access servers from file discarded because they have version 0 and hardcoded servers have version 1", //nolint:lll
"Privatevpn servers from file discarded because they have version 0 and hardcoded servers have version 1",
"Protonvpn servers from file discarded because they have version 0 and hardcoded servers have version 1",
"Purevpn servers from file discarded because they have version 0 and hardcoded servers have version 1",
"Surfshark servers from file discarded because they have version 0 and hardcoded servers have version 1",
"Torguard servers from file discarded because they have version 0 and hardcoded servers have version 1",
"Vpn Unlimited servers from file discarded because they have version 0 and hardcoded servers have version 1",
"Vyprvpn servers from file discarded because they have version 0 and hardcoded servers have version 1",
"Wevpn servers from file discarded because they have version 0 and hardcoded servers have version 1",
"Windscribe servers from file discarded because they have version 0 and hardcoded servers have version 1",
"absent provider keys": {
b: []byte(`{}`),
hardcoded: populateProviders(1, 0, models.AllServers{}),
persisted: models.AllServers{
ProviderToServers: map[string]models.Servers{},
},
},
"same versions": {
b: []byte(`{
"cyberghost": {"version": 1, "timestamp": 1},
"expressvpn": {"version": 1, "timestamp": 1},
"fastestvpn": {"version": 1, "timestamp": 1},
"hidemyass": {"version": 1, "timestamp": 1},
"ipvanish": {"version": 1, "timestamp": 1},
"ivpn": {"version": 1, "timestamp": 1},
"mullvad": {"version": 1, "timestamp": 1},
"nordvpn": {"version": 1, "timestamp": 1},
"perfect privacy": {"version": 1, "timestamp": 1},
"privado": {"version": 1, "timestamp": 1},
"private internet access": {"version": 1, "timestamp": 1},
"privatevpn": {"version": 1, "timestamp": 1},
"protonvpn": {"version": 1, "timestamp": 1},
"purevpn": {"version": 1, "timestamp": 1},
"surfshark": {"version": 1, "timestamp": 1},
"torguard": {"version": 1, "timestamp": 1},
"vpn unlimited": {"version": 1, "timestamp": 1},
"vyprvpn": {"version": 1, "timestamp": 1},
"wevpn": {"version": 1, "timestamp": 1},
"windscribe": {"version": 1, "timestamp": 1}
}`),
hardcoded: populateProviders(1, 0, models.AllServers{}),
persisted: populateProviders(1, 1, models.AllServers{}),
},
"different versions": {
b: []byte(`{
"cyberghost": {"version": 1, "timestamp": 1},
"expressvpn": {"version": 1, "timestamp": 1},
@@ -97,49 +106,31 @@ func Test_extractServersFromBytes(t *testing.T) {
"wevpn": {"version": 1, "timestamp": 1},
"windscribe": {"version": 1, "timestamp": 1}
}`),
hardcoded: models.AllServers{
Cyberghost: models.Servers{Version: 1},
Expressvpn: models.Servers{Version: 1},
Fastestvpn: models.Servers{Version: 1},
HideMyAss: models.Servers{Version: 1},
Ipvanish: models.Servers{Version: 1},
Ivpn: models.Servers{Version: 1},
Mullvad: models.Servers{Version: 1},
Nordvpn: models.Servers{Version: 1},
Perfectprivacy: models.Servers{Version: 1},
Privado: models.Servers{Version: 1},
Pia: models.Servers{Version: 1},
Privatevpn: models.Servers{Version: 1},
Protonvpn: models.Servers{Version: 1},
Purevpn: models.Servers{Version: 1},
Surfshark: models.Servers{Version: 1},
Torguard: models.Servers{Version: 1},
VPNUnlimited: models.Servers{Version: 1},
Vyprvpn: models.Servers{Version: 1},
Wevpn: models.Servers{Version: 1},
Windscribe: models.Servers{Version: 1},
hardcoded: populateProviders(2, 0, models.AllServers{}),
logged: []string{
"Cyberghost servers from file discarded because they have version 1 and hardcoded servers have version 2",
"Expressvpn servers from file discarded because they have version 1 and hardcoded servers have version 2",
"Fastestvpn servers from file discarded because they have version 1 and hardcoded servers have version 2",
"Hidemyass servers from file discarded because they have version 1 and hardcoded servers have version 2",
"Ipvanish servers from file discarded because they have version 1 and hardcoded servers have version 2",
"Ivpn servers from file discarded because they have version 1 and hardcoded servers have version 2",
"Mullvad servers from file discarded because they have version 1 and hardcoded servers have version 2",
"Nordvpn servers from file discarded because they have version 1 and hardcoded servers have version 2",
"Perfect Privacy servers from file discarded because they have version 1 and hardcoded servers have version 2",
"Privado servers from file discarded because they have version 1 and hardcoded servers have version 2",
"Private Internet Access servers from file discarded because they have version 1 and hardcoded servers have version 2", //nolint:lll
"Privatevpn servers from file discarded because they have version 1 and hardcoded servers have version 2",
"Protonvpn servers from file discarded because they have version 1 and hardcoded servers have version 2",
"Purevpn servers from file discarded because they have version 1 and hardcoded servers have version 2",
"Surfshark servers from file discarded because they have version 1 and hardcoded servers have version 2",
"Torguard servers from file discarded because they have version 1 and hardcoded servers have version 2",
"Vpn Unlimited servers from file discarded because they have version 1 and hardcoded servers have version 2",
"Vyprvpn servers from file discarded because they have version 1 and hardcoded servers have version 2",
"Wevpn servers from file discarded because they have version 1 and hardcoded servers have version 2",
"Windscribe servers from file discarded because they have version 1 and hardcoded servers have version 2",
},
persisted: models.AllServers{
Cyberghost: models.Servers{Version: 1, Timestamp: 1},
Expressvpn: models.Servers{Version: 1, Timestamp: 1},
Fastestvpn: models.Servers{Version: 1, Timestamp: 1},
HideMyAss: models.Servers{Version: 1, Timestamp: 1},
Ipvanish: models.Servers{Version: 1, Timestamp: 1},
Ivpn: models.Servers{Version: 1, Timestamp: 1},
Mullvad: models.Servers{Version: 1, Timestamp: 1},
Nordvpn: models.Servers{Version: 1, Timestamp: 1},
Perfectprivacy: models.Servers{Version: 1, Timestamp: 1},
Privado: models.Servers{Version: 1, Timestamp: 1},
Pia: models.Servers{Version: 1, Timestamp: 1},
Privatevpn: models.Servers{Version: 1, Timestamp: 1},
Protonvpn: models.Servers{Version: 1, Timestamp: 1},
Purevpn: models.Servers{Version: 1, Timestamp: 1},
Surfshark: models.Servers{Version: 1, Timestamp: 1},
Torguard: models.Servers{Version: 1, Timestamp: 1},
VPNUnlimited: models.Servers{Version: 1, Timestamp: 1},
Vyprvpn: models.Servers{Version: 1, Timestamp: 1},
Wevpn: models.Servers{Version: 1, Timestamp: 1},
Windscribe: models.Servers{Version: 1, Timestamp: 1},
ProviderToServers: map[string]models.Servers{},
},
},
}
@@ -151,8 +142,13 @@ func Test_extractServersFromBytes(t *testing.T) {
ctrl := gomock.NewController(t)
logger := NewMockInfoErrorer(ctrl)
var previousLogCall *gomock.Call
for _, logged := range testCase.logged {
logger.EXPECT().Info(logged)
call := logger.EXPECT().Info(logged)
if previousLogCall != nil {
call.After(previousLogCall)
}
previousLogCall = call
}
s := &Storage{
@@ -161,9 +157,8 @@ func Test_extractServersFromBytes(t *testing.T) {
servers, err := s.extractServersFromBytes(testCase.b, testCase.hardcoded)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
if testCase.errMessage != "" {
assert.EqualError(t, err, testCase.errMessage)
} else {
assert.NoError(t, err)
}
@@ -171,4 +166,25 @@ func Test_extractServersFromBytes(t *testing.T) {
assert.Equal(t, testCase.persisted, servers)
})
}
t.Run("hardcoded panic", func(t *testing.T) {
t.Parallel()
s := &Storage{}
allProviders := providers.All()
require.GreaterOrEqual(t, len(allProviders), 2)
b := []byte(`{}`)
hardcoded := models.AllServers{
ProviderToServers: map[string]models.Servers{
allProviders[0]: {},
// Missing provider allProviders[1]
},
}
expectedPanicValue := fmt.Sprintf("provider %s not found in hardcoded servers map", allProviders[1])
assert.PanicsWithValue(t, expectedPanicValue, func() {
_, _ = s.extractServersFromBytes(b, hardcoded)
})
})
}

View File

@@ -7,27 +7,11 @@ import (
"github.com/qdm12/gluetun/internal/models"
)
func countServers(allServers models.AllServers) int {
return len(allServers.Cyberghost.Servers) +
len(allServers.Expressvpn.Servers) +
len(allServers.Fastestvpn.Servers) +
len(allServers.HideMyAss.Servers) +
len(allServers.Ipvanish.Servers) +
len(allServers.Ivpn.Servers) +
len(allServers.Mullvad.Servers) +
len(allServers.Nordvpn.Servers) +
len(allServers.Perfectprivacy.Servers) +
len(allServers.Privado.Servers) +
len(allServers.Pia.Servers) +
len(allServers.Privatevpn.Servers) +
len(allServers.Protonvpn.Servers) +
len(allServers.Purevpn.Servers) +
len(allServers.Surfshark.Servers) +
len(allServers.Torguard.Servers) +
len(allServers.VPNUnlimited.Servers) +
len(allServers.Vyprvpn.Servers) +
len(allServers.Wevpn.Servers) +
len(allServers.Windscribe.Servers)
func countServers(allServers models.AllServers) (count int) {
for _, servers := range allServers.ProviderToServers {
count += len(servers.Servers)
}
return count
}
func (s *Storage) SyncServers() (err error) {
@@ -57,7 +41,7 @@ func (s *Storage) SyncServers() (err error) {
return nil
}
if err := flushToFile(s.filepath, s.mergedServers); err != nil {
if err := flushToFile(s.filepath, &s.mergedServers); err != nil {
return fmt.Errorf("cannot write servers to file: %w", err)
}
return nil