Files
mygo/internal/config/load_test.go
Huxley f4212cddf0 Change config JWT duration fields to time.Duration
- fix: The AccessTTL and RefreshTTL fields in JWTConfig now use
  time.Duration type directly instead of string with ParseDuration
  methods. The config validation now checks for positive durations
  rather than parsing strings.
2026-04-29 17:02:49 +08:00

213 lines
4.8 KiB
Go

package config
import (
"os"
"path/filepath"
"testing"
"time"
)
func TestDefaults(t *testing.T) {
v := New()
cfg, err := Load(v, "")
if err != nil {
t.Fatal(err)
}
tests := []struct {
name string
got any
want any
}{
{"server.host", cfg.Server.Host, "0.0.0.0"},
{"server.port", cfg.Server.Port, 10086},
{"database.driver", cfg.Database.Driver, "sqlite3"},
{"database.sqlite.path", cfg.Database.SQLite.Path, "data/mygo.db"},
{"storage.driver", cfg.Storage.Driver, "local"},
{"storage.local.path", cfg.Storage.Local.Path, "data/files"},
{"jwt.access_ttl", cfg.JWT.AccessTTL, 15 * time.Minute},
{"jwt.refresh_ttl", cfg.JWT.RefreshTTL, 168 * time.Hour},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.got != tt.want {
t.Errorf("got %v, want %v", tt.got, tt.want)
}
})
}
}
func TestFromYAML(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "config.yaml")
yaml := `
server:
host: 127.0.0.1
port: 9090
database:
driver: sqlite3
sqlite:
path: /tmp/mygo.db
storage:
driver: local
local:
path: /tmp/mygo-storage
jwt:
secret: test-secret
access_ttl: 30m
refresh_ttl: 72h
`
if err := os.WriteFile(path, []byte(yaml), 0644); err != nil {
t.Fatal(err)
}
v := New()
cfg, err := Load(v, path)
if err != nil {
t.Fatal(err)
}
if cfg.Server.Host != "127.0.0.1" {
t.Errorf("server.host = %q, want %q", cfg.Server.Host, "127.0.0.1")
}
if cfg.Server.Port != 9090 {
t.Errorf("server.port = %d, want %d", cfg.Server.Port, 9090)
}
if cfg.Database.SQLite.Path != "/tmp/mygo.db" {
t.Errorf("database.sqlite.path = %q, want %q", cfg.Database.SQLite.Path, "/tmp/mygo.db")
}
if cfg.Storage.Local.Path != "/tmp/mygo-storage" {
t.Errorf("storage.local.path = %q, want %q", cfg.Storage.Local.Path, "/tmp/mygo-storage")
}
if cfg.JWT.Secret != "test-secret" {
t.Errorf("jwt.secret = %q, want %q", cfg.JWT.Secret, "test-secret")
}
if cfg.JWT.AccessTTL != 30*time.Minute {
t.Errorf("jwt.access_ttl = %v, want %v", cfg.JWT.AccessTTL, 30*time.Minute)
}
if cfg.JWT.RefreshTTL != 72*time.Hour {
t.Errorf("jwt.refresh_ttl = %v, want %v", cfg.JWT.RefreshTTL, 72*time.Hour)
}
}
func TestEnvOverride(t *testing.T) {
t.Setenv("MYGO_SERVER_PORT", "8080")
t.Setenv("MYGO_SERVER_HOST", "192.168.1.1")
t.Setenv("MYGO_JWT_SECRET", "env-secret")
t.Setenv("MYGO_DATABASE_SQLITE_PATH", "/env/path/db.sqlite")
v := New()
cfg, err := Load(v, "")
if err != nil {
t.Fatal(err)
}
if cfg.Server.Port != 8080 {
t.Errorf("server.port = %d, want %d", cfg.Server.Port, 8080)
}
if cfg.Server.Host != "192.168.1.1" {
t.Errorf("server.host = %q, want %q", cfg.Server.Host, "192.168.1.1")
}
if cfg.JWT.Secret != "env-secret" {
t.Errorf("jwt.secret = %q, want %q", cfg.JWT.Secret, "env-secret")
}
if cfg.Database.SQLite.Path != "/env/path/db.sqlite" {
t.Errorf("database.sqlite.path = %q, want %q", cfg.Database.SQLite.Path, "/env/path/db.sqlite")
}
}
func TestEnvOverridesYAML(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "config.yaml")
yaml := `
server:
host: 0.0.0.0
port: 10086
`
if err := os.WriteFile(path, []byte(yaml), 0644); err != nil {
t.Fatal(err)
}
t.Setenv("MYGO_SERVER_PORT", "9999")
v := New()
cfg, err := Load(v, path)
if err != nil {
t.Fatal(err)
}
if cfg.Server.Port != 9999 {
t.Errorf("server.port = %d, want %d (env should override YAML)", cfg.Server.Port, 9999)
}
if cfg.Server.Host != "0.0.0.0" {
t.Errorf("server.host = %q, want %q (YAML should be used when env is absent)", cfg.Server.Host, "0.0.0.0")
}
}
func TestValidatePortRange(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "config.yaml")
yaml := `
server:
host: 0.0.0.0
port: 0
`
if err := os.WriteFile(path, []byte(yaml), 0644); err != nil {
t.Fatal(err)
}
v := New()
_, err := Load(v, path)
if err == nil {
t.Fatal("expected error for port 0, got nil")
}
}
func TestValidateEmptySecret(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "config.yaml")
yaml := `
jwt:
secret: ""
`
if err := os.WriteFile(path, []byte(yaml), 0644); err != nil {
t.Fatal(err)
}
v := New()
_, err := Load(v, path)
if err == nil {
t.Fatal("expected error for empty jwt.secret, got nil")
}
}
func TestExplicitConfigFileNotFound(t *testing.T) {
v := New()
_, err := Load(v, "/nonexistent/path/config.yaml")
if err == nil {
t.Fatal("expected error when explicitly specifying a nonexistent config file")
}
}
func TestJWTConfigAccessDuration(t *testing.T) {
j := JWTConfig{AccessTTL: 15 * time.Minute}
if j.AccessTTL != 15*time.Minute {
t.Errorf("AccessTTL = %v, want %v", j.AccessTTL, 15*time.Minute)
}
}
func TestJWTConfigRefreshDuration(t *testing.T) {
j := JWTConfig{RefreshTTL: 168 * time.Hour}
if j.RefreshTTL != 168*time.Hour {
t.Errorf("RefreshTTL = %v, want %v", j.RefreshTTL, 168*time.Hour)
}
}