go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/gae/filter/count/count_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 count
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"testing"
    21  
    22  	"go.chromium.org/luci/gae/filter/featureBreaker"
    23  	"go.chromium.org/luci/gae/impl/memory"
    24  	ds "go.chromium.org/luci/gae/service/datastore"
    25  	"go.chromium.org/luci/gae/service/info"
    26  	"go.chromium.org/luci/gae/service/mail"
    27  	"go.chromium.org/luci/gae/service/memcache"
    28  	"go.chromium.org/luci/gae/service/taskqueue"
    29  	"go.chromium.org/luci/gae/service/user"
    30  
    31  	. "github.com/smartystreets/goconvey/convey"
    32  	. "go.chromium.org/luci/common/testing/assertions"
    33  )
    34  
    35  func shouldHaveSuccessesAndErrors(actual any, expected ...any) string {
    36  	a := actual.(Entry)
    37  	if len(expected) != 2 {
    38  		panic("Invalid number of expected, should be 2 (successes, errors).")
    39  	}
    40  	s, e := expected[0].(int), expected[1].(int)
    41  
    42  	if val := a.Successes(); val != s {
    43  		return fmt.Sprintf("Actual successes (%d) don't match expected (%d)", val, s)
    44  	}
    45  	if val := a.Errors(); val != e {
    46  		return fmt.Sprintf("Actual errors (%d) don't match expected (%d)", val, e)
    47  	}
    48  	return ""
    49  }
    50  
    51  func die(err error) {
    52  	if err != nil {
    53  		panic(err)
    54  	}
    55  }
    56  
    57  func TestCount(t *testing.T) {
    58  	t.Parallel()
    59  
    60  	Convey("Test Count filter", t, func() {
    61  		c, fb := featureBreaker.FilterRDS(memory.Use(context.Background()), nil)
    62  		c, ctr := FilterRDS(c)
    63  
    64  		So(c, ShouldNotBeNil)
    65  		So(ctr, ShouldNotBeNil)
    66  
    67  		vals := []ds.PropertyMap{{
    68  			"Val":  ds.MkProperty(100),
    69  			"$key": ds.MkPropertyNI(ds.NewKey(c, "Kind", "", 1, nil)),
    70  		}}
    71  
    72  		Convey("Calling a ds function should reflect in counter", func() {
    73  			So(ds.Put(c, vals), ShouldBeNil)
    74  			So(ctr.PutMulti.Successes(), ShouldEqual, 1)
    75  
    76  			Convey("effects are cumulative", func() {
    77  				So(ds.Put(c, vals), ShouldBeNil)
    78  				So(ctr.PutMulti.Successes(), ShouldEqual, 2)
    79  
    80  				Convey("even within transactions", func() {
    81  					die(ds.RunInTransaction(c, func(c context.Context) error {
    82  						So(ds.Put(c, append(vals, vals[0])), ShouldBeNil)
    83  						return nil
    84  					}, nil))
    85  				})
    86  			})
    87  		})
    88  
    89  		Convey("errors count against errors", func() {
    90  			fb.BreakFeatures(nil, "GetMulti")
    91  
    92  			So(ds.Get(c, vals), ShouldErrLike, `"GetMulti" is broken`)
    93  			So(ctr.GetMulti.Errors(), ShouldEqual, 1)
    94  
    95  			fb.UnbreakFeatures("GetMulti")
    96  
    97  			So(ds.Put(c, vals), ShouldBeNil)
    98  
    99  			die(ds.Get(c, vals))
   100  			So(ctr.GetMulti.Errors(), ShouldEqual, 1)
   101  			So(ctr.GetMulti.Successes(), ShouldEqual, 1)
   102  			So(ctr.GetMulti.Total(), ShouldEqual, 2)
   103  		})
   104  
   105  		Convey(`datastore.Stop does not count as an error for queries`, func() {
   106  			fb.BreakFeatures(ds.Stop, "Run")
   107  
   108  			So(ds.Run(c, ds.NewQuery("foof"), func(_ ds.PropertyMap) error {
   109  				return nil
   110  			}), ShouldBeNil)
   111  			So(ctr.Run.Successes(), ShouldEqual, 1)
   112  			So(ctr.Run.Errors(), ShouldEqual, 0)
   113  			So(ctr.Run.Total(), ShouldEqual, 1)
   114  		})
   115  	})
   116  
   117  	Convey("works for memcache", t, func() {
   118  		c, ctr := FilterMC(memory.Use(context.Background()))
   119  		So(c, ShouldNotBeNil)
   120  		So(ctr, ShouldNotBeNil)
   121  
   122  		die(memcache.Set(c, memcache.NewItem(c, "hello").SetValue([]byte("sup"))))
   123  
   124  		_, err := memcache.GetKey(c, "Wat")
   125  		So(err, ShouldNotBeNil)
   126  
   127  		_, err = memcache.GetKey(c, "hello")
   128  		die(err)
   129  
   130  		So(ctr.SetMulti, shouldHaveSuccessesAndErrors, 1, 0)
   131  		So(ctr.GetMulti, shouldHaveSuccessesAndErrors, 2, 0)
   132  		So(ctr.NewItem, shouldHaveSuccessesAndErrors, 3, 0)
   133  	})
   134  
   135  	Convey("works for taskqueue", t, func() {
   136  		c, ctr := FilterTQ(memory.Use(context.Background()))
   137  		So(c, ShouldNotBeNil)
   138  		So(ctr, ShouldNotBeNil)
   139  
   140  		die(taskqueue.Add(c, "", &taskqueue.Task{Name: "wat"}))
   141  		So(taskqueue.Add(c, "DNE_QUEUE", &taskqueue.Task{Name: "wat"}),
   142  			ShouldErrLike, "UNKNOWN_QUEUE")
   143  
   144  		So(ctr.AddMulti, shouldHaveSuccessesAndErrors, 1, 1)
   145  	})
   146  
   147  	Convey("works for global info", t, func() {
   148  		c, fb := featureBreaker.FilterGI(memory.Use(context.Background()), nil)
   149  		c, ctr := FilterGI(c)
   150  		So(c, ShouldNotBeNil)
   151  		So(ctr, ShouldNotBeNil)
   152  
   153  		_, err := info.Namespace(c, "foo")
   154  		die(err)
   155  		fb.BreakFeatures(nil, "Namespace")
   156  		_, err = info.Namespace(c, "boom")
   157  		So(err, ShouldErrLike, `"Namespace" is broken`)
   158  
   159  		So(ctr.Namespace, shouldHaveSuccessesAndErrors, 1, 1)
   160  	})
   161  
   162  	Convey("works for user", t, func() {
   163  		c, fb := featureBreaker.FilterUser(memory.Use(context.Background()), nil)
   164  		c, ctr := FilterUser(c)
   165  		So(c, ShouldNotBeNil)
   166  		So(ctr, ShouldNotBeNil)
   167  
   168  		_, err := user.CurrentOAuth(c, "foo")
   169  		die(err)
   170  		fb.BreakFeatures(nil, "CurrentOAuth")
   171  		_, err = user.CurrentOAuth(c, "foo")
   172  		So(err, ShouldErrLike, `"CurrentOAuth" is broken`)
   173  
   174  		So(ctr.CurrentOAuth, shouldHaveSuccessesAndErrors, 1, 1)
   175  	})
   176  
   177  	Convey("works for mail", t, func() {
   178  		c, fb := featureBreaker.FilterMail(memory.Use(context.Background()), nil)
   179  		c, ctr := FilterMail(c)
   180  		So(c, ShouldNotBeNil)
   181  		So(ctr, ShouldNotBeNil)
   182  
   183  		err := mail.Send(c, &mail.Message{
   184  			Sender: "admin@example.com",
   185  			To:     []string{"coolDood@example.com"},
   186  			Body:   "hi",
   187  		})
   188  		die(err)
   189  
   190  		fb.BreakFeatures(nil, "Send")
   191  		err = mail.Send(c, &mail.Message{
   192  			Sender: "admin@example.com",
   193  			To:     []string{"coolDood@example.com"},
   194  			Body:   "hi",
   195  		})
   196  		So(err, ShouldErrLike, `"Send" is broken`)
   197  
   198  		So(ctr.Send, shouldHaveSuccessesAndErrors, 1, 1)
   199  	})
   200  }
   201  
   202  func ExampleFilterRDS() {
   203  	// Set up your context using a base service implementation (memory or prod)
   204  	c := memory.Use(context.Background())
   205  
   206  	// Apply the counter.FilterRDS
   207  	c, counter := FilterRDS(c)
   208  
   209  	// functions use ds from the context like normal... they don't need to know
   210  	// that there are any filters at all.
   211  	someCalledFunc := func(c context.Context) {
   212  		vals := []ds.PropertyMap{{
   213  			"FieldName": ds.MkProperty(100),
   214  			"$key":      ds.MkProperty(ds.NewKey(c, "Kind", "", 1, nil))},
   215  		}
   216  		if err := ds.Put(c, vals); err != nil {
   217  			panic(err)
   218  		}
   219  	}
   220  
   221  	// Using the other function.
   222  	someCalledFunc(c)
   223  	someCalledFunc(c)
   224  
   225  	// Then we can see what happened!
   226  	fmt.Printf("%d\n", counter.PutMulti.Successes())
   227  	// Output:
   228  	// 2
   229  }