trpc.group/trpc-go/trpc-go@v1.0.3/errs/errs_test.go (about)

     1  //
     2  //
     3  // Tencent is pleased to support the open source community by making tRPC available.
     4  //
     5  // Copyright (C) 2023 THL A29 Limited, a Tencent company.
     6  // All rights reserved.
     7  //
     8  // If you have downloaded a copy of the tRPC source code from Tencent,
     9  // please note that tRPC source code is licensed under the  Apache 2.0 License,
    10  // A copy of the Apache 2.0 License is included in this file.
    11  //
    12  //
    13  
    14  package errs_test
    15  
    16  import (
    17  	"bufio"
    18  	"bytes"
    19  	"errors"
    20  	"fmt"
    21  	"os"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/stretchr/testify/assert"
    26  	"github.com/stretchr/testify/require"
    27  	"trpc.group/trpc/trpc-protocol/pb/go/trpc"
    28  
    29  	"trpc.group/trpc-go/trpc-go/errs"
    30  )
    31  
    32  // go test -v -coverprofile=cover.out
    33  // go tool cover -func=cover.out
    34  
    35  func TestErrs(t *testing.T) {
    36  	var err *errs.Error
    37  	str := err.Error()
    38  	assert.Contains(t, str, "success")
    39  
    40  	e := errs.New(111, "inner fail")
    41  	assert.NotNil(t, e)
    42  
    43  	assert.EqualValues(t, 111, errs.Code(e))
    44  	assert.Equal(t, "inner fail", errs.Msg(e))
    45  
    46  	err, ok := e.(*errs.Error)
    47  	assert.Equal(t, true, ok)
    48  	assert.NotNil(t, err)
    49  	assert.Equal(t, errs.ErrorTypeBusiness, err.Type)
    50  
    51  	str = err.Error()
    52  	assert.Contains(t, str, "business")
    53  
    54  	e = errs.NewFrameError(111, "inner fail")
    55  	assert.NotNil(t, e)
    56  
    57  	assert.EqualValues(t, 111, errs.Code(e))
    58  	assert.Equal(t, "inner fail", errs.Msg(e))
    59  
    60  	err, ok = e.(*errs.Error)
    61  	assert.Equal(t, true, ok)
    62  	assert.NotNil(t, err)
    63  	assert.Equal(t, errs.ErrorTypeFramework, err.Type)
    64  
    65  	str = err.Error()
    66  	assert.Contains(t, str, "framework")
    67  
    68  	assert.EqualValues(t, 0, errs.Code(nil))
    69  	assert.Equal(t, "success", errs.Msg(nil))
    70  
    71  	assert.EqualValues(t, 0, errs.Code((*errs.Error)(nil)))
    72  	assert.Equal(t, "success", errs.Msg((*errs.Error)(nil)))
    73  
    74  	e = errors.New("unknown error")
    75  	assert.Equal(t, errs.RetUnknown, errs.Code(e))
    76  	assert.Equal(t, "unknown error", errs.Msg(e))
    77  
    78  	err.Type = errs.ErrorTypeCalleeFramework
    79  	assert.Contains(t, err.Error(), "type:callee framework")
    80  }
    81  
    82  func TestNonEmptyStringOnEmptyMsg(t *testing.T) {
    83  	e := errs.New(errs.RetServerSystemErr, "")
    84  	require.Contains(t, e.Error(), "code:")
    85  	require.Contains(t, e.Error(), "type:")
    86  }
    87  
    88  func TestErrsFormat(t *testing.T) {
    89  	err := errs.New(10000, "test error")
    90  
    91  	s := fmt.Sprintf("%s", err)
    92  	assert.Equal(t, "type:business, code:10000, msg:test error", s)
    93  
    94  	s = fmt.Sprintf("%q", err)
    95  	assert.Equal(t, `"type:business, code:10000, msg:test error"`, s)
    96  
    97  	s = fmt.Sprintf("%v", err)
    98  	assert.Equal(t, "type:business, code:10000, msg:test error", s)
    99  
   100  	s = fmt.Sprintf("%d", err)
   101  	assert.Equal(t, "%!d(errs.Error=type:business, code:10000, msg:test error)", s)
   102  }
   103  
   104  func TestNewFrameError(t *testing.T) {
   105  	ok := true
   106  	errs.SetTraceable(ok)
   107  	e := errs.NewFrameError(111, "inner fail")
   108  	assert.NotNil(t, e)
   109  }
   110  
   111  func TestWrapFrameError(t *testing.T) {
   112  	ok := true
   113  	errs.SetTraceable(ok)
   114  	e := errs.WrapFrameError(errs.New(123, "inner fail"), 456, "wrap frame error")
   115  	assert.NotNil(t, e)
   116  	e = errs.WrapFrameError(nil, 456, "wrap frame error")
   117  	assert.Nil(t, e)
   118  }
   119  
   120  func TestTraceError(t *testing.T) {
   121  
   122  	errs.SetTraceable(true)
   123  
   124  	err := parent()
   125  	assert.NotNil(t, err)
   126  
   127  	s := fmt.Sprintf("%+v", err)
   128  	br := bufio.NewReader(strings.NewReader(s))
   129  
   130  	line, isPrefix, err := br.ReadLine()
   131  	assert.Equal(t, "type:business, code:111, msg:inner fail", string(line))
   132  	assert.Equal(t, isPrefix, false)
   133  	assert.Nil(t, err)
   134  
   135  	line, isPrefix, err = br.ReadLine()
   136  	assert.Equal(t, "trpc.group/trpc-go/trpc-go/errs_test.grandson", string(line))
   137  	assert.Equal(t, isPrefix, false)
   138  	assert.Nil(t, err)
   139  
   140  	_, _, _ = br.ReadLine()
   141  	line, isPrefix, err = br.ReadLine()
   142  	assert.Equal(t, "trpc.group/trpc-go/trpc-go/errs_test.child", string(line))
   143  	assert.Equal(t, isPrefix, false)
   144  	assert.Nil(t, err)
   145  
   146  	_, _, _ = br.ReadLine()
   147  	line, isPrefix, err = br.ReadLine()
   148  	assert.Equal(t, "trpc.group/trpc-go/trpc-go/errs_test.parent", string(line))
   149  	assert.Equal(t, isPrefix, false)
   150  	assert.Nil(t, err)
   151  }
   152  
   153  func TestTraceErrorSetStackSkip(t *testing.T) {
   154  	errs.SetTraceable(true)
   155  	errs.SetStackSkip(4)
   156  
   157  	err := func() error {
   158  		return func() error {
   159  			return newMyErr(11, "TestTraceErrorSetStackSkip error")
   160  		}()
   161  	}()
   162  	assert.NotNil(t, err)
   163  
   164  	s := fmt.Sprintf("%+v", err)
   165  	br := bufio.NewReader(strings.NewReader(s))
   166  
   167  	line, isPrefix, err := br.ReadLine()
   168  	assert.Equal(t, "type:business, code:11, msg:TestTraceErrorSetStackSkip error", string(line))
   169  	assert.Equal(t, isPrefix, false)
   170  	assert.Nil(t, err)
   171  
   172  	line, isPrefix, err = br.ReadLine()
   173  	t.Log(string(line))
   174  	assert.Contains(t, string(line), "trpc.group/trpc-go/trpc-go/errs_test.TestTraceErrorSetStackSkip")
   175  	assert.Equal(t, isPrefix, false)
   176  	assert.Nil(t, err)
   177  
   178  	_, _, _ = br.ReadLine()
   179  	line, isPrefix, err = br.ReadLine()
   180  	assert.Equal(t, "trpc.group/trpc-go/trpc-go/errs_test.TestTraceErrorSetStackSkip.func1", string(line))
   181  	assert.Equal(t, isPrefix, false)
   182  	assert.Nil(t, err)
   183  
   184  	_, _, _ = br.ReadLine()
   185  	line, isPrefix, err = br.ReadLine()
   186  	assert.Equal(t, "trpc.group/trpc-go/trpc-go/errs_test.TestTraceErrorSetStackSkip", string(line))
   187  	assert.Equal(t, isPrefix, false)
   188  	assert.Nil(t, err)
   189  }
   190  
   191  func newMyErr(code int, msg string) error {
   192  	return errs.New(code, msg)
   193  }
   194  
   195  // TestSetTraceableWithContent SetTraceableWithContent interface test case,
   196  // filter and print stack information according to Content.
   197  func TestSetTraceableWithContent(t *testing.T) {
   198  	errs.SetTraceableWithContent("child")
   199  
   200  	err := parent()
   201  	assert.NotNil(t, err)
   202  
   203  	s := fmt.Sprintf("%+v", err)
   204  	br := bufio.NewReader(strings.NewReader(s))
   205  	line, isPrefix, err := br.ReadLine()
   206  	assert.Equal(t, "type:business, code:111, msg:inner fail", string(line))
   207  	assert.Equal(t, isPrefix, false)
   208  	assert.Nil(t, err)
   209  
   210  	line, isPrefix, err = br.ReadLine()
   211  	assert.Equal(t, "trpc.group/trpc-go/trpc-go/errs_test.child", string(line))
   212  	assert.Equal(t, isPrefix, false)
   213  	assert.Nil(t, err)
   214  }
   215  
   216  func TestErrorChain(t *testing.T) {
   217  	var e error = errs.Wrap(os.ErrDeadlineExceeded, int(trpc.TrpcRetCode_TRPC_CLIENT_INVOKE_TIMEOUT_ERR), "just wrap")
   218  	require.Contains(t, errs.Msg(e), os.ErrDeadlineExceeded.Error())
   219  	e = fmt.Errorf("%w", e)
   220  	require.Equal(t, trpc.TrpcRetCode_TRPC_CLIENT_INVOKE_TIMEOUT_ERR, errs.Code(e))
   221  	require.True(t, errors.Is(e, os.ErrDeadlineExceeded))
   222  	require.Contains(t, e.Error(), os.ErrDeadlineExceeded.Error())
   223  }
   224  
   225  func TestWrap(t *testing.T) {
   226  	err := parent()
   227  	assert.NotNil(t, err)
   228  
   229  	err = errs.Wrap(err, 222, "wrap err")
   230  	assert.NotNil(t, err)
   231  
   232  	s := fmt.Sprintf("%v", err)
   233  	assert.Contains(t, s, "type:business, code:222, msg:wrap err")
   234  	s = fmt.Sprintf("%s", err)
   235  	assert.Contains(t, s, "type:business, code:222, msg:wrap err")
   236  
   237  	s = fmt.Sprintf("%+v", err)
   238  	br := bufio.NewReader(strings.NewReader(s))
   239  	line, isPrefix, err := br.ReadLine()
   240  	assert.Equal(t, "type:business, code:222, msg:wrap err", string(line))
   241  	assert.Equal(t, isPrefix, false)
   242  	assert.Nil(t, err)
   243  
   244  	line, isPrefix, err = br.ReadLine()
   245  	assert.Equal(t, "Cause by type:business, code:111, msg:inner fail", string(line))
   246  	assert.Equal(t, isPrefix, false)
   247  	assert.Nil(t, err)
   248  }
   249  
   250  func TestWrapf(t *testing.T) {
   251  	err := parent()
   252  	assert.NotNil(t, err)
   253  
   254  	err = errs.Wrapf(err, 222, "wrap %v", "err")
   255  	assert.NotNil(t, err)
   256  
   257  	s := fmt.Sprintf("%+v", err)
   258  	br := bufio.NewReader(strings.NewReader(s))
   259  	line, isPrefix, err := br.ReadLine()
   260  	assert.Equal(t, "type:business, code:222, msg:wrap err", string(line))
   261  	assert.Equal(t, isPrefix, false)
   262  	assert.Nil(t, err)
   263  
   264  	line, isPrefix, err = br.ReadLine()
   265  	assert.Equal(t, "Cause by type:business, code:111, msg:inner fail", string(line))
   266  	assert.Equal(t, isPrefix, false)
   267  	assert.Nil(t, err)
   268  }
   269  
   270  func TestWrapSetTraceable(t *testing.T) {
   271  	// reset
   272  	errs.SetStackSkip(3)
   273  	errs.SetTraceableWithContent("")
   274  
   275  	err := parent()
   276  	assert.NotNil(t, err)
   277  
   278  	err = errs.Wrap(err, 222, "wrap err")
   279  	assert.NotNil(t, err)
   280  
   281  	s := fmt.Sprintf("%+v", err)
   282  	br := bufio.NewReader(strings.NewReader(s))
   283  	line, isPrefix, err := br.ReadLine()
   284  	assert.Equal(t, "type:business, code:222, msg:wrap err", string(line))
   285  	assert.Equal(t, isPrefix, false)
   286  	assert.Nil(t, err)
   287  
   288  	line, isPrefix, err = br.ReadLine()
   289  	assert.Equal(t, "Cause by type:business, code:111, msg:inner fail", string(line))
   290  	assert.Equal(t, isPrefix, false)
   291  	assert.Nil(t, err)
   292  
   293  	line, isPrefix, err = br.ReadLine()
   294  	assert.Equal(t, "trpc.group/trpc-go/trpc-go/errs_test.grandson", string(line))
   295  	assert.Equal(t, isPrefix, false)
   296  	assert.Nil(t, err)
   297  }
   298  
   299  func TestIsTimeout(t *testing.T) {
   300  	require.True(t, (&errs.Error{
   301  		Type: errs.ErrorTypeFramework,
   302  		Code: trpc.TrpcRetCode_TRPC_CLIENT_INVOKE_TIMEOUT_ERR,
   303  	}).IsTimeout(errs.ErrorTypeFramework))
   304  	require.True(t, (&errs.Error{
   305  		Type: errs.ErrorTypeCalleeFramework,
   306  		Code: trpc.TrpcRetCode_TRPC_CLIENT_INVOKE_TIMEOUT_ERR,
   307  	}).IsTimeout(errs.ErrorTypeCalleeFramework))
   308  	require.False(t, (&errs.Error{
   309  		Type: errs.ErrorTypeBusiness,
   310  		Code: trpc.TrpcRetCode_TRPC_CLIENT_INVOKE_TIMEOUT_ERR,
   311  	}).IsTimeout(errs.ErrorTypeFramework))
   312  	require.True(t, (&errs.Error{
   313  		Type: errs.ErrorTypeFramework,
   314  		Code: trpc.TrpcRetCode_TRPC_CLIENT_FULL_LINK_TIMEOUT_ERR,
   315  	}).IsTimeout(errs.ErrorTypeFramework))
   316  	require.True(t, (&errs.Error{
   317  		Type: errs.ErrorTypeFramework,
   318  		Code: trpc.TrpcRetCode_TRPC_SERVER_TIMEOUT_ERR,
   319  	}).IsTimeout(errs.ErrorTypeFramework))
   320  	require.True(t, (&errs.Error{
   321  		Type: errs.ErrorTypeFramework,
   322  		Code: trpc.TrpcRetCode_TRPC_SERVER_FULL_LINK_TIMEOUT_ERR,
   323  	}).IsTimeout(errs.ErrorTypeFramework))
   324  	require.False(t, (&errs.Error{
   325  		Type: errs.ErrorTypeFramework,
   326  		Code: errs.RetServerNoService,
   327  	}).IsTimeout(errs.ErrorTypeFramework))
   328  }
   329  
   330  func TestErrorFormatPrint(t *testing.T) {
   331  	errs.SetTraceable(false)
   332  	defer errs.SetTraceable(true)
   333  	err := errs.New(errs.ErrorTypeFramework, "")
   334  	var buf bytes.Buffer
   335  	fmt.Fprintf(&buf, "%+v", err)
   336  	require.Equal(t, "type:business, code:1, msg:", buf.String())
   337  }
   338  
   339  func TestNestErrors(t *testing.T) {
   340  	errs.SetTraceable(false)
   341  	defer errs.SetTraceable(true)
   342  	const (
   343  		code trpc.TrpcRetCode = 101
   344  		msg                   = "test error"
   345  	)
   346  	require.Equal(t, code, errs.Code(&testError{Err: errs.New(code, msg)}))
   347  	require.Equal(t, msg, errs.Msg(&testError{Err: errs.New(code, msg)}))
   348  }
   349  
   350  type testError struct {
   351  	Err error
   352  }
   353  
   354  func (te *testError) Error() string {
   355  	return te.Err.Error()
   356  }
   357  
   358  func (te *testError) Unwrap() error {
   359  	return te.Err
   360  }
   361  
   362  //go:noinline
   363  func parent() error {
   364  	if err := child(); err != nil {
   365  		return err
   366  	}
   367  	return nil
   368  }
   369  
   370  //go:noinline
   371  func child() error {
   372  	if err := grandson(); err != nil {
   373  		return err
   374  	}
   375  	return nil
   376  }
   377  
   378  //go:noinline
   379  func grandson() error {
   380  	return errs.Newf(111, "%s", "inner fail")
   381  }