// testErrorHelper will check one instance of each error type, // to make sure we propagate the errors properly. func testErrorHelper(t *testing.T, f *FakeQueryService, name string, ef func(context.Context) error) { errors := []*tabletserver.TabletError{ // A few generic errors tabletserver.NewTabletError(vtrpcpb.ErrorCode_BAD_INPUT, "generic error"), tabletserver.NewTabletError(vtrpcpb.ErrorCode_UNKNOWN_ERROR, "uncaught panic"), tabletserver.NewTabletError(vtrpcpb.ErrorCode_UNAUTHENTICATED, "missing caller id"), tabletserver.NewTabletError(vtrpcpb.ErrorCode_PERMISSION_DENIED, "table acl error: nil acl"), // Client will retry on this specific error tabletserver.NewTabletError(vtrpcpb.ErrorCode_QUERY_NOT_SERVED, "Query disallowed due to rule: %v", "cool rule"), // Client may retry on another server on this specific error tabletserver.NewTabletError(vtrpcpb.ErrorCode_INTERNAL_ERROR, "Could not verify strict mode"), // This is usually transaction pool full tabletserver.NewTabletError(vtrpcpb.ErrorCode_RESOURCE_EXHAUSTED, "Transaction pool connection limit exceeded"), // Transaction expired or was unknown tabletserver.NewTabletError(vtrpcpb.ErrorCode_NOT_IN_TX, "Transaction 12"), } for _, e := range errors { f.TabletError = e ctx := context.Background() err := ef(ctx) if err == nil { t.Errorf("error wasn't returned for %v?", name) continue } // First we check the recoverable vtrpc code is right. code := vterrors.RecoverVtErrorCode(err) if code != e.ErrorCode { t.Errorf("unexpected server code from %v: got %v, wanted %v", name, code, e.ErrorCode) } // Double-check we always get a ServerError, although // we don't really care that much. if !f.TestingGateway { if _, ok := err.(*tabletconn.ServerError); !ok { t.Errorf("error wasn't a tabletconn.ServerError for %v?", name) continue } } // and last we check we preserve the text, with the right prefix if !strings.Contains(err.Error(), e.Prefix()+e.Message) { t.Errorf("client error message '%v' for %v doesn't contain expected server text message '%v'", err.Error(), name, e.Prefix()+e.Message) } } f.TabletError = nil }
streamExecutePanicsEarly bool panicWait chan struct{} errorWait chan struct{} // if set, we will also check Target, ImmediateCallerId and EffectiveCallerId checkExtraFields bool } // HandlePanic is part of the queryservice.QueryService interface func (f *FakeQueryService) HandlePanic(err *error) { if x := recover(); x != nil { *err = fmt.Errorf("caught test panic: %v", x) } } var testTabletError = tabletserver.NewTabletError(tabletserver.ErrFail, "generic error") const expectedErrMatch string = "error: generic error" // Verifies the returned error has the properties that we expect. func verifyError(t *testing.T, err error, method string) { if err == nil { t.Errorf("%s was expecting an error, didn't get one", method) return } if se, ok := err.(*tabletconn.ServerError); ok { if se.Code != tabletconn.ERR_NORMAL { t.Errorf("Unexpected error code from %s: got %v, wanted %v", method, se.Code, tabletconn.ERR_NORMAL) } } else { t.Errorf("Unexpected error type from %s: got %v, wanted tabletconn.ServerError", method, reflect.TypeOf(err))
// if set, we will also check Target, ImmediateCallerId and EffectiveCallerId checkExtraFields bool } // HandlePanic is part of the queryservice.QueryService interface func (f *FakeQueryService) HandlePanic(err *error) { if x := recover(); x != nil { *err = fmt.Errorf("caught test panic: %v", x) } } const expectedErrMatch string = "error: generic error" const expectedCode vtrpc.ErrorCode = vtrpc.ErrorCode_BAD_INPUT var testTabletError = tabletserver.NewTabletError(tabletserver.ErrFail, expectedCode, "generic error") // Verifies the returned error has the properties that we expect. func verifyError(t *testing.T, err error, method string) { if err == nil { t.Errorf("%s was expecting an error, didn't get one", method) return } code := vterrors.RecoverVtErrorCode(err) if code != expectedCode { t.Errorf("Unexpected server code from %s: got %v, wanted %v", method, code, expectedCode) } verifyErrorExceptServerCode(t, err, method) } func verifyErrorExceptServerCode(t *testing.T, err error, method string) {
streamExecutePanicsEarly bool panicWait chan struct{} errorWait chan struct{} // if set, we will also check Target, ImmediateCallerId and EffectiveCallerId checkExtraFields bool } // HandlePanic is part of the queryservice.QueryService interface func (f *FakeQueryService) HandlePanic(err *error) { if x := recover(); x != nil { *err = fmt.Errorf("caught test panic: %v", x) } } var testTabletError = tabletserver.NewTabletError(tabletserver.ErrFail, pbv.ErrorCode_UNKNOWN_ERROR, "generic error") const expectedErrMatch string = "error: generic error" // Verifies the returned error has the properties that we expect. func verifyError(t *testing.T, err error, method string) { if err == nil { t.Errorf("%s was expecting an error, didn't get one", method) return } if se, ok := err.(*tabletconn.ServerError); ok { if se.Code != tabletconn.ERR_NORMAL { t.Errorf("Unexpected error code from %s: got %v, wanted %v", method, se.Code, tabletconn.ERR_NORMAL) } } else { t.Errorf("Unexpected error type from %s: got %v, wanted tabletconn.ServerError", method, reflect.TypeOf(err))