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