cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists 馃崈
charm leaflet readability golang
29
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 0dbb5b3bba74dcb62db8b636cddd0f41ac28d44a 287 lines 6.8 kB view raw
1package utils 2 3import ( 4 "bytes" 5 "os" 6 "strings" 7 "testing" 8 9 "github.com/charmbracelet/log" 10) 11 12func TestNewLogger(t *testing.T) { 13 t.Run("creates logger with info level", func(t *testing.T) { 14 logger := NewLogger("info", "text") 15 if logger == nil { 16 t.Fatal("Logger should not be nil") 17 } 18 19 if logger.GetLevel() != log.InfoLevel { 20 t.Errorf("Expected InfoLevel, got %v", logger.GetLevel()) 21 } 22 }) 23 24 t.Run("creates logger with debug level", func(t *testing.T) { 25 logger := NewLogger("debug", "text") 26 if logger.GetLevel() != log.DebugLevel { 27 t.Errorf("Expected DebugLevel, got %v", logger.GetLevel()) 28 } 29 }) 30 31 t.Run("creates logger with warn level", func(t *testing.T) { 32 logger := NewLogger("warn", "text") 33 if logger.GetLevel() != log.WarnLevel { 34 t.Errorf("Expected WarnLevel, got %v", logger.GetLevel()) 35 } 36 }) 37 38 t.Run("creates logger with warning level alias", func(t *testing.T) { 39 logger := NewLogger("warning", "text") 40 if logger.GetLevel() != log.WarnLevel { 41 t.Errorf("Expected WarnLevel, got %v", logger.GetLevel()) 42 } 43 }) 44 45 t.Run("creates logger with error level", func(t *testing.T) { 46 logger := NewLogger("error", "text") 47 if logger.GetLevel() != log.ErrorLevel { 48 t.Errorf("Expected ErrorLevel, got %v", logger.GetLevel()) 49 } 50 }) 51 52 t.Run("defaults to info level for invalid level", func(t *testing.T) { 53 logger := NewLogger("invalid", "text") 54 if logger.GetLevel() != log.InfoLevel { 55 t.Errorf("Expected InfoLevel for invalid input, got %v", logger.GetLevel()) 56 } 57 }) 58 59 t.Run("handles case insensitive levels", func(t *testing.T) { 60 logger := NewLogger("DEBUG", "text") 61 if logger.GetLevel() != log.DebugLevel { 62 t.Errorf("Expected DebugLevel for uppercase input, got %v", logger.GetLevel()) 63 } 64 }) 65 66 t.Run("creates logger with json format", func(t *testing.T) { 67 var buf bytes.Buffer 68 logger := NewLogger("info", "json") 69 logger.SetOutput(&buf) 70 71 logger.Info("test message") 72 output := buf.String() 73 74 if !strings.Contains(output, "{") || !strings.Contains(output, "}") { 75 t.Error("Expected JSON formatted output") 76 } 77 }) 78 79 t.Run("creates logger with text format", func(t *testing.T) { 80 var buf bytes.Buffer 81 logger := NewLogger("info", "text") 82 logger.SetOutput(&buf) 83 84 logger.Info("test message") 85 output := buf.String() 86 87 if strings.Contains(output, "{") && strings.Contains(output, "}") { 88 t.Error("Expected text formatted output, not JSON") 89 } 90 }) 91 92 t.Run("text format includes timestamp", func(t *testing.T) { 93 var buf bytes.Buffer 94 logger := NewLogger("info", "text") 95 logger.SetOutput(&buf) 96 97 logger.Info("test message") 98 output := buf.String() 99 100 if !strings.Contains(output, ":") { 101 t.Error("Expected timestamp in text format output") 102 } 103 }) 104} 105 106func TestGetLogger(t *testing.T) { 107 t.Run("returns global logger when set", func(t *testing.T) { 108 originalLogger := Logger 109 defer func() { Logger = originalLogger }() 110 111 testLogger := NewLogger("debug", "json") 112 Logger = testLogger 113 114 retrieved := GetLogger() 115 if retrieved != testLogger { 116 t.Error("GetLogger should return the global logger") 117 } 118 }) 119 120 t.Run("creates default logger when global is nil", func(t *testing.T) { 121 originalLogger := Logger 122 defer func() { Logger = originalLogger }() 123 124 Logger = nil 125 126 retrieved := GetLogger() 127 if retrieved == nil { 128 t.Fatal("GetLogger should create a default logger") 129 } 130 131 if retrieved.GetLevel() != log.InfoLevel { 132 t.Error("Default logger should have InfoLevel") 133 } 134 135 if Logger != retrieved { 136 t.Error("Global logger should be set after GetLogger call") 137 } 138 }) 139 140 t.Run("subsequent calls return same logger", func(t *testing.T) { 141 originalLogger := Logger 142 defer func() { Logger = originalLogger }() 143 144 Logger = nil 145 146 logger1 := GetLogger() 147 logger2 := GetLogger() 148 149 if logger1 != logger2 { 150 t.Error("Subsequent GetLogger calls should return the same instance") 151 } 152 }) 153} 154 155func TestLoggerIntegration(t *testing.T) { 156 t.Run("logger writes to stderr by default", func(t *testing.T) { 157 oldStderr := os.Stderr 158 r, w, _ := os.Pipe() 159 os.Stderr = w 160 161 logger := NewLogger("info", "text") 162 logger.Info("test message") 163 164 w.Close() 165 os.Stderr = oldStderr 166 167 var buf bytes.Buffer 168 buf.ReadFrom(r) 169 output := buf.String() 170 171 if !strings.Contains(output, "test message") { 172 t.Error("Logger should write to stderr by default") 173 } 174 }) 175 176 t.Run("logger respects level filtering", func(t *testing.T) { 177 var buf bytes.Buffer 178 logger := NewLogger("error", "text") 179 logger.SetOutput(&buf) 180 181 logger.Debug("debug message") 182 logger.Info("info message") 183 logger.Warn("warn message") 184 logger.Error("error message") 185 186 output := buf.String() 187 188 if strings.Contains(output, "debug message") { 189 t.Error("Debug message should be filtered out at error level") 190 } 191 if strings.Contains(output, "info message") { 192 t.Error("Info message should be filtered out at error level") 193 } 194 if strings.Contains(output, "warn message") { 195 t.Error("Warn message should be filtered out at error level") 196 } 197 if !strings.Contains(output, "error message") { 198 t.Error("Error message should be included at error level") 199 } 200 }) 201 202 t.Run("global logger persists between function calls", func(t *testing.T) { 203 originalLogger := Logger 204 defer func() { Logger = originalLogger }() 205 206 Logger = NewLogger("debug", "json") 207 208 retrieved := GetLogger() 209 210 if retrieved.GetLevel() != log.DebugLevel { 211 t.Error("Global logger settings should persist") 212 } 213 }) 214} 215 216func TestTitlecase(t *testing.T) { 217 tests := []struct { 218 name string 219 input string 220 expected string 221 }{ 222 { 223 name: "single word lowercase", 224 input: "hello", 225 expected: "Hello", 226 }, 227 { 228 name: "single word uppercase", 229 input: "HELLO", 230 expected: "HELLO", 231 }, 232 { 233 name: "multiple words", 234 input: "hello world", 235 expected: "Hello World", 236 }, 237 { 238 name: "mixed case", 239 input: "hELLo WoRLD", 240 expected: "HELLo WoRLD", 241 }, 242 { 243 name: "with punctuation", 244 input: "hello, world!", 245 expected: "Hello, World!", 246 }, 247 { 248 name: "empty string", 249 input: "", 250 expected: "", 251 }, 252 { 253 name: "with numbers", 254 input: "hello 123 world", 255 expected: "Hello 123 World", 256 }, 257 { 258 name: "with special characters", 259 input: "hello-world_test", 260 expected: "Hello-World_test", 261 }, 262 { 263 name: "already title case", 264 input: "Hello World", 265 expected: "Hello World", 266 }, 267 { 268 name: "single character", 269 input: "a", 270 expected: "A", 271 }, 272 { 273 name: "apostrophes", 274 input: "it's a beautiful day", 275 expected: "It's A Beautiful Day", 276 }, 277 } 278 279 for _, tt := range tests { 280 t.Run(tt.name, func(t *testing.T) { 281 result := Titlecase(tt.input) 282 if result != tt.expected { 283 t.Errorf("Titlecase(%q) = %q, expected %q", tt.input, result, tt.expected) 284 } 285 }) 286 } 287}