130 lines
3.4 KiB
Go
130 lines
3.4 KiB
Go
package config
|
|
|
|
import (
|
|
"encoding/json"
|
|
"log/slog"
|
|
"os"
|
|
"path/filepath"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
|
|
"go.yaml.in/yaml/v3"
|
|
)
|
|
|
|
// captureStdout returns what fn prints to os.Stdout.
|
|
func captureStdout(t *testing.T, fn func()) string {
|
|
t.Helper()
|
|
path := filepath.Join(t.TempDir(), "stdout")
|
|
f, err := os.Create(filepath.Clean(path))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
old := os.Stdout
|
|
os.Stdout = f
|
|
fn()
|
|
os.Stdout = old
|
|
if err := f.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b, err := os.ReadFile(filepath.Clean(path))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return string(b)
|
|
}
|
|
|
|
func TestPrintTerminal(t *testing.T) {
|
|
conf := defaultConfig(t)
|
|
got := captureStdout(t, conf.PrintTerminal)
|
|
if want := conf.String() + "\n"; got != want {
|
|
t.Errorf("PrintTerminal printed %q; want %q", got, want)
|
|
}
|
|
}
|
|
|
|
// String lists every key once, sorted, so the output is the same each run.
|
|
func TestStringSorted(t *testing.T) {
|
|
conf := defaultConfig(t)
|
|
var keys []string
|
|
for _, line := range strings.Split(strings.TrimSpace(conf.String()), "\n") {
|
|
key, _, _ := strings.Cut(line, " = ")
|
|
keys = append(keys, key)
|
|
}
|
|
if !slices.IsSorted(keys) || len(keys) != len(conf.Flatten()) {
|
|
t.Errorf("String keys = %v; want each key once, sorted", keys)
|
|
}
|
|
if first := conf.String(); conf.String() != first {
|
|
t.Error("String output differs between calls")
|
|
}
|
|
}
|
|
|
|
func TestPrintJson(t *testing.T) {
|
|
conf := defaultConfig(t)
|
|
out := captureStdout(t, conf.PrintJson)
|
|
var m map[string]any
|
|
if err := json.Unmarshal([]byte(out), &m); err != nil {
|
|
t.Fatalf("PrintJson output is not JSON: %v\n%s", err, out)
|
|
}
|
|
if got := m["logging"].(map[string]any)["level"]; got != "warn" {
|
|
t.Errorf("logging.level = %v; want warn", got)
|
|
}
|
|
}
|
|
|
|
func TestPrintYaml(t *testing.T) {
|
|
conf := defaultConfig(t)
|
|
out := captureStdout(t, conf.PrintYaml)
|
|
var m map[string]any
|
|
if err := yaml.Unmarshal([]byte(out), &m); err != nil {
|
|
t.Fatalf("PrintYaml output is not YAML: %v\n%s", err, out)
|
|
}
|
|
if got := m["logging"].(map[string]any)["level"]; got != "warn" {
|
|
t.Errorf("logging.level = %v; want warn", got)
|
|
}
|
|
}
|
|
|
|
// PrintLog logs one line per key, at the level asked for (info for any
|
|
// level other than debug, warn and error).
|
|
func TestPrintLog(t *testing.T) {
|
|
conf := defaultConfig(t)
|
|
for _, tt := range []struct {
|
|
at slog.Level
|
|
want string
|
|
}{
|
|
{slog.LevelDebug, "level=DEBUG"},
|
|
{slog.LevelInfo, "level=INFO"},
|
|
{slog.LevelWarn, "level=WARN"},
|
|
{slog.LevelError, "level=ERROR"},
|
|
{slog.LevelError + 4, "level=INFO"},
|
|
} {
|
|
logs := captureLogs(t, slog.LevelDebug)
|
|
conf.PrintLog(tt.at)
|
|
|
|
lines := strings.Split(strings.TrimSpace(logs.String()), "\n")
|
|
if len(lines) != len(conf.Flatten()) {
|
|
t.Errorf("PrintLog(%v) logged %d lines; want %d", tt.at, len(lines), len(conf.Flatten()))
|
|
}
|
|
for _, l := range lines {
|
|
if !strings.Contains(l, tt.want) {
|
|
t.Errorf("PrintLog(%v) line %q; want %s", tt.at, l, tt.want)
|
|
}
|
|
}
|
|
if !strings.Contains(logs.String(), "logging.level = warn") {
|
|
t.Errorf("PrintLog(%v) missing logging.level:\n%s", tt.at, logs)
|
|
}
|
|
// Each line is msg="\t<key> = <value>".
|
|
flat := conf.Flatten()
|
|
var keys []string
|
|
for _, l := range lines {
|
|
_, msg, _ := strings.Cut(l, `msg="\t`)
|
|
key, _, _ := strings.Cut(msg, " = ")
|
|
if _, ok := flat[key]; !ok {
|
|
t.Errorf("PrintLog(%v) line %q: no key", tt.at, l)
|
|
}
|
|
keys = append(keys, key)
|
|
}
|
|
if !slices.IsSorted(keys) {
|
|
t.Errorf("PrintLog(%v) keys not sorted: %v", tt.at, keys)
|
|
}
|
|
}
|
|
}
|