Files
gochat/internal/config/config_test.go
T

239 lines
7.6 KiB
Go

package config
import (
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestValidate_ValidConfig(t *testing.T) {
cfg := &Config{
Server: ServerConfig{Host: "localhost", Port: 8080, Mode: "debug"},
Database: DatabaseConfig{
Host: "localhost",
Port: 5432,
User: "gochat",
Password: "secret",
Name: "gochat_db",
},
Redis: RedisConfig{
URL: "redis://localhost:6379",
},
JWT: JWTConfig{
Secret: "test-secret-key-min-32-chars!!",
ExpiryHours: 24,
RefreshExpiryHours: 168,
},
Log: LogConfig{Level: "info", Format: "json"},
Captain: CaptainConfig{Enabled: false},
Worker: WorkerConfig{Concurrency: 4},
OAuth: OAuthConfig{},
RateLimit: RateLimitConfig{Enabled: true, RequestsPerMinute: 100, WindowSeconds: 60},
Search: SearchConfig{Engine: "meilisearch", Host: "http://localhost:7700", IndexPrefix: "gochat_", TimeoutSeconds: 5},
}
err := Validate(cfg)
assert.NoError(t, err)
}
func TestValidate_InvalidPort(t *testing.T) {
cfg := &Config{
Server: ServerConfig{Host: "localhost", Port: 0, Mode: "debug"},
Database: DatabaseConfig{Host: "localhost", Name: "db", User: "user"},
Redis: RedisConfig{URL: "redis://localhost:6379"},
JWT: JWTConfig{Secret: "test-secret-key-min-32-chars!!"},
}
err := Validate(cfg)
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid server port")
}
func TestValidate_InvalidMode(t *testing.T) {
cfg := &Config{
Server: ServerConfig{Host: "localhost", Port: 8080, Mode: "invalid"},
Database: DatabaseConfig{Host: "localhost", Name: "db", User: "user"},
Redis: RedisConfig{URL: "redis://localhost:6379"},
JWT: JWTConfig{Secret: "test-secret-key-min-32-chars!!"},
}
err := Validate(cfg)
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid server mode")
}
func TestValidate_MissingDBHost(t *testing.T) {
cfg := &Config{
Server: ServerConfig{Host: "localhost", Port: 8080, Mode: "debug"},
Database: DatabaseConfig{Name: "db", User: "user"},
Redis: RedisConfig{URL: "redis://localhost:6379"},
JWT: JWTConfig{Secret: "test-secret-key-min-32-chars!!"},
}
err := Validate(cfg)
assert.Error(t, err)
assert.Contains(t, err.Error(), "database host is required")
}
func TestValidate_MissingDBName(t *testing.T) {
cfg := &Config{
Server: ServerConfig{Host: "localhost", Port: 8080, Mode: "debug"},
Database: DatabaseConfig{Host: "localhost", User: "user"},
Redis: RedisConfig{URL: "redis://localhost:6379"},
JWT: JWTConfig{Secret: "test-secret-key-min-32-chars!!"},
}
err := Validate(cfg)
assert.Error(t, err)
assert.Contains(t, err.Error(), "database name is required")
}
func TestValidate_MissingRedisURL(t *testing.T) {
cfg := &Config{
Server: ServerConfig{Host: "localhost", Port: 8080, Mode: "debug"},
Database: DatabaseConfig{Host: "localhost", Name: "db", User: "user"},
JWT: JWTConfig{Secret: "test-secret-key-min-32-chars!!"},
}
err := Validate(cfg)
assert.Error(t, err)
assert.Contains(t, err.Error(), "redis url is required")
}
func TestValidate_InvalidRedisURL(t *testing.T) {
cfg := &Config{
Server: ServerConfig{Host: "localhost", Port: 8080, Mode: "debug"},
Database: DatabaseConfig{Host: "localhost", Name: "db", User: "user"},
Redis: RedisConfig{URL: "not-a-valid-url://::"},
JWT: JWTConfig{Secret: "test-secret-key-min-32-chars!!"},
}
err := Validate(cfg)
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid redis URL")
}
func TestValidate_JWTSecretInProduction(t *testing.T) {
cfg := &Config{
Server: ServerConfig{Host: "localhost", Port: 8080, Mode: "release"},
Database: DatabaseConfig{Host: "localhost", Name: "db", User: "user"},
Redis: RedisConfig{URL: "redis://localhost:6379"},
JWT: JWTConfig{Secret: "change-me-in-production"},
}
err := Validate(cfg)
assert.Error(t, err)
assert.Contains(t, err.Error(), "JWT secret must be changed in production")
}
func TestValidate_InvalidLogLevel(t *testing.T) {
cfg := &Config{
Server: ServerConfig{Host: "localhost", Port: 8080, Mode: "debug"},
Database: DatabaseConfig{Host: "localhost", Name: "db", User: "user"},
Redis: RedisConfig{URL: "redis://localhost:6379"},
JWT: JWTConfig{Secret: "test-secret-key-min-32-chars!!"},
Log: LogConfig{Level: "invalid"},
}
err := Validate(cfg)
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid log level")
}
func TestValidate_InvalidWorkerConcurrency(t *testing.T) {
cfg := &Config{
Server: ServerConfig{Host: "localhost", Port: 8080, Mode: "debug"},
Database: DatabaseConfig{Host: "localhost", Name: "db", User: "user"},
Redis: RedisConfig{URL: "redis://localhost:6379"},
JWT: JWTConfig{Secret: "test-secret-key-min-32-chars!!"},
Log: LogConfig{Level: "info"},
Worker: WorkerConfig{Concurrency: 0},
}
err := Validate(cfg)
assert.Error(t, err)
assert.Contains(t, err.Error(), "worker concurrency")
}
func TestValidate_SearchMeilisearchRequiresValidHost(t *testing.T) {
cfg := &Config{
Server: ServerConfig{Host: "localhost", Port: 8080, Mode: "debug"},
Database: DatabaseConfig{Host: "localhost", Name: "db", User: "user"},
Redis: RedisConfig{URL: "redis://localhost:6379"},
JWT: JWTConfig{Secret: "test-secret-key-min-32-chars!!"},
Log: LogConfig{Level: "info"},
Worker: WorkerConfig{Concurrency: 1},
RateLimit: RateLimitConfig{RequestsPerMinute: 100, WindowSeconds: 60},
Search: SearchConfig{Engine: "meilisearch", Host: "not a url", TimeoutSeconds: 5},
}
err := Validate(cfg)
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid search.host")
}
func TestValidate_SearchDBFallbackAllowed(t *testing.T) {
cfg := &Config{
Server: ServerConfig{Host: "localhost", Port: 8080, Mode: "debug"},
Database: DatabaseConfig{Host: "localhost", Name: "db", User: "user"},
Redis: RedisConfig{URL: "redis://localhost:6379"},
JWT: JWTConfig{Secret: "test-secret-key-min-32-chars!!"},
Log: LogConfig{Level: "info"},
Worker: WorkerConfig{Concurrency: 1},
RateLimit: RateLimitConfig{RequestsPerMinute: 100, WindowSeconds: 60},
Search: SearchConfig{Engine: "db"},
}
err := Validate(cfg)
assert.NoError(t, err)
}
func TestValidate_SearchDBFallbackRejectedInRelease(t *testing.T) {
cfg := &Config{
Server: ServerConfig{Host: "localhost", Port: 8080, Mode: "release"},
Database: DatabaseConfig{Host: "localhost", Name: "db", User: "user"},
Redis: RedisConfig{URL: "redis://localhost:6379"},
JWT: JWTConfig{Secret: "test-secret-key-min-32-chars!!"},
Log: LogConfig{Level: "info"},
Worker: WorkerConfig{Concurrency: 1},
RateLimit: RateLimitConfig{RequestsPerMinute: 100, WindowSeconds: 60},
Search: SearchConfig{Engine: "db"},
}
err := Validate(cfg)
assert.Error(t, err)
assert.Contains(t, err.Error(), "release mode requires meilisearch")
}
func TestDatabaseConfig_DSN(t *testing.T) {
cfg := DatabaseConfig{
Host: "localhost",
Port: 5432,
User: "gochat",
Password: "secret",
DBName: "gochat_db",
SSLMode: "disable",
}
dsn := cfg.DSN()
assert.Contains(t, dsn, "host=localhost")
assert.Contains(t, dsn, "port=5432")
assert.Contains(t, dsn, "user=gochat")
assert.Contains(t, dsn, "dbname=gochat_db")
assert.Contains(t, dsn, "sslmode=disable")
}
func TestJWTConfig_ExpiryDuration(t *testing.T) {
cfg := JWTConfig{ExpiryHours: 24}
dur := cfg.ExpiryDuration()
assert.Equal(t, 24*time.Hour, dur)
}
func TestServerConfig_Address(t *testing.T) {
cfg := ServerConfig{Host: "0.0.0.0", Port: 3000}
addr := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)
assert.Equal(t, "0.0.0.0:3000", addr)
}