github.com/cockroachdb/errors@v1.11.1/errbase/migrations_test.go (about)

     1  // Copyright 2019 The Cockroach Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    12  // implied. See the License for the specific language governing
    13  // permissions and limitations under the License.
    14  
    15  package errbase_test
    16  
    17  import (
    18  	"context"
    19  	"reflect"
    20  	"testing"
    21  
    22  	"github.com/cockroachdb/errors/errbase"
    23  	"github.com/cockroachdb/errors/markers"
    24  	"github.com/gogo/protobuf/proto"
    25  )
    26  
    27  // Scenario 1: simple migration, forward direction
    28  // - v2 renames foo -> bar
    29  // - v2 and v1 are connected
    30  // - v1 sends an error to v2.
    31  func TestSimpleMigrationForward(t *testing.T) {
    32  	defer errbase.TestingWithEmptyMigrationRegistry()()
    33  
    34  	// == Scenario on v1 ==
    35  	origErr := fooErr{}
    36  	errbase.RegisterLeafDecoder(errbase.GetTypeKey(origErr),
    37  		func(_ context.Context, _ string, _ []string, _ proto.Message) error { return fooErr{} })
    38  
    39  	// Send the error to v2.
    40  	enc := errbase.EncodeError(context.Background(), origErr)
    41  	// Clean up, so that type foo becomes unknown.
    42  	errbase.RegisterLeafDecoder(errbase.GetTypeKey(origErr), nil)
    43  
    44  	// == Scenario on v2 ==
    45  	// Register the fact that foo was migrated to bar.
    46  	errbase.RegisterTypeMigration(myPkgPath, "errbase_test.fooErr", barErr{})
    47  	// Register the bar decoder.
    48  	errbase.RegisterLeafDecoder(errbase.GetTypeKey(barErr{}),
    49  		func(_ context.Context, _ string, _ []string, _ proto.Message) error { return barErr{} })
    50  	// Receive the error from v1.
    51  	dec := errbase.DecodeError(context.Background(), enc)
    52  	// Clean up, so that type bar becomes unknown for further tests.
    53  	errbase.RegisterLeafDecoder(errbase.GetTypeKey(barErr{}), nil)
    54  
    55  	// Main test: check that v2 recognized the foo error from
    56  	// v1 and instantiated it as bar.
    57  	if _, ok := dec.(barErr); !ok {
    58  		t.Errorf("migration failed; expected type barErr, got %T", dec)
    59  	}
    60  }
    61  
    62  // Scenario 1: simple migration, backward direction
    63  // - v2 renames foo -> bar
    64  // - v2 and v1 are connected
    65  // - v2 sends an error to v1.
    66  func TestSimpleMigrationBackward(t *testing.T) {
    67  	defer errbase.TestingWithEmptyMigrationRegistry()()
    68  
    69  	// == Scenario on v2 ==
    70  	origErr := barErr{}
    71  	// Register the fact that foo was migrated to bar.
    72  	errbase.RegisterTypeMigration(myPkgPath, "errbase_test.fooErr", origErr)
    73  	// Send the error to v1.
    74  	enc := errbase.EncodeError(context.Background(), origErr)
    75  	// Clean up the decoder, so that type becomes unknown for further tests.
    76  	errbase.RegisterLeafDecoder(errbase.GetTypeKey(origErr), nil)
    77  	// Erase the migration we have set up above, so that the test
    78  	// underneath does not know about it.
    79  	defer errbase.TestingWithEmptyMigrationRegistry()()
    80  
    81  	// == Scenario on v1 ==
    82  	errbase.RegisterLeafDecoder(errbase.GetTypeKey(fooErr{}),
    83  		func(_ context.Context, _ string, _ []string, _ proto.Message) error { return fooErr{} })
    84  
    85  	// Receive the error from v2.
    86  	dec := errbase.DecodeError(context.Background(), enc)
    87  	// Clean up, so that type foo becomes unknown.
    88  	errbase.RegisterLeafDecoder(errbase.GetTypeKey(fooErr{}), nil)
    89  
    90  	// Main test: check that v1 recognized the foo error from
    91  	// v2 and instantiated it as foo.
    92  	if _, ok := dec.(fooErr); !ok {
    93  		t.Errorf("migration failed; expected type fooErr, got %T", dec)
    94  	}
    95  }
    96  
    97  // This is the same as above, using a pointer receiver for the error type.
    98  func TestSimpleMigrationForwardPtr(t *testing.T) {
    99  	defer errbase.TestingWithEmptyMigrationRegistry()()
   100  
   101  	// == Scenario on v1 ==
   102  	origErr := (*fooErrP)(nil)
   103  	errbase.RegisterLeafDecoder(errbase.GetTypeKey(origErr),
   104  		func(_ context.Context, _ string, _ []string, _ proto.Message) error { return (*fooErrP)(nil) })
   105  
   106  	// Send the error to v2.
   107  	enc := errbase.EncodeError(context.Background(), origErr)
   108  	// Clean up, so that type foo becomes unknown.
   109  	errbase.RegisterLeafDecoder(errbase.GetTypeKey(origErr), nil)
   110  
   111  	// == Scenario on v2 ==
   112  	// Register the fact that foo was migrated to bar.
   113  	errbase.RegisterTypeMigration(myPkgPath, "*errbase_test.fooErrP", (*barErrP)(nil))
   114  	// Register the bar decoder.
   115  	errbase.RegisterLeafDecoder(errbase.GetTypeKey((*barErrP)(nil)),
   116  		func(_ context.Context, _ string, _ []string, _ proto.Message) error { return (*barErrP)(nil) })
   117  	// Receive the error from v1.
   118  	dec := errbase.DecodeError(context.Background(), enc)
   119  	// Clean up, so that type bar becomes unknown for further tests.
   120  	errbase.RegisterLeafDecoder(errbase.GetTypeKey((*barErrP)(nil)), nil)
   121  
   122  	// Main test: check that v2 recognized the foo error from
   123  	// v1 and instantiated it as bar.
   124  	if _, ok := dec.(*barErrP); !ok {
   125  		t.Errorf("migration failed; expected type *barErrP, got %T", dec)
   126  	}
   127  }
   128  
   129  // Scenario 2: simultaneous migration
   130  // - vA renames foo -> bar
   131  //   vA calls RegisterTypeMigration("foo", (*bar)(nil))
   132  // - vB renames foo -> qux
   133  //   vB calls RegisterTypeMigration("foo", (*qux)(nil))
   134  // - vA and vB are connected
   135  // - vA sends an error to vB:
   136  //   - vA translates the error key upon send from bar to foo's key
   137  //   - vB recognizes that "foo" refers to qux
   138  func TestSimultaneousMigration(t *testing.T) {
   139  	// == Scenario on vA ==
   140  	defer errbase.TestingWithEmptyMigrationRegistry()()
   141  	origErr := barErr{}
   142  	// Register the fact that foo was migrated to bar.
   143  	errbase.RegisterTypeMigration(myPkgPath, "errbase_test.fooErr", origErr)
   144  	// Send the error to vB.
   145  	enc := errbase.EncodeError(context.Background(), origErr)
   146  	// Clean up the decoder, so that type becomes unknown for further tests.
   147  	errbase.RegisterLeafDecoder(errbase.GetTypeKey(origErr), nil)
   148  	// Erase the migration we have set up above, so that the test
   149  	// underneath does not know about it.
   150  	defer errbase.TestingWithEmptyMigrationRegistry()()
   151  
   152  	// == Scenario on v2 ==
   153  	defer errbase.TestingWithEmptyMigrationRegistry()()
   154  	// Register the fact that foo was migrated to qux.
   155  	errbase.RegisterTypeMigration(myPkgPath, "errbase_test.fooErr", quxErr{})
   156  	// Register the qux decoder.
   157  	errbase.RegisterLeafDecoder(errbase.GetTypeKey(quxErr{}),
   158  		func(_ context.Context, _ string, _ []string, _ proto.Message) error { return quxErr{} })
   159  	// Receive the error from vA.
   160  	dec := errbase.DecodeError(context.Background(), enc)
   161  	// Clean up, so that type qux becomes unknown for further tests.
   162  	errbase.RegisterLeafDecoder(errbase.GetTypeKey(quxErr{}), nil)
   163  
   164  	// Main test: check that vA normalized the bar error to foo,
   165  	// and v2 instantiated it as qux.
   166  	if _, ok := dec.(quxErr); !ok {
   167  		t.Errorf("migration failed; expected type quxErr, got %T", dec)
   168  	}
   169  }
   170  
   171  // Scenario 3: migrated error passing through
   172  // - v2 renames foo -> bar
   173  // - v2.a, v2.b and v1 are connected: v2.a -> v1 -> v2.b
   174  // - v2.a sends an error to v2.b via v1
   175  func TestMigratedErrorPassingThrough(t *testing.T) {
   176  	defer errbase.TestingWithEmptyMigrationRegistry()()
   177  
   178  	// == Scenario on v2.a ==
   179  	origErr := barErr{}
   180  	// Register the fact that foo was migrated to bar.
   181  	errbase.RegisterTypeMigration(myPkgPath, "errbase_test.fooErr", origErr)
   182  	// Send the error to v1.
   183  	enc := errbase.EncodeError(context.Background(), origErr)
   184  	// Clean up the decoder, so that type becomes unknown for further tests.
   185  	errbase.RegisterLeafDecoder(errbase.GetTypeKey(origErr), nil)
   186  	// Erase the migration we have set up above, so that the test
   187  	// underneath does not know about it.
   188  	defer errbase.TestingWithEmptyMigrationRegistry()()
   189  
   190  	// == Scenario on v1 ==
   191  	errbase.RegisterLeafDecoder(errbase.GetTypeKey(fooErr{}),
   192  		func(_ context.Context, _ string, _ []string, _ proto.Message) error { return fooErr{} })
   193  	// Receive the error from v2.b.
   194  	dec := errbase.DecodeError(context.Background(), enc)
   195  	// Send the error to v2.b.
   196  	enc2 := errbase.EncodeError(context.Background(), dec)
   197  	// Clean up, so that type foo becomes unknown.
   198  	errbase.RegisterLeafDecoder(errbase.GetTypeKey(fooErr{}), nil)
   199  
   200  	// == Scenario on v2.b ==
   201  	// Register the fact that foo was migrated to bar.
   202  	errbase.RegisterTypeMigration(myPkgPath, "errbase_test.fooErr", barErr{})
   203  	errbase.RegisterLeafDecoder(errbase.GetTypeKey(barErr{}),
   204  		func(_ context.Context, _ string, _ []string, _ proto.Message) error { return barErr{} })
   205  	// Receive the error from v1.
   206  	dec2 := errbase.DecodeError(context.Background(), enc2)
   207  	// Clean up the decoder, so that type becomes unknown for further tests.
   208  	errbase.RegisterLeafDecoder(errbase.GetTypeKey(barErr{}), nil)
   209  	// Erase the migration we have set up above, so that the test
   210  	// underneath does not know about it.
   211  	defer errbase.TestingWithEmptyMigrationRegistry()()
   212  
   213  	// Main test: check that v2.b recognized the foo error
   214  	// that was passed from v2.a.
   215  	if _, ok := dec2.(barErr); !ok {
   216  		t.Errorf("migration failed; expected type barErr, got %T", dec2)
   217  	}
   218  }
   219  
   220  // Scenario 4: migrated error passing through node that
   221  // does not know about the error type whatsoever.
   222  // - v2 renames foo -> bar
   223  // - v2.a, v2.b and v0 are connected: v2.a -> v0 -> v2.b
   224  //   (v0 does not know about error foo at all)
   225  // - v2.a sends an error to v2.b via v0:
   226  func TestMigratedErrorPassingThroughAsUnknown(t *testing.T) {
   227  	defer errbase.TestingWithEmptyMigrationRegistry()()
   228  
   229  	// == Scenario on v2.a ==
   230  	origErr := barErr{}
   231  	// Register the fact that foo was migrated to bar.
   232  	errbase.RegisterTypeMigration(myPkgPath, "errbase_test.fooErr", origErr)
   233  	// Send the error to v1.
   234  	enc := errbase.EncodeError(context.Background(), origErr)
   235  	// Clean up the decoder, so that type becomes unknown for further tests.
   236  	errbase.RegisterLeafDecoder(errbase.GetTypeKey(origErr), nil)
   237  	// Erase the migration we have set up above, so that the test
   238  	// underneath does not know about it.
   239  	defer errbase.TestingWithEmptyMigrationRegistry()()
   240  
   241  	// == Scenario on v1 ==
   242  	// Receive the error from v2.b. Will decode as opaqueLeaf{}.
   243  	dec := errbase.DecodeError(context.Background(), enc)
   244  	// Send the error to v2.b.
   245  	enc2 := errbase.EncodeError(context.Background(), dec)
   246  
   247  	// == Scenario on v2.b ==
   248  	// Register the fact that foo was migrated to bar.
   249  	errbase.RegisterTypeMigration(myPkgPath, "errbase_test.fooErr", barErr{})
   250  	errbase.RegisterLeafDecoder(errbase.GetTypeKey(barErr{}),
   251  		func(_ context.Context, _ string, _ []string, _ proto.Message) error { return barErr{} })
   252  	// Receive the error from v1.
   253  	dec2 := errbase.DecodeError(context.Background(), enc2)
   254  	// Clean up the decoder, so that type becomes unknown for further tests.
   255  	errbase.RegisterLeafDecoder(errbase.GetTypeKey(barErr{}), nil)
   256  	// Erase the migration we have set up above, so that the test
   257  	// underneath does not know about it.
   258  	defer errbase.TestingWithEmptyMigrationRegistry()()
   259  
   260  	// Main test: check that v2.b recognized the foo error
   261  	// that was passed from v2.a.
   262  	if _, ok := dec2.(barErr); !ok {
   263  		t.Errorf("migration failed; expected type barErr, got %T", dec2)
   264  	}
   265  }
   266  
   267  // Scenario 5: comparison between migrated and non-migrated errors
   268  // on 3rd party node that doesn't know about the type.
   269  // - v2 renames foo -> bar
   270  // - v2 sends error bar to v0
   271  // - v1 sends error foo to v0
   272  // - v0 (that doesn't know about the type) compares the two errors.
   273  func TestUnknownErrorComparisonAfterHeterogeneousMigration(t *testing.T) {
   274  	defer errbase.TestingWithEmptyMigrationRegistry()()
   275  
   276  	// == Scenario on v1 ==
   277  	// Send the error to v0.
   278  	enc1 := errbase.EncodeError(context.Background(), fooErr{})
   279  
   280  	// == Scenario on v2 ==
   281  	// Register the fact that foo was migrated to bar.
   282  	errbase.RegisterTypeMigration(myPkgPath, "errbase_test.fooErr", barErr{})
   283  	// Send the error to v0.
   284  	enc2 := errbase.EncodeError(context.Background(), barErr{})
   285  	// Clear the migration so that the type is invisible to v0 below.
   286  	defer errbase.TestingWithEmptyMigrationRegistry()()
   287  
   288  	// == Scenario on v0 ==
   289  	// Receive the two errors.
   290  	dec1 := errbase.DecodeError(context.Background(), enc1)
   291  	dec2 := errbase.DecodeError(context.Background(), enc2)
   292  
   293  	// Main test: check that v0 recognizes the two errors as equivalent.
   294  	if !markers.Is(dec1, dec2) {
   295  		t.Error("equivalence after migration failed")
   296  	}
   297  	if !markers.Is(dec2, dec1) {
   298  		t.Error("equivalence after migration failed")
   299  	}
   300  }
   301  
   302  type fooErr struct{}
   303  
   304  func (fooErr) Error() string { return "" }
   305  
   306  type barErr struct{}
   307  
   308  func (barErr) Error() string { return "" }
   309  
   310  type quxErr struct{}
   311  
   312  func (quxErr) Error() string { return "" }
   313  
   314  type fooErrP struct{}
   315  
   316  func (*fooErrP) Error() string { return "" }
   317  
   318  type barErrP struct{}
   319  
   320  func (*barErrP) Error() string { return "" }
   321  
   322  var myPkgPath = func() string {
   323  	t := fooErr{}
   324  	return reflect.TypeOf(t).PkgPath()
   325  }()