155 lines
3.1 KiB
Go
155 lines
3.1 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func writeTempConfig(t *testing.T, body string) string {
|
|
t.Helper()
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "config.yaml")
|
|
if err := os.WriteFile(path, []byte(body), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return path
|
|
}
|
|
|
|
func TestLoadValidConfig(t *testing.T) {
|
|
path := writeTempConfig(t, `
|
|
listen:
|
|
host: 127.0.0.1
|
|
port: 13306
|
|
local_auth:
|
|
username: root
|
|
password: "local-secret"
|
|
tunnel:
|
|
url: "https://example.invalid/ntunnel?token=abc"
|
|
remote:
|
|
host: mysql
|
|
port: 3306
|
|
username: remote-root
|
|
password: "remote-secret"
|
|
database: app
|
|
charset: utf8mb4
|
|
options:
|
|
request_timeout: 15
|
|
max_connections: 3
|
|
log_level: debug
|
|
log_sql: false
|
|
`)
|
|
cfg, err := Load(path, Overrides{})
|
|
if err != nil {
|
|
t.Fatalf("Load: %v", err)
|
|
}
|
|
if cfg.ListenHost != "127.0.0.1" || cfg.ListenPort != 13306 {
|
|
t.Fatalf("listen = %s:%d", cfg.ListenHost, cfg.ListenPort)
|
|
}
|
|
if cfg.RemoteHost != "mysql" || cfg.RemoteUsername != "remote-root" {
|
|
t.Fatalf("remote mismatch")
|
|
}
|
|
if cfg.RequestTimeout.Seconds() != 15 {
|
|
t.Fatalf("timeout = %s", cfg.RequestTimeout)
|
|
}
|
|
}
|
|
|
|
func TestRejectUnspecifiedListen(t *testing.T) {
|
|
path := writeTempConfig(t, `
|
|
listen:
|
|
host: 0.0.0.0
|
|
port: 13306
|
|
local_auth:
|
|
username: root
|
|
password: x
|
|
tunnel:
|
|
url: "https://example.invalid/?token=t"
|
|
remote:
|
|
host: mysql
|
|
username: root
|
|
password: x
|
|
`)
|
|
_, err := Load(path, Overrides{})
|
|
if err == nil || !strings.Contains(err.Error(), "0.0.0.0") {
|
|
t.Fatalf("expected unspecified listen error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestMissingTunnelURL(t *testing.T) {
|
|
path := writeTempConfig(t, `
|
|
local_auth:
|
|
username: root
|
|
password: x
|
|
remote:
|
|
host: mysql
|
|
username: root
|
|
password: x
|
|
`)
|
|
_, err := Load(path, Overrides{})
|
|
if err == nil || !strings.Contains(err.Error(), "tunnel.url") {
|
|
t.Fatalf("expected tunnel.url error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCLIOverrides(t *testing.T) {
|
|
path := writeTempConfig(t, `
|
|
listen:
|
|
host: 127.0.0.1
|
|
port: 13306
|
|
local_auth:
|
|
username: root
|
|
password: x
|
|
tunnel:
|
|
url: "https://example.invalid/?token=t"
|
|
remote:
|
|
host: mysql
|
|
username: root
|
|
password: x
|
|
`)
|
|
cfg, err := Load(path, Overrides{ListenHost: "127.0.0.1", ListenPort: 23306})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if cfg.ListenPort != 23306 {
|
|
t.Fatalf("port override failed: %d", cfg.ListenPort)
|
|
}
|
|
}
|
|
|
|
func TestRedactedTunnelURL(t *testing.T) {
|
|
got := RedactedTunnelURL("https://bth-ntunnel.example/?token=super-secret&x=1")
|
|
if strings.Contains(got, "super-secret") {
|
|
t.Fatalf("token leaked: %s", got)
|
|
}
|
|
if !strings.Contains(got, "token=%2A%2A%2A") && !strings.Contains(got, "token=***") {
|
|
t.Fatalf("token not redacted: %s", got)
|
|
}
|
|
}
|
|
|
|
func TestWarnWhenTokenMissing(t *testing.T) {
|
|
path := writeTempConfig(t, `
|
|
local_auth:
|
|
username: root
|
|
password: x
|
|
tunnel:
|
|
url: "https://example.invalid/ntunnel"
|
|
remote:
|
|
host: mysql
|
|
username: root
|
|
password: x
|
|
`)
|
|
cfg, err := Load(path, Overrides{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
found := false
|
|
for _, w := range cfg.Warnings {
|
|
if strings.Contains(w, "token") {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatalf("expected token warning, got %#v", cfg.Warnings)
|
|
}
|
|
}
|