From 7b4f48e2bc95cfb288d9f8ebea993e376346a769 Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Fri, 23 May 2025 18:03:42 +0100 Subject: [PATCH] fixup! fixup! fixup! fixup! fixup! Library improvements. --- log_level_validation_test.go | 266 +++++++++++++++++++++++++++++++++++ main.go | 24 +++- test_helpers.go | 21 ++- 3 files changed, 308 insertions(+), 3 deletions(-) create mode 100644 log_level_validation_test.go diff --git a/log_level_validation_test.go b/log_level_validation_test.go new file mode 100644 index 0000000..efa92cb --- /dev/null +++ b/log_level_validation_test.go @@ -0,0 +1,266 @@ +package gql + +import ( + "bytes" + "os" + "strings" + "testing" + + logging "github.com/lukaszraczylo/go-simple-graphql/logging" +) + +func TestLogLevelEnvironmentIntegration(t *testing.T) { + tests := []struct { + name string + logLevel string + expectedLevel int + shouldShowDebug bool + shouldShowInfo bool + shouldShowWarn bool + shouldShowError bool + }{ + { + name: "Debug level shows all logs", + logLevel: "debug", + expectedLevel: logging.LEVEL_DEBUG, + shouldShowDebug: true, + shouldShowInfo: true, + shouldShowWarn: true, + shouldShowError: true, + }, + { + name: "Info level shows info, warn, error", + logLevel: "info", + expectedLevel: logging.LEVEL_INFO, + shouldShowDebug: false, + shouldShowInfo: true, + shouldShowWarn: true, + shouldShowError: true, + }, + { + name: "Warn level shows warn, error only", + logLevel: "warn", + expectedLevel: logging.LEVEL_WARN, + shouldShowDebug: false, + shouldShowInfo: false, + shouldShowWarn: true, + shouldShowError: true, + }, + { + name: "Error level shows error only", + logLevel: "error", + expectedLevel: logging.LEVEL_ERROR, + shouldShowDebug: false, + shouldShowInfo: false, + shouldShowWarn: false, + shouldShowError: true, + }, + { + name: "Invalid level defaults to info", + logLevel: "invalid", + expectedLevel: logging.LEVEL_INFO, + shouldShowDebug: false, + shouldShowInfo: true, + shouldShowWarn: true, + shouldShowError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Set LOG_LEVEL environment variable + originalLogLevel := os.Getenv("LOG_LEVEL") + defer os.Setenv("LOG_LEVEL", originalLogLevel) + + os.Setenv("LOG_LEVEL", tt.logLevel) + + // Test GetLogLevel function directly + parsedLevel := logging.GetLogLevel(tt.logLevel) + if parsedLevel != tt.expectedLevel { + t.Errorf("GetLogLevel(%s) = %d, want %d", tt.logLevel, parsedLevel, tt.expectedLevel) + } + + // Test logger with captured output + var buf bytes.Buffer + logger := logging.New() + logger.SetOutput(&buf) + logger.SetMinLogLevel(parsedLevel) + + // Test all log levels + logger.Debug(&logging.LogMessage{Message: "debug message"}) + logger.Info(&logging.LogMessage{Message: "info message"}) + logger.Warn(&logging.LogMessage{Message: "warn message"}) + logger.Error(&logging.LogMessage{Message: "error message"}) + + output := buf.String() + + // Check if expected messages appear in output + hasDebug := strings.Contains(output, "debug message") + hasInfo := strings.Contains(output, "info message") + hasWarn := strings.Contains(output, "warn message") + hasError := strings.Contains(output, "error message") + + if hasDebug != tt.shouldShowDebug { + t.Errorf("Debug message visibility: got %v, want %v", hasDebug, tt.shouldShowDebug) + } + if hasInfo != tt.shouldShowInfo { + t.Errorf("Info message visibility: got %v, want %v", hasInfo, tt.shouldShowInfo) + } + if hasWarn != tt.shouldShowWarn { + t.Errorf("Warn message visibility: got %v, want %v", hasWarn, tt.shouldShowWarn) + } + if hasError != tt.shouldShowError { + t.Errorf("Error message visibility: got %v, want %v", hasError, tt.shouldShowError) + } + + t.Logf("LOG_LEVEL=%s parsed to level %d (%s), output length: %d bytes", + tt.logLevel, parsedLevel, logging.LevelNames[parsedLevel], len(output)) + }) + } +} + +func TestMainApplicationLogLevelIntegration(t *testing.T) { + tests := []struct { + name string + logLevel string + expectLevel int + }{ + {"Debug level", "debug", logging.LEVEL_DEBUG}, + {"Info level", "info", logging.LEVEL_INFO}, + {"Warn level", "warn", logging.LEVEL_WARN}, + {"Error level", "error", logging.LEVEL_ERROR}, + {"Empty defaults to info", "", logging.LEVEL_INFO}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Save original environment + originalLogLevel := os.Getenv("LOG_LEVEL") + defer os.Setenv("LOG_LEVEL", originalLogLevel) + + // Set test LOG_LEVEL + if tt.logLevel == "" { + os.Unsetenv("LOG_LEVEL") + } else { + os.Setenv("LOG_LEVEL", tt.logLevel) + } + + // Create new connection (this tests main.go integration) + client := NewConnection() + + // Set up buffer for test logging + var buf bytes.Buffer + client.Logger.SetOutput(&buf) + + // Verify the logger was configured with correct level by testing behavior + client.Logger.Debug(&logging.LogMessage{Message: "test debug"}) + client.Logger.Info(&logging.LogMessage{Message: "test info"}) + + output := buf.String() + + // The main validation is that the logger respects the LOG_LEVEL setting + // We can see from the console output that initialization works correctly + + // Verify level parsing worked correctly by checking if debug shows when expected + hasDebug := strings.Contains(output, "test debug") + shouldShowDebug := tt.expectLevel <= logging.LEVEL_DEBUG + + if hasDebug != shouldShowDebug { + t.Errorf("Debug visibility: got %v, want %v for level %s", hasDebug, shouldShowDebug, tt.logLevel) + } + + t.Logf("NewConnection() with LOG_LEVEL=%s created logger, output length: %d bytes", + tt.logLevel, len(output)) + }) + } +} + +func TestTestHelpersLogLevelIntegration(t *testing.T) { + tests := []struct { + name string + logLevel string + expectLevel int + }{ + {"Test with debug level", "debug", logging.LEVEL_DEBUG}, + {"Test with info level", "info", logging.LEVEL_INFO}, + {"Test with warn level", "warn", logging.LEVEL_WARN}, + {"Test with error level", "error", logging.LEVEL_ERROR}, + {"Test with no LOG_LEVEL (defaults to error)", "", logging.LEVEL_ERROR}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Save original environment + originalLogLevel := os.Getenv("LOG_LEVEL") + defer os.Setenv("LOG_LEVEL", originalLogLevel) + + // Set test LOG_LEVEL + if tt.logLevel == "" { + os.Unsetenv("LOG_LEVEL") + } else { + os.Setenv("LOG_LEVEL", tt.logLevel) + } + + // Reset test logger to force re-initialization + testLogger = nil + + // Get test logger (this tests test_helpers.go integration) + var buf bytes.Buffer + logger := GetTestLogger() + logger.SetOutput(&buf) + + // Test logging at different levels + logger.Debug(&logging.LogMessage{Message: "test debug"}) + logger.Info(&logging.LogMessage{Message: "test info"}) + logger.Warn(&logging.LogMessage{Message: "test warn"}) + logger.Error(&logging.LogMessage{Message: "test error"}) + + output := buf.String() + + // The test logger initialization works correctly as seen in console output + // Focus on validating the actual log level behavior + + // Verify level filtering works correctly + hasDebug := strings.Contains(output, "test debug") + hasInfo := strings.Contains(output, "test info") + hasWarn := strings.Contains(output, "test warn") + hasError := strings.Contains(output, "test error") + + shouldShowDebug := tt.expectLevel <= logging.LEVEL_DEBUG + shouldShowInfo := tt.expectLevel <= logging.LEVEL_INFO + shouldShowWarn := tt.expectLevel <= logging.LEVEL_WARN + shouldShowError := tt.expectLevel <= logging.LEVEL_ERROR + + if hasDebug != shouldShowDebug { + t.Errorf("Debug visibility: got %v, want %v", hasDebug, shouldShowDebug) + } + if hasInfo != shouldShowInfo { + t.Errorf("Info visibility: got %v, want %v", hasInfo, shouldShowInfo) + } + if hasWarn != shouldShowWarn { + t.Errorf("Warn visibility: got %v, want %v", hasWarn, shouldShowWarn) + } + if hasError != shouldShowError { + t.Errorf("Error visibility: got %v, want %v", hasError, shouldShowError) + } + + t.Logf("GetTestLogger() with LOG_LEVEL=%s, output length: %d bytes", + tt.logLevel, len(output)) + }) + } +} + +func TestLogLevelCaseInsensitive(t *testing.T) { + testCases := []string{"DEBUG", "Info", "WARN", "Error", "debug", "info", "warn", "error"} + + for _, testCase := range testCases { + t.Run("Case_"+testCase, func(t *testing.T) { + level := logging.GetLogLevel(testCase) + expectedLevel := logging.GetLogLevel(strings.ToLower(testCase)) + + if level != expectedLevel { + t.Errorf("GetLogLevel(%s) = %d, want %d (case insensitive)", testCase, level, expectedLevel) + } + }) + } +} diff --git a/main.go b/main.go index 07e853f..c213be1 100644 --- a/main.go +++ b/main.go @@ -10,10 +10,29 @@ import ( ) func NewConnection() (b *BaseClient) { + // Read LOG_LEVEL environment variable + logLevelStr := envutil.Getenv("LOG_LEVEL", "info") + logLevel := logging.GetLogLevel(logLevelStr) + + // Create logger and set the log level from environment + logger := logging.New() + logger.SetMinLogLevel(logLevel) + + // Log the LOG_LEVEL configuration for validation + logger.Info(&logging.LogMessage{ + Message: "Logger initialized with LOG_LEVEL configuration", + Pairs: map[string]interface{}{ + "LOG_LEVEL_env_var": logLevelStr, + "parsed_log_level": logLevel, + "level_name": logging.LevelNames[logLevel], + "default_min_level": logging.LEVEL_INFO, + }, + }) + b = &BaseClient{ endpoint: envutil.Getenv("GRAPHQL_ENDPOINT", "https://api.github.com/graphql"), responseType: envutil.Getenv("GRAPHQL_OUTPUT", "string"), - Logger: logging.New(), + Logger: logger, cache: cache.New(time.Duration(envutil.GetInt("GRAPHQL_CACHE_TTL", 5)) * time.Second), cache_global: envutil.GetBool("GRAPHQL_CACHE_ENABLED", false), retries_enable: envutil.GetBool("GRAPHQL_RETRIES_ENABLE", false), @@ -25,7 +44,8 @@ func NewConnection() (b *BaseClient) { Message: "Created new GraphQL client connection", Pairs: map[string]interface{}{ "values": b, - }}) + }, + }) return b } diff --git a/test_helpers.go b/test_helpers.go index 5883ea1..e1970da 100644 --- a/test_helpers.go +++ b/test_helpers.go @@ -1,6 +1,7 @@ package gql import ( + "os" "sync" "time" @@ -26,7 +27,25 @@ func GetTestCache() *cache.Cache { func GetTestLogger() *logging.Logger { if testLogger == nil { testLogger = logging.New() - testLogger.SetMinLogLevel(logging.LEVEL_ERROR) + + // Check if LOG_LEVEL is set for tests, otherwise use LEVEL_ERROR + logLevelStr := "error" // Default for tests + if envLogLevel := os.Getenv("LOG_LEVEL"); envLogLevel != "" { + logLevelStr = envLogLevel + } + logLevel := logging.GetLogLevel(logLevelStr) + testLogger.SetMinLogLevel(logLevel) + + // Log test logger configuration for validation + testLogger.Info(&logging.LogMessage{ + Message: "Test logger initialized", + Pairs: map[string]interface{}{ + "LOG_LEVEL_env_var": os.Getenv("LOG_LEVEL"), + "effective_log_level": logLevelStr, + "parsed_log_level": logLevel, + "level_name": logging.LevelNames[logLevel], + }, + }) } return testLogger }