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  }