backend/pkg/library/errors/ez_functions_test.go

348 lines
17 KiB
Go

package errs
import (
"errors"
"reflect"
"testing"
"backend/pkg/library/errors/code"
)
// fakeLogger as before
type fakeLogger struct {
calls []string
lastMsg string
fieldsStack [][]LogField
callerSkips []int
}
func (l *fakeLogger) WithCallerSkip(n int) Logger { l.callerSkips = append(l.callerSkips, n); return l }
func (l *fakeLogger) WithFields(fields ...LogField) Logger {
cp := make([]LogField, len(fields))
copy(cp, fields)
l.fieldsStack = append(l.fieldsStack, cp)
return l
}
func (l *fakeLogger) Error(msg string) { l.calls = append(l.calls, "ERROR"); l.lastMsg = msg }
func (l *fakeLogger) Warn(msg string) { l.calls = append(l.calls, "WARN"); l.lastMsg = msg }
func (l *fakeLogger) Info(msg string) { l.calls = append(l.calls, "INFO"); l.lastMsg = msg }
func (l *fakeLogger) reset() {
l.calls, l.lastMsg, l.fieldsStack, l.callerSkips = nil, "", nil, nil
}
func init() { Scope = code.Gateway }
func TestJoinMsg(t *testing.T) {
tests := []struct {
name string
in []string
want string
}{
{"nil", nil, ""},
{"empty", []string{}, ""},
{"single", []string{"a"}, "a"},
{"multi", []string{"a", "b", "c"}, "a b c"},
{"with spaces", []string{"hello", "world"}, "hello world"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := joinMsg(tt.in); got != tt.want {
t.Errorf("joinMsg() = %q, want %q", got, tt.want)
}
})
}
}
func TestLogErr(t *testing.T) {
tests := []struct {
name string
l Logger
fields []LogField
e *Error
wantCall string
wantFields bool
wantCallerSkip int
}{
{"nil logger", nil, nil, New(10, 101, 0, "err"), "", false, 0},
{"nil error", &fakeLogger{}, nil, nil, "", false, 0},
{"basic log", &fakeLogger{}, nil, New(10, 101, 0, "err"), "ERROR", false, 1},
{"with fields", &fakeLogger{}, []LogField{{Key: "k", Val: "v"}}, New(10, 101, 0, "err"), "ERROR", true, 1},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var fl *fakeLogger
if tt.l != nil {
var ok bool
fl, ok = tt.l.(*fakeLogger)
if !ok {
t.Fatalf("logger is not *fakeLogger")
}
fl.reset()
}
logErr(tt.l, tt.fields, tt.e)
if fl == nil {
if tt.wantCall != "" {
t.Errorf("expected log but logger is nil")
}
return
}
if tt.wantCall == "" && len(fl.calls) > 0 {
t.Errorf("unexpected log call")
}
if tt.wantCall != "" && (len(fl.calls) == 0 || fl.calls[0] != tt.wantCall) {
t.Errorf("expected call %q, got %v", tt.wantCall, fl.calls)
}
if tt.wantFields && (len(fl.fieldsStack) == 0 || !reflect.DeepEqual(fl.fieldsStack[0], tt.fields)) {
t.Errorf("fields mismatch: got %v, want %v", fl.fieldsStack, tt.fields)
}
if tt.wantCallerSkip != 0 && (len(fl.callerSkips) == 0 || fl.callerSkips[0] != tt.wantCallerSkip) {
t.Errorf("callerSkip = %v, want %d", fl.callerSkips, tt.wantCallerSkip)
}
})
}
}
func TestEL(t *testing.T) {
tests := []struct {
name string
cat code.Category
det code.Detail
s []string
wantCat uint32
wantDet uint32
wantMsg string
wantLog bool
}{
{"basic", code.ResNotFound, 123, []string{"not found"}, uint32(code.ResNotFound), 123, "not found", true},
{"nil logger", code.ResNotFound, 0, []string{}, uint32(code.ResNotFound), 0, "", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
l := &fakeLogger{}
e := EL(l, nil, tt.cat, tt.det, tt.s...)
if e.Category() != tt.wantCat || e.Detail() != tt.wantDet || e.Error() != tt.wantMsg {
t.Errorf("EL = cat=%d det=%d msg=%q, want %d %d %q", e.Category(), e.Detail(), e.Error(), tt.wantCat, tt.wantDet, tt.wantMsg)
}
if tt.wantLog && len(l.calls) == 0 {
t.Errorf("expected log")
}
})
}
}
func TestELWrap(t *testing.T) {
tests := []struct {
name string
cat code.Category
det code.Detail
cause error
s []string
wantCat uint32
wantDet uint32
wantMsg string
wantUnwrap string
wantLog bool
}{
{"basic", code.SysInternal, 456, errors.New("internal"), []string{"sys err"}, uint32(code.SysInternal), 456, "sys err", "internal", true},
{"no log", code.SysInternal, 0, nil, []string{}, uint32(code.SysInternal), 0, "", "", false}, // nil cause ok
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
l := &fakeLogger{}
e := ELWrap(l, nil, tt.cat, tt.det, tt.cause, tt.s...)
if e.Category() != tt.wantCat || e.Detail() != tt.wantDet || e.Error() != tt.wantMsg {
t.Errorf("ELWrap = cat=%d det=%d msg=%q, want %d %d %q", e.Category(), e.Detail(), e.Error(), tt.wantCat, tt.wantDet, tt.wantMsg)
}
unw := e.Unwrap()
gotUnwrap := ""
if unw != nil {
gotUnwrap = unw.Error()
}
if gotUnwrap != tt.wantUnwrap {
t.Errorf("Unwrap = %q, want %q", gotUnwrap, tt.wantUnwrap)
}
if tt.wantLog && len(l.calls) == 0 {
t.Errorf("expected log")
}
})
}
}
// Expand TestBaseConstructors with all base funcs
func TestBaseConstructors(t *testing.T) {
tests := []struct {
name string
fn func(...string) *Error
wantCat uint32
wantDet uint32
wantMsg string
}{
{"InputInvalidFormatError", InputInvalidFormatError, uint32(code.InputInvalidFormat), 0, "test msg"},
{"InputNotValidImplementationError", InputNotValidImplementationError, uint32(code.InputNotValidImplementation), 0, "test msg"},
{"InputInvalidRangeError", InputInvalidRangeError, uint32(code.InputInvalidRange), 0, "test msg"},
{"DBErrorError", DBErrorError, uint32(code.DBError), 0, "test msg"},
{"DBDataConvertError", DBDataConvertError, uint32(code.DBDataConvert), 0, "test msg"},
{"DBDuplicateError", DBDuplicateError, uint32(code.DBDuplicate), 0, "test msg"},
{"ResNotFoundError", ResNotFoundError, uint32(code.ResNotFound), 0, "test msg"},
{"ResInvalidFormatError", ResInvalidFormatError, uint32(code.ResInvalidFormat), 0, "test msg"},
{"ResAlreadyExistError", ResAlreadyExistError, uint32(code.ResAlreadyExist), 0, "test msg"},
{"ResInsufficientError", ResInsufficientError, uint32(code.ResInsufficient), 0, "test msg"},
{"ResInsufficientPermError", ResInsufficientPermError, uint32(code.ResInsufficientPerm), 0, "test msg"},
{"ResInvalidMeasureIDError", ResInvalidMeasureIDError, uint32(code.ResInvalidMeasureID), 0, "test msg"},
{"ResExpiredError", ResExpiredError, uint32(code.ResExpired), 0, "test msg"},
{"ResMigratedError", ResMigratedError, uint32(code.ResMigrated), 0, "test msg"},
{"ResInvalidStateError", ResInvalidStateError, uint32(code.ResInvalidState), 0, "test msg"},
{"ResInsufficientQuotaError", ResInsufficientQuotaError, uint32(code.ResInsufficientQuota), 0, "test msg"},
{"ResMultiOwnerError", ResMultiOwnerError, uint32(code.ResMultiOwner), 0, "test msg"},
{"AuthUnauthorizedError", AuthUnauthorizedError, uint32(code.AuthUnauthorized), 0, "test msg"},
{"AuthExpiredError", AuthExpiredError, uint32(code.AuthExpired), 0, "test msg"},
{"AuthInvalidPosixTimeError", AuthInvalidPosixTimeError, uint32(code.AuthInvalidPosixTime), 0, "test msg"},
{"AuthSigPayloadMismatchError", AuthSigPayloadMismatchError, uint32(code.AuthSigPayloadMismatch), 0, "test msg"},
{"AuthForbiddenError", AuthForbiddenError, uint32(code.AuthForbidden), 0, "test msg"},
{"SysInternalError", SysInternalError, uint32(code.SysInternal), 0, "test msg"},
{"SysMaintainError", SysMaintainError, uint32(code.SysMaintain), 0, "test msg"},
{"SysTimeoutError", SysTimeoutError, uint32(code.SysTimeout), 0, "test msg"},
{"SysTooManyRequestError", SysTooManyRequestError, uint32(code.SysTooManyRequest), 0, "test msg"},
{"PSuPublishError", PSuPublishError, uint32(code.PSuPublish), 0, "test msg"},
{"PSuConsumeError", PSuConsumeError, uint32(code.PSuConsume), 0, "test msg"},
{"PSuTooLargeError", PSuTooLargeError, uint32(code.PSuTooLarge), 0, "test msg"},
{"SvcInternalError", SvcInternalError, uint32(code.SvcInternal), 0, "test msg"},
{"SvcThirdPartyError", SvcThirdPartyError, uint32(code.SvcThirdParty), 0, "test msg"},
{"SvcHTTP400Error", SvcHTTP400Error, uint32(code.SvcHTTP400), 0, "test msg"},
{"SvcMaintenanceError", SvcMaintenanceError, uint32(code.SvcMaintenance), 0, "test msg"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
e := tt.fn("test", "msg")
if e == nil || e.Category() != tt.wantCat || e.Detail() != tt.wantDet || e.Error() != "test msg" {
t.Errorf("%s = cat=%d det=%d msg=%q, want %d 0 %q", tt.name, e.Category(), e.Detail(), e.Error(), tt.wantCat, "test msg")
}
})
}
}
// Expand TestLConstructors with all L funcs
func TestLConstructors(t *testing.T) {
tests := []struct {
name string
fn func(Logger, []LogField, ...string) *Error
wantCat uint32
wantDet uint32
wantMsg string
wantLog bool
}{
{"InputInvalidFormatErrorL", InputInvalidFormatErrorL, uint32(code.InputInvalidFormat), 0, "test msg", true},
{"InputNotValidImplementationErrorL", InputNotValidImplementationErrorL, uint32(code.InputNotValidImplementation), 0, "test msg", true},
{"InputInvalidRangeErrorL", InputInvalidRangeErrorL, uint32(code.InputInvalidRange), 0, "test msg", true},
{"DBErrorErrorL", DBErrorErrorL, uint32(code.DBError), 0, "test msg", true},
{"DBDataConvertErrorL", DBDataConvertErrorL, uint32(code.DBDataConvert), 0, "test msg", true},
{"DBDuplicateErrorL", DBDuplicateErrorL, uint32(code.DBDuplicate), 0, "test msg", true},
{"ResNotFoundErrorL", ResNotFoundErrorL, uint32(code.ResNotFound), 0, "test msg", true},
{"ResInvalidFormatErrorL", ResInvalidFormatErrorL, uint32(code.ResInvalidFormat), 0, "test msg", true},
{"ResAlreadyExistErrorL", ResAlreadyExistErrorL, uint32(code.ResAlreadyExist), 0, "test msg", true},
{"ResInsufficientErrorL", ResInsufficientErrorL, uint32(code.ResInsufficient), 0, "test msg", true},
{"ResInsufficientPermErrorL", ResInsufficientPermErrorL, uint32(code.ResInsufficientPerm), 0, "test msg", true},
{"ResInvalidMeasureIDErrorL", ResInvalidMeasureIDErrorL, uint32(code.ResInvalidMeasureID), 0, "test msg", true},
{"ResExpiredErrorL", ResExpiredErrorL, uint32(code.ResExpired), 0, "test msg", true},
{"ResMigratedErrorL", ResMigratedErrorL, uint32(code.ResMigrated), 0, "test msg", true},
{"ResInvalidStateErrorL", ResInvalidStateErrorL, uint32(code.ResInvalidState), 0, "test msg", true},
{"ResInsufficientQuotaErrorL", ResInsufficientQuotaErrorL, uint32(code.ResInsufficientQuota), 0, "test msg", true},
{"ResMultiOwnerErrorL", ResMultiOwnerErrorL, uint32(code.ResMultiOwner), 0, "test msg", true},
{"AuthUnauthorizedErrorL", AuthUnauthorizedErrorL, uint32(code.AuthUnauthorized), 0, "test msg", true},
{"AuthExpiredErrorL", AuthExpiredErrorL, uint32(code.AuthExpired), 0, "test msg", true},
{"AuthInvalidPosixTimeErrorL", AuthInvalidPosixTimeErrorL, uint32(code.AuthInvalidPosixTime), 0, "test msg", true},
{"AuthSigPayloadMismatchErrorL", AuthSigPayloadMismatchErrorL, uint32(code.AuthSigPayloadMismatch), 0, "test msg", true},
{"AuthForbiddenErrorL", AuthForbiddenErrorL, uint32(code.AuthForbidden), 0, "test msg", true},
{"SysInternalErrorL", SysInternalErrorL, uint32(code.SysInternal), 0, "test msg", true},
{"SysMaintainErrorL", SysMaintainErrorL, uint32(code.SysMaintain), 0, "test msg", true},
{"SysTimeoutErrorL", SysTimeoutErrorL, uint32(code.SysTimeout), 0, "test msg", true},
{"SysTooManyRequestErrorL", SysTooManyRequestErrorL, uint32(code.SysTooManyRequest), 0, "test msg", true},
{"PSuPublishErrorL", PSuPublishErrorL, uint32(code.PSuPublish), 0, "test msg", true},
{"PSuConsumeErrorL", PSuConsumeErrorL, uint32(code.PSuConsume), 0, "test msg", true},
{"PSuTooLargeErrorL", PSuTooLargeErrorL, uint32(code.PSuTooLarge), 0, "test msg", true},
{"SvcInternalErrorL", SvcInternalErrorL, uint32(code.SvcInternal), 0, "test msg", true},
{"SvcThirdPartyErrorL", SvcThirdPartyErrorL, uint32(code.SvcThirdParty), 0, "test msg", true},
{"SvcHTTP400ErrorL", SvcHTTP400ErrorL, uint32(code.SvcHTTP400), 0, "test msg", true},
{"SvcMaintenanceErrorL", SvcMaintenanceErrorL, uint32(code.SvcMaintenance), 0, "test msg", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
l := &fakeLogger{}
fields := []LogField{}
e := tt.fn(l, fields, "test", "msg")
if e == nil || e.Category() != tt.wantCat || e.Detail() != tt.wantDet || e.Error() != "test msg" {
t.Errorf("%s = cat=%d det=%d msg=%q, want %d 0 %q", tt.name, e.Category(), e.Detail(), e.Error(), tt.wantCat, "test msg")
}
if tt.wantLog && len(l.calls) == 0 {
t.Errorf("expected log call")
}
})
}
}
// Add TestWrapLConstructors similarly
func TestWrapLConstructors(t *testing.T) {
tests := []struct {
name string
fn func(Logger, []LogField, error, ...string) *Error
wantCat uint32
wantDet uint32
wantMsg string
wantUnwrap string
wantLog bool
}{
{"InputInvalidFormatErrorWrapL", InputInvalidFormatErrorWrapL, uint32(code.InputInvalidFormat), 0, "test msg", "cause err", true},
{"InputNotValidImplementationErrorWrapL", InputNotValidImplementationErrorWrapL, uint32(code.InputNotValidImplementation), 0, "test msg", "cause err", true},
{"InputInvalidRangeErrorWrapL", InputInvalidRangeErrorWrapL, uint32(code.InputInvalidRange), 0, "test msg", "cause err", true},
{"DBErrorErrorWrapL", DBErrorErrorWrapL, uint32(code.DBError), 0, "test msg", "cause err", true},
{"DBDataConvertErrorWrapL", DBDataConvertErrorWrapL, uint32(code.DBDataConvert), 0, "test msg", "cause err", true},
{"DBDuplicateErrorWrapL", DBDuplicateErrorWrapL, uint32(code.DBDuplicate), 0, "test msg", "cause err", true},
{"ResNotFoundErrorWrapL", ResNotFoundErrorWrapL, uint32(code.ResNotFound), 0, "test msg", "cause err", true},
{"ResInvalidFormatErrorWrapL", ResInvalidFormatErrorWrapL, uint32(code.ResInvalidFormat), 0, "test msg", "cause err", true},
{"ResAlreadyExistErrorWrapL", ResAlreadyExistErrorWrapL, uint32(code.ResAlreadyExist), 0, "test msg", "cause err", true},
{"ResInsufficientErrorWrapL", ResInsufficientErrorWrapL, uint32(code.ResInsufficient), 0, "test msg", "cause err", true},
{"ResInsufficientPermErrorWrapL", ResInsufficientPermErrorWrapL, uint32(code.ResInsufficientPerm), 0, "test msg", "cause err", true},
{"ResInvalidMeasureIDErrorWrapL", ResInvalidMeasureIDErrorWrapL, uint32(code.ResInvalidMeasureID), 0, "test msg", "cause err", true},
{"ResExpiredErrorWrapL", ResExpiredErrorWrapL, uint32(code.ResExpired), 0, "test msg", "cause err", true},
{"ResMigratedErrorWrapL", ResMigratedErrorWrapL, uint32(code.ResMigrated), 0, "test msg", "cause err", true},
{"ResInvalidStateErrorWrapL", ResInvalidStateErrorWrapL, uint32(code.ResInvalidState), 0, "test msg", "cause err", true},
{"ResInsufficientQuotaErrorWrapL", ResInsufficientQuotaErrorWrapL, uint32(code.ResInsufficientQuota), 0, "test msg", "cause err", true},
{"ResMultiOwnerErrorWrapL", ResMultiOwnerErrorWrapL, uint32(code.ResMultiOwner), 0, "test msg", "cause err", true},
{"AuthUnauthorizedErrorWrapL", AuthUnauthorizedErrorWrapL, uint32(code.AuthUnauthorized), 0, "test msg", "cause err", true},
{"AuthExpiredErrorWrapL", AuthExpiredErrorWrapL, uint32(code.AuthExpired), 0, "test msg", "cause err", true},
{"AuthInvalidPosixTimeErrorWrapL", AuthInvalidPosixTimeErrorWrapL, uint32(code.AuthInvalidPosixTime), 0, "test msg", "cause err", true},
{"AuthSigPayloadMismatchErrorWrapL", AuthSigPayloadMismatchErrorWrapL, uint32(code.AuthSigPayloadMismatch), 0, "test msg", "cause err", true},
{"AuthForbiddenErrorWrapL", AuthForbiddenErrorWrapL, uint32(code.AuthForbidden), 0, "test msg", "cause err", true},
{"SysInternalErrorWrapL", SysInternalErrorWrapL, uint32(code.SysInternal), 0, "test msg", "cause err", true},
{"SysMaintainErrorWrapL", SysMaintainErrorWrapL, uint32(code.SysMaintain), 0, "test msg", "cause err", true},
{"SysTimeoutErrorWrapL", SysTimeoutErrorWrapL, uint32(code.SysTimeout), 0, "test msg", "cause err", true},
{"SysTooManyRequestErrorWrapL", SysTooManyRequestErrorWrapL, uint32(code.SysTooManyRequest), 0, "test msg", "cause err", true},
{"PSuPublishErrorWrapL", PSuPublishErrorWrapL, uint32(code.PSuPublish), 0, "test msg", "cause err", true},
{"PSuConsumeErrorWrapL", PSuConsumeErrorWrapL, uint32(code.PSuConsume), 0, "test msg", "cause err", true},
{"PSuTooLargeErrorWrapL", PSuTooLargeErrorWrapL, uint32(code.PSuTooLarge), 0, "test msg", "cause err", true},
{"SvcInternalErrorWrapL", SvcInternalErrorWrapL, uint32(code.SvcInternal), 0, "test msg", "cause err", true},
{"SvcThirdPartyErrorWrapL", SvcThirdPartyErrorWrapL, uint32(code.SvcThirdParty), 0, "test msg", "cause err", true},
{"SvcHTTP400ErrorWrapL", SvcHTTP400ErrorWrapL, uint32(code.SvcHTTP400), 0, "test msg", "cause err", true},
{"SvcMaintenanceErrorWrapL", SvcMaintenanceErrorWrapL, uint32(code.SvcMaintenance), 0, "test msg", "cause err", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
l := &fakeLogger{}
fields := []LogField{}
cause := errors.New("cause err")
e := tt.fn(l, fields, cause, "test", "msg")
if e == nil || e.Category() != tt.wantCat || e.Detail() != tt.wantDet || e.Error() != "test msg" {
t.Errorf("%s = cat=%d det=%d msg=%q, want %d 0 %q", tt.name, e.Category(), e.Detail(), e.Error(), tt.wantCat, "test msg")
}
if tt.wantUnwrap != "" && e.Unwrap().Error() != tt.wantUnwrap {
t.Errorf("Unwrap() = %q, want %q", e.Unwrap().Error(), tt.wantUnwrap)
}
if tt.wantLog && len(l.calls) == 0 {
t.Errorf("expected log call")
}
})
}
}
// ... Add more tests for edge cases, like empty strings, multiple args in joinMsg, etc.