func TestFail(t *testing.T) { Convey("Standard error", t, func() { err := errors.New("Error 1 occurred.") Convey("should have no inner", func() { So(fail.GetInner(err), ShouldBeNil) }) Convey("should have no location", func() { So(fail.GetLocation(err), ShouldBeEmpty) }) Convey("should have no stack trace", func() { So(fail.GetStackTrace(err), ShouldBeEmpty) }) Convey("GetOriginalError() should return itself", func() { So(fail.GetOriginalError(err), ShouldEqual, err) }) Convey("GetType() should return type of itself", func() { So(fail.GetType(err), ShouldEqual, reflect.TypeOf(err)) }) }) Convey("Simple extended error", t, func() { errInner := errors.New("Error 1 occurred.") err := fail.New(errInner) Convey("should have correct message", func() { So(err.Error(), ShouldEqual, "Error 1 occurred.") }) Convey("should have no inner", func() { So(fail.GetInner(err), ShouldBeNil) }) Convey("should have correct location", func() { So(fail.GetLocation(err), ShouldContainSubstring, "github.com/nbgo/fail/fail_test.go:60") So(fail.GetLocation(err), ShouldContainSubstring, "TestFail.") }) Convey("should have correct stack trace", func() { stackTrace := fail.GetStackTrace(err) So(stackTrace, ShouldContainSubstring, "TestFail.") So(stackTrace, ShouldContainSubstring, "fail_test.go:60") So(stackTrace, ShouldContainSubstring, "fail_test.go:77") }) }) Convey("Composite extended error", t, func() { err1 := fail.Newf("Error %v occurred.", 1) err2 := fail.News("Error 2 occurred.") err1c := fail.NewWithInner(err2, err1) err2c := fail.New(&MyError{"Error 3 occured", err1c}) Convey("stack trace 1 check", func() { So(strings.Split(fail.GetStackTrace(err2c), "\n")[0], ShouldContainSubstring, "fail_test.go") }) Convey("stack trace 2 check", func() { So(strings.Split(fail.GetStackTrace(err1c), "\n")[0], ShouldContainSubstring, "fail_test.go") }) Convey("stack trace 3 check", func() { So(strings.Split(fail.GetStackTrace(err2), "\n")[0], ShouldContainSubstring, "fail_test.go") }) Convey("stack trace 4 check", func() { So(strings.Split(fail.GetStackTrace(err1), "\n")[0], ShouldContainSubstring, "fail_test.go") }) Convey("should have correct full details", func() { details := fail.GetFullDetails(err2c) fmt.Println("\n" + details) So(details, ShouldNotContainSubstring, "fail.extendedError") }) Convey("should have correct message", func() { So(err2c.Error(), ShouldEqual, "MyError: Error 3 occured. Reason: Error 2 occurred.") }) Convey("should have correct inner 1 level error", func() { inner := fail.GetInner(err2c) So(inner, ShouldNotBeNil) So(inner.Error(), ShouldEqual, "Error 2 occurred.") Convey("should have correct inner 2 level error", func() { inner2 := fail.GetInner(inner) So(inner2, ShouldNotBeNil) So(inner2.Error(), ShouldEqual, "Error 1 occurred.") }) }) Convey("GetOriginalError() should return correct error", func() { So(fail.GetOriginalError(err1c), ShouldEqual, fail.GetOriginalError(err2)) }) }) Convey("ErrWithReason", t, func() { innerErr := fail.News("inner error") err := fail.NewErrWithReason("some error", innerErr) Convey("should have its own text and reason", func() { So(err.Error(), ShouldEqual, "some error: inner error") }) Convey("should return correct inner error", func() { gotInnerErr := fail.GetInner(err) So(gotInnerErr, ShouldNotBeNil) So(gotInnerErr.Error(), ShouldEqual, "inner error") So(gotInnerErr, ShouldEqual, innerErr) }) Convey("should have correct stack trace", func() { So(strings.Split(fail.GetStackTrace(err), "\n")[0], ShouldContainSubstring, "fail_test.go") }) }) Convey("StackTrace()", t, func() { stackTrace := fail.StackTrace() fmt.Println("\n" + stackTrace) So(strings.Split(stackTrace, "\n")[0], ShouldContainSubstring, "fail_test.go") func() { stackTrace := fail.StackTrace(1) fmt.Println("\n" + stackTrace) So(strings.Split(stackTrace, "\n")[0], ShouldContainSubstring, "fail_test.go") }() }) Convey("IsError()", t, func() { innerErr := errors.New("test1") err2 := fail.NewErrWithReason("test2", innerErr) err3 := fail.NewErrWithReason("test3", err2) err4 := fail.News("test4") Convey("should return true when composite error has checked error in its hierarchy", func() { So(fail.IsError(err3, innerErr), ShouldBeTrue) }) Convey("should return true when checked error is itself", func() { So(fail.IsError(innerErr, innerErr), ShouldBeTrue) }) Convey("should return false when composite error does not have checked error in its hierarchy", func() { So(fail.IsError(err4, innerErr), ShouldBeFalse) }) }) Convey("AreErrorsOfEqualType()", t, func() { err1 := &MyError{} err2 := &MyError{} err3 := MyError{} err4 := fail.News("test") So(fail.AreErrorsOfEqualType(err1, err2), ShouldBeTrue) So(fail.AreErrorsOfEqualType(err1, err3), ShouldBeTrue) So(fail.AreErrorsOfEqualType(err3, err2), ShouldBeTrue) So(fail.AreErrorsOfEqualType(err1, err4), ShouldBeFalse) So(fail.AreErrorsOfEqualType(err3, err4), ShouldBeFalse) }) Convey("GetErrorByType()", t, func() { innerErr := &MyError{} err2 := fail.NewErrWithReason("test2", innerErr) err3 := fail.NewErrWithReason("test3", err2) err4 := fail.News("test4") So(fail.GetErrorByType(err3, MyError{}), ShouldEqual, innerErr) So(fail.GetErrorByType(err4, MyError{}), ShouldBeNil) }) Convey("ErrWithFields", t, func() { err := fail.New(fail.New(MyErrWithFields{"p1", "p2"})) So(err.Error(), ShouldEqual, "This is an error with fields: p1, p2") errWithFields := err.(fail.ErrorWithFields) fields := errWithFields.Fields() So(fields, ShouldNotBeNil) So(errWithFields.Fields(), ShouldResemble, map[string]interface{}{"param1": "p1", "param2": "p2"}) err = fail.News("test") errWithFields = err.(fail.ErrorWithFields) fields = errWithFields.Fields() So(fields, ShouldBeNil) }) }
func (provider *DirectServiceProvider) processRequests(c context.Context, r *http.Request, reqs []*request) []*response { resps := make([]*response, len(reqs)) respsChannel := make(chan *response, len(reqs)) for _, req := range reqs { go func(req *request) { resp := &response{ Tid: req.Tid, Action: req.Action, Method: req.Method, Type: req.Type, } var tStart time.Time profilingStarted := false logProfiling := func() { if profilingStarted { duration := time.Now().Sub(tStart) log.Print(logLevelInfo, fmt.Sprintf("%s.%s() %v ", req.Action, req.Method, duration), map[string]interface{}{"duration": duration, "action": req.Action, "method": req.Method}) profilingStarted = false } } defer func() { logProfiling() if err := recover(); err != nil { log.Print(fail.New(ErrDirectActionMethod{req.Action, req.Method, err, true})) resp.Type = "exception" respMessage := fmt.Sprintf("%v", err) resp.Message = &respMessage } respsChannel <- resp }() // Create instance of action type actionInfo := provider.actionsInfo[req.Action] if provider.debug { log.Print(fmt.Sprintf("Create instance of action %s (type %v)", req.Action, actionInfo.Type)) } actionVal := reflect.New(actionInfo.Type).Elem() // Set context and request if c != nil || r != nil { if provider.debug { log.Print("Set action context/request.") } contextType := reflect.TypeOf((*context.Context)(nil)).Elem() requestType := reflect.TypeOf(&http.Request{}) fieldsLen := actionInfo.Type.NumField() for i := 0; i < fieldsLen; i++ { t := actionInfo.Type.Field(i).Type if t.Implements(contextType) { if c != nil { if provider.debug { log.Print("Set action context.") } actionVal.Field(i).Set(reflect.ValueOf(c)) } else { if provider.debug { log.Print(logLevelWarn, "Context cannot be set to action instance because context is nil.") } } } if t == requestType { if r != nil { if provider.debug { log.Print("Set action request.") } actionVal.Field(i).Set(reflect.ValueOf(r)) } } } } if provider.debug { log.Print(fmt.Sprintf("Prepare arguments for method %s.%s", req.Action, req.Method)) } methodInfo := actionInfo.Methods[req.Method] directMethod := actionInfo.DirectMethods[req.Method] isFormHandler := false if directMethod.FormHandler != nil { isFormHandler = *directMethod.FormHandler } if provider.debug { log.Print(fmt.Sprintf("Direct method to use: %s, formhandler=%v", directMethod.Name, isFormHandler)) } methodArgsLen := methodInfo.Type.NumIn() - 1 var args []reflect.Value if (req.Data != nil && !isFormHandler) || (req.FormData != nil && isFormHandler) { if isFormHandler { if provider.debug { log.Print("Prepare arguments for form handler call.") } args = make([]reflect.Value, 1) args[0] = reflect.ValueOf(req.FormData) // TODO: Support structure type argument for form handler. } else { args = make([]reflect.Value, methodArgsLen) var argsArray []json.RawMessage if err := json.Unmarshal(req.Data, &argsArray); err != nil { panic(fail.NewErrWithReason("could not parse request data", err)) } for i, arg := range argsArray { methodArgType := methodInfo.Type.In(i + 1) if provider.debug { log.Print(fmt.Sprintf("Parse `%v` into %v", string(arg), methodArgType)) } argValue := reflect.New(methodArgType).Elem() argRef := argValue.Addr().Interface() json.Unmarshal(arg, argRef) args[i] = reflect.ValueOf(argValue.Interface()) } } } if provider.profile { profilingStarted = true tStart = time.Now() } if provider.debug { log.Print(fmt.Sprintf("Call method %s.%s", req.Action, req.Method)) } // Call action method. resultsValues := actionVal.MethodByName(methodInfo.Name).Call(args) logProfiling() for i, resultValue := range resultsValues { if methodInfo.Type.Out(i).Name() == "error" { if err, isErr := resultValue.Interface().(error); isErr { log.Print(&ErrDirectActionMethod{req.Action, req.Method, err, false}) resp.Type = "exception" respMessage := fmt.Sprintf("%v", err) resp.Message = &respMessage resp.Result = nil break } } else { result := resultValue.Interface() resp.Result = result } } }(req) } for i := 0; i < len(reqs); i++ { var resp = <-respsChannel resps[i] = resp } return resps }