kra-new/internal/config/runtime_test.go

148 lines
4.6 KiB
Go

package config
import (
"os"
"path/filepath"
"sync"
"sync/atomic"
"testing"
"time"
)
func writeConfig(t *testing.T, path, body string) {
t.Helper()
if err := os.WriteFile(path, []byte(body), 0o600); err != nil {
t.Fatal(err)
}
}
func TestLoadDirectoryAndDurations(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "config.yaml")
writeConfig(t, path, "server:\n http:\n addr: :9000\n timeout: 600s\ndata:\n redis:\n read_timeout: 200ms\nadmin:\n jwt:\n expires_time: 168h\n media:\n session_ttl: 36\n")
loaded, err := Load(dir)
if err != nil {
t.Fatal(err)
}
if loaded.Server == nil || loaded.Server.HTTP == nil || loaded.Server.HTTP.Timeout != 10*time.Minute {
t.Fatalf("server timeout = %#v", loaded.Server)
}
if loaded.Data == nil || loaded.Data.Redis == nil || loaded.Data.Redis.ReadTimeout != 200*time.Millisecond {
t.Fatalf("redis timeout = %#v", loaded.Data)
}
if loaded.Admin == nil || loaded.Admin.JWT == nil || loaded.Admin.JWT.ExpiresTime != 7*24*time.Hour {
t.Fatalf("jwt duration = %#v", loaded.Admin)
}
if loaded.Admin.ConfigPath != path {
t.Fatalf("config path = %q, want %q", loaded.Admin.ConfigPath, path)
}
}
func TestLoadRejectsInvalidDuration(t *testing.T) {
path := filepath.Join(t.TempDir(), "config.yaml")
writeConfig(t, path, "server:\n http:\n timeout: definitely-not-a-duration\n")
if _, err := Load(path); err == nil {
t.Fatal("invalid duration was accepted")
}
}
func TestLoadEnvironmentOverride(t *testing.T) {
path := filepath.Join(t.TempDir(), "config.yaml")
writeConfig(t, path, "server:\n http:\n addr: :8000\n")
t.Setenv("KRA_SERVER_HTTP_ADDR", ":9100")
loaded, err := Load(path)
if err != nil {
t.Fatal(err)
}
if loaded.Server == nil || loaded.Server.HTTP == nil || loaded.Server.HTTP.Addr != ":9100" {
t.Fatalf("environment override = %#v", loaded.Server)
}
}
func TestStoreSnapshotIsolationAndSubscriptions(t *testing.T) {
store := NewStore(&Config{Admin: &Admin{JWT: &JWT{SigningKey: "before"}}})
var calls atomic.Int32
stop := store.Subscribe(func(value *Config) {
calls.Add(1)
value.Admin.JWT.SigningKey = "callback-mutation"
})
store.Replace(&Config{Admin: &Admin{JWT: &JWT{SigningKey: "after"}}})
stop()
stop()
value := store.Snapshot()
if value == nil || value.Admin == nil || value.Admin.JWT == nil || value.Admin.JWT.SigningKey != "after" {
t.Fatalf("snapshot was mutated by callback: %#v", value)
}
if calls.Load() != 1 {
t.Fatalf("callback count = %d", calls.Load())
}
}
func TestStoreConcurrentReadersAndReplacements(t *testing.T) {
store := NewStore(&Config{Data: &Data{Database: &Database{Name: "initial"}}})
var wait sync.WaitGroup
for i := 0; i < 8; i++ {
wait.Add(1)
go func() {
defer wait.Done()
for j := 0; j < 500; j++ {
_ = store.Snapshot()
}
}()
}
for i := 0; i < 500; i++ {
store.Replace(&Config{Data: &Data{Database: &Database{Name: "next"}}})
}
wait.Wait()
}
func TestStoreWatchReloadAndClose(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "config.yaml")
writeConfig(t, path, "admin:\n app:\n app_id: first\n")
store, err := LoadStore(path)
if err != nil {
t.Fatal(err)
}
defer store.Close()
writeConfig(t, path, "admin:\n app:\n app_id: second\n")
deadline := time.Now().Add(3 * time.Second)
for time.Now().Before(deadline) {
value := store.Snapshot()
if value != nil && value.Admin != nil && value.Admin.App != nil && value.Admin.App.AppID == "second" {
return
}
time.Sleep(20 * time.Millisecond)
}
t.Fatalf("watcher did not reload configuration: %#v", store.Snapshot())
}
func TestStoreWatchPreservesDatabaseBackedIntegrationSettings(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "config.yaml")
writeConfig(t, path, "data: {}\nadmin: {}\n")
store, err := LoadStore(path)
if err != nil {
t.Fatal(err)
}
defer store.Close()
current := store.Snapshot()
current.Admin.Storage = &Storage{Type: "local"}
current.Admin.Email = &Email{Host: "smtp.example.com"}
store.Replace(current)
writeConfig(t, path, "data:\n redis:\n addr: 127.0.0.1:6379\nadmin: {}\n")
deadline := time.Now().Add(3 * time.Second)
for time.Now().Before(deadline) {
value := store.Snapshot()
if value != nil && value.Data != nil && value.Data.Redis != nil && value.Data.Redis.Addr != "" {
if value.Admin == nil || value.Admin.Storage == nil || value.Admin.Storage.Type != "local" || value.Admin.Email == nil || value.Admin.Email.Host != "smtp.example.com" {
t.Fatalf("database-backed settings were lost: %#v", value.Admin)
}
return
}
time.Sleep(20 * time.Millisecond)
}
t.Fatal("watcher did not publish the changed file")
}