Files
devplacepy/nodes/client/config_test.go
T
retoor 6f80a820da
DevPlace CI / test (push) Failing after 3m13s
Zo doen wijdat in oost
2026-10-16 07:20:44 +02:00

117 lines
3.8 KiB
Go

package main
import (
"encoding/json"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
)
func validConfig() Config {
config := DefaultConfig()
config.Server = "https://devplace.net"
config.Secret = "dpn_abcdefghijklmnop"
return config
}
func TestConfigRoundTripShapeAndMode(t *testing.T) {
path := filepath.Join(t.TempDir(), "sub", "config.json")
if err := SaveConfig(path, validConfig()); err != nil {
t.Fatal(err)
}
raw, _ := os.ReadFile(path)
keys := map[string]any{}
if err := json.Unmarshal(raw, &keys); err != nil {
t.Fatal(err)
}
for _, key := range []string{"server", "secret", "bandwidth_mb_day", "max_concurrent", "max_tunnels", "active_from", "active_to", "timezone"} {
if _, ok := keys[key]; !ok {
t.Errorf("missing key %s", key)
}
}
if len(keys) != 8 {
t.Errorf("unexpected keys %v", keys)
}
if runtime.GOOS != "windows" {
info, _ := os.Stat(path)
if info.Mode().Perm() != 0o600 {
t.Errorf("mode %v", info.Mode().Perm())
}
}
loaded, err := LoadConfig(path)
if err != nil || loaded != validConfig() {
t.Fatalf("loaded %+v %v", loaded, err)
}
}
func TestConfigValidationTable(t *testing.T) {
cases := map[string]func(*Config){
"server scheme": func(c *Config) { c.Server = "ftp://x" },
"server empty": func(c *Config) { c.Server = "" },
"secret prefix": func(c *Config) { c.Secret = "abc_1234567890" },
"secret short": func(c *Config) { c.Secret = "dpn_" },
"bandwidth low": func(c *Config) { c.BandwidthMBDay = 0 },
"bandwidth high": func(c *Config) { c.BandwidthMBDay = MaxDonorMBPerDay + 1 },
"concurrency": func(c *Config) { c.MaxConcurrent = 99 },
"tunnels": func(c *Config) { c.MaxTunnels = 0 },
"hours format": func(c *Config) { c.ActiveFrom, c.ActiveTo = "9:00", "17:00" },
"hours lone": func(c *Config) { c.ActiveFrom = "09:00" },
"timezone": func(c *Config) { c.Timezone = "Mars/Base" },
}
for name, mutate := range cases {
config := validConfig()
mutate(&config)
if config.Validate() == nil {
t.Errorf("%s accepted", name)
}
}
good := validConfig()
good.ActiveFrom, good.ActiveTo, good.Timezone = "22:00", "06:00", "Europe/Amsterdam"
if err := good.Validate(); err != nil {
t.Errorf("good config: %v", err)
}
}
func TestLoadRejectsUnknownFieldsAndMissingFile(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "config.json")
if _, err := LoadConfig(path); err == nil || !strings.Contains(err.Error(), "login") {
t.Errorf("missing file: %v", err)
}
_ = os.WriteFile(path, []byte(`{"server":"https://x.y","secret":"dpn_abcdefghijk","surprise":1}`), 0o600)
if _, err := LoadConfig(path); err == nil {
t.Error("unknown field accepted")
}
}
func TestCommandsVersionAndUsage(t *testing.T) {
var out, errOut strings.Builder
if code := execute([]string{"version"}, &out, &errOut); code != exitOK || !strings.Contains(out.String(), Version) {
t.Fatalf("version: %d %q", code, out.String())
}
if code := execute(nil, &out, &errOut); code != exitUsage {
t.Fatalf("no args: %d", code)
}
if code := execute([]string{"bogus"}, &out, &errOut); code != exitUsage {
t.Fatalf("bogus: %d", code)
}
}
func TestLoginStoresWithoutVerifyAndNeverEchoesSecret(t *testing.T) {
path := filepath.Join(t.TempDir(), "config.json")
var out, errOut strings.Builder
args := []string{"--config", path, "--server", "https://devplace.net", "--secret", "dpn_abcdefghijklmnop", "--no-verify"}
if code := execute(append([]string{"login"}, args...), &out, &errOut); code != exitOK {
t.Fatalf("login: %d %s", code, errOut.String())
}
if strings.Contains(out.String()+errOut.String(), "dpn_abcdefghijklmnop") {
t.Fatal("secret was printed")
}
out.Reset()
if code := execute([]string{"login", "--config", path, "--server", "nope", "--secret", "x"}, &out, &errOut); code != exitUsage {
t.Fatalf("bad login accepted: %d", code)
}
}