opendtu-logger/config_test.go

561 lines
17 KiB
Go

package main
import (
"os"
"path/filepath"
"testing"
)
// TestConfigLoadFromJSONFile tests loading config from a valid JSON file
func TestConfigLoadFromJSONFile(t *testing.T) {
// Save original environment
originalConfigFile := os.Getenv("CONFIG_FILE")
defer func() { os.Setenv("CONFIG_FILE", originalConfigFile) }()
// Set CONFIG_FILE to our test data
testConfigPath := filepath.Join("testdata", "test_config.json")
absPath, err := filepath.Abs(testConfigPath)
if err != nil {
t.Fatalf("Failed to get absolute path: %v", err)
}
os.Setenv("CONFIG_FILE", absPath)
// Load config from JSON file
cfg, err := loadConfig()
if err != nil {
t.Fatalf("loadConfig() failed: %v", err)
}
// Verify all fields loaded correctly from JSON
if cfg.DB != "postgres://user:password@localhost:5432/opendtu" {
t.Errorf("Expected DB from JSON, got %v", cfg.DB)
}
if cfg.OpenDTUAddress != "192.168.1.100" {
t.Errorf("Expected OpenDTUAddress from JSON, got %v", cfg.OpenDTUAddress)
}
if !cfg.OpenDTUAuth {
t.Errorf("Expected OpenDTUAuth=true from JSON")
}
if cfg.OpenDTUUser != "admin" {
t.Errorf("Expected OpenDTUUser from JSON, got %v", cfg.OpenDTUUser)
}
if cfg.OpenDTUPassword != "secret123" {
t.Errorf("Expected OpenDTUPassword from JSON, got %v", cfg.OpenDTUPassword)
}
if !cfg.TimescaleDB {
t.Errorf("Expected TimescaleDB=true from JSON")
}
if cfg.TZ != "Europe/Amsterdam" {
t.Errorf("Expected TZ from JSON, got %v", cfg.TZ)
}
if cfg.LogLevel != "INFO" {
t.Errorf("Expected LogLevel from JSON, got %v", cfg.LogLevel)
}
}
// TestConfigLoadFromJSONFile_NoAuth tests loading config from JSON without auth
func TestConfigLoadFromJSONFile_NoAuth(t *testing.T) {
// Create a temporary config file without auth
tmpDir := t.TempDir()
tmpFile := filepath.Join(tmpDir, "config_noauth.json")
configContent := `{
"db": "postgres://localhost/testdb",
"opendtu_address": "192.168.1.200",
"opendtu_auth": false,
"timescaledb": false,
"tz": "UTC",
"log_level": "DEBUG"
}`
err := os.WriteFile(tmpFile, []byte(configContent), 0644)
if err != nil {
t.Fatalf("Failed to write temp config: %v", err)
}
// Save original environment
originalConfigFile := os.Getenv("CONFIG_FILE")
defer func() { os.Setenv("CONFIG_FILE", originalConfigFile) }()
os.Setenv("CONFIG_FILE", tmpFile)
// Load config
cfg, err := loadConfig()
if err != nil {
t.Fatalf("loadConfig() failed: %v", err)
}
// Verify fields
if cfg.DB != "postgres://localhost/testdb" {
t.Errorf("Expected DB from JSON, got %v", cfg.DB)
}
if cfg.OpenDTUAddress != "192.168.1.200" {
t.Errorf("Expected OpenDTUAddress from JSON, got %v", cfg.OpenDTUAddress)
}
if cfg.OpenDTUAuth {
t.Errorf("Expected OpenDTUAuth=false from JSON")
}
if cfg.TimescaleDB {
t.Errorf("Expected TimescaleDB=false from JSON")
}
}
// TestConfigLoadFromJSONFile_InvalidTimezone tests the timezone validation
func TestConfigLoadFromJSONFile_InvalidTimezone(t *testing.T) {
// Create a temporary config file with invalid timezone
tmpDir := t.TempDir()
tmpFile := filepath.Join(tmpDir, "config_badtz.json")
configContent := `{
"db": "postgres://localhost/testdb",
"opendtu_address": "192.168.1.200",
"opendtu_auth": false,
"tz": "Invalid/Timezone"
}`
err := os.WriteFile(tmpFile, []byte(configContent), 0644)
if err != nil {
t.Fatalf("Failed to write temp config: %v", err)
}
// Save original environment
originalConfigFile := os.Getenv("CONFIG_FILE")
defer func() { os.Setenv("CONFIG_FILE", originalConfigFile) }()
os.Setenv("CONFIG_FILE", tmpFile)
// Load config - should not fatal, just log warning
cfg, err := loadConfig()
if err != nil {
t.Fatalf("loadConfig() failed: %v", err)
}
// Should still load other fields successfully
if cfg.DB != "postgres://localhost/testdb" {
t.Errorf("Expected DB to load despite invalid timezone")
}
}
// TestConfigLoadFromEnv_WithTimescaleDB tests env var path with TimescaleDB enabled
func TestConfigLoadFromEnv_WithTimescaleDB(t *testing.T) {
// Save original environment
originalVars := map[string]string{
"CONFIG_FILE": os.Getenv("CONFIG_FILE"),
"DB_URL": os.Getenv("DB_URL"),
"OPENDTU_ADDRESS": os.Getenv("OPENDTU_ADDRESS"),
"OPENDTU_AUTH": os.Getenv("OPENDTU_AUTH"),
"TIMESCALEDB_ENABLED": os.Getenv("TIMESCALEDB_ENABLED"),
"TZ": os.Getenv("TZ"),
"LOG_LEVEL": os.Getenv("LOG_LEVEL"),
}
defer func() {
for k, v := range originalVars {
os.Setenv(k, v)
}
}()
// Set environment for non-existent config file
os.Setenv("CONFIG_FILE", "/nonexistent/config.json")
os.Setenv("DB_URL", "postgres://testhost/testdb")
os.Setenv("OPENDTU_ADDRESS", "10.0.0.1")
os.Setenv("OPENDTU_AUTH", "false")
os.Setenv("TIMESCALEDB_ENABLED", "true")
os.Setenv("TZ", "America/New_York")
os.Setenv("LOG_LEVEL", "WARN")
cfg, err := loadConfig()
if err != nil {
t.Fatalf("loadConfig() failed: %v", err)
}
if cfg.DB != "postgres://testhost/testdb" {
t.Errorf("Expected DB from env, got %v", cfg.DB)
}
if !cfg.TimescaleDB {
t.Errorf("Expected TimescaleDB=true from env")
}
if cfg.TZ != "America/New_York" {
t.Errorf("Expected TZ from env, got %v", cfg.TZ)
}
if cfg.LogLevel != "WARN" {
t.Errorf("Expected LogLevel from env, got %v", cfg.LogLevel)
}
}
// TestConfigLoadFromEnv_WithAuth tests env var path with auth enabled
func TestConfigLoadFromEnv_WithAuth(t *testing.T) {
// Save original environment
originalVars := map[string]string{
"CONFIG_FILE": os.Getenv("CONFIG_FILE"),
"DB_URL": os.Getenv("DB_URL"),
"OPENDTU_ADDRESS": os.Getenv("OPENDTU_ADDRESS"),
"OPENDTU_AUTH": os.Getenv("OPENDTU_AUTH"),
"OPENDTU_USERNAME": os.Getenv("OPENDTU_USERNAME"),
"OPENDTU_PASSWORD": os.Getenv("OPENDTU_PASSWORD"),
}
defer func() {
for k, v := range originalVars {
os.Setenv(k, v)
}
}()
// Set environment with auth enabled
os.Setenv("CONFIG_FILE", "/nonexistent/config.json")
os.Setenv("DB_URL", "postgres://testhost/testdb")
os.Setenv("OPENDTU_ADDRESS", "10.0.0.1")
os.Setenv("OPENDTU_AUTH", "true")
os.Setenv("OPENDTU_USERNAME", "testuser")
os.Setenv("OPENDTU_PASSWORD", "testpass")
cfg, err := loadConfig()
if err != nil {
t.Fatalf("loadConfig() failed: %v", err)
}
if !cfg.OpenDTUAuth {
t.Errorf("Expected OpenDTUAuth=true from env")
}
if cfg.OpenDTUUser != "testuser" {
t.Errorf("Expected OpenDTUUser from env, got %v", cfg.OpenDTUUser)
}
if cfg.OpenDTUPassword != "testpass" {
t.Errorf("Expected OpenDTUPassword from env, got %v", cfg.OpenDTUPassword)
}
}
// TestConfigLoadFromJSONFile_InvalidJSON tests error when JSON is malformed
func TestConfigLoadFromJSONFile_InvalidJSON(t *testing.T) {
tmpDir := t.TempDir()
tmpFile := filepath.Join(tmpDir, "invalid.json")
// Write invalid JSON
err := os.WriteFile(tmpFile, []byte("{invalid json}"), 0644)
if err != nil {
t.Fatalf("Failed to write temp config: %v", err)
}
originalConfigFile := os.Getenv("CONFIG_FILE")
defer func() { os.Setenv("CONFIG_FILE", originalConfigFile) }()
os.Setenv("CONFIG_FILE", tmpFile)
_, err = loadConfig()
if err == nil {
t.Error("Expected error for invalid JSON, got nil")
}
if err != nil && !contains(err.Error(), "error parsing config file") {
t.Errorf("Expected 'error parsing config file' error, got: %v", err)
}
}
// TestConfigLoadFromJSONFile_MissingDB tests error when DB is not set in JSON
func TestConfigLoadFromJSONFile_MissingDB(t *testing.T) {
// Reset global config to avoid test pollution
config = Config{}
tmpDir := t.TempDir()
tmpFile := filepath.Join(tmpDir, "no_db.json")
configContent := `{
"opendtu_address": "192.168.1.100"
}`
err := os.WriteFile(tmpFile, []byte(configContent), 0644)
if err != nil {
t.Fatalf("Failed to write temp config: %v", err)
}
originalConfigFile := os.Getenv("CONFIG_FILE")
defer func() { os.Setenv("CONFIG_FILE", originalConfigFile) }()
os.Setenv("CONFIG_FILE", tmpFile)
_, err = loadConfig()
if err == nil {
t.Error("Expected error for missing DB, got nil")
}
if err != nil && !contains(err.Error(), "db connection settings are not set") {
t.Errorf("Expected 'db connection settings' error, got: %v", err)
}
}
// TestConfigLoadFromJSONFile_MissingOpenDTUAddress tests error when opendtu_address is not set
func TestConfigLoadFromJSONFile_MissingOpenDTUAddress(t *testing.T) {
// Reset global config to avoid test pollution
config = Config{}
tmpDir := t.TempDir()
tmpFile := filepath.Join(tmpDir, "no_address.json")
configContent := `{
"db": "postgres://localhost/testdb"
}`
err := os.WriteFile(tmpFile, []byte(configContent), 0644)
if err != nil {
t.Fatalf("Failed to write temp config: %v", err)
}
originalConfigFile := os.Getenv("CONFIG_FILE")
defer func() { os.Setenv("CONFIG_FILE", originalConfigFile) }()
os.Setenv("CONFIG_FILE", tmpFile)
_, err = loadConfig()
if err == nil {
t.Error("Expected error for missing OpenDTU address, got nil")
}
if err != nil && !contains(err.Error(), "opendtu_address is not set") {
t.Errorf("Expected 'opendtu_address is not set' error, got: %v", err)
}
}
// TestConfigLoadFromJSONFile_MissingUsername tests error when username is missing with auth enabled
func TestConfigLoadFromJSONFile_MissingUsername(t *testing.T) {
// Reset global config to avoid test pollution
config = Config{}
tmpDir := t.TempDir()
tmpFile := filepath.Join(tmpDir, "no_username.json")
configContent := `{
"db": "postgres://localhost/testdb",
"opendtu_address": "192.168.1.100",
"opendtu_auth": true,
"opendtu_password": "secret"
}`
err := os.WriteFile(tmpFile, []byte(configContent), 0644)
if err != nil {
t.Fatalf("Failed to write temp config: %v", err)
}
originalConfigFile := os.Getenv("CONFIG_FILE")
defer func() { os.Setenv("CONFIG_FILE", originalConfigFile) }()
os.Setenv("CONFIG_FILE", tmpFile)
_, err = loadConfig()
if err == nil {
t.Error("Expected error for missing username, got nil")
}
if err != nil && !contains(err.Error(), "opendtu_username is not set") {
t.Errorf("Expected 'opendtu_username is not set' error, got: %v", err)
}
}
// TestConfigLoadFromJSONFile_MissingPassword tests error when password is missing with auth enabled
func TestConfigLoadFromJSONFile_MissingPassword(t *testing.T) {
// Reset global config to avoid test pollution
config = Config{}
tmpDir := t.TempDir()
tmpFile := filepath.Join(tmpDir, "no_password.json")
configContent := `{
"db": "postgres://localhost/testdb",
"opendtu_address": "192.168.1.100",
"opendtu_auth": true,
"opendtu_username": "admin"
}`
err := os.WriteFile(tmpFile, []byte(configContent), 0644)
if err != nil {
t.Fatalf("Failed to write temp config: %v", err)
}
originalConfigFile := os.Getenv("CONFIG_FILE")
defer func() { os.Setenv("CONFIG_FILE", originalConfigFile) }()
os.Setenv("CONFIG_FILE", tmpFile)
_, err = loadConfig()
if err == nil {
t.Error("Expected error for missing password, got nil")
}
if err != nil && !contains(err.Error(), "opendtu_password is not set") {
t.Errorf("Expected 'opendtu_password is not set' error, got: %v", err)
}
}
// TestConfigLoadFromEnv_MissingDBURL tests error when DB_URL env var is missing
func TestConfigLoadFromEnv_MissingDBURL(t *testing.T) {
originalVars := map[string]string{
"CONFIG_FILE": os.Getenv("CONFIG_FILE"),
"DB_URL": os.Getenv("DB_URL"),
"OPENDTU_ADDRESS": os.Getenv("OPENDTU_ADDRESS"),
}
defer func() {
for k, v := range originalVars {
os.Setenv(k, v)
}
}()
os.Setenv("CONFIG_FILE", "/nonexistent/config.json")
os.Unsetenv("DB_URL")
os.Setenv("OPENDTU_ADDRESS", "192.168.1.100")
_, err := loadConfig()
if err == nil {
t.Error("Expected error for missing DB_URL, got nil")
}
if err != nil && !contains(err.Error(), "DB_URL environment variable is not set") {
t.Errorf("Expected 'DB_URL environment variable is not set' error, got: %v", err)
}
}
// TestConfigLoadFromEnv_MissingOpenDTUAddress tests error when OPENDTU_ADDRESS is missing
func TestConfigLoadFromEnv_MissingOpenDTUAddress(t *testing.T) {
originalVars := map[string]string{
"CONFIG_FILE": os.Getenv("CONFIG_FILE"),
"DB_URL": os.Getenv("DB_URL"),
"OPENDTU_ADDRESS": os.Getenv("OPENDTU_ADDRESS"),
}
defer func() {
for k, v := range originalVars {
os.Setenv(k, v)
}
}()
os.Setenv("CONFIG_FILE", "/nonexistent/config.json")
os.Setenv("DB_URL", "postgres://localhost/testdb")
os.Unsetenv("OPENDTU_ADDRESS")
_, err := loadConfig()
if err == nil {
t.Error("Expected error for missing OPENDTU_ADDRESS, got nil")
}
if err != nil && !contains(err.Error(), "OPENDTU_ADDRESS environment variable is not set") {
t.Errorf("Expected 'OPENDTU_ADDRESS environment variable is not set' error, got: %v", err)
}
}
// TestConfigLoadFromEnv_InvalidAuthBool tests error when OPENDTU_AUTH has invalid boolean
func TestConfigLoadFromEnv_InvalidAuthBool(t *testing.T) {
originalVars := map[string]string{
"CONFIG_FILE": os.Getenv("CONFIG_FILE"),
"DB_URL": os.Getenv("DB_URL"),
"OPENDTU_ADDRESS": os.Getenv("OPENDTU_ADDRESS"),
"OPENDTU_AUTH": os.Getenv("OPENDTU_AUTH"),
}
defer func() {
for k, v := range originalVars {
os.Setenv(k, v)
}
}()
os.Setenv("CONFIG_FILE", "/nonexistent/config.json")
os.Setenv("DB_URL", "postgres://localhost/testdb")
os.Setenv("OPENDTU_ADDRESS", "192.168.1.100")
os.Setenv("OPENDTU_AUTH", "invalid")
_, err := loadConfig()
if err == nil {
t.Error("Expected error for invalid OPENDTU_AUTH, got nil")
}
if err != nil && !contains(err.Error(), "error parsing OPENDTU_AUTH") {
t.Errorf("Expected 'error parsing OPENDTU_AUTH' error, got: %v", err)
}
}
// TestConfigLoadFromEnv_MissingUsername tests error when OPENDTU_USERNAME is missing with auth
func TestConfigLoadFromEnv_MissingUsername(t *testing.T) {
originalVars := map[string]string{
"CONFIG_FILE": os.Getenv("CONFIG_FILE"),
"DB_URL": os.Getenv("DB_URL"),
"OPENDTU_ADDRESS": os.Getenv("OPENDTU_ADDRESS"),
"OPENDTU_AUTH": os.Getenv("OPENDTU_AUTH"),
"OPENDTU_USERNAME": os.Getenv("OPENDTU_USERNAME"),
}
defer func() {
for k, v := range originalVars {
os.Setenv(k, v)
}
}()
os.Setenv("CONFIG_FILE", "/nonexistent/config.json")
os.Setenv("DB_URL", "postgres://localhost/testdb")
os.Setenv("OPENDTU_ADDRESS", "192.168.1.100")
os.Setenv("OPENDTU_AUTH", "true")
os.Unsetenv("OPENDTU_USERNAME")
os.Setenv("OPENDTU_PASSWORD", "secret")
_, err := loadConfig()
if err == nil {
t.Error("Expected error for missing OPENDTU_USERNAME, got nil")
}
if err != nil && !contains(err.Error(), "OPENDTU_USERNAME environment variable is not set") {
t.Errorf("Expected 'OPENDTU_USERNAME environment variable is not set' error, got: %v", err)
}
}
// TestConfigLoadFromEnv_MissingPassword tests error when OPENDTU_PASSWORD is missing with auth
func TestConfigLoadFromEnv_MissingPassword(t *testing.T) {
originalVars := map[string]string{
"CONFIG_FILE": os.Getenv("CONFIG_FILE"),
"DB_URL": os.Getenv("DB_URL"),
"OPENDTU_ADDRESS": os.Getenv("OPENDTU_ADDRESS"),
"OPENDTU_AUTH": os.Getenv("OPENDTU_AUTH"),
"OPENDTU_USERNAME": os.Getenv("OPENDTU_USERNAME"),
"OPENDTU_PASSWORD": os.Getenv("OPENDTU_PASSWORD"),
}
defer func() {
for k, v := range originalVars {
os.Setenv(k, v)
}
}()
os.Setenv("CONFIG_FILE", "/nonexistent/config.json")
os.Setenv("DB_URL", "postgres://localhost/testdb")
os.Setenv("OPENDTU_ADDRESS", "192.168.1.100")
os.Setenv("OPENDTU_AUTH", "true")
os.Setenv("OPENDTU_USERNAME", "admin")
os.Unsetenv("OPENDTU_PASSWORD")
_, err := loadConfig()
if err == nil {
t.Error("Expected error for missing OPENDTU_PASSWORD, got nil")
}
if err != nil && !contains(err.Error(), "OPENDTU_PASSWORD environment variable is not set") {
t.Errorf("Expected 'OPENDTU_PASSWORD environment variable is not set' error, got: %v", err)
}
}
// TestConfigLoadFromEnv_InvalidTimescaleDBBool tests error when TIMESCALEDB_ENABLED has invalid boolean
func TestConfigLoadFromEnv_InvalidTimescaleDBBool(t *testing.T) {
originalVars := map[string]string{
"CONFIG_FILE": os.Getenv("CONFIG_FILE"),
"DB_URL": os.Getenv("DB_URL"),
"OPENDTU_ADDRESS": os.Getenv("OPENDTU_ADDRESS"),
"OPENDTU_AUTH": os.Getenv("OPENDTU_AUTH"),
"TIMESCALEDB_ENABLED": os.Getenv("TIMESCALEDB_ENABLED"),
}
defer func() {
for k, v := range originalVars {
os.Setenv(k, v)
}
}()
os.Setenv("CONFIG_FILE", "/nonexistent/config.json")
os.Setenv("DB_URL", "postgres://localhost/testdb")
os.Setenv("OPENDTU_ADDRESS", "192.168.1.100")
os.Setenv("OPENDTU_AUTH", "false") // Set auth to false so we don't need username/password
os.Setenv("TIMESCALEDB_ENABLED", "not-a-bool")
_, err := loadConfig()
if err == nil {
t.Error("Expected error for invalid TIMESCALEDB_ENABLED, got nil")
}
if err != nil && !contains(err.Error(), "error parsing TIMESCALEDB_ENABLED") {
t.Errorf("Expected 'error parsing TIMESCALEDB_ENABLED' error, got: %v", err)
}
}
// Helper function to check if a string contains a substring
func contains(s, substr string) bool {
return len(s) >= len(substr) && (s == substr || len(s) > len(substr) &&
(s[:len(substr)] == substr || s[len(s)-len(substr):] == substr ||
func() bool {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return true
}
}
return false
}()))
}