package logger import ( "os" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestParseLevel_AllLevels(t *testing.T) { tests := []struct { input string expected string // string form for comparison }{ {"debug", "debug"}, {"info", "info"}, {"warn", "warn"}, {"error", "error"}, } for _, tt := range tests { t.Run(tt.input, func(t *testing.T) { level, err := parseLevel(tt.input) assert.NoError(t, err) assert.Equal(t, tt.expected, level.String()) }) } } func TestParseLevel_UnknownDefaultsToInfo(t *testing.T) { level, err := parseLevel("nonexistent") assert.NoError(t, err) assert.Equal(t, "info", level.String()) } func TestParseLevel_EmptyString(t *testing.T) { level, err := parseLevel("") assert.NoError(t, err) assert.Equal(t, "info", level.String()) } func TestGetOutput_Stdout(t *testing.T) { f, err := getOutput("stdout") assert.NoError(t, err) assert.Equal(t, os.Stdout, f) } func TestGetOutput_Stderr(t *testing.T) { f, err := getOutput("stderr") assert.NoError(t, err) assert.Equal(t, os.Stderr, f) } func TestGetOutput_File(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "test.log") f, err := getOutput(path) assert.NoError(t, err) require.NotNil(t, f) defer f.Close() // File should have been created _, statErr := os.Stat(path) assert.NoError(t, statErr) } func TestGetOutput_InvalidPath(t *testing.T) { // A path inside a nonexistent directory should fail _, err := getOutput("/nonexistent/dir/that/does/not/exist/log.txt") assert.Error(t, err) } func TestInit_JSONFormat(t *testing.T) { cfg := Config{ Level: "info", Format: "json", Output: "stdout", ErrorOutput: "stderr", } err := Init(cfg) assert.NoError(t, err) defer func() { globalLogger = nil }() // globalLogger should be set require.NotNil(t, globalLogger) } func TestInit_ConsoleFormat(t *testing.T) { cfg := Config{ Level: "debug", Format: "console", Output: "stdout", ErrorOutput: "stderr", } err := Init(cfg) assert.NoError(t, err) defer func() { globalLogger = nil }() require.NotNil(t, globalLogger) } func TestInit_FileOutput(t *testing.T) { dir := t.TempDir() logPath := filepath.Join(dir, "app.log") errPath := filepath.Join(dir, "err.log") cfg := Config{ Level: "warn", Format: "json", Output: logPath, ErrorOutput: errPath, } err := Init(cfg) assert.NoError(t, err) defer func() { globalLogger = nil }() require.NotNil(t, globalLogger) // Write a log and sync L().Info("test message from file output") Sync() // The log file should exist _, statErr := os.Stat(logPath) assert.NoError(t, statErr) } func TestInit_InvalidLevel(t *testing.T) { // parseLevel never returns an error, so Init should succeed even with an // unknown level (defaults to info). This test verifies that behavior. cfg := Config{ Level: "totally-invalid-level", Format: "json", Output: "stdout", ErrorOutput: "stderr", } err := Init(cfg) assert.NoError(t, err) defer func() { globalLogger = nil }() } func TestInit_InvalidOutput(t *testing.T) { cfg := Config{ Level: "info", Format: "json", Output: "/nonexistent/dir/that/does/not/exist/log.txt", ErrorOutput: "stderr", } err := Init(cfg) assert.Error(t, err) } func TestInit_InvalidErrorOutput(t *testing.T) { cfg := Config{ Level: "info", Format: "json", Output: "stdout", ErrorOutput: "/nonexistent/dir/that/does/not/exist/err.txt", } err := Init(cfg) assert.Error(t, err) } func TestL_WithoutInit(t *testing.T) { // Reset globalLogger to nil to test the fallback path globalLogger = nil l := L() assert.NotNil(t, l) // Should be a usable sugared logger l.Info("fallback logger works") } func TestL_AfterInit(t *testing.T) { cfg := Config{ Level: "info", Format: "json", Output: "stdout", ErrorOutput: "stderr", } err := Init(cfg) require.NoError(t, err) defer func() { globalLogger = nil }() l := L() assert.NotNil(t, l) } func TestSync_WithLogger(t *testing.T) { cfg := Config{ Level: "info", Format: "json", Output: "stdout", ErrorOutput: "stderr", } err := Init(cfg) require.NoError(t, err) defer func() { globalLogger = nil }() L().Info("message before sync") Sync() } func TestSync_WithoutLogger(t *testing.T) { globalLogger = nil // Should not panic Sync() } func TestL_LogsAtVariousLevels(t *testing.T) { cfg := Config{ Level: "debug", Format: "json", Output: "stdout", ErrorOutput: "stderr", } err := Init(cfg) require.NoError(t, err) defer func() { globalLogger = nil }() l := L() l.Debug("debug message") l.Info("info message") l.Warn("warn message") l.Error("error message") l.Infof("formatted %s", "info") l.Errorf("formatted %s", "error") }