github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/pgwire/pgerror/internal_errors_test.go (about)

     1  // Copyright 2019 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package pgerror
    12  
    13  import (
    14  	"fmt"
    15  	"regexp"
    16  	"strings"
    17  	"testing"
    18  
    19  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
    20  	"github.com/cockroachdb/errors"
    21  	"github.com/kr/pretty"
    22  )
    23  
    24  // f formats an error.
    25  func f(format string, e *Error) string {
    26  	return fmt.Sprintf(format, e)
    27  }
    28  
    29  // m checks a match against a single regexp.
    30  func m(t *testing.T, s, re string) {
    31  	t.Helper()
    32  	matched, err := regexp.MatchString(re, s)
    33  	if err != nil {
    34  		t.Fatal(err)
    35  	}
    36  	if !matched {
    37  		t.Errorf("string does not match pattern %q:\n%s", re, s)
    38  	}
    39  }
    40  
    41  // ml checks a match against an array of regexps.
    42  func ml(t *testing.T, s string, re []string) {
    43  	t.Helper()
    44  	lines := strings.Split(s, "\n")
    45  	for i := 0; i < len(lines) && i < len(re); i++ {
    46  		matched, err := regexp.MatchString(re[i], lines[i])
    47  		if err != nil {
    48  			t.Fatal(err)
    49  		}
    50  		if !matched {
    51  			t.Errorf("line %d: %q: does not match pattern %q; s:\n%s", i+1, lines[i], re[i], s)
    52  		}
    53  	}
    54  	if len(lines) < len(re) {
    55  		t.Errorf("too few lines in message: expected %d, got %d; s:\n%s", len(re), len(lines), s)
    56  	}
    57  }
    58  
    59  // eq checks a string equality.
    60  func eq(t *testing.T, s, exp string) {
    61  	t.Helper()
    62  	if s != exp {
    63  		t.Errorf("got %q, expected %q", s, exp)
    64  	}
    65  }
    66  
    67  func TestInternalError(t *testing.T) {
    68  	type pred struct {
    69  		name string
    70  		fn   func(*testing.T, *Error)
    71  	}
    72  
    73  	const ie = "internal error: "
    74  
    75  	testData := []struct {
    76  		err   error
    77  		preds []pred
    78  	}{
    79  		{
    80  			errors.AssertionFailedf("woo %s", "waa"),
    81  			[]pred{
    82  				// Verify that the information is captured.
    83  				{"basefields", func(t *testing.T, e *Error) {
    84  					eq(t, e.Message, ie+"woo waa")
    85  					eq(t, e.Code, pgcode.Internal)
    86  					m(t, e.Source.File, ".*internal_errors_test.go")
    87  					eq(t, e.Source.Function, "TestInternalError")
    88  					ml(t, e.Detail, []string{"stack trace:",
    89  						".*internal_errors_test.go.*TestInternalError.*",
    90  						".*testing.go.*tRunner()"})
    91  				}},
    92  
    93  				// Verify that formatting works.
    94  				{"format", func(t *testing.T, e *Error) {
    95  					eq(t, f("%v", e), ie+"woo waa")
    96  					eq(t, f("%s", e), ie+"woo waa")
    97  					eq(t, f("%#v", e), "(XX000) "+ie+"woo waa")
    98  					m(t, f("%+v", e),
    99  						// Heading line: source, code, error message.
   100  						`.*internal_errors_test.go:\d+ in TestInternalError\(\): \(XX000\) `+
   101  							ie+"woo waa")
   102  				}},
   103  			},
   104  		},
   105  		{
   106  			// Check that the non-formatting constructor preserves the
   107  			// format spec without making a mess.
   108  			New(pgcode.Syntax, "syn %s"),
   109  			[]pred{
   110  				{"basefields", func(t *testing.T, e *Error) {
   111  					eq(t, e.Message, "syn %s")
   112  					eq(t, e.Code, pgcode.Syntax)
   113  					m(t, e.Source.File, ".*internal_errors_test.go")
   114  					eq(t, e.Source.Function, "TestInternalError")
   115  				}},
   116  			},
   117  		},
   118  		{
   119  			// Check that a wrapped pgerr with errors.Wrap is wrapped properly.
   120  			Wrap(errors.Wrap(makeNormal(), "wrapA"), pgcode.Syntax, "wrapB"),
   121  			[]pred{
   122  				{"basefields", func(t *testing.T, e *Error) {
   123  					eq(t, e.Code, pgcode.Syntax)
   124  					eq(t, e.Message, "wrapB: wrapA: syn")
   125  					// Source info is stored.
   126  					m(t, e.Source.File, ".*internal_errors_test.go")
   127  					eq(t, e.Source.Function, "makeNormal")
   128  				}},
   129  			},
   130  		},
   131  		{
   132  			// Check that a wrapped internal error with errors.Wrap is wrapped properly.
   133  			Wrap(errors.Wrap(makeBoo(), "wrapA"), pgcode.Syntax, "wrapB"),
   134  			[]pred{
   135  				{"basefields", func(t *testing.T, e *Error) {
   136  					eq(t, e.Code, pgcode.Internal)
   137  					eq(t, e.Message, ie+"wrapB: wrapA: boo")
   138  					// Source info is stored.
   139  					m(t, e.Source.File, ".*internal_errors_test.go")
   140  					eq(t, e.Source.Function, "makeBoo")
   141  				}},
   142  				{"no-details", func(t *testing.T, e *Error) {
   143  					// Simple errors do not store stack trace details.
   144  					ml(t, e.Detail, []string{
   145  						`stack trace:`,
   146  						`.*makeBoo.*`,
   147  					})
   148  				}},
   149  			},
   150  		},
   151  		{
   152  			// Check that Wrapf respects the original code for regular errors.
   153  			Wrapf(New(pgcode.Syntax, "syn foo"), pgcode.AdminShutdown, "wrap %s", "waa"),
   154  			[]pred{
   155  				{"basefields", func(t *testing.T, e *Error) {
   156  					// Wrap adds a prefix to the message.
   157  					eq(t, e.Message, "wrap waa: syn foo")
   158  					// Original code is preserved.
   159  					eq(t, e.Code, pgcode.Syntax)
   160  					// Source info is stored.
   161  					m(t, e.Source.File, ".*internal_errors_test.go")
   162  					eq(t, e.Source.Function, "TestInternalError")
   163  				}},
   164  			},
   165  		},
   166  		{
   167  			// Check that Wrapf around a regular fmt.Error makes sense.
   168  			Wrapf(fmt.Errorf("fmt err"), pgcode.AdminShutdown, "wrap %s", "waa"),
   169  			[]pred{
   170  				{"basefields", func(t *testing.T, e *Error) {
   171  					// Wrap adds a prefix to the message.
   172  					eq(t, e.Message, "wrap waa: fmt err")
   173  					// New code was added.
   174  					eq(t, e.Code, pgcode.AdminShutdown)
   175  					// Source info is stored.
   176  					m(t, e.Source.File, ".*internal_errors_test.go")
   177  					eq(t, e.Source.Function, "TestInternalError")
   178  				}},
   179  			},
   180  		},
   181  		{
   182  			// Check that Wrap does the same thing
   183  			Wrap(fmt.Errorf("fmt err"), pgcode.AdminShutdown, "wrapx waa"),
   184  			[]pred{
   185  				{"basefields", func(t *testing.T, e *Error) {
   186  					// Wrap adds a prefix to the message.
   187  					eq(t, e.Message, "wrapx waa: fmt err")
   188  					// New code was added.
   189  					eq(t, e.Code, pgcode.AdminShutdown)
   190  					// Source info is stored.
   191  					m(t, e.Source.File, ".*internal_errors_test.go")
   192  					eq(t, e.Source.Function, "TestInternalError")
   193  				}},
   194  				{"no-details", func(t *testing.T, e *Error) {
   195  					eq(t, e.Detail, "")
   196  				}},
   197  			},
   198  		},
   199  		{
   200  			// Check that Wrapf around an error.Wrap extracts something useful.
   201  			Wrapf(errors.Wrap(fmt.Errorf("fmt"), "wrap1"), pgcode.AdminShutdown, "wrap2 %s", "waa"),
   202  			[]pred{
   203  				{"basefields", func(t *testing.T, e *Error) {
   204  					// Wrap adds a prefix to the message.
   205  					eq(t, e.Message, "wrap2 waa: wrap1: fmt")
   206  					// New code was added.
   207  					eq(t, e.Code, pgcode.AdminShutdown)
   208  					// Source info is stored.
   209  					m(t, e.Source.File, ".*internal_errors_test.go")
   210  					eq(t, e.Source.Function, "TestInternalError")
   211  				}},
   212  				{"no-details", func(t *testing.T, e *Error) {
   213  					// Simple errors do not store stack trace details.
   214  					eq(t, e.Detail, "")
   215  				}},
   216  			},
   217  		},
   218  		{
   219  			// Check that a Wrap around an internal error preserves the internal error.
   220  			doWrap(makeBoo()),
   221  			[]pred{
   222  				{"basefields", func(t *testing.T, e *Error) {
   223  					// Wrap adds a prefix to the message.
   224  					eq(t, e.Message, ie+"wrap woo: boo")
   225  					// Internal error was preserved.
   226  					eq(t, e.Code, pgcode.Internal)
   227  					// Source info is preserved from original error.
   228  					m(t, e.Source.File, ".*internal_errors_test.go")
   229  					eq(t, e.Source.Function, "makeBoo")
   230  				}},
   231  				{"retained-details", func(t *testing.T, e *Error) {
   232  					ml(t, e.Detail,
   233  						[]string{
   234  							// Ensure that the original stack trace remains at the top.
   235  							"stack trace:",
   236  							".*makeBoo.*",
   237  							".*TestInternalError.*",
   238  							".*tRunner.*",
   239  						},
   240  					)
   241  				}},
   242  			},
   243  		},
   244  		{
   245  			// Check that an internal error Wrap around a regular error
   246  			// creates internal error details.
   247  			errors.NewAssertionErrorWithWrappedErrf(New(pgcode.Syntax, "syn err"), "iewrap %s", "waa"),
   248  			[]pred{
   249  				{"basefields", func(t *testing.T, e *Error) {
   250  					// Wrap adds a prefix to the message.
   251  					eq(t, e.Message, ie+"iewrap waa: syn err")
   252  					// Internal error was preserved.
   253  					eq(t, e.Code, pgcode.Internal)
   254  					// Source info is preserved from original error.
   255  					m(t, e.Source.File, ".*internal_errors_test.go")
   256  					eq(t, e.Source.Function, "TestInternalError")
   257  				}},
   258  				{"retained-details", func(t *testing.T, e *Error) {
   259  					ml(t, e.Detail,
   260  						[]string{
   261  							// Ensure that the assertion catcher is captured in details.
   262  							"stack trace:",
   263  							".*TestInternalError.*",
   264  							".*tRunner.*",
   265  						},
   266  					)
   267  				}},
   268  			},
   269  		},
   270  		{
   271  			// Check that an internal error Wrap around another internal
   272  			// error creates internal error details and a sane error
   273  			// message.
   274  			errors.NewAssertionErrorWithWrappedErrf(
   275  				makeBoo(), "iewrap2 %s", "waa"),
   276  			[]pred{
   277  				{"basefields", func(t *testing.T, e *Error) {
   278  					// Ensure the "internal error" prefix only occurs once.
   279  					eq(t, e.Message, ie+"iewrap2 waa: boo")
   280  					// Internal error was preserved.
   281  					eq(t, e.Code, pgcode.Internal)
   282  					// Source info is preserved from original error.
   283  					m(t, e.Source.File, ".*internal_errors_test.go")
   284  					// The original cause is masked by the barrier.
   285  					eq(t, e.Source.Function, "TestInternalError")
   286  				}},
   287  				{"retained-details", func(t *testing.T, e *Error) {
   288  					ml(t, e.Detail,
   289  						[]string{
   290  							// Ensure that the assertion catcher is captured in details.
   291  							// Also makeBoo() is masked here.
   292  							"stack trace:",
   293  							".*TestInternalError.*",
   294  							".*tRunner.*",
   295  						},
   296  					)
   297  				}},
   298  			},
   299  		},
   300  	}
   301  
   302  	for i, test := range testData {
   303  		t.Run(fmt.Sprintf("%d %s", i, test.err), func(t *testing.T) {
   304  			for _, pred := range test.preds {
   305  				t.Run(pred.name, func(t *testing.T) {
   306  					pgErr := Flatten(test.err)
   307  					pred.fn(t, pgErr)
   308  					if t.Failed() {
   309  						t.Logf("input error: %# v", pretty.Formatter(test.err))
   310  						t.Logf("pg error: %# v", pretty.Formatter(pgErr))
   311  					}
   312  				})
   313  			}
   314  		})
   315  	}
   316  }
   317  
   318  func makeNormal() error {
   319  	return New(pgcode.Syntax, "syn")
   320  }
   321  
   322  func doWrap(err error) error {
   323  	return Wrapf(err, pgcode.AdminShutdown, "wrap %s", "woo")
   324  }
   325  
   326  func makeBoo() error {
   327  	return errors.AssertionFailedf("boo")
   328  }