go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cv/internal/aggrmetrics/driver_test.go (about) 1 // Copyright 2020 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 aggrmetrics 16 17 import ( 18 "context" 19 "sync/atomic" 20 "testing" 21 "time" 22 23 "go.chromium.org/luci/common/errors" 24 "go.chromium.org/luci/common/logging" 25 "go.chromium.org/luci/common/retry/transient" 26 "go.chromium.org/luci/common/tsmon/field" 27 "go.chromium.org/luci/common/tsmon/metric" 28 "go.chromium.org/luci/common/tsmon/types" 29 30 cfgpb "go.chromium.org/luci/cv/api/config/v2" 31 "go.chromium.org/luci/cv/internal/configs/prjcfg/prjcfgtest" 32 "go.chromium.org/luci/cv/internal/cvtesting" 33 34 . "github.com/smartystreets/goconvey/convey" 35 . "go.chromium.org/luci/common/testing/assertions" 36 ) 37 38 func TestDriver(t *testing.T) { 39 t.Parallel() 40 41 Convey("Driver smoke test", t, func() { 42 ct := cvtesting.Test{} 43 _, cancel := ct.SetUp(t) 44 defer cancel() 45 _ = New(ct.Env) 46 }) 47 48 Convey("Driver works", t, func() { 49 ct := cvtesting.Test{} 50 ctx, cancel := ct.SetUp(t) 51 defer cancel() 52 53 mSent := func(fields ...any) any { 54 return ct.TSMonSentValue(ctx, testMetric, fields...) 55 } 56 57 d := Driver{ 58 aggregators: []aggregator{ 59 &testAggregator{name: "agg"}, 60 }, 61 } 62 63 Convey("No projects", func() { 64 So(d.Cron(ctx), ShouldBeNil) 65 So(ct.TSMonStore.GetAll(ctx), ShouldBeEmpty) 66 }) 67 68 Convey("With one active project", func() { 69 prjcfgtest.Create(ctx, "first", &cfgpb.Config{}) 70 So(d.Cron(ctx), ShouldBeNil) 71 72 Convey("Reports new data", func() { 73 So(ct.TSMonStore.GetAll(ctx), ShouldHaveLength, 1) 74 So(mSent("first", "agg"), ShouldEqual, 1001) 75 }) 76 77 Convey("Calling again won't report old data", func() { 78 prjcfgtest.Disable(ctx, "first") 79 prjcfgtest.Create(ctx, "second", &cfgpb.Config{}) 80 ct.Clock.Add(1 * time.Hour) 81 So(d.Cron(ctx), ShouldBeNil) 82 So(ct.TSMonStore.GetAll(ctx), ShouldHaveLength, 1) 83 So(mSent("second", "agg"), ShouldEqual, 1001) 84 }) 85 }) 86 87 Convey("Multiple aggregators", func() { 88 aggregatorFoo := &testAggregator{name: "foo"} 89 aggregatorBar := &testAggregator{name: "bar"} 90 d.aggregators = []aggregator{ 91 aggregatorFoo, 92 aggregatorBar, 93 } 94 95 prjcfgtest.Create(ctx, "first", &cfgpb.Config{}) 96 97 Convey("All succeeds", func() { 98 So(d.Cron(ctx), ShouldBeNil) 99 So(ct.TSMonStore.GetAll(ctx), ShouldHaveLength, 2) 100 So(mSent("first", "foo"), ShouldEqual, 1001) 101 So(mSent("first", "bar"), ShouldEqual, 1001) 102 }) 103 104 Convey("Fails but report partially", func() { 105 aggregatorBar.err = errors.New("something wrong") 106 So(d.Cron(ctx), ShouldErrLike, "something wrong") 107 So(ct.TSMonStore.GetAll(ctx), ShouldHaveLength, 1) 108 So(mSent("first", "foo"), ShouldEqual, 1001) 109 }) 110 111 Convey("All failed", func() { 112 aggregatorFoo.err = transient.Tag.Apply(errors.New("foo went wrong")) 113 aggregatorBar.err = errors.New("bar went wrong") 114 So(d.Cron(ctx), ShouldErrLike, "bar went wrong") // use most serve error 115 So(ct.TSMonStore.GetAll(ctx), ShouldBeEmpty) 116 }) 117 }) 118 }) 119 } 120 121 var testMetric = metric.NewInt("test/aggrmetrics", "test only", nil, field.String("project"), field.String("name")) 122 123 type testAggregator struct { 124 name string 125 err error 126 cnt int32 127 } 128 129 func (t *testAggregator) metrics() []types.Metric { 130 return []types.Metric{testMetric} 131 } 132 133 func (t *testAggregator) report(ctx context.Context, projects []string) error { 134 cnt := atomic.AddInt32(&t.cnt, 1) 135 if t.err != nil { 136 return t.err 137 } 138 vals := make(map[string]int64, len(projects)) 139 for rank, p := range projects { 140 testMetric.Set(ctx, int64(1000+1+rank), p, t.name) 141 } 142 logging.Debugf(ctx, "testAggregator %q [%d]: reported %d values", t.name, cnt, len(vals)) 143 return nil 144 }