3f7f956f80
To bring ErrorCode into liine with Go conventions, ErrorCode.Error() now returns the "nice" value of the error code. This ensures error message assembly works similar to commonly used Go conventions when directly using ErrorCode as an error. Signed-off-by: Stephen J Day <stephen.day@docker.com>
186 lines
5.6 KiB
Go
186 lines
5.6 KiB
Go
package errcode
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestErrorsManagement does a quick check of the Errors type to ensure that
|
|
// members are properly pushed and marshaled.
|
|
var ErrorCodeTest1 = Register("test.errors", ErrorDescriptor{
|
|
Value: "TEST1",
|
|
Message: "test error 1",
|
|
Description: `Just a test message #1.`,
|
|
HTTPStatusCode: http.StatusInternalServerError,
|
|
})
|
|
|
|
var ErrorCodeTest2 = Register("test.errors", ErrorDescriptor{
|
|
Value: "TEST2",
|
|
Message: "test error 2",
|
|
Description: `Just a test message #2.`,
|
|
HTTPStatusCode: http.StatusNotFound,
|
|
})
|
|
|
|
var ErrorCodeTest3 = Register("test.errors", ErrorDescriptor{
|
|
Value: "TEST3",
|
|
Message: "Sorry %q isn't valid",
|
|
Description: `Just a test message #3.`,
|
|
HTTPStatusCode: http.StatusNotFound,
|
|
})
|
|
|
|
// TestErrorCodes ensures that error code format, mappings and
|
|
// marshaling/unmarshaling. round trips are stable.
|
|
func TestErrorCodes(t *testing.T) {
|
|
if len(errorCodeToDescriptors) == 0 {
|
|
t.Fatal("errors aren't loaded!")
|
|
}
|
|
|
|
for ec, desc := range errorCodeToDescriptors {
|
|
if ec != desc.Code {
|
|
t.Fatalf("error code in descriptor isn't correct, %q != %q", ec, desc.Code)
|
|
}
|
|
|
|
if idToDescriptors[desc.Value].Code != ec {
|
|
t.Fatalf("error code in idToDesc isn't correct, %q != %q", idToDescriptors[desc.Value].Code, ec)
|
|
}
|
|
|
|
if ec.Message() != desc.Message {
|
|
t.Fatalf("ec.Message doesn't mtach desc.Message: %q != %q", ec.Message(), desc.Message)
|
|
}
|
|
|
|
// Test (de)serializing the ErrorCode
|
|
p, err := json.Marshal(ec)
|
|
if err != nil {
|
|
t.Fatalf("couldn't marshal ec %v: %v", ec, err)
|
|
}
|
|
|
|
if len(p) <= 0 {
|
|
t.Fatalf("expected content in marshaled before for error code %v", ec)
|
|
}
|
|
|
|
// First, unmarshal to interface and ensure we have a string.
|
|
var ecUnspecified interface{}
|
|
if err := json.Unmarshal(p, &ecUnspecified); err != nil {
|
|
t.Fatalf("error unmarshaling error code %v: %v", ec, err)
|
|
}
|
|
|
|
if _, ok := ecUnspecified.(string); !ok {
|
|
t.Fatalf("expected a string for error code %v on unmarshal got a %T", ec, ecUnspecified)
|
|
}
|
|
|
|
// Now, unmarshal with the error code type and ensure they are equal
|
|
var ecUnmarshaled ErrorCode
|
|
if err := json.Unmarshal(p, &ecUnmarshaled); err != nil {
|
|
t.Fatalf("error unmarshaling error code %v: %v", ec, err)
|
|
}
|
|
|
|
if ecUnmarshaled != ec {
|
|
t.Fatalf("unexpected error code during error code marshal/unmarshal: %v != %v", ecUnmarshaled, ec)
|
|
}
|
|
|
|
expectedErrorString := strings.ToLower(strings.Replace(ec.Descriptor().Value, "_", " ", -1))
|
|
if ec.Error() != expectedErrorString {
|
|
t.Fatalf("unexpected return from %v.Error(): %q != %q", ec, ec.Error(), expectedErrorString)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
func TestErrorsManagement(t *testing.T) {
|
|
var errs Errors
|
|
|
|
errs = append(errs, ErrorCodeTest1)
|
|
errs = append(errs, ErrorCodeTest2.WithDetail(
|
|
map[string]interface{}{"digest": "sometestblobsumdoesntmatter"}))
|
|
errs = append(errs, ErrorCodeTest3.WithArgs("BOOGIE"))
|
|
errs = append(errs, ErrorCodeTest3.WithArgs("BOOGIE").WithDetail("data"))
|
|
|
|
p, err := json.Marshal(errs)
|
|
|
|
if err != nil {
|
|
t.Fatalf("error marashaling errors: %v", err)
|
|
}
|
|
|
|
expectedJSON := `{"errors":[` +
|
|
`{"code":"TEST1","message":"test error 1"},` +
|
|
`{"code":"TEST2","message":"test error 2","detail":{"digest":"sometestblobsumdoesntmatter"}},` +
|
|
`{"code":"TEST3","message":"Sorry \"BOOGIE\" isn't valid"},` +
|
|
`{"code":"TEST3","message":"Sorry \"BOOGIE\" isn't valid","detail":"data"}` +
|
|
`]}`
|
|
|
|
if string(p) != expectedJSON {
|
|
t.Fatalf("unexpected json:\ngot:\n%q\n\nexpected:\n%q", string(p), expectedJSON)
|
|
}
|
|
|
|
// Now test the reverse
|
|
var unmarshaled Errors
|
|
if err := json.Unmarshal(p, &unmarshaled); err != nil {
|
|
t.Fatalf("unexpected error unmarshaling error envelope: %v", err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(unmarshaled, errs) {
|
|
t.Fatalf("errors not equal after round trip:\nunmarshaled:\n%#v\n\nerrs:\n%#v", unmarshaled, errs)
|
|
}
|
|
|
|
// Test the arg substitution stuff
|
|
e1 := unmarshaled[3].(Error)
|
|
exp1 := `Sorry "BOOGIE" isn't valid`
|
|
if e1.Message != exp1 {
|
|
t.Fatalf("Wrong msg, got:\n%q\n\nexpected:\n%q", e1.Message, exp1)
|
|
}
|
|
|
|
exp1 = "test3: " + exp1
|
|
if e1.Error() != exp1 {
|
|
t.Fatalf("Error() didn't return the right string, got:%s\nexpected:%s", e1.Error(), exp1)
|
|
}
|
|
|
|
// Test again with a single value this time
|
|
errs = Errors{ErrorCodeUnknown}
|
|
expectedJSON = "{\"errors\":[{\"code\":\"UNKNOWN\",\"message\":\"unknown error\"}]}"
|
|
p, err = json.Marshal(errs)
|
|
|
|
if err != nil {
|
|
t.Fatalf("error marashaling errors: %v", err)
|
|
}
|
|
|
|
if string(p) != expectedJSON {
|
|
t.Fatalf("unexpected json: %q != %q", string(p), expectedJSON)
|
|
}
|
|
|
|
// Now test the reverse
|
|
unmarshaled = nil
|
|
if err := json.Unmarshal(p, &unmarshaled); err != nil {
|
|
t.Fatalf("unexpected error unmarshaling error envelope: %v", err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(unmarshaled, errs) {
|
|
t.Fatalf("errors not equal after round trip:\nunmarshaled:\n%#v\n\nerrs:\n%#v", unmarshaled, errs)
|
|
}
|
|
|
|
// Verify that calling WithArgs() more than once does the right thing.
|
|
// Meaning creates a new Error and uses the ErrorCode Message
|
|
e1 = ErrorCodeTest3.WithArgs("test1")
|
|
e2 := e1.WithArgs("test2")
|
|
if &e1 == &e2 {
|
|
t.Fatalf("args: e2 and e1 should not be the same, but they are")
|
|
}
|
|
if e2.Message != `Sorry "test2" isn't valid` {
|
|
t.Fatalf("e2 had wrong message: %q", e2.Message)
|
|
}
|
|
|
|
// Verify that calling WithDetail() more than once does the right thing.
|
|
// Meaning creates a new Error and overwrites the old detail field
|
|
e1 = ErrorCodeTest3.WithDetail("stuff1")
|
|
e2 = e1.WithDetail("stuff2")
|
|
if &e1 == &e2 {
|
|
t.Fatalf("detail: e2 and e1 should not be the same, but they are")
|
|
}
|
|
if e2.Detail != `stuff2` {
|
|
t.Fatalf("e2 had wrong detail: %q", e2.Detail)
|
|
}
|
|
|
|
}
|