package config import ( "os" "testing" "github.com/spf13/afero" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/fx" "go.uber.org/fx/fxtest" "sneak.berlin/go/webhooker/internal/globals" "sneak.berlin/go/webhooker/internal/logger" pkgconfig "sneak.berlin/go/webhooker/pkg/config" ) // createTestConfig creates a test configuration file in memory func createTestConfig(fs afero.Fs) error { configYAML := ` environments: dev: config: port: 8080 debug: true maintenanceMode: false developmentMode: true environment: dev dburl: postgres://test:test@localhost:5432/test_dev?sslmode=disable metricsUsername: testuser metricsPassword: testpass secrets: sentryDSN: "" prod: config: port: $ENV:PORT debug: $ENV:DEBUG maintenanceMode: $ENV:MAINTENANCE_MODE developmentMode: false environment: prod dburl: $ENV:DBURL metricsUsername: $ENV:METRICS_USERNAME metricsPassword: $ENV:METRICS_PASSWORD secrets: sessionKey: $ENV:SESSION_KEY sentryDSN: $ENV:SENTRY_DSN configDefaults: port: 8080 debug: false maintenanceMode: false developmentMode: false environment: dev metricsUsername: "" metricsPassword: "" ` return afero.WriteFile(fs, "config.yaml", []byte(configYAML), 0644) } func TestEnvironmentConfig(t *testing.T) { tests := []struct { name string envValue string envVars map[string]string expectError bool isDev bool isProd bool }{ { name: "default is dev", envValue: "", expectError: false, isDev: true, isProd: false, }, { name: "explicit dev", envValue: "dev", expectError: false, isDev: true, isProd: false, }, { name: "explicit prod with session key", envValue: "prod", envVars: map[string]string{ "SESSION_KEY": "cHJvZC1zZXNzaW9uLWtleS0zMi1ieXRlcy1sb25nISE=", "DBURL": "postgres://prod:prod@localhost:5432/prod?sslmode=require", }, expectError: false, isDev: false, isProd: true, }, { name: "invalid environment", envValue: "staging", expectError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Create in-memory filesystem with test config fs := afero.NewMemMapFs() require.NoError(t, createTestConfig(fs)) pkgconfig.SetFs(fs) // Set environment variable if specified if tt.envValue != "" { os.Setenv("WEBHOOKER_ENVIRONMENT", tt.envValue) defer os.Unsetenv("WEBHOOKER_ENVIRONMENT") } // Set additional environment variables for k, v := range tt.envVars { os.Setenv(k, v) defer os.Unsetenv(k) } if tt.expectError { // Use regular fx.New for error cases since fxtest doesn't expose errors the same way var cfg *Config app := fx.New( fx.NopLogger, // Suppress fx logs in tests fx.Provide( globals.New, logger.New, New, ), fx.Populate(&cfg), ) assert.Error(t, app.Err()) } else { // Use fxtest for success cases var cfg *Config app := fxtest.New( t, fx.Provide( globals.New, logger.New, New, ), fx.Populate(&cfg), ) require.NoError(t, app.Err()) app.RequireStart() defer app.RequireStop() assert.Equal(t, tt.isDev, cfg.IsDev()) assert.Equal(t, tt.isProd, cfg.IsProd()) } }) } } func TestSessionKeyDefaults(t *testing.T) { tests := []struct { name string environment string sessionKey string dburl string expectError bool expectedKey string }{ { name: "dev mode with default session key", environment: "dev", sessionKey: "", expectError: false, expectedKey: DevSessionKey, }, { name: "dev mode with custom session key", environment: "dev", sessionKey: "Y3VzdG9tLXNlc3Npb24ta2V5LTMyLWJ5dGVzLWxvbmchIQ==", expectError: false, expectedKey: "Y3VzdG9tLXNlc3Npb24ta2V5LTMyLWJ5dGVzLWxvbmchIQ==", }, { name: "prod mode with no session key fails", environment: "prod", sessionKey: "", dburl: "postgres://prod:prod@localhost:5432/prod", expectError: true, }, { name: "prod mode with session key succeeds", environment: "prod", sessionKey: "cHJvZC1zZXNzaW9uLWtleS0zMi1ieXRlcy1sb25nISE=", dburl: "postgres://prod:prod@localhost:5432/prod", expectError: false, expectedKey: "cHJvZC1zZXNzaW9uLWtleS0zMi1ieXRlcy1sb25nISE=", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Create in-memory filesystem with test config fs := afero.NewMemMapFs() // Create custom config for session key tests configYAML := ` environments: dev: config: environment: dev developmentMode: true dburl: postgres://test:test@localhost:5432/test_dev secrets:` // Only add sessionKey line if it's not empty if tt.sessionKey != "" { configYAML += ` sessionKey: ` + tt.sessionKey } // Add prod config if testing prod if tt.environment == "prod" { configYAML += ` prod: config: environment: prod developmentMode: false dburl: $ENV:DBURL secrets: sessionKey: $ENV:SESSION_KEY` } require.NoError(t, afero.WriteFile(fs, "config.yaml", []byte(configYAML), 0644)) pkgconfig.SetFs(fs) // Clean up any existing env vars os.Unsetenv("WEBHOOKER_ENVIRONMENT") os.Unsetenv("SESSION_KEY") os.Unsetenv("DBURL") // Set environment variables os.Setenv("WEBHOOKER_ENVIRONMENT", tt.environment) defer os.Unsetenv("WEBHOOKER_ENVIRONMENT") if tt.sessionKey != "" && tt.environment == "prod" { os.Setenv("SESSION_KEY", tt.sessionKey) defer os.Unsetenv("SESSION_KEY") } if tt.dburl != "" { os.Setenv("DBURL", tt.dburl) defer os.Unsetenv("DBURL") } if tt.expectError { // Use regular fx.New for error cases var cfg *Config app := fx.New( fx.NopLogger, // Suppress fx logs in tests fx.Provide( globals.New, logger.New, New, ), fx.Populate(&cfg), ) assert.Error(t, app.Err()) } else { // Use fxtest for success cases var cfg *Config app := fxtest.New( t, fx.Provide( globals.New, logger.New, New, ), fx.Populate(&cfg), ) require.NoError(t, app.Err()) app.RequireStart() defer app.RequireStop() if tt.environment == "dev" && tt.sessionKey == "" { // Dev mode with no session key uses default assert.Equal(t, DevSessionKey, cfg.SessionKey) } else { assert.Equal(t, tt.expectedKey, cfg.SessionKey) } } }) } }