package errs import ( "errors" "net/http" "testing" "backend/pkg/library/errors/code" "google.golang.org/grpc/codes" ) func TestNew(t *testing.T) { tests := []struct { name string scope uint32 category uint32 detail uint32 displayMsg string wantScope uint32 wantCategory uint32 wantDetail uint32 wantMsg string }{ {"basic", 10, 201, 123, "test", 10, 201, 123, "test"}, {"clamp category", 10, 1000, 0, "clamp cat", 10, 999, 0, "clamp cat"}, {"clamp detail", 10, 101, 1000, "clamp det", 10, 101, 999, "clamp det"}, {"zero values", 0, 0, 0, "", 0, 0, 0, ""}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { e := New(tt.scope, tt.category, tt.detail, tt.displayMsg) if e.Scope() != tt.wantScope || e.Category() != tt.wantCategory || e.Detail() != tt.wantDetail || e.msg != tt.wantMsg { t.Errorf("New() = %+v, want scope=%d cat=%d det=%d msg=%q", e, tt.wantScope, tt.wantCategory, tt.wantDetail, tt.wantMsg) } }) } } func TestErrorMethods(t *testing.T) { e := New(10, 201, 123, "test error") tests := []struct { name string err *Error wantErr string wantScope uint32 wantCat uint32 wantDet uint32 }{ {"non-nil", e, "test error", 10, 201, 123}, {"nil", nil, "", uint32(code.Unset), uint32(code.DefaultCategory), uint32(code.DefaultDetail)}, // Adjust if Default* not defined; use 0 } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := tt.err.Error(); got != tt.wantErr { t.Errorf("Error() = %q, want %q", got, tt.wantErr) } if got := tt.err.Scope(); got != tt.wantScope { t.Errorf("Scope() = %d, want %d", got, tt.wantScope) } if got := tt.err.Category(); got != tt.wantCat { t.Errorf("Category() = %d, want %d", got, tt.wantCat) } if got := tt.err.Detail(); got != tt.wantDet { t.Errorf("Detail() = %d, want %d", got, tt.wantDet) } }) } } func TestCodes(t *testing.T) { tests := []struct { name string err *Error wantSubCode uint32 wantCode uint32 wantDisplay string }{ {"basic", New(10, 201, 123, ""), 201123, 10201123, "10201123"}, {"nil", nil, code.OK, code.NonCode, "00000000"}, {"max clamp", New(99, 999, 999, ""), 999999, 99999999, "99999999"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := tt.err.SubCode(); got != tt.wantSubCode { t.Errorf("SubCode() = %d, want %d", got, tt.wantSubCode) } if got := tt.err.Code(); got != tt.wantCode { t.Errorf("Code() = %d, want %d", got, tt.wantCode) } if got := tt.err.DisplayCode(); got != tt.wantDisplay { t.Errorf("DisplayCode() = %q, want %q", got, tt.wantDisplay) } }) } } func TestIs(t *testing.T) { e1 := New(10, 201, 123, "") e2 := New(10, 201, 123, "") // same subcode e3 := New(10, 202, 123, "") // different category stdErr := errors.New("std") tests := []struct { name string err error target error want bool }{ {"match", e1, e2, true}, {"mismatch", e1, e3, false}, {"not Error type", e1, stdErr, false}, {"nil err", nil, e2, false}, {"nil target", e1, nil, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := errors.Is(tt.err, tt.target); got != tt.want { t.Errorf("Is() = %v, want %v", got, tt.want) } }) } } func TestWrapUnwrap(t *testing.T) { internal := errors.New("internal") tests := []struct { name string err *Error wrapErr error wantUnwrap error }{ {"wrap non-nil", New(10, 201, 0, ""), internal, internal}, {"wrap nil", nil, internal, nil}, // Wrap on nil does nothing } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.err.Wrap(tt.wrapErr) if unwrapped := got.Unwrap(); unwrapped != tt.wantUnwrap { t.Errorf("Unwrap() = %v, want %v", unwrapped, tt.wantUnwrap) } }) } } func TestGRPCStatus(t *testing.T) { tests := []struct { name string err *Error wantCode codes.Code wantMsg string }{ {"non-nil", New(10, 201, 123, "grpc err"), codes.Code(10201123), "grpc err"}, {"nil", nil, codes.OK, ""}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s := tt.err.GRPCStatus() if s.Code() != tt.wantCode || s.Message() != tt.wantMsg { t.Errorf("GRPCStatus() = code=%v msg=%q, want code=%v msg=%q", s.Code(), s.Message(), tt.wantCode, tt.wantMsg) } }) } } func TestHTTPStatus(t *testing.T) { tests := []struct { name string err *Error want int }{ {"nil", nil, http.StatusOK}, {"OK subcode", New(10, 0, 0, ""), http.StatusOK}, {"InputInvalidFormat", New(10, uint32(code.InputInvalidFormat), 0, ""), http.StatusBadRequest}, {"InputNotValidImplementation", New(10, uint32(code.InputNotValidImplementation), 0, ""), http.StatusUnprocessableEntity}, {"DBError", New(10, uint32(code.DBError), 0, ""), http.StatusInternalServerError}, {"ResNotFound", New(10, uint32(code.ResNotFound), 0, ""), http.StatusNotFound}, // Add all other categories to cover switch branches {"InputInvalidRange", New(10, uint32(code.InputInvalidRange), 0, ""), http.StatusUnprocessableEntity}, {"DBDataConvert", New(10, uint32(code.DBDataConvert), 0, ""), http.StatusUnprocessableEntity}, {"DBDuplicate", New(10, uint32(code.DBDuplicate), 0, ""), http.StatusConflict}, {"ResInvalidFormat", New(10, uint32(code.ResInvalidFormat), 0, ""), http.StatusUnprocessableEntity}, {"ResAlreadyExist", New(10, uint32(code.ResAlreadyExist), 0, ""), http.StatusConflict}, {"ResInsufficient", New(10, uint32(code.ResInsufficient), 0, ""), http.StatusBadRequest}, {"ResInsufficientPerm", New(10, uint32(code.ResInsufficientPerm), 0, ""), http.StatusForbidden}, {"ResInvalidMeasureID", New(10, uint32(code.ResInvalidMeasureID), 0, ""), http.StatusBadRequest}, {"ResExpired", New(10, uint32(code.ResExpired), 0, ""), http.StatusGone}, {"ResMigrated", New(10, uint32(code.ResMigrated), 0, ""), http.StatusGone}, {"ResInvalidState", New(10, uint32(code.ResInvalidState), 0, ""), http.StatusConflict}, {"ResInsufficientQuota", New(10, uint32(code.ResInsufficientQuota), 0, ""), http.StatusTooManyRequests}, {"ResMultiOwner", New(10, uint32(code.ResMultiOwner), 0, ""), http.StatusConflict}, {"AuthUnauthorized", New(10, uint32(code.AuthUnauthorized), 0, ""), http.StatusUnauthorized}, {"AuthExpired", New(10, uint32(code.AuthExpired), 0, ""), http.StatusUnauthorized}, {"AuthInvalidPosixTime", New(10, uint32(code.AuthInvalidPosixTime), 0, ""), http.StatusUnauthorized}, {"AuthSigPayloadMismatch", New(10, uint32(code.AuthSigPayloadMismatch), 0, ""), http.StatusUnauthorized}, {"AuthForbidden", New(10, uint32(code.AuthForbidden), 0, ""), http.StatusForbidden}, {"SysTooManyRequest", New(10, uint32(code.SysTooManyRequest), 0, ""), http.StatusTooManyRequests}, {"SysInternal", New(10, uint32(code.SysInternal), 0, ""), http.StatusInternalServerError}, {"SysMaintain", New(10, uint32(code.SysMaintain), 0, ""), http.StatusServiceUnavailable}, {"SysTimeout", New(10, uint32(code.SysTimeout), 0, ""), http.StatusGatewayTimeout}, {"PSuPublish", New(10, uint32(code.PSuPublish), 0, ""), http.StatusBadGateway}, {"PSuConsume", New(10, uint32(code.PSuConsume), 0, ""), http.StatusBadGateway}, {"PSuTooLarge", New(10, uint32(code.PSuTooLarge), 0, ""), http.StatusRequestEntityTooLarge}, {"SvcMaintenance", New(10, uint32(code.SvcMaintenance), 0, ""), http.StatusServiceUnavailable}, {"SvcInternal", New(10, uint32(code.SvcInternal), 0, ""), http.StatusInternalServerError}, {"SvcThirdParty", New(10, uint32(code.SvcThirdParty), 0, ""), http.StatusBadGateway}, {"SvcHTTP400", New(10, uint32(code.SvcHTTP400), 0, ""), http.StatusBadRequest}, {"fallback unknown", New(10, 999, 0, ""), http.StatusInternalServerError}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := tt.err.HTTPStatus(); got != tt.want { t.Errorf("HTTPStatus() = %d, want %d", got, tt.want) } }) } }