go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/gae/filter/featureBreaker/featurebreaker_test.go (about) 1 // Copyright 2015 The LUCI 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 implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package featureBreaker 16 17 import ( 18 "context" 19 "testing" 20 21 "go.chromium.org/luci/common/errors" 22 23 "go.chromium.org/luci/gae/impl/memory" 24 ds "go.chromium.org/luci/gae/service/datastore" 25 26 . "github.com/smartystreets/goconvey/convey" 27 ) 28 29 func TestBrokenFeatures(t *testing.T) { 30 t.Parallel() 31 32 e := errors.New("default err") 33 34 Convey("BrokenFeatures", t, func() { 35 c := memory.Use(context.Background()) 36 37 Convey("Can break ds", func() { 38 Convey("without a default", func() { 39 c, bf := FilterRDS(c, nil) 40 vals := []ds.PropertyMap{{ 41 "$key": ds.MkPropertyNI(ds.NewKey(c, "Wut", "", 1, nil)), 42 }} 43 44 Convey("by specifying an error", func() { 45 bf.BreakFeatures(e, "GetMulti", "PutMulti") 46 So(ds.Get(c, vals), ShouldEqual, e) 47 48 Convey("and you can unbreak them as well", func() { 49 bf.UnbreakFeatures("GetMulti") 50 51 So(errors.SingleError(ds.Get(c, vals)), ShouldEqual, ds.ErrNoSuchEntity) 52 53 Convey("no broken features at all is a shortcut", func() { 54 bf.UnbreakFeatures("PutMulti") 55 So(errors.SingleError(ds.Get(c, vals)), ShouldEqual, ds.ErrNoSuchEntity) 56 }) 57 }) 58 }) 59 60 Convey("Not specifying an error gets you a generic error", func() { 61 bf.BreakFeatures(nil, "GetMulti") 62 So(ds.Get(c, vals).Error(), ShouldContainSubstring, `feature "GetMulti" is broken`) 63 }) 64 65 Convey("Callback work and receives correct context", func() { 66 errToReturn := errors.New("err from callback") 67 key := "some key" 68 69 bf.BreakFeaturesWithCallback(func(c context.Context, feature string) error { 70 So(c.Value(&key), ShouldEqual, "some value") 71 return errToReturn 72 }, "GetMulti") 73 74 ctx := context.WithValue(c, &key, "some value") 75 So(ds.Get(ctx, vals), ShouldEqual, errToReturn) 76 77 errToReturn = nil 78 So(errors.SingleError(ds.Get(ctx, vals)), ShouldEqual, ds.ErrNoSuchEntity) 79 }) 80 81 Convey("Transaction hooks work", func() { 82 // A sequence of errors emulating a bunch of failing RPCs that cause 83 // the transaction body to be retried once. 84 errs := []struct { 85 name string 86 err error 87 }{ 88 {"BeginTransaction", nil}, 89 {"CommitTransaction", ds.ErrConcurrentTransaction}, 90 {"BeginTransaction", ds.ErrConcurrentTransaction}, 91 {"BeginTransaction", nil}, 92 {"CommitTransaction", nil}, 93 } 94 95 bf.BreakFeaturesWithCallback(func(c context.Context, feature string) error { 96 So(len(errs), ShouldBeGreaterThan, 0) 97 So(errs[0].name, ShouldEqual, feature) 98 err := errs[0].err 99 errs = errs[1:] 100 return err 101 }, "BeginTransaction", "CommitTransaction") 102 103 calls := 0 104 So(ds.RunInTransaction(c, func(c context.Context) error { 105 calls++ 106 return nil 107 }, nil), ShouldBeNil) 108 109 So(calls, ShouldEqual, 2) 110 So(errs, ShouldBeEmpty) 111 }) 112 }) 113 114 Convey("with a default", func() { 115 c, bf := FilterRDS(c, e) 116 vals := []ds.PropertyMap{{ 117 "$key": ds.MkPropertyNI(ds.NewKey(c, "Wut", "", 1, nil)), 118 }} 119 bf.BreakFeatures(nil, "GetMulti") 120 So(ds.Get(c, vals), ShouldEqual, e) 121 }) 122 }) 123 }) 124 }