feat(config): Gesetz-Felder rate_limit, allow_high_burst, quiet_hours, quiet_mode
Queue-Section ([queue] rate_limit = "30s", allow_high_burst = true) und
Ruhezeiten (quiet_hours = "18:00-08:00" lokal über Mitternacht,
quiet_mode = discard|toast) mit Env-Overrides TALK_RATE_LIMIT,
TALK_ALLOW_HIGH_BURST, TALK_QUIET_HOURS ("off" = aus), TALK_QUIET_MODE.
Default: 1 Ansage / 30 s, Burst an, Ruhezeit aus (Opt-in wie Mute —
ein Uhrzeit-Gesetz als Default würde jeden Cron in den Abendstunden
stilllegen). Validierung gegen queue.ParseQuietHours als eine Quelle;
partielle [queue]-Tabellen behalten Defaults (tischgetestet).
This commit is contained in:
1 parent
dc94f45d11
commit
b20e6dd319
2 files changed
+263
-8
No files matched your search
@@ -5,6 +5,9 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"code.m3ta.dev/m3tam3re/talk/internal/queue"
|
||||
)
|
||||
|
||||
func env(kv map[string]string) func(string) string {
|
||||
@@ -352,3 +355,179 @@ func TestLoadServerPort(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestLoadGesetze deckt die Task-05-Felder ab: Rate-Limit (Default 30 s,
|
||||
// "0s" = unbegrenzt), High-Burst (Default an), Ruhezeiten (Default aus;
|
||||
// Format validiert) samt Env-Overrides und klaren Fehlern.
|
||||
func TestLoadGesetze(t *testing.T) {
|
||||
writeTOML := func(t *testing.T, content string) string {
|
||||
t.Helper()
|
||||
path := filepath.Join(t.TempDir(), "config.toml")
|
||||
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return path
|
||||
}
|
||||
fehlendeDatei := func(t *testing.T) string {
|
||||
t.Helper()
|
||||
return filepath.Join(t.TempDir(), "gibt-es-nicht.toml")
|
||||
}
|
||||
|
||||
t.Run("Defaults: 30 s, Burst an, Ruhezeit aus, Modus discard", func(t *testing.T) {
|
||||
cfg, err := Load(Options{File: fehlendeDatei(t), Getenv: env(nil)})
|
||||
if err != nil {
|
||||
t.Fatalf("Load: %v", err)
|
||||
}
|
||||
if cfg.Queue.RateLimit != DefaultRateLimit {
|
||||
t.Errorf("Queue.RateLimit = %v, want Default %v", cfg.Queue.RateLimit, DefaultRateLimit)
|
||||
}
|
||||
if !cfg.Queue.AllowHighBurst {
|
||||
t.Error("Queue.AllowHighBurst = false, want Default true")
|
||||
}
|
||||
if cfg.QuietHours != "" {
|
||||
t.Errorf("QuietHours = %q, want Default leer (aus)", cfg.QuietHours)
|
||||
}
|
||||
if cfg.QuietMode != queue.QuietModeDiscard {
|
||||
t.Errorf("QuietMode = %q, want Default %q", cfg.QuietMode, queue.QuietModeDiscard)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("TOML setzt alle Gesetz-Felder", func(t *testing.T) {
|
||||
cfg, err := Load(Options{
|
||||
File: writeTOML(t, `
|
||||
quiet_hours = "18:00-08:00"
|
||||
quiet_mode = "toast"
|
||||
|
||||
[queue]
|
||||
rate_limit = "10s"
|
||||
allow_high_burst = false
|
||||
`),
|
||||
Getenv: env(nil),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Load: %v", err)
|
||||
}
|
||||
if cfg.Queue.RateLimit != 10*time.Second {
|
||||
t.Errorf("Queue.RateLimit = %v, want 10s", cfg.Queue.RateLimit)
|
||||
}
|
||||
if cfg.Queue.AllowHighBurst {
|
||||
t.Error("Queue.AllowHighBurst = true, want false aus TOML")
|
||||
}
|
||||
if cfg.QuietHours != "18:00-08:00" {
|
||||
t.Errorf("QuietHours = %q, want Fenster aus TOML", cfg.QuietHours)
|
||||
}
|
||||
if cfg.QuietMode != queue.QuietModeToast {
|
||||
t.Errorf("QuietMode = %q, want toast", cfg.QuietMode)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("partielle [queue]-Tabelle behält Defaults", func(t *testing.T) {
|
||||
cfg, err := Load(Options{
|
||||
File: writeTOML(t, "[queue]\nrate_limit = \"0s\"\n"),
|
||||
Getenv: env(nil),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Load: %v", err)
|
||||
}
|
||||
if cfg.Queue.RateLimit != 0 {
|
||||
t.Errorf("Queue.RateLimit = %v, want 0 (unbegrenzt)", cfg.Queue.RateLimit)
|
||||
}
|
||||
if !cfg.Queue.AllowHighBurst {
|
||||
t.Error("Queue.AllowHighBurst = false, want unveränderten Default true")
|
||||
}
|
||||
if cfg.QuietMode != queue.QuietModeDiscard {
|
||||
t.Errorf("QuietMode = %q, want unveränderten Default discard", cfg.QuietMode)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Env überschreibt Datei (inkl. off)", func(t *testing.T) {
|
||||
cfg, err := Load(Options{
|
||||
File: writeTOML(t, `
|
||||
quiet_hours = "18:00-08:00"
|
||||
quiet_mode = "discard"
|
||||
|
||||
[queue]
|
||||
rate_limit = "10s"
|
||||
allow_high_burst = false
|
||||
`),
|
||||
Getenv: env(map[string]string{
|
||||
"TALK_RATE_LIMIT": "1s",
|
||||
"TALK_ALLOW_HIGH_BURST": "true",
|
||||
"TALK_QUIET_HOURS": "off",
|
||||
"TALK_QUIET_MODE": "toast",
|
||||
}),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Load: %v", err)
|
||||
}
|
||||
if cfg.Queue.RateLimit != time.Second {
|
||||
t.Errorf("Queue.RateLimit = %v, want 1s aus Env", cfg.Queue.RateLimit)
|
||||
}
|
||||
if !cfg.Queue.AllowHighBurst {
|
||||
t.Error("Queue.AllowHighBurst = false, want true aus Env")
|
||||
}
|
||||
if cfg.QuietHours != "" {
|
||||
t.Errorf("QuietHours = %q, want leer (off aus Env)", cfg.QuietHours)
|
||||
}
|
||||
if cfg.QuietMode != queue.QuietModeToast {
|
||||
t.Errorf("QuietMode = %q, want toast aus Env", cfg.QuietMode)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ungültige Gesetz-Werte sind klare Fehler", func(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
toml string
|
||||
envs map[string]string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "TALK_RATE_LIMIT Müll",
|
||||
envs: map[string]string{"TALK_RATE_LIMIT": "schnell"},
|
||||
want: "TALK_RATE_LIMIT",
|
||||
},
|
||||
{
|
||||
name: "TALK_RATE_LIMIT negativ",
|
||||
envs: map[string]string{"TALK_RATE_LIMIT": "-5s"},
|
||||
want: "TALK_RATE_LIMIT",
|
||||
},
|
||||
{
|
||||
name: "rate_limit Müll in Datei",
|
||||
toml: "[queue]\nrate_limit = \"müll\"\n",
|
||||
want: "rate_limit",
|
||||
},
|
||||
{
|
||||
name: "TALK_ALLOW_HIGH_BURST Müll",
|
||||
envs: map[string]string{"TALK_ALLOW_HIGH_BURST": "vielleicht"},
|
||||
want: "TALK_ALLOW_HIGH_BURST",
|
||||
},
|
||||
{
|
||||
name: "quiet_hours Müll in Datei",
|
||||
toml: "quiet_hours = \"abends\"\n",
|
||||
want: "quiet_hours",
|
||||
},
|
||||
{
|
||||
name: "TALK_QUIET_HOURS Müll",
|
||||
envs: map[string]string{"TALK_QUIET_HOURS": "25:00-26:00"},
|
||||
want: "TALK_QUIET_HOURS",
|
||||
},
|
||||
{
|
||||
name: "TALK_QUIET_MODE Müll",
|
||||
envs: map[string]string{"TALK_QUIET_MODE": "vibrieren"},
|
||||
want: "TALK_QUIET_MODE",
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
file := fehlendeDatei(t)
|
||||
if tc.toml != "" {
|
||||
file = writeTOML(t, tc.toml)
|
||||
}
|
||||
_, err := Load(Options{File: file, Getenv: env(tc.envs)})
|
||||
if err == nil || !strings.Contains(err.Error(), tc.want) {
|
||||
t.Fatalf("Fehler = %v, will klaren Fehler mit %q", err, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user