mirror of
https://gitee.com/johng/gf
synced 2026-06-06 16:21:40 +08:00
This pull request refactors the error handling code to improve support for error text formatting with arguments, making it easier to retrieve both the error message template and its arguments (useful for i18n and structured error handling). It introduces the new `ITextArgs` interface, updates error constructors to store format strings and arguments separately, and adds methods to retrieve them. Several usages and tests are updated to reflect these changes. ### Error formatting and argument support * Introduced the `ITextArgs` interface to allow errors to expose their text template and arguments separately, supporting advanced use cases like internationalization (`errors/gerror/gerror.go`). * Updated the `Error` struct to include an `args` field for error arguments, and added methods `TextWithArgs()`, `Text()`, and `Args()` to retrieve formatted error text, the template, and arguments respectively (`errors/gerror/gerror_error.go`). [[1]](diffhunk://#diff-b56b52e546735b8196ec3e8bd25c0b007ac134e2f13b116ee3abcb2f92c3bdd9R23) [[2]](diffhunk://#diff-b56b52e546735b8196ec3e8bd25c0b007ac134e2f13b116ee3abcb2f92c3bdd9L121-R145) * Changed all error creation and wrapping functions (e.g., `Newf`, `Wrapf`, `NewCodef`, etc.) to store the format string and arguments separately, rather than pre-formatting the error text (`errors/gerror/gerror_api.go`, `errors/gerror/gerror_api_code.go`). [[1]](diffhunk://#diff-847475c1de42114004c50163aa2f34a4095e05122b4c2993aa3df4e5923e83cbL24-R27) [[2]](diffhunk://#diff-847475c1de42114004c50163aa2f34a4095e05122b4c2993aa3df4e5923e83cbL43-R48) [[3]](diffhunk://#diff-847475c1de42114004c50163aa2f34a4095e05122b4c2993aa3df4e5923e83cbL77-R78) [[4]](diffhunk://#diff-31ee6b1493f4b206c060a98818226b1b78102c91b5ae22e34ed4d1bb4a38c185L25-R29) [[5]](diffhunk://#diff-31ee6b1493f4b206c060a98818226b1b78102c91b5ae22e34ed4d1bb4a38c185L44-R50) [[6]](diffhunk://#diff-31ee6b1493f4b206c060a98818226b1b78102c91b5ae22e34ed4d1bb4a38c185L77-R79) [[7]](diffhunk://#diff-31ee6b1493f4b206c060a98818226b1b78102c91b5ae22e34ed4d1bb4a38c185L107-R110) * Updated the `Option` struct and related constructor to handle error arguments (`errors/gerror/gerror_api_option.go`). [[1]](diffhunk://#diff-4b458af6df9a0d8289303cf408b082ed472360b286cdc5a556c8fe7541973caaR16) [[2]](diffhunk://#diff-4b458af6df9a0d8289303cf408b082ed472360b286cdc5a556c8fe7541973caaR26) ### Code and test improvements * Updated formatting and equality checks to use the new methods for retrieving formatted error text and arguments, ensuring consistent behavior (`errors/gerror/gerror_error.go`, `errors/gerror/gerror_error_format.go`). [[1]](diffhunk://#diff-b56b52e546735b8196ec3e8bd25c0b007ac134e2f13b116ee3abcb2f92c3bdd9L45-R46) [[2]](diffhunk://#diff-fa801ef307f6c6fdda49fe9853593de29eda5b4d3712ea5bf9ed39de6e6859ebL26-R26) * Improved unit tests to verify the new interface and argument handling, including tests for the `ITextArgs` interface (`errors/gerror/gerror_z_unit_test.go`). * Minor code cleanup, such as removing unused imports and updating comments for clarity (`errors/gerror/gerror_api.go`, `errors/gerror/gerror_api_code.go`, `errors/gerror/gerror_error_json.go`). [[1]](diffhunk://#diff-847475c1de42114004c50163aa2f34a4095e05122b4c2993aa3df4e5923e83cbL10-L11) [[2]](diffhunk://#diff-31ee6b1493f4b206c060a98818226b1b78102c91b5ae22e34ed4d1bb4a38c185L10) [[3]](diffhunk://#diff-3e4ba207e242eb338f31f1091466374e8e72754a8969d92724bfb5c6b88f25edL15-R15) These changes make error handling more flexible and maintainable, especially for scenarios where error messages need to be localized or programmatically inspected. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
830 lines
22 KiB
Go
830 lines
22 KiB
Go
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
|
//
|
|
// This Source Code Form is subject to the terms of the MIT License.
|
|
// If a copy of the MIT was not distributed with this file,
|
|
// You can obtain one at https://github.com/gogf/gf.
|
|
|
|
package gerror_test
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/gogf/gf/v2/errors/gcode"
|
|
"github.com/gogf/gf/v2/errors/gerror"
|
|
"github.com/gogf/gf/v2/internal/json"
|
|
"github.com/gogf/gf/v2/test/gtest"
|
|
)
|
|
|
|
// customError is used to test As function
|
|
type customError struct {
|
|
Message string
|
|
}
|
|
|
|
func (e *customError) Error() string {
|
|
return e.Message
|
|
}
|
|
|
|
// anotherError is used to test As function with different error type
|
|
type anotherError struct{}
|
|
|
|
func (e *anotherError) Error() string {
|
|
return "another error"
|
|
}
|
|
|
|
// customCauseError implements ICause interface
|
|
type customCauseError struct {
|
|
msg string
|
|
cause error
|
|
}
|
|
|
|
func (e *customCauseError) Error() string { return e.msg }
|
|
func (e *customCauseError) Cause() error { return e.cause }
|
|
|
|
// customStackError implements IStack interface
|
|
type customStackError struct {
|
|
msg string
|
|
stack string
|
|
}
|
|
|
|
func (e *customStackError) Error() string { return e.msg }
|
|
func (e *customStackError) Stack() string { return e.stack }
|
|
|
|
// customCurrentError implements ICurrent interface
|
|
type customCurrentError struct {
|
|
msg string
|
|
current error
|
|
}
|
|
|
|
func (e *customCurrentError) Error() string { return e.msg }
|
|
func (e *customCurrentError) Current() error { return e.current }
|
|
|
|
// customUnwrapError implements IUnwrap interface
|
|
type customUnwrapError struct {
|
|
msg string
|
|
unwrap error
|
|
}
|
|
|
|
func (e *customUnwrapError) Error() string { return e.msg }
|
|
func (e *customUnwrapError) Unwrap() error { return e.unwrap }
|
|
|
|
// customEqualError implements IEqual interface
|
|
type customEqualError struct {
|
|
msg string
|
|
}
|
|
|
|
func (e *customEqualError) Error() string { return e.msg }
|
|
func (e *customEqualError) Equal(target error) bool {
|
|
if target == nil {
|
|
return false
|
|
}
|
|
return e.msg == target.Error()
|
|
}
|
|
|
|
// customCodeError implements ICode interface
|
|
type customCodeError struct {
|
|
msg string
|
|
code gcode.Code
|
|
}
|
|
|
|
func (e *customCodeError) Error() string { return e.msg }
|
|
func (e *customCodeError) Code() gcode.Code { return e.code }
|
|
|
|
func nilError() error {
|
|
return nil
|
|
}
|
|
|
|
func Test_Nil(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
t.Assert(gerror.New(""), nil)
|
|
t.Assert(gerror.Wrap(nilError(), "test"), nil)
|
|
})
|
|
}
|
|
|
|
func Test_New(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.New("1")
|
|
t.AssertNE(err, nil)
|
|
t.Assert(err.Error(), "1")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.Newf("%d", 1)
|
|
t.AssertNE(err, nil)
|
|
t.Assert(err.Error(), "1")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.NewSkip(1, "1")
|
|
t.AssertNE(err, nil)
|
|
t.Assert(err.Error(), "1")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.NewSkipf(1, "%d", 1)
|
|
t.AssertNE(err, nil)
|
|
t.Assert(err.Error(), "1")
|
|
})
|
|
}
|
|
|
|
func Test_Wrap(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := errors.New("1")
|
|
err = gerror.Wrap(err, "2")
|
|
err = gerror.Wrap(err, "3")
|
|
t.AssertNE(err, nil)
|
|
t.Assert(err.Error(), "3: 2: 1")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.New("1")
|
|
err = gerror.Wrap(err, "2")
|
|
err = gerror.Wrap(err, "3")
|
|
t.AssertNE(err, nil)
|
|
t.Assert(err.Error(), "3: 2: 1")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.New("1")
|
|
err = gerror.Wrap(err, "")
|
|
t.AssertNE(err, nil)
|
|
t.Assert(err.Error(), "1")
|
|
})
|
|
}
|
|
|
|
func Test_Wrapf(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := errors.New("1")
|
|
err = gerror.Wrapf(err, "%d", 2)
|
|
err = gerror.Wrapf(err, "%d", 3)
|
|
t.AssertNE(err, nil)
|
|
t.Assert(err.Error(), "3: 2: 1")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.New("1")
|
|
err = gerror.Wrapf(err, "%d", 2)
|
|
err = gerror.Wrapf(err, "%d", 3)
|
|
t.AssertNE(err, nil)
|
|
t.Assert(err.Error(), "3: 2: 1")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.New("1")
|
|
err = gerror.Wrapf(err, "")
|
|
t.AssertNE(err, nil)
|
|
t.Assert(err.Error(), "1")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
t.Assert(gerror.Wrapf(nil, ""), nil)
|
|
})
|
|
}
|
|
|
|
func Test_WrapSkip(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
t.Assert(gerror.WrapSkip(1, nil, "2"), nil)
|
|
err := errors.New("1")
|
|
err = gerror.WrapSkip(1, err, "2")
|
|
err = gerror.WrapSkip(1, err, "3")
|
|
t.AssertNE(err, nil)
|
|
t.Assert(err.Error(), "3: 2: 1")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.New("1")
|
|
err = gerror.WrapSkip(1, err, "2")
|
|
err = gerror.WrapSkip(1, err, "3")
|
|
t.AssertNE(err, nil)
|
|
t.Assert(err.Error(), "3: 2: 1")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.New("1")
|
|
err = gerror.WrapSkip(1, err, "")
|
|
t.AssertNE(err, nil)
|
|
t.Assert(err.Error(), "1")
|
|
})
|
|
}
|
|
|
|
func Test_WrapSkipf(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
t.Assert(gerror.WrapSkipf(1, nil, "2"), nil)
|
|
err := errors.New("1")
|
|
err = gerror.WrapSkipf(1, err, "2")
|
|
err = gerror.WrapSkipf(1, err, "3")
|
|
t.AssertNE(err, nil)
|
|
t.Assert(err.Error(), "3: 2: 1")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.New("1")
|
|
err = gerror.WrapSkipf(1, err, "2")
|
|
err = gerror.WrapSkipf(1, err, "3")
|
|
t.AssertNE(err, nil)
|
|
t.Assert(err.Error(), "3: 2: 1")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.New("1")
|
|
err = gerror.WrapSkipf(1, err, "")
|
|
t.AssertNE(err, nil)
|
|
t.Assert(err.Error(), "1")
|
|
})
|
|
}
|
|
|
|
func Test_Cause(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
t.Assert(gerror.Cause(nil), nil)
|
|
err := errors.New("1")
|
|
t.Assert(gerror.Cause(err), err)
|
|
})
|
|
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := errors.New("1")
|
|
err = gerror.Wrap(err, "2")
|
|
err = gerror.Wrap(err, "3")
|
|
t.Assert(gerror.Cause(err), "1")
|
|
})
|
|
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.New("1")
|
|
t.Assert(gerror.Cause(err), "1")
|
|
})
|
|
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.New("1")
|
|
err = gerror.Wrap(err, "2")
|
|
err = gerror.Wrap(err, "3")
|
|
t.Assert(gerror.Cause(err), "1")
|
|
})
|
|
|
|
gtest.C(t, func(t *gtest.T) {
|
|
t.Assert(gerror.Stack(nil), "")
|
|
err := errors.New("1")
|
|
t.Assert(gerror.Stack(err), err)
|
|
})
|
|
|
|
gtest.C(t, func(t *gtest.T) {
|
|
var e *gerror.Error = nil
|
|
t.Assert(e.Cause(), nil)
|
|
})
|
|
}
|
|
|
|
func Test_Format(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := errors.New("1")
|
|
err = gerror.Wrap(err, "2")
|
|
err = gerror.Wrap(err, "3")
|
|
t.AssertNE(err, nil)
|
|
t.Assert(fmt.Sprintf("%s", err), "3: 2: 1")
|
|
t.Assert(fmt.Sprintf("%v", err), "3: 2: 1")
|
|
})
|
|
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.New("1")
|
|
err = gerror.Wrap(err, "2")
|
|
err = gerror.Wrap(err, "3")
|
|
t.AssertNE(err, nil)
|
|
t.Assert(fmt.Sprintf("%s", err), "3: 2: 1")
|
|
t.Assert(fmt.Sprintf("%v", err), "3: 2: 1")
|
|
})
|
|
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.New("1")
|
|
err = gerror.Wrap(err, "2")
|
|
err = gerror.Wrap(err, "3")
|
|
t.AssertNE(err, nil)
|
|
t.Assert(fmt.Sprintf("%-s", err), "3")
|
|
t.Assert(fmt.Sprintf("%-v", err), "3")
|
|
})
|
|
}
|
|
|
|
func Test_Stack(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := errors.New("1")
|
|
t.Assert(fmt.Sprintf("%+v", err), "1")
|
|
})
|
|
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := errors.New("1")
|
|
err = gerror.Wrap(err, "2")
|
|
err = gerror.Wrap(err, "3")
|
|
t.AssertNE(err, nil)
|
|
// fmt.Printf("%+v", err)
|
|
})
|
|
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.New("1")
|
|
t.AssertNE(fmt.Sprintf("%+v", err), "1")
|
|
// fmt.Printf("%+v", err)
|
|
})
|
|
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.New("1")
|
|
err = gerror.Wrap(err, "2")
|
|
err = gerror.Wrap(err, "3")
|
|
t.AssertNE(err, nil)
|
|
// fmt.Printf("%+v", err)
|
|
})
|
|
}
|
|
|
|
func Test_Current(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
t.Assert(gerror.Current(nil), nil)
|
|
err := errors.New("1")
|
|
err = gerror.Wrap(err, "2")
|
|
err = gerror.Wrap(err, "3")
|
|
t.Assert(err.Error(), "3: 2: 1")
|
|
t.Assert(gerror.Current(err).Error(), "3")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
var e *gerror.Error = nil
|
|
t.Assert(e.Current(), nil)
|
|
})
|
|
}
|
|
|
|
func Test_Unwrap(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
t.Assert(gerror.Unwrap(nil), nil)
|
|
err := errors.New("1")
|
|
err = gerror.Wrap(err, "2")
|
|
err = gerror.Wrap(err, "3")
|
|
t.Assert(err.Error(), "3: 2: 1")
|
|
|
|
err = gerror.Unwrap(err)
|
|
t.Assert(err.Error(), "2: 1")
|
|
|
|
err = gerror.Unwrap(err)
|
|
t.Assert(err.Error(), "1")
|
|
|
|
err = gerror.Unwrap(err)
|
|
t.AssertNil(err)
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
var e *gerror.Error = nil
|
|
t.Assert(e.Unwrap(), nil)
|
|
})
|
|
}
|
|
|
|
func Test_Code(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := errors.New("123")
|
|
t.Assert(gerror.Code(err), -1)
|
|
t.Assert(err.Error(), "123")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.NewCode(gcode.CodeUnknown, "123")
|
|
t.Assert(gerror.Code(err), gcode.CodeUnknown)
|
|
t.Assert(err.Error(), "123")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.NewCodef(gcode.New(1, "", nil), "%s", "123")
|
|
t.Assert(gerror.Code(err).Code(), 1)
|
|
t.Assert(err.Error(), "123")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.NewCodeSkip(gcode.New(1, "", nil), 0, "123")
|
|
t.Assert(gerror.Code(err).Code(), 1)
|
|
t.Assert(err.Error(), "123")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.NewCodeSkipf(gcode.New(1, "", nil), 0, "%s", "123")
|
|
t.Assert(gerror.Code(err).Code(), 1)
|
|
t.Assert(err.Error(), "123")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
t.Assert(gerror.WrapCode(gcode.New(1, "", nil), nil, "3"), nil)
|
|
err := errors.New("1")
|
|
err = gerror.Wrap(err, "2")
|
|
err = gerror.WrapCode(gcode.New(1, "", nil), err, "3")
|
|
t.Assert(gerror.Code(err).Code(), 1)
|
|
t.Assert(err.Error(), "3: 2: 1")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
t.Assert(gerror.WrapCodef(gcode.New(1, "", nil), nil, "%s", "3"), nil)
|
|
err := errors.New("1")
|
|
err = gerror.Wrap(err, "2")
|
|
err = gerror.WrapCodef(gcode.New(1, "", nil), err, "%s", "3")
|
|
t.Assert(gerror.Code(err).Code(), 1)
|
|
t.Assert(err.Error(), "3: 2: 1")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
t.Assert(gerror.WrapCodeSkip(gcode.New(1, "", nil), 100, nil, "3"), nil)
|
|
err := errors.New("1")
|
|
err = gerror.Wrap(err, "2")
|
|
err = gerror.WrapCodeSkip(gcode.New(1, "", nil), 100, err, "3")
|
|
t.Assert(gerror.Code(err).Code(), 1)
|
|
t.Assert(err.Error(), "3: 2: 1")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
t.Assert(gerror.WrapCodeSkipf(gcode.New(1, "", nil), 100, nil, "%s", "3"), nil)
|
|
err := errors.New("1")
|
|
err = gerror.Wrap(err, "2")
|
|
err = gerror.WrapCodeSkipf(gcode.New(1, "", nil), 100, err, "%s", "3")
|
|
t.Assert(gerror.Code(err).Code(), 1)
|
|
t.Assert(err.Error(), "3: 2: 1")
|
|
})
|
|
}
|
|
|
|
func TestError_Error(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
var e *gerror.Error = nil
|
|
t.Assert(e.Error(), nil)
|
|
})
|
|
}
|
|
|
|
func TestError_Code(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
var e *gerror.Error = nil
|
|
t.Assert(e.Code(), gcode.CodeNil)
|
|
})
|
|
}
|
|
|
|
func Test_SetCode(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.New("123")
|
|
t.Assert(gerror.Code(err), -1)
|
|
t.Assert(err.Error(), "123")
|
|
|
|
err.(*gerror.Error).SetCode(gcode.CodeValidationFailed)
|
|
t.Assert(gerror.Code(err), gcode.CodeValidationFailed)
|
|
t.Assert(err.Error(), "123")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
var err *gerror.Error = nil
|
|
err.SetCode(gcode.CodeValidationFailed)
|
|
})
|
|
}
|
|
|
|
func Test_Json(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.Wrap(gerror.New("1"), "2")
|
|
b, e := json.Marshal(err)
|
|
t.Assert(e, nil)
|
|
t.Assert(string(b), `"2: 1"`)
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
errNormal := gerror.New("test")
|
|
b, e := json.Marshal(errNormal)
|
|
t.Assert(e, nil)
|
|
t.Assert(string(b), `"test"`)
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// The string contains special characters.
|
|
errWithSign := gerror.New(`test ""`)
|
|
b, e := json.Marshal(errWithSign)
|
|
t.Assert(e, nil)
|
|
t.Assert(string(b), `"test \"\""`)
|
|
})
|
|
}
|
|
|
|
func Test_HasStack(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err1 := errors.New("1")
|
|
err2 := gerror.New("1")
|
|
t.Assert(gerror.HasStack(err1), false)
|
|
t.Assert(gerror.HasStack(err2), true)
|
|
})
|
|
}
|
|
|
|
func Test_Equal(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err1 := errors.New("1")
|
|
err2 := errors.New("1")
|
|
err3 := gerror.New("1")
|
|
err4 := gerror.New("4")
|
|
t.Assert(gerror.Equal(err1, err2), false)
|
|
t.Assert(gerror.Equal(err1, err3), true)
|
|
t.Assert(gerror.Equal(err2, err3), true)
|
|
t.Assert(gerror.Equal(err3, err4), false)
|
|
t.Assert(gerror.Equal(err1, err4), false)
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
var e = new(gerror.Error)
|
|
t.Assert(e.Equal(e), true)
|
|
})
|
|
}
|
|
|
|
func Test_Is(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err1 := errors.New("1")
|
|
err2 := gerror.Wrap(err1, "2")
|
|
err2 = gerror.Wrap(err2, "3")
|
|
t.Assert(gerror.Is(err2, err1), true)
|
|
|
|
var (
|
|
errNotFound = errors.New("not found")
|
|
gerror1 = gerror.Wrap(errNotFound, "wrapped")
|
|
gerror2 = gerror.New("not found")
|
|
)
|
|
t.Assert(errors.Is(errNotFound, errNotFound), true)
|
|
t.Assert(errors.Is(nil, errNotFound), false)
|
|
t.Assert(errors.Is(nil, nil), true)
|
|
|
|
t.Assert(gerror.Is(errNotFound, errNotFound), true)
|
|
t.Assert(gerror.Is(nil, errNotFound), false)
|
|
t.Assert(gerror.Is(nil, nil), true)
|
|
|
|
t.Assert(errors.Is(gerror1, errNotFound), true)
|
|
t.Assert(errors.Is(gerror2, errNotFound), false)
|
|
t.Assert(gerror.Is(gerror1, errNotFound), true)
|
|
t.Assert(gerror.Is(gerror2, errNotFound), false)
|
|
})
|
|
}
|
|
|
|
func Test_HasError(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err1 := errors.New("1")
|
|
err2 := gerror.Wrap(err1, "2")
|
|
err2 = gerror.Wrap(err2, "3")
|
|
t.Assert(gerror.HasError(err2, err1), true)
|
|
})
|
|
}
|
|
|
|
func Test_HasCode(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
t.Assert(gerror.HasCode(nil, gcode.CodeNotAuthorized), false)
|
|
err1 := errors.New("1")
|
|
err2 := gerror.WrapCode(gcode.CodeNotAuthorized, err1, "2")
|
|
err3 := gerror.Wrap(err2, "3")
|
|
err4 := gerror.Wrap(err3, "4")
|
|
err5 := gerror.WrapCode(gcode.CodeInvalidParameter, err4, "5")
|
|
t.Assert(gerror.HasCode(err1, gcode.CodeNotAuthorized), false)
|
|
t.Assert(gerror.HasCode(err2, gcode.CodeNotAuthorized), true)
|
|
t.Assert(gerror.HasCode(err3, gcode.CodeNotAuthorized), true)
|
|
t.Assert(gerror.HasCode(err4, gcode.CodeNotAuthorized), true)
|
|
t.Assert(gerror.HasCode(err5, gcode.CodeNotAuthorized), true)
|
|
t.Assert(gerror.HasCode(err5, gcode.CodeInvalidParameter), true)
|
|
t.Assert(gerror.HasCode(err5, gcode.CodeInternalError), false)
|
|
})
|
|
}
|
|
|
|
func Test_NewOption(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
t.AssertNE(gerror.NewWithOption(gerror.Option{
|
|
Error: errors.New("NewOptionError"),
|
|
Stack: true,
|
|
Text: "Text",
|
|
Code: gcode.CodeNotAuthorized,
|
|
}), gerror.New("NewOptionError"))
|
|
})
|
|
}
|
|
|
|
func Test_As(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
var myerr = &customError{Message: "custom error"}
|
|
|
|
// Test with nil error
|
|
var targetErr *customError
|
|
t.Assert(gerror.As(nil, &targetErr), false)
|
|
t.Assert(targetErr, nil)
|
|
|
|
// Test with standard error
|
|
err1 := errors.New("standard error")
|
|
t.Assert(gerror.As(err1, &targetErr), false)
|
|
t.Assert(targetErr, nil)
|
|
|
|
// Test with custom error type
|
|
err2 := myerr
|
|
t.Assert(gerror.As(err2, &targetErr), true)
|
|
t.Assert(targetErr.Message, "custom error")
|
|
|
|
// Test with wrapped error
|
|
err3 := gerror.Wrap(myerr, "wrapped")
|
|
targetErr = nil
|
|
t.Assert(gerror.As(err3, &targetErr), true)
|
|
t.Assert(targetErr.Message, "custom error")
|
|
|
|
// Test with deeply wrapped error
|
|
err4 := gerror.Wrap(gerror.Wrap(gerror.Wrap(myerr, "wrap3"), "wrap2"), "wrap1")
|
|
targetErr = nil
|
|
t.Assert(gerror.As(err4, &targetErr), true)
|
|
t.Assert(targetErr.Message, "custom error")
|
|
|
|
// Test with different error type
|
|
var otherErr *anotherError
|
|
t.Assert(gerror.As(err4, &otherErr), false)
|
|
t.Assert(otherErr, nil)
|
|
|
|
// Test with non-pointer target
|
|
defer func() {
|
|
t.Assert(recover() != nil, true)
|
|
}()
|
|
var nonPtr customError
|
|
gerror.As(err4, nonPtr)
|
|
})
|
|
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test with nil target
|
|
defer func() {
|
|
t.Assert(recover() != nil, true)
|
|
}()
|
|
gerror.As(errors.New("error"), nil)
|
|
})
|
|
}
|
|
|
|
func Test_NewOption_Deprecated(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test deprecated NewOption function
|
|
err := gerror.NewOption(gerror.Option{
|
|
Error: errors.New("base error"),
|
|
Stack: true,
|
|
Text: "option text",
|
|
Code: gcode.CodeInternalError,
|
|
})
|
|
t.AssertNE(err, nil)
|
|
t.Assert(gerror.Code(err), gcode.CodeInternalError)
|
|
})
|
|
}
|
|
|
|
func Test_Code_WithIUnwrap(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test Code() with custom error that implements IUnwrap but not ICode
|
|
innerErr := gerror.NewCode(gcode.CodeInternalError, "inner error")
|
|
unwrapErr := &customUnwrapError{msg: "unwrap error", unwrap: innerErr}
|
|
t.Assert(gerror.Code(unwrapErr), gcode.CodeInternalError)
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test Code() with nil
|
|
t.Assert(gerror.Code(nil), gcode.CodeNil)
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test Code() with custom error that implements ICode
|
|
codeErr := &customCodeError{msg: "code error", code: gcode.CodeNotFound}
|
|
t.Assert(gerror.Code(codeErr), gcode.CodeNotFound)
|
|
})
|
|
}
|
|
|
|
func Test_Cause_WithIUnwrap(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test Cause() with custom error that implements IUnwrap but not ICause
|
|
rootErr := errors.New("root error")
|
|
unwrapErr := &customUnwrapError{msg: "unwrap error", unwrap: rootErr}
|
|
t.Assert(gerror.Cause(unwrapErr), rootErr)
|
|
})
|
|
}
|
|
|
|
func Test_Cause_WithICause(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test Cause() with custom error that implements ICause
|
|
rootErr := errors.New("root error")
|
|
causeErr := &customCauseError{msg: "cause error", cause: rootErr}
|
|
t.Assert(gerror.Cause(causeErr), rootErr)
|
|
})
|
|
}
|
|
|
|
func Test_Stack_WithIStack(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test Stack() with custom error that implements IStack
|
|
stackErr := &customStackError{msg: "stack error", stack: "custom stack trace"}
|
|
t.Assert(gerror.Stack(stackErr), "custom stack trace")
|
|
})
|
|
}
|
|
|
|
func Test_Current_WithICurrent(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test Current() with custom error that implements ICurrent
|
|
currentErr := errors.New("current error")
|
|
customErr := &customCurrentError{msg: "custom error", current: currentErr}
|
|
t.Assert(gerror.Current(customErr), currentErr)
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test Current() with standard error (does not implement ICurrent)
|
|
stdErr := errors.New("standard error")
|
|
t.Assert(gerror.Current(stdErr), stdErr)
|
|
})
|
|
}
|
|
|
|
func Test_Equal_WithIEqual(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test Equal() when target implements IEqual
|
|
err1 := errors.New("test error")
|
|
err2 := &customEqualError{msg: "test error"}
|
|
t.Assert(gerror.Equal(err1, err2), true)
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test Equal() when both are the same
|
|
err := errors.New("test error")
|
|
t.Assert(gerror.Equal(err, err), true)
|
|
})
|
|
}
|
|
|
|
func Test_Error_Cause_WithICause(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test Error.Cause() when inner error implements ICause
|
|
rootErr := errors.New("root")
|
|
causeErr := &customCauseError{msg: "cause", cause: rootErr}
|
|
wrappedErr := gerror.Wrap(causeErr, "wrapped")
|
|
t.Assert(gerror.Cause(wrappedErr), rootErr)
|
|
})
|
|
}
|
|
|
|
func Test_Error_WithCodeMessage(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test Error.Error() when text is empty but code has message
|
|
err := gerror.NewCode(gcode.CodeInternalError)
|
|
t.Assert(err.Error(), "Internal Error")
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test Error.Error() when text is empty and code has message, with wrapped error
|
|
innerErr := errors.New("inner")
|
|
err := gerror.WrapCode(gcode.CodeInternalError, innerErr)
|
|
t.Assert(err.Error(), "Internal Error: inner")
|
|
})
|
|
}
|
|
|
|
func Test_Format_PlusS(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test %+s format (stack only)
|
|
err := gerror.New("test error")
|
|
stackStr := fmt.Sprintf("%+s", err)
|
|
t.Assert(len(stackStr) > 0, true)
|
|
t.AssertNE(stackStr, "test error")
|
|
})
|
|
}
|
|
|
|
func Test_Format_MinusS_EmptyText(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test %-s format when text is empty but code has message
|
|
err := gerror.NewCode(gcode.CodeInternalError)
|
|
result := fmt.Sprintf("%-s", err)
|
|
t.Assert(result, "Internal Error")
|
|
})
|
|
}
|
|
|
|
func Test_Stack_DeepNested(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test deeply nested errors stack
|
|
err := gerror.New("level1")
|
|
for i := 2; i <= 5; i++ {
|
|
err = gerror.Wrap(err, fmt.Sprintf("level%d", i))
|
|
}
|
|
stack := gerror.Stack(err)
|
|
t.Assert(len(stack) > 0, true)
|
|
})
|
|
}
|
|
|
|
func Test_Stack_NilError(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
var err *gerror.Error = nil
|
|
t.Assert(err.Stack(), "")
|
|
})
|
|
}
|
|
|
|
func Test_Stack_WithStandardError(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test stack with wrapped standard error
|
|
stdErr := errors.New("standard error")
|
|
err := gerror.Wrap(stdErr, "wrapped")
|
|
stack := gerror.Stack(err)
|
|
t.Assert(len(stack) > 0, true)
|
|
})
|
|
}
|
|
|
|
func Test_NewCode_MultipleTexts(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test NewCode with multiple text arguments
|
|
err := gerror.NewCode(gcode.CodeInternalError, "text1", "text2", "text3")
|
|
t.Assert(err.Error(), "text1, text2, text3")
|
|
})
|
|
}
|
|
|
|
func Test_NewCodeSkip_MultipleTexts(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test NewCodeSkip with multiple text arguments
|
|
err := gerror.NewCodeSkip(gcode.CodeInternalError, 0, "text1", "text2")
|
|
t.Assert(err.Error(), "text1, text2")
|
|
})
|
|
}
|
|
|
|
func Test_WrapCode_MultipleTexts(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test WrapCode with multiple text arguments
|
|
innerErr := errors.New("inner")
|
|
err := gerror.WrapCode(gcode.CodeInternalError, innerErr, "text1", "text2")
|
|
t.Assert(err.Error(), "text1, text2: inner")
|
|
})
|
|
}
|
|
|
|
func Test_WrapCodeSkip_MultipleTexts(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
// Test WrapCodeSkip with multiple text arguments
|
|
innerErr := errors.New("inner")
|
|
err := gerror.WrapCodeSkip(gcode.CodeInternalError, 0, innerErr, "text1", "text2")
|
|
t.Assert(err.Error(), "text1, text2: inner")
|
|
})
|
|
}
|
|
|
|
func Test_TextArgs(t *testing.T) {
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.New("text")
|
|
textArgs := err.(gerror.ITextArgs)
|
|
t.Assert(textArgs.Text(), "text")
|
|
t.Assert(textArgs.Args(), nil)
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err := gerror.Newf("text: %s", "arg1")
|
|
textArgs := err.(gerror.ITextArgs)
|
|
t.Assert(textArgs.Text(), "text: %s")
|
|
t.Assert(textArgs.Args(), []any{"arg1"})
|
|
})
|
|
gtest.C(t, func(t *gtest.T) {
|
|
err1 := errors.New("text")
|
|
err2 := gerror.Wrapf(err1, "wrap: %s", "arg1")
|
|
textArgs := err2.(gerror.ITextArgs)
|
|
t.Assert(textArgs.Error(), "wrap: arg1: text")
|
|
t.Assert(textArgs.Text(), "wrap: %s")
|
|
t.Assert(textArgs.Args(), []any{"arg1"})
|
|
})
|
|
}
|