mirror of
https://github.com/ivuorinen/gibidify.git
synced 2026-02-14 19:49:59 +00:00
feat: update go to 1.25, add permissions and envs (#49)
* chore(ci): update go to 1.25, add permissions and envs * fix(ci): update pr-lint.yml * chore: update go, fix linting * fix: tests and linting * fix(lint): lint fixes, renovate should now pass * fix: updates, security upgrades * chore: workflow updates, lint * fix: more lint, checkmake, and other fixes * fix: more lint, convert scripts to POSIX compliant * fix: simplify codeql workflow * tests: increase test coverage, fix found issues * fix(lint): editorconfig checking, add to linters * fix(lint): shellcheck, add to linters * fix(lint): apply cr comment suggestions * fix(ci): remove step-security/harden-runner * fix(lint): remove duplication, apply cr fixes * fix(ci): tests in CI/CD pipeline * chore(lint): deduplication of strings * fix(lint): apply cr comment suggestions * fix(ci): actionlint * fix(lint): apply cr comment suggestions * chore: lint, add deps management
This commit is contained in:
963
cli/errors_test.go
Normal file
963
cli/errors_test.go
Normal file
@@ -0,0 +1,963 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/ivuorinen/gibidify/gibidiutils"
|
||||
)
|
||||
|
||||
func TestNewErrorFormatter(t *testing.T) {
|
||||
ui := &UIManager{
|
||||
output: &bytes.Buffer{},
|
||||
}
|
||||
|
||||
ef := NewErrorFormatter(ui)
|
||||
|
||||
assert.NotNil(t, ef)
|
||||
assert.Equal(t, ui, ef.ui)
|
||||
}
|
||||
|
||||
func TestFormatError(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
err error
|
||||
expectedOutput []string
|
||||
notExpected []string
|
||||
}{
|
||||
{
|
||||
name: "nil error",
|
||||
err: nil,
|
||||
expectedOutput: []string{},
|
||||
},
|
||||
{
|
||||
name: "structured error",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeFileSystem,
|
||||
gibidiutils.CodeFSNotFound,
|
||||
testErrFileNotFound,
|
||||
"/test/file.txt",
|
||||
map[string]interface{}{"size": 1024},
|
||||
),
|
||||
expectedOutput: []string{
|
||||
gibidiutils.IconError + testErrorSuffix,
|
||||
"FileSystem",
|
||||
testErrFileNotFound,
|
||||
"/test/file.txt",
|
||||
"NOT_FOUND",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "generic error",
|
||||
err: errors.New("something went wrong"),
|
||||
expectedOutput: []string{gibidiutils.IconError + testErrorSuffix, "something went wrong"},
|
||||
},
|
||||
{
|
||||
name: "wrapped structured error",
|
||||
err: gibidiutils.WrapError(
|
||||
errors.New("inner error"),
|
||||
gibidiutils.ErrorTypeValidation,
|
||||
gibidiutils.CodeValidationRequired,
|
||||
"validation failed",
|
||||
),
|
||||
expectedOutput: []string{
|
||||
gibidiutils.IconError + testErrorSuffix,
|
||||
"validation failed",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
ui := &UIManager{
|
||||
enableColors: false,
|
||||
output: buf,
|
||||
}
|
||||
prev := color.NoColor
|
||||
color.NoColor = true
|
||||
t.Cleanup(func() { color.NoColor = prev })
|
||||
|
||||
ef := NewErrorFormatter(ui)
|
||||
ef.FormatError(tt.err)
|
||||
|
||||
output := buf.String()
|
||||
for _, expected := range tt.expectedOutput {
|
||||
assert.Contains(t, output, expected)
|
||||
}
|
||||
for _, notExpected := range tt.notExpected {
|
||||
assert.NotContains(t, output, notExpected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatStructuredError(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
err *gibidiutils.StructuredError
|
||||
expectedOutput []string
|
||||
}{
|
||||
{
|
||||
name: "filesystem error",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeFileSystem,
|
||||
gibidiutils.CodeFSPermission,
|
||||
testErrPermissionDenied,
|
||||
"/etc/shadow",
|
||||
nil,
|
||||
),
|
||||
expectedOutput: []string{
|
||||
"FileSystem",
|
||||
testErrPermissionDenied,
|
||||
"/etc/shadow",
|
||||
"PERMISSION_DENIED",
|
||||
testSuggestionsHeader,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "validation error",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeValidation,
|
||||
gibidiutils.CodeValidationFormat,
|
||||
testErrInvalidFormat,
|
||||
"",
|
||||
map[string]interface{}{"format": "xml"},
|
||||
),
|
||||
expectedOutput: []string{
|
||||
"Validation",
|
||||
testErrInvalidFormat,
|
||||
"FORMAT",
|
||||
testSuggestionsHeader,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "processing error",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeProcessing,
|
||||
gibidiutils.CodeProcessingFileRead,
|
||||
"failed to read file",
|
||||
"large.bin",
|
||||
nil,
|
||||
),
|
||||
expectedOutput: []string{
|
||||
"Processing",
|
||||
"failed to read file",
|
||||
"large.bin",
|
||||
"FILE_READ",
|
||||
testSuggestionsHeader,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "IO error",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeIO,
|
||||
gibidiutils.CodeIOFileWrite,
|
||||
"disk full",
|
||||
"/output/result.txt",
|
||||
nil,
|
||||
),
|
||||
expectedOutput: []string{
|
||||
"IO",
|
||||
"disk full",
|
||||
"/output/result.txt",
|
||||
"FILE_WRITE",
|
||||
testSuggestionsHeader,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
ui := &UIManager{
|
||||
enableColors: false,
|
||||
output: buf,
|
||||
}
|
||||
prev := color.NoColor
|
||||
color.NoColor = true
|
||||
t.Cleanup(func() { color.NoColor = prev })
|
||||
|
||||
ef := &ErrorFormatter{ui: ui}
|
||||
ef.formatStructuredError(tt.err)
|
||||
|
||||
output := buf.String()
|
||||
for _, expected := range tt.expectedOutput {
|
||||
assert.Contains(t, output, expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatGenericError(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
ui := &UIManager{
|
||||
enableColors: false,
|
||||
output: buf,
|
||||
}
|
||||
prev := color.NoColor
|
||||
color.NoColor = true
|
||||
t.Cleanup(func() { color.NoColor = prev })
|
||||
|
||||
ef := &ErrorFormatter{ui: ui}
|
||||
ef.formatGenericError(errors.New("generic error message"))
|
||||
|
||||
output := buf.String()
|
||||
assert.Contains(t, output, gibidiutils.IconError+testErrorSuffix)
|
||||
assert.Contains(t, output, "generic error message")
|
||||
}
|
||||
|
||||
func TestProvideSuggestions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
err *gibidiutils.StructuredError
|
||||
expectedSugges []string
|
||||
}{
|
||||
{
|
||||
name: "filesystem permission error",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeFileSystem,
|
||||
gibidiutils.CodeFSPermission,
|
||||
testErrPermissionDenied,
|
||||
"/root/file",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
testSuggestCheckPerms,
|
||||
testSuggestVerifyPath,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "filesystem not found error",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeFileSystem,
|
||||
gibidiutils.CodeFSNotFound,
|
||||
testErrFileNotFound,
|
||||
"/missing/file",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
"Check if the file/directory exists: /missing/file",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "validation format error",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeValidation,
|
||||
gibidiutils.CodeValidationFormat,
|
||||
"unsupported format",
|
||||
"",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
testSuggestFormat,
|
||||
testSuggestFormatEx,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "validation path error",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeValidation,
|
||||
gibidiutils.CodeValidationPath,
|
||||
"invalid path",
|
||||
"../../etc",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
testSuggestCheckArgs,
|
||||
testSuggestHelp,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "processing file read error",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeProcessing,
|
||||
gibidiutils.CodeProcessingFileRead,
|
||||
"read error",
|
||||
"corrupted.dat",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
"Check file permissions",
|
||||
"Verify the file is not corrupted",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "IO file write error",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeIO,
|
||||
gibidiutils.CodeIOFileWrite,
|
||||
"write failed",
|
||||
"/output.txt",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
testSuggestCheckPerms,
|
||||
testSuggestDiskSpace,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unknown error type",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeUnknown,
|
||||
"UNKNOWN",
|
||||
"unknown error",
|
||||
"",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
testSuggestCheckArgs,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
ui := &UIManager{
|
||||
enableColors: false,
|
||||
output: buf,
|
||||
}
|
||||
prev := color.NoColor
|
||||
color.NoColor = true
|
||||
t.Cleanup(func() { color.NoColor = prev })
|
||||
|
||||
ef := &ErrorFormatter{ui: ui}
|
||||
ef.provideSuggestions(tt.err)
|
||||
|
||||
output := buf.String()
|
||||
for _, suggestion := range tt.expectedSugges {
|
||||
assert.Contains(t, output, suggestion)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvideFileSystemSuggestions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
err *gibidiutils.StructuredError
|
||||
expectedSugges []string
|
||||
}{
|
||||
{
|
||||
name: testErrPermissionDenied,
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeFileSystem,
|
||||
gibidiutils.CodeFSPermission,
|
||||
testErrPermissionDenied,
|
||||
"/root/secret",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
testSuggestCheckPerms,
|
||||
testSuggestVerifyPath,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "path resolution error",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeFileSystem,
|
||||
gibidiutils.CodeFSPathResolution,
|
||||
"path error",
|
||||
"../../../etc",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
"Use an absolute path instead of relative",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: testErrFileNotFound,
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeFileSystem,
|
||||
gibidiutils.CodeFSNotFound,
|
||||
"not found",
|
||||
"/missing.txt",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
"Check if the file/directory exists: /missing.txt",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default filesystem error",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeFileSystem,
|
||||
"OTHER_FS_ERROR",
|
||||
testErrOther,
|
||||
"/some/path",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
testSuggestCheckPerms,
|
||||
testSuggestVerifyPath,
|
||||
"Path: /some/path",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
ui := &UIManager{
|
||||
enableColors: false,
|
||||
output: buf,
|
||||
}
|
||||
|
||||
ef := &ErrorFormatter{ui: ui}
|
||||
ef.provideFileSystemSuggestions(tt.err)
|
||||
|
||||
output := buf.String()
|
||||
for _, suggestion := range tt.expectedSugges {
|
||||
assert.Contains(t, output, suggestion)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvideValidationSuggestions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
err *gibidiutils.StructuredError
|
||||
expectedSugges []string
|
||||
}{
|
||||
{
|
||||
name: "format validation",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeValidation,
|
||||
gibidiutils.CodeValidationFormat,
|
||||
testErrInvalidFormat,
|
||||
"",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
testSuggestFormat,
|
||||
testSuggestFormatEx,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "path validation",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeValidation,
|
||||
gibidiutils.CodeValidationPath,
|
||||
"invalid path",
|
||||
"",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
testSuggestCheckArgs,
|
||||
testSuggestHelp,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "size validation",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeValidation,
|
||||
gibidiutils.CodeValidationSize,
|
||||
"size error",
|
||||
"",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
"Increase file size limit in config.yaml",
|
||||
"Use smaller files or exclude large files",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "required validation",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeValidation,
|
||||
gibidiutils.CodeValidationRequired,
|
||||
"required",
|
||||
"",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
testSuggestCheckArgs,
|
||||
testSuggestHelp,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default validation",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeValidation,
|
||||
"OTHER_VALIDATION",
|
||||
"other",
|
||||
"",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
testSuggestCheckArgs,
|
||||
testSuggestHelp,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
ui := &UIManager{
|
||||
enableColors: false,
|
||||
output: buf,
|
||||
}
|
||||
|
||||
ef := &ErrorFormatter{ui: ui}
|
||||
ef.provideValidationSuggestions(tt.err)
|
||||
|
||||
output := buf.String()
|
||||
for _, suggestion := range tt.expectedSugges {
|
||||
assert.Contains(t, output, suggestion)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvideProcessingSuggestions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
err *gibidiutils.StructuredError
|
||||
expectedSugges []string
|
||||
}{
|
||||
{
|
||||
name: "file read error",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeProcessing,
|
||||
gibidiutils.CodeProcessingFileRead,
|
||||
"read error",
|
||||
"",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
"Check file permissions",
|
||||
"Verify the file is not corrupted",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "collection error",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeProcessing,
|
||||
gibidiutils.CodeProcessingCollection,
|
||||
"collection error",
|
||||
"",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
"Check if the source directory exists and is readable",
|
||||
"Verify directory permissions",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: testErrEncoding,
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeProcessing,
|
||||
gibidiutils.CodeProcessingEncode,
|
||||
testErrEncoding,
|
||||
"",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
"Try reducing concurrency: -concurrency 1",
|
||||
"Check available system resources",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default processing",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeProcessing,
|
||||
"OTHER",
|
||||
testErrOther,
|
||||
"",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
"Try reducing concurrency: -concurrency 1",
|
||||
"Check available system resources",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
ui := &UIManager{
|
||||
enableColors: false,
|
||||
output: buf,
|
||||
}
|
||||
|
||||
ef := &ErrorFormatter{ui: ui}
|
||||
ef.provideProcessingSuggestions(tt.err)
|
||||
|
||||
output := buf.String()
|
||||
for _, suggestion := range tt.expectedSugges {
|
||||
assert.Contains(t, output, suggestion)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvideIOSuggestions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
err *gibidiutils.StructuredError
|
||||
expectedSugges []string
|
||||
}{
|
||||
{
|
||||
name: "file create error",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeIO,
|
||||
gibidiutils.CodeIOFileCreate,
|
||||
"create error",
|
||||
"",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
"Check if the destination directory exists",
|
||||
"Verify write permissions for the output file",
|
||||
"Ensure sufficient disk space",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "file write error",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeIO,
|
||||
gibidiutils.CodeIOFileWrite,
|
||||
"write error",
|
||||
"",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
testSuggestCheckPerms,
|
||||
testSuggestDiskSpace,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: testErrEncoding,
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeIO,
|
||||
gibidiutils.CodeIOEncoding,
|
||||
testErrEncoding,
|
||||
"",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
testSuggestCheckPerms,
|
||||
testSuggestDiskSpace,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default IO error",
|
||||
err: gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeIO,
|
||||
"OTHER",
|
||||
testErrOther,
|
||||
"",
|
||||
nil,
|
||||
),
|
||||
expectedSugges: []string{
|
||||
testSuggestCheckPerms,
|
||||
testSuggestDiskSpace,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
ui := &UIManager{
|
||||
enableColors: false,
|
||||
output: buf,
|
||||
}
|
||||
|
||||
ef := &ErrorFormatter{ui: ui}
|
||||
ef.provideIOSuggestions(tt.err)
|
||||
|
||||
output := buf.String()
|
||||
for _, suggestion := range tt.expectedSugges {
|
||||
assert.Contains(t, output, suggestion)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvideGenericSuggestions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
err error
|
||||
expectedSugges []string
|
||||
}{
|
||||
{
|
||||
name: "permission error",
|
||||
err: errors.New("permission denied accessing file"),
|
||||
expectedSugges: []string{
|
||||
testSuggestCheckPerms,
|
||||
"Try running with appropriate privileges",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "not found error",
|
||||
err: errors.New("no such file or directory"),
|
||||
expectedSugges: []string{
|
||||
"Verify the file/directory path is correct",
|
||||
"Check if the file exists",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "memory error",
|
||||
err: errors.New("out of memory"),
|
||||
expectedSugges: []string{
|
||||
testSuggestCheckArgs,
|
||||
testSuggestHelp,
|
||||
testSuggestReduceConcur,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "timeout error",
|
||||
err: errors.New("operation timed out"),
|
||||
expectedSugges: []string{
|
||||
testSuggestCheckArgs,
|
||||
testSuggestHelp,
|
||||
testSuggestReduceConcur,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "connection error",
|
||||
err: errors.New("connection refused"),
|
||||
expectedSugges: []string{
|
||||
testSuggestCheckArgs,
|
||||
testSuggestHelp,
|
||||
testSuggestReduceConcur,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default error",
|
||||
err: errors.New("unknown error occurred"),
|
||||
expectedSugges: []string{
|
||||
testSuggestCheckArgs,
|
||||
testSuggestHelp,
|
||||
testSuggestReduceConcur,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
ui := &UIManager{
|
||||
enableColors: false,
|
||||
output: buf,
|
||||
}
|
||||
|
||||
ef := &ErrorFormatter{ui: ui}
|
||||
ef.provideGenericSuggestions(tt.err)
|
||||
|
||||
output := buf.String()
|
||||
for _, suggestion := range tt.expectedSugges {
|
||||
assert.Contains(t, output, suggestion)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMissingSourceError(t *testing.T) {
|
||||
err := &MissingSourceError{}
|
||||
|
||||
assert.Equal(t, "source directory is required", err.Error())
|
||||
}
|
||||
|
||||
func TestNewMissingSourceErrorType(t *testing.T) {
|
||||
err := NewMissingSourceError()
|
||||
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, "source directory is required", err.Error())
|
||||
|
||||
var msErr *MissingSourceError
|
||||
ok := errors.As(err, &msErr)
|
||||
assert.True(t, ok)
|
||||
assert.NotNil(t, msErr)
|
||||
}
|
||||
|
||||
// Test error formatting with colors enabled
|
||||
func TestFormatErrorWithColors(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
ui := &UIManager{
|
||||
enableColors: true,
|
||||
output: buf,
|
||||
}
|
||||
prev := color.NoColor
|
||||
color.NoColor = false
|
||||
t.Cleanup(func() { color.NoColor = prev })
|
||||
|
||||
ef := NewErrorFormatter(ui)
|
||||
err := gibidiutils.NewStructuredError(
|
||||
gibidiutils.ErrorTypeValidation,
|
||||
gibidiutils.CodeValidationFormat,
|
||||
testErrInvalidFormat,
|
||||
"",
|
||||
nil,
|
||||
)
|
||||
|
||||
ef.FormatError(err)
|
||||
|
||||
output := buf.String()
|
||||
// When colors are enabled, some output may go directly to stdout
|
||||
// Check for suggestions that are captured in the buffer
|
||||
assert.Contains(t, output, testSuggestFormat)
|
||||
assert.Contains(t, output, testSuggestFormatEx)
|
||||
}
|
||||
|
||||
// Test wrapped error handling
|
||||
func TestFormatWrappedError(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
ui := &UIManager{
|
||||
enableColors: false,
|
||||
output: buf,
|
||||
}
|
||||
|
||||
ef := NewErrorFormatter(ui)
|
||||
|
||||
innerErr := errors.New("inner error")
|
||||
wrappedErr := gibidiutils.WrapError(
|
||||
innerErr,
|
||||
gibidiutils.ErrorTypeProcessing,
|
||||
gibidiutils.CodeProcessingFileRead,
|
||||
"wrapper message",
|
||||
)
|
||||
|
||||
ef.FormatError(wrappedErr)
|
||||
|
||||
output := buf.String()
|
||||
assert.Contains(t, output, "wrapper message")
|
||||
}
|
||||
|
||||
// Test all suggestion paths get called
|
||||
func TestSuggestionPathCoverage(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
ui := &UIManager{
|
||||
enableColors: false,
|
||||
output: buf,
|
||||
}
|
||||
ef := &ErrorFormatter{ui: ui}
|
||||
|
||||
// Test all error types
|
||||
errorTypes := []gibidiutils.ErrorType{
|
||||
gibidiutils.ErrorTypeFileSystem,
|
||||
gibidiutils.ErrorTypeValidation,
|
||||
gibidiutils.ErrorTypeProcessing,
|
||||
gibidiutils.ErrorTypeIO,
|
||||
gibidiutils.ErrorTypeConfiguration,
|
||||
gibidiutils.ErrorTypeUnknown,
|
||||
}
|
||||
|
||||
for _, errType := range errorTypes {
|
||||
t.Run(errType.String(), func(t *testing.T) {
|
||||
buf.Reset()
|
||||
err := gibidiutils.NewStructuredError(
|
||||
errType,
|
||||
"TEST_CODE",
|
||||
"test error",
|
||||
"/test/path",
|
||||
nil,
|
||||
)
|
||||
ef.provideSuggestions(err)
|
||||
|
||||
output := buf.String()
|
||||
// Should have some suggestion output
|
||||
assert.NotEmpty(t, output)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Test suggestion helper functions with various inputs
|
||||
func TestSuggestHelpers(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
testFunc func(*ErrorFormatter)
|
||||
}{
|
||||
{
|
||||
name: "suggestFileAccess",
|
||||
testFunc: func(ef *ErrorFormatter) {
|
||||
ef.suggestFileAccess("/root/file")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "suggestPathResolution",
|
||||
testFunc: func(ef *ErrorFormatter) {
|
||||
ef.suggestPathResolution("../../../etc")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "suggestFileNotFound",
|
||||
testFunc: func(ef *ErrorFormatter) {
|
||||
ef.suggestFileNotFound("/missing")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "suggestFileSystemGeneral",
|
||||
testFunc: func(ef *ErrorFormatter) {
|
||||
ef.suggestFileSystemGeneral("/path")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "provideDefaultSuggestions",
|
||||
testFunc: func(ef *ErrorFormatter) {
|
||||
ef.provideDefaultSuggestions()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
ui := &UIManager{
|
||||
enableColors: false,
|
||||
output: buf,
|
||||
}
|
||||
ef := &ErrorFormatter{ui: ui}
|
||||
|
||||
tt.testFunc(ef)
|
||||
|
||||
output := buf.String()
|
||||
// Each should produce some output
|
||||
assert.NotEmpty(t, output)
|
||||
// Should contain bullet point
|
||||
assert.Contains(t, output, gibidiutils.IconBullet)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Test edge cases in error message analysis
|
||||
func TestGenericSuggestionsEdgeCases(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
err error
|
||||
}{
|
||||
{"empty message", errors.New("")},
|
||||
{"very long message", errors.New(strings.Repeat("error ", 100))},
|
||||
{"special characters", errors.New("error!@#$%^&*()")},
|
||||
{"newlines", errors.New("error\nwith\nnewlines")},
|
||||
{"unicode", errors.New("error with 中文 characters")},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
ui := &UIManager{
|
||||
enableColors: false,
|
||||
output: buf,
|
||||
}
|
||||
ef := &ErrorFormatter{ui: ui}
|
||||
|
||||
// Should not panic
|
||||
ef.provideGenericSuggestions(tt.err)
|
||||
|
||||
output := buf.String()
|
||||
// Should have some output
|
||||
assert.NotEmpty(t, output)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user