From 655e5756af3ac33d652f618dd0139a0b8ae90dbf Mon Sep 17 00:00:00 2001 From: Ukane philemon Date: Tue, 15 Mar 2022 11:20:07 +0100 Subject: [PATCH] Using error.Is and error.As * This allow using error.Is and error.As within vsp. * Add test cases for apiError type. --- webapi/errors.go | 11 +++++- webapi/errors_test.go | 90 +++++++++++++++++++++++++++++++++++++++++++ webapi/webapi.go | 2 +- 3 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 webapi/errors_test.go diff --git a/webapi/errors.go b/webapi/errors.go index c3aaf32..f932ef1 100644 --- a/webapi/errors.go +++ b/webapi/errors.go @@ -6,6 +6,8 @@ package webapi import "net/http" +// apiError is a kind of error. It has full support for errors.Is and +// errors.As. type apiError int const ( @@ -73,8 +75,8 @@ func (e apiError) httpStatus() int { } } -// defaultMessage returns a descriptive error string for a given error code. -func (e apiError) defaultMessage() string { +// String returns a descriptive error string for a given error code. +func (e apiError) String() string { switch e { case errBadRequest: return "bad request" @@ -116,3 +118,8 @@ func (e apiError) defaultMessage() string { return "unknown error" } } + +// Error satisfies the error interface and returns a human-readable error string. +func (e apiError) Error() string { + return e.String() +} diff --git a/webapi/errors_test.go b/webapi/errors_test.go new file mode 100644 index 0000000..9cf22f1 --- /dev/null +++ b/webapi/errors_test.go @@ -0,0 +1,90 @@ +// Copyright (c) 2022 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package webapi + +import ( + "errors" + "testing" +) + +// TestApiErrorString tests the stringized output for the apiError type. +func TestApiErrorString(t *testing.T) { + tests := []struct { + in apiError + want string + }{ + {errBadRequest, "bad request"}, + {errInternalError, "internal error"}, + {errVspClosed, "vsp is closed"}, + {errFeeAlreadyReceived, "fee tx already received for ticket"}, + {errInvalidFeeTx, "invalid fee tx"}, + {errFeeTooSmall, "fee too small"}, + {errUnknownTicket, "unknown ticket"}, + {errTicketCannotVote, "ticket not eligible to vote"}, + {errFeeExpired, "fee has expired"}, + {errInvalidVoteChoices, "invalid vote choices"}, + {errBadSignature, "bad request signature"}, + {errInvalidPrivKey, "invalid private key"}, + {errFeeNotReceived, "no fee tx received for ticket"}, + {errInvalidTicket, "not a valid ticket tx"}, + {errCannotBroadcastTicket, "ticket transaction could not be broadcast"}, + {errCannotBroadcastFee, "fee transaction could not be broadcast"}, + {errCannotBroadcastFeeUnknownOutputs, "fee transaction could not be broadcast due to unknown outputs"}, + {errInvalidTimestamp, "old or reused timestamp"}, + } + + for i, test := range tests { + result := test.in.Error() + if result != test.want { + t.Errorf("%d: got: %s want: %s", i, result, test.want) + continue + } + } +} + +// TestApiErrorIsAs ensures apiError can be identified via errors.Is and unwrapped via errors.As. +func TestApiErrorIsAs(t *testing.T) { + tests := []struct { + name string + err error + target error + wantMatch bool + wantAs apiError + }{{ + name: "errBadRequest == errBadRequest", + err: errBadRequest, + target: errBadRequest, + wantMatch: true, + wantAs: errBadRequest, + }, { + name: "errBadRequest != errInternalError", + err: errBadRequest, + target: errInternalError, + wantMatch: false, + wantAs: errBadRequest, + }} + for _, test := range tests { + // Ensure the error matches or not depending on the expected result. + result := errors.Is(test.err, test.target) + if result != test.wantMatch { + t.Errorf("%s: incorrect error identification -- got %v, want %v", + test.name, result, test.wantMatch) + continue + } + + // Ensure the underlying apiError can be unwrapped and is the + // expected type. + var err apiError + if !errors.As(test.err, &err) { + t.Errorf("%s: unable to unwrap error", test.name) + continue + } + if err != test.wantAs { + t.Errorf("%s: unexpected unwrapped error -- got %v, want %v", + test.name, err, test.wantAs) + continue + } + } +} diff --git a/webapi/webapi.go b/webapi/webapi.go index c5083b9..92d6e61 100644 --- a/webapi/webapi.go +++ b/webapi/webapi.go @@ -285,7 +285,7 @@ func sendJSONResponse(resp interface{}, c *gin.Context) (string, string) { // sendError sends an error response to the client using the default error // message. func sendError(e apiError, c *gin.Context) { - msg := e.defaultMessage() + msg := e.Error() sendErrorWithMsg(msg, e, c) }