From 2f20d0eacfbc16111fa273f4d6cac339cc61ef51 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Thu, 19 Jun 2025 20:29:21 +0300 Subject: Implement Phase 1: Foundation for improved maintainability and testability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add standardized error handling package (internal/errors) - Sentinel errors for common conditions - Error wrapping and chaining support - MultiError for batch operations - Add comprehensive test utilities package (internal/testutil) - File/directory test helpers - Assertion functions for common test patterns - Mock SSH server for integration testing - Test data generators - Add unit tests for core packages - Protocol package: delimiter validation and usage tests - Config package: comprehensive configuration tests - Discovery package: server discovery method tests - IO/FS package: stats tracking and grep processor tests All tests passing. This establishes a solid foundation for further improvements. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- internal/errors/errors_test.go | 109 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 internal/errors/errors_test.go (limited to 'internal/errors/errors_test.go') diff --git a/internal/errors/errors_test.go b/internal/errors/errors_test.go new file mode 100644 index 0000000..9193e38 --- /dev/null +++ b/internal/errors/errors_test.go @@ -0,0 +1,109 @@ +package errors + +import ( + "errors" + "strings" + "testing" +) + +func TestWrap(t *testing.T) { + tests := []struct { + name string + err error + msg string + expected string + }{ + { + name: "wrap with message", + err: ErrFileNotFound, + msg: "opening config file", + expected: "opening config file: file not found", + }, + { + name: "wrap nil error", + err: nil, + msg: "should return nil", + expected: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := Wrap(tt.err, tt.msg) + if tt.err == nil && result != nil { + t.Errorf("expected nil, got %v", result) + } + if tt.err != nil && result.Error() != tt.expected { + t.Errorf("expected %q, got %q", tt.expected, result.Error()) + } + }) + } +} + +func TestWrapf(t *testing.T) { + err := Wrapf(ErrConnectionFailed, "connecting to %s:%d", "localhost", 2222) + expected := "connecting to localhost:2222: connection failed" + if err.Error() != expected { + t.Errorf("expected %q, got %q", expected, err.Error()) + } +} + +func TestIs(t *testing.T) { + wrapped := Wrap(ErrPermissionDenied, "accessing /etc/passwd") + + if !Is(wrapped, ErrPermissionDenied) { + t.Error("expected Is to return true for wrapped error") + } + + if Is(wrapped, ErrFileNotFound) { + t.Error("expected Is to return false for different error") + } +} + +func TestMultiError(t *testing.T) { + multi := NewMultiError() + + // Test empty multi-error + if multi.HasErrors() { + t.Error("new MultiError should not have errors") + } + if multi.ErrorOrNil() != nil { + t.Error("ErrorOrNil should return nil for empty MultiError") + } + + // Add errors + multi.Add(ErrConnectionFailed) + multi.Add(nil) // Should be ignored + multi.Add(ErrTimeout) + + if !multi.HasErrors() { + t.Error("MultiError should have errors after adding") + } + + if len(multi.Errors()) != 2 { + t.Errorf("expected 2 errors, got %d", len(multi.Errors())) + } + + // Test error message + errMsg := multi.Error() + if !strings.Contains(errMsg, "multiple errors occurred") { + t.Errorf("unexpected error message: %s", errMsg) + } + + // Test single error + single := NewMultiError() + single.Add(ErrInvalidArgument) + if single.Error() != "invalid argument" { + t.Errorf("single error message incorrect: %s", single.Error()) + } +} + +func TestErrorUnwrapping(t *testing.T) { + base := errors.New("base error") + wrapped := Wrap(base, "context") + + unwrapped := Unwrap(wrapped) + if unwrapped != base { + t.Error("Unwrap did not return base error") + } +} \ No newline at end of file -- cgit v1.2.3