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 }()