86 lines
3.8 KiB
Go
86 lines
3.8 KiB
Go
package config_test
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.yuntuo.hk/k2safe/go-omni/config"
|
|
mysql "git.yuntuo.hk/k2safe/go-omni/database/mysql"
|
|
"git.yuntuo.hk/k2safe/go-omni/httpc"
|
|
)
|
|
|
|
type moduleConfig struct {
|
|
Database mysql.Config `json:"database"`
|
|
Server config.Config `json:"server"`
|
|
Client httpc.Config `json:"client"`
|
|
}
|
|
|
|
func (c moduleConfig) Validate() error {
|
|
if err := c.Database.Validate(); err != nil {
|
|
return config.AtField("database", err)
|
|
}
|
|
if err := c.Server.Validate(); err != nil {
|
|
return config.AtField("server", err)
|
|
}
|
|
return config.AtField("client", c.Client.Validate())
|
|
}
|
|
func TestModuleFieldDiagnostics(t *testing.T) {
|
|
for _, tc := range []struct{ name, source, field, reason string }{
|
|
{"port", "database:\n port: 70000\n", "database.port", "1 and 65535"},
|
|
{"host", "database:\n host: 'invalid host'\n", "database.host", "hostname"},
|
|
{"pool", "database:\n pool:\n maxIdleConns: 999\n", "database.pool.maxIdleConns", "maxOpenConns"},
|
|
{"ping", "database:\n pool:\n pingTimeout: 0s\n", "database.pool.pingTimeout", "positive"},
|
|
{"timeout", "database:\n readTimeout: -1s\n", "database.readTimeout", "nonnegative"},
|
|
{"mysqlZone", "database:\n location: Secret/Not_A_Zone\n", "database.location", "IANA"},
|
|
{"tlsPair", "database:\n tls:\n certFile: secret-file\n keyFile: ''\n", "database.tls.keyFile", "certFile"},
|
|
{"level", "server:\n log:\n level: secret-level\n", "server.log.level", "debug"},
|
|
{"format", "server:\n log:\n format: secret-format\n", "server.log.format", "json or text"},
|
|
{"rotation", "server:\n log:\n rotation:\n maxBackups: -1\n", "server.log.rotation.maxBackups", "10000"},
|
|
{"zone", "server:\n log:\n timeZone: Secret/Not_A_Zone\n", "server.log.timeZone", "IANA"},
|
|
{"lifecycle", "server:\n lifecycle:\n stopTimeout: -1s\n", "server.lifecycle.stopTimeout", "nonnegative"},
|
|
{"policy", "server:\n request:\n body: secret-mode\n", "server.request.body", "required"},
|
|
{"clientPool", "client:\n maxIdleConns: -1\n", "client.maxIdleConns", "131072"},
|
|
{"proxy", "client:\n proxy:\n mode: url\n url: 'https://private:secret@localhost/path'\n", "client.proxy.url", "origin"},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "config.yaml")
|
|
if err := os.WriteFile(path, []byte(tc.source), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err := config.LoadProject(path, moduleConfig{Database: mysql.DefaultConfig(), Server: config.Default()})
|
|
var d *config.Error
|
|
if !errors.As(err, &d) || d.Field != tc.field || d.File != path || d.Line <= 1 || d.Column <= 0 || !strings.Contains(d.Reason, tc.reason) {
|
|
t.Fatalf("diagnostic: %v", err)
|
|
}
|
|
if strings.Contains(err.Error(), "secret") || strings.Contains(err.Error(), "Secret/") || strings.Contains(err.Error(), "private") {
|
|
t.Fatalf("value leaked: %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
func TestModuleDiagnosticsKeepReferenceAndCause(t *testing.T) {
|
|
t.Setenv("CHECK_PORT", "70000")
|
|
path := filepath.Join(t.TempDir(), "config.yaml")
|
|
os.WriteFile(path, []byte("database:\n port: '${CHECK_PORT}'\n"), 0600)
|
|
_, err := config.LoadProject(path, moduleConfig{Database: mysql.DefaultConfig(), Server: config.Default()})
|
|
var d *config.Error
|
|
if !errors.As(err, &d) || d.Field != "database.port" || d.Env != "CHECK_PORT" || d.Line != 2 {
|
|
t.Fatal(err)
|
|
}
|
|
original := mysql.DefaultConfig()
|
|
original.Port = 70000
|
|
cause := original.Validate()
|
|
wrapped := config.AtField("database", cause)
|
|
if !errors.Is(wrapped, cause) {
|
|
t.Fatal("lost original validation error")
|
|
}
|
|
// Unknown project errors continue to be redacted rather than echoed.
|
|
wrapped = config.AtField("custom", errors.New("private-password"))
|
|
if strings.Contains(wrapped.Error(), "private-password") {
|
|
t.Fatal(wrapped)
|
|
}
|
|
}
|