mirror of
https://github.com/ivuorinen/gibidify.git
synced 2026-01-26 03:24:05 +00:00
* 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
964 lines
20 KiB
Go
964 lines
20 KiB
Go
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)
|
|
})
|
|
}
|
|
}
|