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.