backend/pkg/library/errors/errors_test.go

216 lines
7.8 KiB
Go

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)
}
})
}
}