From 75bf449bedb237d3f56b03c7447fd65003cb44d3 Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Fri, 18 Apr 2025 12:47:47 +0200 Subject: [PATCH] Remove logger and replace it with a library --- cmd/log_format_test/main.go | 3 +- go.mod | 3 +- go.sum | 2 + logger/logger.go | 465 ------------------------------------ logger/panic_handler.go | 49 ---- main.go | 4 +- processor/processor.go | 93 +++++++- processor/regex.go | 5 +- processor/test_helper.go | 5 +- utils/git.go | 2 +- utils/modifycommand.go | 2 +- utils/modifycommand_test.go | 271 --------------------- utils/replacecommand.go | 3 +- 13 files changed, 108 insertions(+), 799 deletions(-) delete mode 100644 logger/logger.go delete mode 100644 logger/panic_handler.go diff --git a/cmd/log_format_test/main.go b/cmd/log_format_test/main.go index 370a5d0..8e2cf67 100644 --- a/cmd/log_format_test/main.go +++ b/cmd/log_format_test/main.go @@ -1,8 +1,9 @@ package main import ( - "modify/logger" "time" + + logger "git.site.quack-lab.dev/dave/cylogger" ) func main() { diff --git a/go.mod b/go.mod index 87ee085..62bfc59 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,9 @@ module cook -go 1.24.1 +go 1.24.2 require ( + git.site.quack-lab.dev/dave/cylogger v1.1.1 github.com/bmatcuk/doublestar/v4 v4.8.1 github.com/stretchr/testify v1.10.0 github.com/yuin/gopher-lua v1.1.1 diff --git a/go.sum b/go.sum index 25dfd4d..2d0d197 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +git.site.quack-lab.dev/dave/cylogger v1.1.1 h1:LQZaigVKUo07hGbS/ZTKiR+l7j4Z2eNf13zsljednNU= +git.site.quack-lab.dev/dave/cylogger v1.1.1/go.mod h1:VS9MI4Y/cwjCBZgel7dSfCQlwtAgHmfvixOoBgBhtKg= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= diff --git a/logger/logger.go b/logger/logger.go deleted file mode 100644 index 74912d5..0000000 --- a/logger/logger.go +++ /dev/null @@ -1,465 +0,0 @@ -package logger - -import ( - "bytes" - "fmt" - "io" - "log" - "os" - "path/filepath" - "runtime" - "strconv" - "strings" - "sync" - "time" -) - -// LogLevel defines the severity of log messages -type LogLevel int - -const ( - // LevelError is for critical errors that should always be displayed - LevelError LogLevel = iota - // LevelWarning is for important warnings - LevelWarning - // LevelInfo is for informational messages - LevelInfo - // LevelDebug is for detailed debugging information - LevelDebug - // LevelTrace is for very detailed tracing information - LevelTrace - // LevelLua is specifically for output from Lua scripts - LevelLua -) - -var levelNames = map[LogLevel]string{ - LevelError: "ERROR", - LevelWarning: "WARNING", - LevelInfo: "INFO", - LevelDebug: "DEBUG", - LevelTrace: "TRACE", - LevelLua: "LUA", -} - -var levelColors = map[LogLevel]string{ - LevelError: "\033[1;31m", // Bold Red - LevelWarning: "\033[1;33m", // Bold Yellow - LevelInfo: "\033[1;32m", // Bold Green - LevelDebug: "\033[1;36m", // Bold Cyan - LevelTrace: "\033[1;35m", // Bold Magenta - LevelLua: "\033[1;34m", // Bold Blue -} - -// ResetColor is the ANSI code to reset text color -const ResetColor = "\033[0m" - -// Logger is our custom logger with level support -type Logger struct { - mu sync.Mutex - out io.Writer - currentLevel LogLevel - prefix string - flag int - useColors bool - callerOffset int - defaultFields map[string]interface{} - showGoroutine bool -} - -var ( - // DefaultLogger is the global logger instance - DefaultLogger *Logger - // defaultLogLevel is the default log level if not specified - defaultLogLevel = LevelInfo - // Global mutex for DefaultLogger initialization - initMutex sync.Mutex -) - -// ParseLevel converts a string log level to LogLevel -func ParseLevel(levelStr string) LogLevel { - switch strings.ToUpper(levelStr) { - case "ERROR": - return LevelError - case "WARNING", "WARN": - return LevelWarning - case "INFO": - return LevelInfo - case "DEBUG": - return LevelDebug - case "TRACE": - return LevelTrace - case "LUA": - return LevelLua - default: - return defaultLogLevel - } -} - -// String returns the string representation of the log level -func (l LogLevel) String() string { - if name, ok := levelNames[l]; ok { - return name - } - return fmt.Sprintf("Level(%d)", l) -} - -// New creates a new Logger instance -func New(out io.Writer, prefix string, flag int) *Logger { - return &Logger{ - out: out, - currentLevel: defaultLogLevel, - prefix: prefix, - flag: flag, - useColors: true, - callerOffset: 0, - defaultFields: make(map[string]interface{}), - showGoroutine: true, - } -} - -// Init initializes the DefaultLogger -func Init(level LogLevel) { - initMutex.Lock() - defer initMutex.Unlock() - - if DefaultLogger == nil { - DefaultLogger = New(os.Stdout, "", log.Lmicroseconds|log.Lshortfile) - } - DefaultLogger.SetLevel(level) -} - -// SetLevel sets the current log level -func (l *Logger) SetLevel(level LogLevel) { - l.mu.Lock() - defer l.mu.Unlock() - l.currentLevel = level -} - -// GetLevel returns the current log level -func (l *Logger) GetLevel() LogLevel { - l.mu.Lock() - defer l.mu.Unlock() - return l.currentLevel -} - -// SetCallerOffset sets the caller offset for correct file and line reporting -func (l *Logger) SetCallerOffset(offset int) { - l.mu.Lock() - defer l.mu.Unlock() - l.callerOffset = offset -} - -// SetShowGoroutine sets whether to include goroutine ID in log messages -func (l *Logger) SetShowGoroutine(show bool) { - l.mu.Lock() - defer l.mu.Unlock() - l.showGoroutine = show -} - -// ShowGoroutine returns whether goroutine ID is included in log messages -func (l *Logger) ShowGoroutine() bool { - l.mu.Lock() - defer l.mu.Unlock() - return l.showGoroutine -} - -// WithField adds a field to the logger's context -func (l *Logger) WithField(key string, value interface{}) *Logger { - newLogger := &Logger{ - out: l.out, - currentLevel: l.currentLevel, - prefix: l.prefix, - flag: l.flag, - useColors: l.useColors, - callerOffset: l.callerOffset, - defaultFields: make(map[string]interface{}), - showGoroutine: l.showGoroutine, - } - - // Copy existing fields - for k, v := range l.defaultFields { - newLogger.defaultFields[k] = v - } - - // Add new field - newLogger.defaultFields[key] = value - return newLogger -} - -// WithFields adds multiple fields to the logger's context -func (l *Logger) WithFields(fields map[string]interface{}) *Logger { - newLogger := &Logger{ - out: l.out, - currentLevel: l.currentLevel, - prefix: l.prefix, - flag: l.flag, - useColors: l.useColors, - callerOffset: l.callerOffset, - defaultFields: make(map[string]interface{}), - showGoroutine: l.showGoroutine, - } - - // Copy existing fields - for k, v := range l.defaultFields { - newLogger.defaultFields[k] = v - } - - // Add new fields - for k, v := range fields { - newLogger.defaultFields[k] = v - } - return newLogger -} - -// GetGoroutineID extracts the goroutine ID from the runtime stack -func GetGoroutineID() string { - buf := make([]byte, 64) - n := runtime.Stack(buf, false) - // Format of first line is "goroutine N [state]:" - // We only need the N part - buf = buf[:n] - idField := bytes.Fields(bytes.Split(buf, []byte{':'})[0])[1] - return string(idField) -} - -// formatMessage formats a log message with level, time, file, and line information -func (l *Logger) formatMessage(level LogLevel, format string, args ...interface{}) string { - var msg string - if len(args) > 0 { - msg = fmt.Sprintf(format, args...) - } else { - msg = format - } - - // Format default fields if any - var fields string - if len(l.defaultFields) > 0 { - var pairs []string - for k, v := range l.defaultFields { - pairs = append(pairs, fmt.Sprintf("%s=%v", k, v)) - } - fields = " " + strings.Join(pairs, " ") - } - - var levelColor, resetColor string - if l.useColors { - levelColor = levelColors[level] - resetColor = ResetColor - } - - var caller string - if l.flag&log.Lshortfile != 0 || l.flag&log.Llongfile != 0 { - // Find the actual caller by scanning up the stack - // until we find a function outside the logger package - var file string - var line int - var ok bool - - // Start at a reasonable depth and scan up to 10 frames - for depth := 4; depth < 15; depth++ { - _, file, line, ok = runtime.Caller(depth) - if !ok { - break - } - - // If the caller is not in the logger package, we found our caller - if !strings.Contains(file, "logger/logger.go") { - break - } - } - - if !ok { - file = "???" - line = 0 - } - - if l.flag&log.Lshortfile != 0 { - file = filepath.Base(file) - } - caller = fmt.Sprintf("%-25s ", file+":"+strconv.Itoa(line)) - } - - // Format the timestamp with fixed width - var timeStr string - if l.flag&(log.Ldate|log.Ltime|log.Lmicroseconds) != 0 { - t := time.Now() - if l.flag&log.Ldate != 0 { - timeStr += fmt.Sprintf("%04d/%02d/%02d ", t.Year(), t.Month(), t.Day()) - } - if l.flag&(log.Ltime|log.Lmicroseconds) != 0 { - timeStr += fmt.Sprintf("%02d:%02d:%02d", t.Hour(), t.Minute(), t.Second()) - if l.flag&log.Lmicroseconds != 0 { - timeStr += fmt.Sprintf(".%06d", t.Nanosecond()/1000) - } - } - timeStr = fmt.Sprintf("%-15s ", timeStr) - } - - // Add goroutine ID if enabled, with fixed width - var goroutineStr string - if l.showGoroutine { - goroutineID := GetGoroutineID() - goroutineStr = fmt.Sprintf("[g:%-4s] ", goroutineID) - } - - // Create a colored level indicator with both brackets colored - levelStr := fmt.Sprintf("%s[%s]%s", levelColor, levelNames[level], levelColor) - // Add a space after the level and before the reset color - levelColumn := fmt.Sprintf("%s %s", levelStr, resetColor) - - return fmt.Sprintf("%s%s%s%s%s%s%s\n", - l.prefix, timeStr, caller, goroutineStr, levelColumn, msg, fields) -} - -// log logs a message at the specified level -func (l *Logger) log(level LogLevel, format string, args ...interface{}) { - // Always show LUA level logs regardless of the current log level - if level != LevelLua && level > l.currentLevel { - return - } - - l.mu.Lock() - defer l.mu.Unlock() - - msg := l.formatMessage(level, format, args...) - fmt.Fprint(l.out, msg) -} - -// Error logs an error message -func (l *Logger) Error(format string, args ...interface{}) { - l.log(LevelError, format, args...) -} - -// Warning logs a warning message -func (l *Logger) Warning(format string, args ...interface{}) { - l.log(LevelWarning, format, args...) -} - -// Info logs an informational message -func (l *Logger) Info(format string, args ...interface{}) { - l.log(LevelInfo, format, args...) -} - -// Debug logs a debug message -func (l *Logger) Debug(format string, args ...interface{}) { - l.log(LevelDebug, format, args...) -} - -// Trace logs a trace message -func (l *Logger) Trace(format string, args ...interface{}) { - l.log(LevelTrace, format, args...) -} - -// Lua logs a Lua message -func (l *Logger) Lua(format string, args ...interface{}) { - l.log(LevelLua, format, args...) -} - -// Global log functions that use DefaultLogger - -// Error logs an error message using the default logger -func Error(format string, args ...interface{}) { - if DefaultLogger == nil { - Init(defaultLogLevel) - } - DefaultLogger.Error(format, args...) -} - -// Warning logs a warning message using the default logger -func Warning(format string, args ...interface{}) { - if DefaultLogger == nil { - Init(defaultLogLevel) - } - DefaultLogger.Warning(format, args...) -} - -// Info logs an informational message using the default logger -func Info(format string, args ...interface{}) { - if DefaultLogger == nil { - Init(defaultLogLevel) - } - DefaultLogger.Info(format, args...) -} - -// Debug logs a debug message using the default logger -func Debug(format string, args ...interface{}) { - if DefaultLogger == nil { - Init(defaultLogLevel) - } - DefaultLogger.Debug(format, args...) -} - -// Trace logs a trace message using the default logger -func Trace(format string, args ...interface{}) { - if DefaultLogger == nil { - Init(defaultLogLevel) - } - DefaultLogger.Trace(format, args...) -} - -// Lua logs a Lua message using the default logger -func Lua(format string, args ...interface{}) { - if DefaultLogger == nil { - Init(defaultLogLevel) - } - DefaultLogger.Lua(format, args...) -} - -// LogPanic logs a panic error and its stack trace -func LogPanic(r interface{}) { - if DefaultLogger == nil { - Init(defaultLogLevel) - } - stack := make([]byte, 4096) - n := runtime.Stack(stack, false) - DefaultLogger.Error("PANIC: %v\n%s", r, stack[:n]) -} - -// SetLevel sets the log level for the default logger -func SetLevel(level LogLevel) { - if DefaultLogger == nil { - Init(level) - return - } - DefaultLogger.SetLevel(level) -} - -// GetLevel gets the log level for the default logger -func GetLevel() LogLevel { - if DefaultLogger == nil { - Init(defaultLogLevel) - } - return DefaultLogger.GetLevel() -} - -// WithField returns a new logger with the field added to the default logger's context -func WithField(key string, value interface{}) *Logger { - if DefaultLogger == nil { - Init(defaultLogLevel) - } - return DefaultLogger.WithField(key, value) -} - -// WithFields returns a new logger with the fields added to the default logger's context -func WithFields(fields map[string]interface{}) *Logger { - if DefaultLogger == nil { - Init(defaultLogLevel) - } - return DefaultLogger.WithFields(fields) -} - -// SetShowGoroutine enables or disables goroutine ID display in the default logger -func SetShowGoroutine(show bool) { - if DefaultLogger == nil { - Init(defaultLogLevel) - } - DefaultLogger.SetShowGoroutine(show) -} - -// ShowGoroutine returns whether goroutine ID is included in default logger's messages -func ShowGoroutine() bool { - if DefaultLogger == nil { - Init(defaultLogLevel) - } - return DefaultLogger.ShowGoroutine() -} diff --git a/logger/panic_handler.go b/logger/panic_handler.go deleted file mode 100644 index e393a31..0000000 --- a/logger/panic_handler.go +++ /dev/null @@ -1,49 +0,0 @@ -package logger - -import ( - "fmt" - "runtime/debug" -) - -// PanicHandler handles a panic and logs it -func PanicHandler() { - if r := recover(); r != nil { - goroutineID := GetGoroutineID() - stackTrace := debug.Stack() - Error("PANIC in goroutine %s: %v\n%s", goroutineID, r, stackTrace) - } -} - -// SafeGo launches a goroutine with panic recovery -// Usage: logger.SafeGo(func() { ... your code ... }) -func SafeGo(f func()) { - go func() { - defer PanicHandler() - f() - }() -} - -// SafeGoWithArgs launches a goroutine with panic recovery and passes arguments -// Usage: logger.SafeGoWithArgs(func(arg1, arg2 interface{}) { ... }, "value1", 42) -func SafeGoWithArgs(f func(...interface{}), args ...interface{}) { - go func() { - defer PanicHandler() - f(args...) - }() -} - -// SafeExec executes a function with panic recovery -// Useful for code that should not panic -func SafeExec(f func()) (err error) { - defer func() { - if r := recover(); r != nil { - goroutineID := GetGoroutineID() - stackTrace := debug.Stack() - Error("PANIC in goroutine %s: %v\n%s", goroutineID, r, stackTrace) - err = fmt.Errorf("panic recovered: %v", r) - } - }() - - f() - return nil -} diff --git a/main.go b/main.go index 462aa72..736a5e8 100644 --- a/main.go +++ b/main.go @@ -13,7 +13,7 @@ import ( "github.com/go-git/go-git/v5" - "cook/logger" + logger "git.site.quack-lab.dev/dave/cylogger" ) type GlobalStats struct { @@ -259,7 +259,7 @@ func RunOtherCommands(file string, fileDataStr string, association utils.FileCom modifications := []utils.ReplaceCommand{} for _, command := range association.Commands { // Use command-specific logger if available, otherwise fall back to default logger - cmdLogger := logger.DefaultLogger + cmdLogger := logger.Default if cmdLog, ok := commandLoggers[command.Name]; ok { cmdLogger = cmdLog } diff --git a/processor/processor.go b/processor/processor.go index 243d9a3..0261f51 100644 --- a/processor/processor.go +++ b/processor/processor.go @@ -2,11 +2,12 @@ package processor import ( "fmt" + "io" + "net/http" "strings" + logger "git.site.quack-lab.dev/dave/cylogger" lua "github.com/yuin/gopher-lua" - - "cook/logger" ) // Maybe we make this an interface again for the shits and giggles @@ -249,6 +250,7 @@ modified = false logger.Debug("Setting up Lua print function to Go") L.SetGlobal("print", L.NewFunction(printToGo)) + L.SetGlobal("fetch", L.NewFunction(fetch)) return nil } @@ -324,3 +326,90 @@ func printToGo(L *lua.LState) int { logger.Lua("%s", message) return 0 } + +func fetch(L *lua.LState) int { + // Get URL from first argument + url := L.ToString(1) + if url == "" { + L.Push(lua.LNil) + L.Push(lua.LString("URL is required")) + return 2 + } + + // Get options from second argument if provided + var method string = "GET" + var headers map[string]string = make(map[string]string) + var body string = "" + + if L.GetTop() > 1 { + options := L.ToTable(2) + if options != nil { + // Get method + if methodVal := options.RawGetString("method"); methodVal != lua.LNil { + method = methodVal.String() + } + + // Get headers + if headersVal := options.RawGetString("headers"); headersVal != lua.LNil { + if headersTable, ok := headersVal.(*lua.LTable); ok { + headersTable.ForEach(func(key lua.LValue, value lua.LValue) { + headers[key.String()] = value.String() + }) + } + } + + // Get body + if bodyVal := options.RawGetString("body"); bodyVal != lua.LNil { + body = bodyVal.String() + } + } + } + + // Create HTTP request + req, err := http.NewRequest(method, url, strings.NewReader(body)) + if err != nil { + L.Push(lua.LNil) + L.Push(lua.LString(fmt.Sprintf("Error creating request: %v", err))) + return 2 + } + + // Set headers + for key, value := range headers { + req.Header.Set(key, value) + } + + // Make request + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + L.Push(lua.LNil) + L.Push(lua.LString(fmt.Sprintf("Error making request: %v", err))) + return 2 + } + defer resp.Body.Close() + + // Read response body + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + L.Push(lua.LNil) + L.Push(lua.LString(fmt.Sprintf("Error reading response: %v", err))) + return 2 + } + + // Create response table + responseTable := L.NewTable() + responseTable.RawSetString("status", lua.LNumber(resp.StatusCode)) + responseTable.RawSetString("statusText", lua.LString(resp.Status)) + responseTable.RawSetString("ok", lua.LBool(resp.StatusCode >= 200 && resp.StatusCode < 300)) + responseTable.RawSetString("body", lua.LString(string(bodyBytes))) + + // Set headers in response + headersTable := L.NewTable() + for key, values := range resp.Header { + headersTable.RawSetString(key, lua.LString(values[0])) + } + responseTable.RawSetString("headers", headersTable) + + L.Push(responseTable) + return 1 +} diff --git a/processor/regex.go b/processor/regex.go index 860deb1..23c4552 100644 --- a/processor/regex.go +++ b/processor/regex.go @@ -1,16 +1,15 @@ package processor import ( + "cook/utils" "fmt" "regexp" "strconv" "strings" "time" + logger "git.site.quack-lab.dev/dave/cylogger" lua "github.com/yuin/gopher-lua" - - "cook/logger" - "cook/utils" ) type CaptureGroup struct { diff --git a/processor/test_helper.go b/processor/test_helper.go index a27be1f..ee03677 100644 --- a/processor/test_helper.go +++ b/processor/test_helper.go @@ -2,8 +2,9 @@ package processor import ( "io" - "cook/logger" "os" + + logger "git.site.quack-lab.dev/dave/cylogger" ) func init() { @@ -20,7 +21,7 @@ func init() { if disableTestLogs { // Create a new logger that writes to nowhere silentLogger := logger.New(io.Discard, "", 0) - logger.DefaultLogger = silentLogger + logger.Default = silentLogger } } } diff --git a/utils/git.go b/utils/git.go index 4ae8ff9..783fb41 100644 --- a/utils/git.go +++ b/utils/git.go @@ -1,12 +1,12 @@ package utils import ( - "cook/logger" "fmt" "os" "path/filepath" "time" + logger "git.site.quack-lab.dev/dave/cylogger" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing/object" ) diff --git a/utils/modifycommand.go b/utils/modifycommand.go index 6c88c52..08ac08d 100644 --- a/utils/modifycommand.go +++ b/utils/modifycommand.go @@ -1,12 +1,12 @@ package utils import ( - "cook/logger" "fmt" "os" "path/filepath" "strings" + logger "git.site.quack-lab.dev/dave/cylogger" "github.com/bmatcuk/doublestar/v4" "gopkg.in/yaml.v3" ) diff --git a/utils/modifycommand_test.go b/utils/modifycommand_test.go index 4cba676..c3fd8b3 100644 --- a/utils/modifycommand_test.go +++ b/utils/modifycommand_test.go @@ -269,128 +269,6 @@ func TestAggregateGlobs(t *testing.T) { } } -func TestLoadCommandFromArgs(t *testing.T) { - // Save original flags - origGitFlag := *GitFlag - origResetFlag := *ResetFlag - origLogLevel := *LogLevel - - // Restore original flags after test - defer func() { - *GitFlag = origGitFlag - *ResetFlag = origResetFlag - *LogLevel = origLogLevel - }() - - // Test cases - tests := []struct { - name string - args []string - gitFlag bool - resetFlag bool - logLevel string - shouldError bool - }{ - { - name: "Valid command", - args: []string{"pattern", "expr", "file1", "file2"}, - gitFlag: false, - resetFlag: false, - logLevel: "INFO", - shouldError: false, - }, - { - name: "Not enough args", - args: []string{"pattern", "expr"}, - gitFlag: false, - resetFlag: false, - logLevel: "INFO", - shouldError: true, - }, - { - name: "With git flag", - args: []string{"pattern", "expr", "file1"}, - gitFlag: true, - resetFlag: false, - logLevel: "INFO", - shouldError: false, - }, - { - name: "With reset flag (forces git flag)", - args: []string{"pattern", "expr", "file1"}, - gitFlag: false, - resetFlag: true, - logLevel: "INFO", - shouldError: false, - }, - { - name: "With custom log level", - args: []string{"pattern", "expr", "file1"}, - gitFlag: false, - resetFlag: false, - logLevel: "DEBUG", - shouldError: false, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Set flags for this test case - *GitFlag = tc.gitFlag - *ResetFlag = tc.resetFlag - *LogLevel = tc.logLevel - - commands, err := LoadCommandFromArgs(tc.args) - - if tc.shouldError { - if err == nil { - t.Errorf("Expected an error but got none") - } - return - } - - if err != nil { - t.Errorf("Unexpected error: %v", err) - return - } - - if len(commands) != 1 { - t.Errorf("Expected 1 command, got %d", len(commands)) - return - } - - cmd := commands[0] - - // Check command properties - if cmd.Regex != tc.args[0] { - t.Errorf("Expected pattern %q, got %q", tc.args[0], cmd.Regex) - } - - if cmd.Lua != tc.args[1] { - t.Errorf("Expected LuaExpr %q, got %q", tc.args[1], cmd.Lua) - } - - if len(cmd.Files) != len(tc.args)-2 { - t.Errorf("Expected %d files, got %d", len(tc.args)-2, len(cmd.Files)) - } - - // When reset is true, git should be true regardless of what was set - expectedGit := tc.gitFlag || tc.resetFlag - if cmd.Git != expectedGit { - t.Errorf("Expected Git flag %v, got %v", expectedGit, cmd.Git) - } - - if cmd.Reset != tc.resetFlag { - t.Errorf("Expected Reset flag %v, got %v", tc.resetFlag, cmd.Reset) - } - - if cmd.LogLevel != tc.logLevel { - t.Errorf("Expected LogLevel %q, got %q", tc.logLevel, cmd.LogLevel) - } - }) - } -} - // Successfully unmarshal valid YAML data into ModifyCommand slice func TestLoadCommandsFromCookFileSuccess(t *testing.T) { // Arrange @@ -556,155 +434,6 @@ func TestLoadCommandsFromCookFileLegitExample(t *testing.T) { assert.Equal(t, "crewlayabout", commands[0].Name) } -// Valid command with minimum 3 arguments returns a ModifyCommand slice with correct values -func TestLoadCommandFromArgsWithValidArguments(t *testing.T) { - // Setup - oldGitFlag := GitFlag - oldResetFlag := ResetFlag - oldLogLevel := LogLevel - - gitValue := true - resetValue := false - logLevelValue := "info" - - GitFlag = &gitValue - ResetFlag = &resetValue - LogLevel = &logLevelValue - - defer func() { - GitFlag = oldGitFlag - ResetFlag = oldResetFlag - LogLevel = oldLogLevel - }() - - args := []string{"*.go", "return x", "file1.go", "file2.go"} - - // Execute - commands, err := LoadCommandFromArgs(args) - - // Assert - assert.NoError(t, err) - assert.Len(t, commands, 1) - assert.Equal(t, "*.go", commands[0].Regex) - assert.Equal(t, "return x", commands[0].Lua) - assert.Equal(t, []string{"file1.go", "file2.go"}, commands[0].Files) - assert.Equal(t, true, commands[0].Git) - assert.Equal(t, false, commands[0].Reset) - assert.Equal(t, "info", commands[0].LogLevel) -} - -// Less than 3 arguments returns an error with appropriate message -func TestLoadCommandFromArgsWithInsufficientArguments(t *testing.T) { - // Setup - oldGitFlag := GitFlag - oldResetFlag := ResetFlag - oldLogLevel := LogLevel - - gitValue := false - resetValue := false - logLevelValue := "info" - - GitFlag = &gitValue - ResetFlag = &resetValue - LogLevel = &logLevelValue - - defer func() { - GitFlag = oldGitFlag - ResetFlag = oldResetFlag - LogLevel = oldLogLevel - }() - - testCases := []struct { - name string - args []string - }{ - {"empty args", []string{}}, - {"one arg", []string{"*.go"}}, - {"two args", []string{"*.go", "return x"}}, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - // Execute - commands, err := LoadCommandFromArgs(tc.args) - - // Assert - assert.Error(t, err) - assert.Nil(t, commands) - assert.Contains(t, err.Error(), "at least 3 arguments are required") - }) - } -} - -// Pattern, Lua, and Files fields are correctly populated from args -func TestLoadCommandFromArgsPopulatesFieldsCorrectly(t *testing.T) { - // Setup - oldGitFlag := GitFlag - oldResetFlag := ResetFlag - oldLogLevel := LogLevel - - gitValue := false - resetValue := false - logLevelValue := "debug" - - GitFlag = &gitValue - ResetFlag = &resetValue - LogLevel = &logLevelValue - - defer func() { - GitFlag = oldGitFlag - ResetFlag = oldResetFlag - LogLevel = oldLogLevel - }() - - args := []string{"*.txt", "print('Hello')", "file1.txt", "file2.txt"} - - // Execute - commands, err := LoadCommandFromArgs(args) - - // Assert - assert.NoError(t, err) - assert.Len(t, commands, 1) - assert.Equal(t, "*.txt", commands[0].Regex) - assert.Equal(t, "print('Hello')", commands[0].Lua) - assert.Equal(t, []string{"file1.txt", "file2.txt"}, commands[0].Files) - assert.Equal(t, false, commands[0].Git) - assert.Equal(t, false, commands[0].Reset) - assert.Equal(t, "debug", commands[0].LogLevel) -} - -// Git flag is set to true when ResetFlag is true -func TestLoadCommandFromArgsSetsGitFlagWhenResetFlagIsTrue(t *testing.T) { - // Setup - oldGitFlag := GitFlag - oldResetFlag := ResetFlag - oldLogLevel := LogLevel - - gitValue := false - resetValue := true - logLevelValue := "info" - - GitFlag = &gitValue - ResetFlag = &resetValue - LogLevel = &logLevelValue - - defer func() { - GitFlag = oldGitFlag - ResetFlag = oldResetFlag - LogLevel = oldLogLevel - }() - - args := []string{"*.go", "return x", "file1.go", "file2.go"} - - // Execute - commands, err := LoadCommandFromArgs(args) - - // Assert - assert.NoError(t, err) - assert.Len(t, commands, 1) - assert.Equal(t, true, commands[0].Git) -} - // TODO: Figure out how to mock shit // Can't be asked doing that right now... // Successfully loads commands from multiple YAML files in the current directory diff --git a/utils/replacecommand.go b/utils/replacecommand.go index 89f3a06..cca6426 100644 --- a/utils/replacecommand.go +++ b/utils/replacecommand.go @@ -1,9 +1,10 @@ package utils import ( - "cook/logger" "fmt" "sort" + + logger "git.site.quack-lab.dev/dave/cylogger" ) type ReplaceCommand struct {