Files
job-search/internal/config/print_test.go
T

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)
}
}
}