Feat: format-servers CLI command
This commit is contained in:
@@ -130,6 +130,8 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
|
|||||||
return cli.OpenvpnConfig(logger, env)
|
return cli.OpenvpnConfig(logger, env)
|
||||||
case "update":
|
case "update":
|
||||||
return cli.Update(ctx, args[2:], logger)
|
return cli.Update(ctx, args[2:], logger)
|
||||||
|
case "format-servers":
|
||||||
|
return cli.FormatServers(args[2:])
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("%w: %s", errCommandUnknown, args[1])
|
return fmt.Errorf("%w: %s", errCommandUnknown, args[1])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ type CLIer interface {
|
|||||||
HealthChecker
|
HealthChecker
|
||||||
OpenvpnConfigMaker
|
OpenvpnConfigMaker
|
||||||
Updater
|
Updater
|
||||||
|
ServersFormatter
|
||||||
}
|
}
|
||||||
|
|
||||||
type CLI struct {
|
type CLI struct {
|
||||||
|
|||||||
124
internal/cli/formatservers.go
Normal file
124
internal/cli/formatservers.go
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
|
"github.com/qdm12/gluetun/internal/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServersFormatter interface {
|
||||||
|
FormatServers(args []string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrFormatNotRecognized = errors.New("format is not recognized")
|
||||||
|
ErrProviderUnspecified = errors.New("VPN provider to format was not specified")
|
||||||
|
ErrOpenOutputFile = errors.New("cannot open output file")
|
||||||
|
ErrWriteOutput = errors.New("cannot write to output file")
|
||||||
|
ErrCloseOutputFile = errors.New("cannot close output file")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *CLI) FormatServers(args []string) error {
|
||||||
|
var format, output string
|
||||||
|
var cyberghost, fastestvpn, hideMyAss, ipvanish, ivpn, mullvad,
|
||||||
|
nordvpn, pia, privado, privatevpn, protonvpn, purevpn, surfshark,
|
||||||
|
torguard, vpnUnlimited, vyprvpn, windscribe bool
|
||||||
|
flagSet := flag.NewFlagSet("markdown", flag.ExitOnError)
|
||||||
|
flagSet.StringVar(&format, "format", "markdown", "Format to use which can be: 'markdown'")
|
||||||
|
flagSet.StringVar(&output, "output", "/dev/stdout", "Output file to write the formatted data to")
|
||||||
|
flagSet.BoolVar(&cyberghost, "cyberghost", false, "Format Cyberghost servers")
|
||||||
|
flagSet.BoolVar(&fastestvpn, "fastestvpn", false, "Format FastestVPN servers")
|
||||||
|
flagSet.BoolVar(&hideMyAss, "hidemyass", false, "Format HideMyAss servers")
|
||||||
|
flagSet.BoolVar(&ipvanish, "ipvanish", false, "Format IpVanish servers")
|
||||||
|
flagSet.BoolVar(&ivpn, "ivpn", false, "Format IVPN servers")
|
||||||
|
flagSet.BoolVar(&mullvad, "mullvad", false, "Format Mullvad servers")
|
||||||
|
flagSet.BoolVar(&nordvpn, "nordvpn", false, "Format Nordvpn servers")
|
||||||
|
flagSet.BoolVar(&pia, "pia", false, "Format Private Internet Access servers")
|
||||||
|
flagSet.BoolVar(&privado, "privado", false, "Format Privado servers")
|
||||||
|
flagSet.BoolVar(&privatevpn, "privatevpn", false, "Format Private VPN servers")
|
||||||
|
flagSet.BoolVar(&protonvpn, "protonvpn", false, "Format Protonvpn servers")
|
||||||
|
flagSet.BoolVar(&purevpn, "purevpn", false, "Format Purevpn servers")
|
||||||
|
flagSet.BoolVar(&surfshark, "surfshark", false, "Format Surfshark servers")
|
||||||
|
flagSet.BoolVar(&torguard, "torguard", false, "Format Torguard servers")
|
||||||
|
flagSet.BoolVar(&vpnUnlimited, "vpnunlimited", false, "Format VPN Unlimited servers")
|
||||||
|
flagSet.BoolVar(&vyprvpn, "vyprvpn", false, "Format Vyprvpn servers")
|
||||||
|
flagSet.BoolVar(&windscribe, "windscribe", false, "Format Windscribe servers")
|
||||||
|
if err := flagSet.Parse(args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if format != "markdown" {
|
||||||
|
return fmt.Errorf("%w: %s", ErrFormatNotRecognized, format)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger := newNoopLogger()
|
||||||
|
storage, err := storage.New(logger, constants.ServersData)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrNewStorage, err)
|
||||||
|
}
|
||||||
|
currentServers := storage.GetServers()
|
||||||
|
|
||||||
|
var formatted string
|
||||||
|
switch {
|
||||||
|
case cyberghost:
|
||||||
|
formatted = currentServers.Cyberghost.ToMarkdown()
|
||||||
|
case fastestvpn:
|
||||||
|
formatted = currentServers.Fastestvpn.ToMarkdown()
|
||||||
|
case hideMyAss:
|
||||||
|
formatted = currentServers.HideMyAss.ToMarkdown()
|
||||||
|
case ipvanish:
|
||||||
|
formatted = currentServers.Ipvanish.ToMarkdown()
|
||||||
|
case ivpn:
|
||||||
|
formatted = currentServers.Ivpn.ToMarkdown()
|
||||||
|
case mullvad:
|
||||||
|
formatted = currentServers.Mullvad.ToMarkdown()
|
||||||
|
case nordvpn:
|
||||||
|
formatted = currentServers.Nordvpn.ToMarkdown()
|
||||||
|
case pia:
|
||||||
|
formatted = currentServers.Pia.ToMarkdown()
|
||||||
|
case privado:
|
||||||
|
formatted = currentServers.Privado.ToMarkdown()
|
||||||
|
case privatevpn:
|
||||||
|
formatted = currentServers.Privatevpn.ToMarkdown()
|
||||||
|
case protonvpn:
|
||||||
|
formatted = currentServers.Protonvpn.ToMarkdown()
|
||||||
|
case purevpn:
|
||||||
|
formatted = currentServers.Purevpn.ToMarkdown()
|
||||||
|
case surfshark:
|
||||||
|
formatted = currentServers.Surfshark.ToMarkdown()
|
||||||
|
case torguard:
|
||||||
|
formatted = currentServers.Torguard.ToMarkdown()
|
||||||
|
case vpnUnlimited:
|
||||||
|
formatted = currentServers.VPNUnlimited.ToMarkdown()
|
||||||
|
case vyprvpn:
|
||||||
|
formatted = currentServers.Vyprvpn.ToMarkdown()
|
||||||
|
case windscribe:
|
||||||
|
formatted = currentServers.Windscribe.ToMarkdown()
|
||||||
|
default:
|
||||||
|
return ErrProviderUnspecified
|
||||||
|
}
|
||||||
|
|
||||||
|
output = filepath.Clean(output)
|
||||||
|
file, err := os.OpenFile(output, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrOpenOutputFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = fmt.Fprint(file, formatted)
|
||||||
|
if err != nil {
|
||||||
|
_ = file.Close()
|
||||||
|
return fmt.Errorf("%w: %s", ErrWriteOutput, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = file.Close()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrCloseOutputFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
16
internal/cli/nooplogger.go
Normal file
16
internal/cli/nooplogger.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import "github.com/qdm12/golibs/logging"
|
||||||
|
|
||||||
|
type noopLogger struct{}
|
||||||
|
|
||||||
|
func newNoopLogger() *noopLogger {
|
||||||
|
return new(noopLogger)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *noopLogger) Debug(s string) {}
|
||||||
|
func (l *noopLogger) Info(s string) {}
|
||||||
|
func (l *noopLogger) Warn(s string) {}
|
||||||
|
func (l *noopLogger) Error(s string) {}
|
||||||
|
func (l *noopLogger) PatchLevel(level logging.Level) {}
|
||||||
|
func (l *noopLogger) PatchPrefix(prefix string) {}
|
||||||
252
internal/models/markdown.go
Normal file
252
internal/models/markdown.go
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func boolToMarkdown(b bool) string {
|
||||||
|
if b {
|
||||||
|
return "✅"
|
||||||
|
}
|
||||||
|
return "❎"
|
||||||
|
}
|
||||||
|
|
||||||
|
func markdownTableHeading(legendFields ...string) (markdown string) {
|
||||||
|
return "| " + strings.Join(legendFields, " | ") + " |\n" +
|
||||||
|
"|" + strings.Repeat(" --- |", len(legendFields)) + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CyberghostServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Region", "Group", "Hostname")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s CyberghostServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | `%s` |", s.Region, s.Group, s.Hostname)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FastestvpnServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "Hostname", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FastestvpnServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | `%s` | %s | %s |",
|
||||||
|
s.Country, s.Hostname, boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *HideMyAssServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "Region", "City", "Hostname", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *HideMyAssServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | %s | `%s` | %s | %s |",
|
||||||
|
s.Country, s.Region, s.City, s.Hostname,
|
||||||
|
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *IpvanishServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "City", "Hostname", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *IpvanishServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | `%s` | %s | %s |",
|
||||||
|
s.Country, s.City, s.Hostname,
|
||||||
|
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *IvpnServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "City", "ISP", "Hostname", "VPN", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *IvpnServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | %s | `%s` | %s | %s | %s |",
|
||||||
|
s.Country, s.City, s.ISP, s.Hostname, s.VPN,
|
||||||
|
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MullvadServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "City", "ISP", "Owned",
|
||||||
|
"Hostname", "VPN")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MullvadServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | %s | %s | `%s` | %s |",
|
||||||
|
s.Country, s.City, s.ISP, boolToMarkdown(s.Owned),
|
||||||
|
s.Hostname, s.VPN)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NordvpnServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Region", "Hostname", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NordvpnServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | `%s` | %s | %s |",
|
||||||
|
s.Region, s.Hostname,
|
||||||
|
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PrivadoServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "Region", "City", "Hostname")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PrivadoServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | %s | `%s` |",
|
||||||
|
s.Country, s.Region, s.City, s.Hostname)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PiaServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Region", "Hostname", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PIAServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | `%s` | %s | %s |",
|
||||||
|
s.Region, s.Hostname,
|
||||||
|
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PrivatevpnServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "City", "Hostname")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PrivatevpnServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | `%s` |",
|
||||||
|
s.Country, s.City, s.Hostname)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProtonvpnServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "Region", "City", "Hostname", "Free tier")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProtonvpnServer) ToMarkdown() (markdown string) {
|
||||||
|
isFree := strings.Contains(strings.ToLower(s.Name), "free")
|
||||||
|
return fmt.Sprintf("| %s | %s | %s | `%s` | %s |",
|
||||||
|
s.Country, s.Region, s.City, s.Hostname, boolToMarkdown(isFree))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PurevpnServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "Region", "City", "Hostname", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PurevpnServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | %s | `%s` | %s | %s |",
|
||||||
|
s.Country, s.Region, s.City, s.Hostname,
|
||||||
|
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SurfsharkServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Region", "Country", "City", "Hostname", "Multi-hop", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SurfsharkServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | %s | `%s` | %s | %s | %s |",
|
||||||
|
s.Region, s.Country, s.City, s.Hostname, boolToMarkdown(s.MultiHop),
|
||||||
|
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TorguardServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "City", "Hostname", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TorguardServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | `%s` | %s | %s |",
|
||||||
|
s.Country, s.City, s.Hostname,
|
||||||
|
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *VPNUnlimitedServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Country", "City", "Hostname", "Free tier", "Streaming", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *VPNUnlimitedServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | `%s` | %s | %s | %s | %s |",
|
||||||
|
s.Country, s.City, s.Hostname,
|
||||||
|
boolToMarkdown(s.Free), boolToMarkdown(s.Stream),
|
||||||
|
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *VyprvpnServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Region", "Hostname", "TCP", "UDP")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *VyprvpnServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | `%s` | %s | %s |",
|
||||||
|
s.Region, s.Hostname,
|
||||||
|
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *WindscribeServers) ToMarkdown() (markdown string) {
|
||||||
|
markdown = markdownTableHeading("Region", "City", "Hostname", "VPN")
|
||||||
|
for _, server := range s.Servers {
|
||||||
|
markdown += server.ToMarkdown() + "\n"
|
||||||
|
}
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *WindscribeServer) ToMarkdown() (markdown string) {
|
||||||
|
return fmt.Sprintf("| %s | %s | `%s` | %s |",
|
||||||
|
s.Region, s.City, s.Hostname, s.VPN)
|
||||||
|
}
|
||||||
45
internal/models/markdown_test.go
Normal file
45
internal/models/markdown_test.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_CyberghostServers_ToMarkdown(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
servers := CyberghostServers{
|
||||||
|
Servers: []CyberghostServer{
|
||||||
|
{Region: "a", Group: "A", Hostname: "xa"},
|
||||||
|
{Region: "b", Group: "A", Hostname: "xb"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
markdown := servers.ToMarkdown()
|
||||||
|
const expected = "| Region | Group | Hostname |\n" +
|
||||||
|
"| --- | --- | --- |\n" +
|
||||||
|
"| a | A | `xa` |\n" +
|
||||||
|
"| b | A | `xb` |\n"
|
||||||
|
|
||||||
|
assert.Equal(t, expected, markdown)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_FastestvpnServers_ToMarkdown(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
servers := FastestvpnServers{
|
||||||
|
Servers: []FastestvpnServer{
|
||||||
|
{Country: "a", Hostname: "xa", TCP: true},
|
||||||
|
{Country: "b", Hostname: "xb", UDP: true},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
markdown := servers.ToMarkdown()
|
||||||
|
const expected = "| Country | Hostname | TCP | UDP |\n" +
|
||||||
|
"| --- | --- | --- | --- |\n" +
|
||||||
|
"| a | `xa` | ✅ | ❎ |\n" +
|
||||||
|
"| b | `xb` | ❎ | ✅ |\n"
|
||||||
|
|
||||||
|
assert.Equal(t, expected, markdown)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user