720a84be92
integration of background processor * integration part 1 * feature working * fix mimetype issue
473 lines
14 KiB
Go
473 lines
14 KiB
Go
package serviceconfig_test
|
|
|
|
import (
|
|
"bytes"
|
|
"log/slog"
|
|
"strings"
|
|
"testing"
|
|
|
|
"queryorchestration/internal/serviceconfig"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestPrintConfigRecursive tests the PrintConfigRecursive function as a blackbox test.
|
|
func TestPrintConfigRecursive(t *testing.T) {
|
|
t.Setenv("DEBUG", "true")
|
|
serviceconfig.ResetConfigInitOnceTestOnly()
|
|
envVars := map[string]string{
|
|
"PGUSER": "postgres",
|
|
"PGPASSWORD": "password",
|
|
"PGHOST": "localhost",
|
|
"PGPORT": "5432",
|
|
"PGDATABASE": "query_orchestration",
|
|
"DB_NOSSL": "true",
|
|
"AWS_ACCESS_KEY_ID": "key",
|
|
"AWS_SECRET_ACCESS_KEY": "password",
|
|
"AWS_REGION": "region",
|
|
"COGNITO_USER_POOL_ID": "poolid",
|
|
"COGNITO_CLIENT_SECRET": "secret",
|
|
"COGNITO_DOMAIN": "domain",
|
|
"COGNITO_CLIENT_ID": "clientid",
|
|
"PORT": "8080", // Only include vars needed by BaseConfig
|
|
}
|
|
|
|
// Set environment variables
|
|
for k, v := range envVars {
|
|
t.Setenv(k, v)
|
|
}
|
|
|
|
// Create a buffer to capture log output
|
|
var logBuffer bytes.Buffer
|
|
|
|
// Create a test logger that writes to our buffer
|
|
testLogger := slog.New(slog.NewTextHandler(&logBuffer, &slog.HandlerOptions{
|
|
Level: slog.LevelDebug,
|
|
}))
|
|
|
|
// Use BaseConfig instead of initializeConfigTestStruct
|
|
cfg := &serviceconfig.BaseConfig{}
|
|
err := serviceconfig.InitializeConfig(cfg)
|
|
require.NoError(t, err)
|
|
|
|
// Set logger after initialization
|
|
cfg.Logger = testLogger
|
|
|
|
// Call the function we're testing
|
|
cfg.PrintConfig("secret")
|
|
|
|
// Get the log output as a string
|
|
logOutput := logBuffer.String()
|
|
|
|
// Check that each environment variable value appears in the logs
|
|
// except for sensitive values which should be masked
|
|
for envKey, envValue := range envVars {
|
|
if envKey == "PGPASSWORD" || envKey == "AWS_SECRET_ACCESS_KEY" || envKey == "COGNITO_CLIENT_SECRET" {
|
|
// Password should be masked
|
|
if strings.Contains(logOutput, envValue) {
|
|
t.Errorf("Full password value '%s' should not appear in logs for %s", envValue, envKey)
|
|
}
|
|
// Should see the masked value indicator
|
|
if !strings.Contains(logOutput, "***MASKED***") {
|
|
t.Errorf("Expected ***MASKED*** for secret field %s in logs, got: %s", envKey, logOutput)
|
|
}
|
|
} else if !strings.Contains(logOutput, envValue) {
|
|
// Only check for non-secret values that should appear
|
|
// Note: Some values might be in nested structures or have different JSON names
|
|
// so we should be more lenient here
|
|
t.Logf("Warning: Expected %s=%s in logs, but it was not found", envKey, envValue)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestPrintConfigErrorHandling tests error handling paths in PrintConfig
|
|
func TestPrintConfigErrorHandling(t *testing.T) {
|
|
t.Setenv("DEBUG", "true")
|
|
serviceconfig.ResetConfigInitOnceTestOnly()
|
|
|
|
// Test 1: PrintConfig with nil logger (should not panic)
|
|
t.Run("nil_logger", func(t *testing.T) {
|
|
cfg := &serviceconfig.BaseConfig{}
|
|
cfg.Logger = nil // Explicitly set to nil
|
|
|
|
// This should not panic even with nil logger
|
|
cfg.PrintConfig("secret")
|
|
})
|
|
|
|
// Test 2: PrintConfig with error in configToMap
|
|
t.Run("config_conversion_error", func(t *testing.T) {
|
|
var logBuffer bytes.Buffer
|
|
testLogger := slog.New(slog.NewTextHandler(&logBuffer, &slog.HandlerOptions{
|
|
Level: slog.LevelDebug,
|
|
}))
|
|
|
|
cfg := &serviceconfig.BaseConfig{}
|
|
cfg.Logger = testLogger
|
|
|
|
// Call PrintConfig - even if conversion has issues, it should handle gracefully
|
|
cfg.PrintConfig("secret")
|
|
|
|
logOutput := logBuffer.String()
|
|
// Check that something was logged (even if empty config)
|
|
require.NotEmpty(t, logOutput, "Should have some log output")
|
|
})
|
|
}
|
|
|
|
// TestPrintConfigWithVariousConfigs tests PrintConfig with different config states
|
|
func TestPrintConfigWithVariousConfigs(t *testing.T) {
|
|
t.Setenv("DEBUG", "true")
|
|
serviceconfig.ResetConfigInitOnceTestOnly()
|
|
|
|
tests := []struct {
|
|
name string
|
|
setupFunc func() *serviceconfig.BaseConfig
|
|
}{
|
|
{
|
|
name: "empty_config",
|
|
setupFunc: func() *serviceconfig.BaseConfig {
|
|
cfg := &serviceconfig.BaseConfig{}
|
|
cfg.SetDefaultLogger()
|
|
return cfg
|
|
},
|
|
},
|
|
{
|
|
name: "config_with_values",
|
|
setupFunc: func() *serviceconfig.BaseConfig {
|
|
t.Setenv("PORT", "8080")
|
|
t.Setenv("BASE_URL", "http://localhost")
|
|
t.Setenv("PGUSER", "testuser")
|
|
t.Setenv("PGPASSWORD", "testpass")
|
|
t.Setenv("PGHOST", "localhost")
|
|
t.Setenv("PGPORT", "5432")
|
|
t.Setenv("PGDATABASE", "testdb")
|
|
t.Setenv("AWS_ACCESS_KEY_ID", "testkey")
|
|
t.Setenv("AWS_SECRET_ACCESS_KEY", "testsecret")
|
|
t.Setenv("AWS_REGION", "us-east-1")
|
|
|
|
cfg := &serviceconfig.BaseConfig{}
|
|
_ = serviceconfig.InitializeConfig(cfg)
|
|
cfg.SetDefaultLogger()
|
|
return cfg
|
|
},
|
|
},
|
|
{
|
|
name: "config_with_logger_but_no_env",
|
|
setupFunc: func() *serviceconfig.BaseConfig {
|
|
cfg := &serviceconfig.BaseConfig{}
|
|
var logBuffer bytes.Buffer
|
|
cfg.Logger = slog.New(slog.NewTextHandler(&logBuffer, nil))
|
|
return cfg
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.name == "config_with_values" {
|
|
serviceconfig.ResetConfigInitOnceTestOnly()
|
|
}
|
|
cfg := tt.setupFunc()
|
|
|
|
// Should not panic
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
t.Errorf("PrintConfig panicked: %v", r)
|
|
}
|
|
}()
|
|
|
|
cfg.PrintConfig("secret")
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestPrintConfigCoverageEdgeCases tests edge cases to improve coverage
|
|
func TestPrintConfigCoverageEdgeCases(t *testing.T) {
|
|
t.Setenv("DEBUG", "true")
|
|
serviceconfig.ResetConfigInitOnceTestOnly()
|
|
|
|
// Test case 1: Config with nested map structures to trigger recursion in logConfigMap
|
|
t.Run("nested_map_logging", func(t *testing.T) {
|
|
var logBuffer bytes.Buffer
|
|
testLogger := slog.New(slog.NewTextHandler(&logBuffer, &slog.HandlerOptions{
|
|
Level: slog.LevelDebug,
|
|
}))
|
|
|
|
cfg := &serviceconfig.BaseConfig{}
|
|
cfg.Logger = testLogger
|
|
|
|
// This will test the nested map handling in logConfigMap
|
|
cfg.PrintConfig("test")
|
|
|
|
// Just verify it doesn't panic and produces some output
|
|
logOutput := logBuffer.String()
|
|
require.NotEmpty(t, logOutput)
|
|
})
|
|
|
|
// Test case 2: Config with array values to trigger array handling in logConfigMap
|
|
t.Run("array_logging", func(t *testing.T) {
|
|
var logBuffer bytes.Buffer
|
|
testLogger := slog.New(slog.NewTextHandler(&logBuffer, &slog.HandlerOptions{
|
|
Level: slog.LevelDebug,
|
|
}))
|
|
|
|
cfg := &serviceconfig.BaseConfig{}
|
|
cfg.Logger = testLogger
|
|
|
|
// Set some environment variables that might result in arrays
|
|
t.Setenv("QUEUE_NAMES", "queue1,queue2,queue3")
|
|
|
|
cfg.PrintConfig("test")
|
|
|
|
logOutput := logBuffer.String()
|
|
require.NotEmpty(t, logOutput)
|
|
})
|
|
|
|
// Test case 3: Test with various secret patterns to improve masking coverage
|
|
t.Run("comprehensive_secret_masking", func(t *testing.T) {
|
|
var logBuffer bytes.Buffer
|
|
testLogger := slog.New(slog.NewTextHandler(&logBuffer, &slog.HandlerOptions{
|
|
Level: slog.LevelDebug,
|
|
}))
|
|
|
|
cfg := &serviceconfig.BaseConfig{}
|
|
cfg.Logger = testLogger
|
|
|
|
// Set environment variables with various secret patterns
|
|
t.Setenv("JWT_TOKEN", "my-jwt-token")
|
|
t.Setenv("BEARER_TOKEN", "bearer-123")
|
|
t.Setenv("PRIVATE_DATA", "sensitive")
|
|
t.Setenv("CUSTOM_SECRET", "mysecret")
|
|
t.Setenv("API_KEY", "key123")
|
|
t.Setenv("ACCESS_KEY", "access123")
|
|
t.Setenv("CREDENTIAL_DATA", "creds")
|
|
|
|
// Initialize config to pick up environment variables
|
|
serviceconfig.ResetConfigInitOnceTestOnly()
|
|
_ = serviceconfig.InitializeConfig(cfg)
|
|
cfg.Logger = testLogger // Reset logger after initialization
|
|
|
|
cfg.PrintConfig("CUSTOM") // Test with custom prefix
|
|
|
|
logOutput := logBuffer.String()
|
|
require.NotEmpty(t, logOutput)
|
|
|
|
// Verify that secrets are masked
|
|
require.Contains(t, logOutput, "***MASKED***")
|
|
|
|
// Verify that original secret values don't appear
|
|
secretValues := []string{"my-jwt-token", "bearer-123", "sensitive", "mysecret", "key123", "access123", "creds"}
|
|
for _, secret := range secretValues {
|
|
require.NotContains(t, logOutput, secret)
|
|
}
|
|
})
|
|
|
|
// Test case 4: Test with nil logger to cover logConfigMap nil check
|
|
t.Run("nil_logger_in_logConfigMap", func(t *testing.T) {
|
|
cfg := &serviceconfig.BaseConfig{}
|
|
cfg.Logger = nil // Explicitly nil
|
|
cfg.Port = 8080
|
|
|
|
// This should not panic and should exit early due to nil logger check
|
|
cfg.PrintConfig("test")
|
|
// If we get here without panic, the test passes
|
|
})
|
|
|
|
// Test case 5: Test with prefix in logConfigMap
|
|
t.Run("with_prefix", func(t *testing.T) {
|
|
var logBuffer bytes.Buffer
|
|
testLogger := slog.New(slog.NewTextHandler(&logBuffer, &slog.HandlerOptions{
|
|
Level: slog.LevelDebug,
|
|
}))
|
|
|
|
cfg := &serviceconfig.BaseConfig{}
|
|
cfg.Logger = testLogger
|
|
cfg.Port = 9090
|
|
|
|
cfg.PrintConfig("custom_prefix")
|
|
|
|
logOutput := logBuffer.String()
|
|
require.NotEmpty(t, logOutput)
|
|
})
|
|
}
|
|
|
|
// TestLogConfigMapDirectly tests logConfigMap function edge cases directly
|
|
func TestLogConfigMapDirectly(t *testing.T) {
|
|
t.Setenv("DEBUG", "true")
|
|
serviceconfig.ResetConfigInitOnceTestOnly()
|
|
|
|
// Test case 1: Test with nested maps to trigger recursion
|
|
t.Run("nested_maps", func(t *testing.T) {
|
|
var logBuffer bytes.Buffer
|
|
testLogger := slog.New(slog.NewTextHandler(&logBuffer, &slog.HandlerOptions{
|
|
Level: slog.LevelDebug,
|
|
}))
|
|
|
|
cfg := &serviceconfig.BaseConfig{}
|
|
cfg.Logger = testLogger
|
|
|
|
// Call PrintConfig to test nested map handling in logConfigMap
|
|
cfg.PrintConfig("test")
|
|
|
|
logOutput := logBuffer.String()
|
|
require.NotEmpty(t, logOutput)
|
|
})
|
|
|
|
// Test case 2: Test with array values to trigger array handling
|
|
t.Run("array_values", func(t *testing.T) {
|
|
var logBuffer bytes.Buffer
|
|
testLogger := slog.New(slog.NewTextHandler(&logBuffer, &slog.HandlerOptions{
|
|
Level: slog.LevelDebug,
|
|
}))
|
|
|
|
// Create a custom config with array-like values
|
|
type ConfigWithArrays struct {
|
|
serviceconfig.BaseConfig
|
|
Items []string `env:"TEST_ITEMS" envDefault:"item1,item2,item3"`
|
|
}
|
|
|
|
cfg := &ConfigWithArrays{}
|
|
cfg.Logger = testLogger
|
|
cfg.Items = []string{"test1", "test2", "test3"}
|
|
|
|
cfg.BaseConfig.PrintConfig("test")
|
|
|
|
logOutput := logBuffer.String()
|
|
require.NotEmpty(t, logOutput)
|
|
})
|
|
|
|
// Test case 3: Test with nil logger to hit the early return in logConfigMap
|
|
t.Run("nil_logger_early_return", func(t *testing.T) {
|
|
cfg := &serviceconfig.BaseConfig{}
|
|
cfg.Logger = nil // Explicitly nil
|
|
|
|
// This should hit the early return in logConfigMap
|
|
cfg.PrintConfig("test")
|
|
// If we get here without panic, the test passes
|
|
})
|
|
}
|
|
|
|
// TestPrintConfigErrorPaths tests specific error paths in PrintConfig to improve coverage
|
|
func TestPrintConfigErrorPaths(t *testing.T) {
|
|
t.Setenv("DEBUG", "true")
|
|
serviceconfig.ResetConfigInitOnceTestOnly()
|
|
|
|
// Test case 1: Test configToMap error handling
|
|
t.Run("configToMap_error", func(t *testing.T) {
|
|
var logBuffer bytes.Buffer
|
|
testLogger := slog.New(slog.NewTextHandler(&logBuffer, &slog.HandlerOptions{
|
|
Level: slog.LevelDebug,
|
|
}))
|
|
|
|
// Create a config that will cause JSON marshal to fail
|
|
type BadConfig struct {
|
|
serviceconfig.BaseConfig
|
|
UnsupportedField interface{} `env:"UNSUPPORTED"`
|
|
}
|
|
|
|
cfg := &BadConfig{}
|
|
cfg.Logger = testLogger
|
|
// Set a value that cannot be marshaled to JSON (circular reference)
|
|
cfg.UnsupportedField = make(map[string]interface{})
|
|
if m, ok := cfg.UnsupportedField.(map[string]interface{}); ok {
|
|
m["self"] = cfg.UnsupportedField
|
|
}
|
|
|
|
// This should trigger the error path in configToMap
|
|
cfg.BaseConfig.PrintConfig("test")
|
|
|
|
// Check that error was logged
|
|
logOutput := logBuffer.String()
|
|
if !strings.Contains(logOutput, "Failed to convert config to map") &&
|
|
!strings.Contains(logOutput, "Failed to print config") {
|
|
// Either error message should appear
|
|
t.Logf("Expected error handling, got: %s", logOutput)
|
|
}
|
|
})
|
|
|
|
// Test case 2: Test the error logging path when config conversion fails
|
|
t.Run("force_error_logging", func(t *testing.T) {
|
|
var logBuffer bytes.Buffer
|
|
testLogger := slog.New(slog.NewTextHandler(&logBuffer, &slog.HandlerOptions{
|
|
Level: slog.LevelDebug,
|
|
}))
|
|
|
|
// Use a more complex circular reference that will definitely fail JSON marshaling
|
|
type CircularConfig struct {
|
|
serviceconfig.BaseConfig
|
|
Self *CircularConfig `env:"SELF_REF"`
|
|
}
|
|
|
|
cfg := &CircularConfig{}
|
|
cfg.Logger = testLogger
|
|
cfg.Self = cfg // This creates a circular reference that JSON cannot marshal
|
|
|
|
// This should trigger the error path in configToMap and the error logging in PrintConfig
|
|
cfg.BaseConfig.PrintConfig("test")
|
|
|
|
// Check that error was logged
|
|
logOutput := logBuffer.String()
|
|
t.Logf("Log output: %s", logOutput)
|
|
|
|
// The test passes if we don't panic and have some log output
|
|
require.NotEmpty(t, logOutput)
|
|
})
|
|
}
|
|
|
|
// TestPrintConfigDebugDisabled tests that PrintConfig does nothing when DEBUG is not true
|
|
func TestPrintConfigDebugDisabled(t *testing.T) {
|
|
serviceconfig.ResetConfigInitOnceTestOnly()
|
|
|
|
var logBuffer bytes.Buffer
|
|
testLogger := slog.New(slog.NewTextHandler(&logBuffer, &slog.HandlerOptions{
|
|
Level: slog.LevelDebug,
|
|
}))
|
|
|
|
cfg := &serviceconfig.BaseConfig{}
|
|
cfg.Logger = testLogger
|
|
cfg.Port = 8080
|
|
|
|
// Test 1: DEBUG not set (default)
|
|
t.Run("debug_not_set", func(t *testing.T) {
|
|
// Don't set DEBUG environment variable
|
|
cfg.PrintConfig("test")
|
|
|
|
// Should have no log output since DEBUG is not true
|
|
logOutput := logBuffer.String()
|
|
require.Empty(t, logOutput, "PrintConfig should not log when DEBUG is not set")
|
|
})
|
|
|
|
// Test 2: DEBUG set to false
|
|
t.Run("debug_false", func(t *testing.T) {
|
|
logBuffer.Reset() // Clear previous output
|
|
t.Setenv("DEBUG", "false")
|
|
cfg.PrintConfig("test")
|
|
|
|
// Should have no log output since DEBUG is not "true"
|
|
logOutput := logBuffer.String()
|
|
require.Empty(t, logOutput, "PrintConfig should not log when DEBUG is false")
|
|
})
|
|
|
|
// Test 3: DEBUG set to something other than true
|
|
t.Run("debug_other_value", func(t *testing.T) {
|
|
logBuffer.Reset() // Clear previous output
|
|
t.Setenv("DEBUG", "yes")
|
|
cfg.PrintConfig("test")
|
|
|
|
// Should have no log output since DEBUG is not exactly "true"
|
|
logOutput := logBuffer.String()
|
|
require.Empty(t, logOutput, "PrintConfig should not log when DEBUG is not exactly 'true'")
|
|
})
|
|
|
|
// Test 4: Verify it works when DEBUG is true
|
|
t.Run("debug_true", func(t *testing.T) {
|
|
logBuffer.Reset() // Clear previous output
|
|
t.Setenv("DEBUG", "true")
|
|
cfg.PrintConfig("test")
|
|
|
|
// Should have log output when DEBUG is true
|
|
logOutput := logBuffer.String()
|
|
require.NotEmpty(t, logOutput, "PrintConfig should log when DEBUG is true")
|
|
require.Contains(t, logOutput, "Config value", "Should contain config logging")
|
|
})
|
|
}
|