package error import ( "errors" "fmt" "github.com/stretchr/testify/assert" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "member/internal/lib/error/code" "net/http" "testing" ) func TestCode_GivenNilReceiver_CodeReturnOK_CodeStrReturns00000(t *testing.T) { // setup var e *Err = nil // act & assert assert.Equal(t, code.OK, e.Code()) assert.Equal(t, "00000", e.CodeStr()) assert.Equal(t, "", e.Error()) } func TestCode_GivenScope99DetailCode6687_ShouldReturn996687(t *testing.T) { // setup e := Err{scope: 99, code: 6687} // act & assert assert.Equal(t, uint32(6687), e.Code()) assert.Equal(t, "996687", e.CodeStr()) } func TestCode_GivenScope0DetailCode87_ShouldReturn87(t *testing.T) { // setup e := Err{scope: 0, code: 87} // act & assert assert.Equal(t, uint32(87), e.Code()) assert.Equal(t, "00087", e.CodeStr()) } func TestFromCode_Given870005_ShouldHasScope87_Cat0_Detail5(t *testing.T) { // setup e := FromCode(870005) // assert assert.Equal(t, uint32(87), e.Scope()) assert.Equal(t, uint32(0), e.Category()) assert.Equal(t, uint32(5), e.Code()) assert.Equal(t, "", e.Error()) } func TestFromCode_Given0_ShouldHasScope0_Cat0_Detail0(t *testing.T) { // setup e := FromCode(0) // assert assert.Equal(t, uint32(0), e.Scope()) assert.Equal(t, uint32(0), e.Category()) assert.Equal(t, uint32(0), e.Code()) assert.Equal(t, "", e.Error()) } func TestFromCode_Given9105_ShouldHasScope0_Cat9100_Detail9105(t *testing.T) { // setup e := FromCode(9105) // assert assert.Equal(t, uint32(0), e.Scope()) assert.Equal(t, uint32(9100), e.Category()) assert.Equal(t, uint32(9105), e.Code()) assert.Equal(t, "", e.Error()) } func TestErr_ShouldImplementErrorFunction(t *testing.T) { // setup a func return error f := func() error { return InvalidFormat("fake field") } // act err := f() // assert assert.NotNil(t, err) assert.Contains(t, fmt.Sprint(err), "fake field") // can be printed } func TestGeneralError_GivenNilErr_ShouldReturnEmptyString(t *testing.T) { // setup var e *Err = nil // act & assert assert.Equal(t, "", e.GeneralError()) } func TestGeneralError_GivenNotExistCat_ShouldReturnEmptyString(t *testing.T) { // setup e := Err{category: 123456} // act & assert assert.Equal(t, "", e.GeneralError()) } func TestGeneralError_GivenCatDB_ShouldReturnDBError(t *testing.T) { // setup e := Err{category: code.CatDB} catErrStr := code.CatToStr[code.CatDB] // act & assert assert.Equal(t, catErrStr, e.GeneralError()) } func TestError_GivenEmptyMsg_ShouldReturnCatGeneralErrorMessage(t *testing.T) { // setup e := Err{category: code.CatDB, msg: ""} // act errMsg := e.Error() // assert assert.Equal(t, code.CatToStr[code.CatDB], errMsg) } func TestError_GivenMsg_ShouldReturnGiveMsg(t *testing.T) { // setup e := Err{msg: "FAKE"} // act errMsg := e.Error() // assert assert.Equal(t, "FAKE", errMsg) } func TestIs_GivenNilErr_ShouldReturnFalse(t *testing.T) { var nilErrs *Err // act result := errors.Is(nilErrs, DBError()) result2 := errors.Is(DBError(), nilErrs) // assert assert.False(t, result) assert.False(t, result2) } func TestIs_GivenNil_ShouldReturnFalse(t *testing.T) { // act result := errors.Is(nil, DBError()) result2 := errors.Is(DBError(), nil) // assert assert.False(t, result) assert.False(t, result2) } func TestIs_GivenNilReceiver_ShouldReturnCorrectResult(t *testing.T) { var nilErr *Err = nil // test 1: nilErr != DBError var dbErr error = DBError("fake db error") assert.False(t, nilErr.Is(dbErr)) // test 2: nilErr != nil error var nilError error assert.False(t, nilErr.Is(nilError)) // test 3: nilErr == another nilErr var nilErr2 *Err = nil assert.True(t, nilErr.Is(nilErr2)) } func TestIs_GivenDBError_ShouldReturnTrue(t *testing.T) { // setup dbErr := DBError("fake db error") // act result := errors.Is(dbErr, DBError("not care")) result2 := errors.Is(DBError(), dbErr) // assert assert.True(t, result) assert.True(t, result2) } func TestIs_GivenDBErrorAssignToErrorType_ShouldReturnTrue(t *testing.T) { // setup var dbErr error = DBError("fake db error") // act result := errors.Is(dbErr, DBError("not care")) result2 := errors.Is(DBError(), dbErr) // assert assert.True(t, result) assert.True(t, result2) } func TestWrap_GivenNilErr_ShouldNoPanic(t *testing.T) { // act & assert assert.NotPanics(t, func() { var e *Err = nil _ = e.Wrap(fmt.Errorf("test")) }) } func TestWrap_GivenErrorToWrap_ShouldReturnErrorWithWrappedError(t *testing.T) { // act & assert wrappedErr := fmt.Errorf("test") wrappingErr := SystemInternalError("WrappingError").Wrap(wrappedErr) unWrappedErr := wrappingErr.Unwrap() assert.Equal(t, wrappedErr, unWrappedErr) } func TestUnwrap_GivenNilErr_ShouldReturnNil(t *testing.T) { var e *Err = nil internalErr := e.Unwrap() assert.Nil(t, internalErr) } func TestErrorsIs_GivenNilErr_ShouldReturnFalse(t *testing.T) { var e *Err = nil assert.False(t, errors.Is(e, fmt.Errorf("test"))) } func TestErrorsAs_GivenNilErr_ShouldReturnFalse(t *testing.T) { var internalErr *testErr var e *Err = nil assert.False(t, errors.As(e, &internalErr)) } func TestGRPCStatus(t *testing.T) { // setup table driven tests tests := []struct { name string given *Err expect *status.Status expectConvert error }{ { "nil errs.Err", nil, status.New(codes.OK, ""), nil, }, { "InvalidFormat Err", InvalidFormat("fake"), status.New(codes.Code(101), "invalid format: fake"), status.New(codes.Code(101), "invalid format: fake").Err(), }, } // act & assert for _, test := range tests { t.Run(test.name, func(t *testing.T) { s := test.given.GRPCStatus() assert.Equal(t, test.expect.Code(), s.Code()) assert.Equal(t, test.expect.Message(), s.Message()) assert.Equal(t, test.expectConvert, status.Convert(test.given).Err()) }) } } func TestErr_HTTPStatus(t *testing.T) { tests := []struct { name string err *Err want int }{ {name: "nil error", err: nil, want: http.StatusOK}, {name: "invalid measurement id", err: &Err{category: code.CatResource, code: code.InvalidMeasurementID}, want: http.StatusInternalServerError}, {name: "resource already exists", err: &Err{category: code.CatResource, code: code.ResourceAlreadyExist}, want: http.StatusConflict}, {name: "invalid resource state", err: &Err{category: code.CatResource, code: code.InvalidResourceState}, want: http.StatusConflict}, {name: "invalid posix time", err: &Err{category: code.CatAuth, code: code.InvalidPosixTime}, want: http.StatusForbidden}, {name: "unauthorized", err: &Err{category: code.CatAuth, code: code.Unauthorized}, want: http.StatusUnauthorized}, {name: "db error", err: &Err{category: code.CatDB, code: code.DBError}, want: http.StatusInternalServerError}, {name: "insufficient permission", err: &Err{category: code.CatResource, code: code.InsufficientPermission}, want: http.StatusUnauthorized}, {name: "resource insufficient", err: &Err{category: code.CatResource, code: code.ResourceInsufficient}, want: http.StatusBadRequest}, {name: "invalid format", err: &Err{category: code.CatInput, code: code.InvalidFormat}, want: http.StatusBadRequest}, {name: "resource not found", err: &Err{code: code.ResourceNotFound}, want: http.StatusNotFound}, {name: "ok", err: &Err{code: code.OK}, want: http.StatusOK}, {name: "not valid implementation", err: &Err{category: code.CatInput, code: code.NotValidImplementation}, want: http.StatusNotImplemented}, {name: "forbidden", err: &Err{category: code.CatAuth, code: code.Forbidden}, want: http.StatusForbidden}, {name: "insufficient quota", err: &Err{category: code.CatResource, code: code.InsufficientQuota}, want: http.StatusPaymentRequired}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // act got := tt.err.HTTPStatus() // assert assert.Equal(t, tt.want, got) }) } }