go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/resultdb/internal/services/baselineupdater/baselineupdater_test.go (about) 1 // Copyright 2023 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 baselineupdater 16 17 import ( 18 "fmt" 19 "testing" 20 "time" 21 22 "cloud.google.com/go/spanner" 23 . "github.com/smartystreets/goconvey/convey" 24 25 . "go.chromium.org/luci/common/testing/assertions" 26 27 "go.chromium.org/luci/resultdb/internal/baselines" 28 btv "go.chromium.org/luci/resultdb/internal/baselines/testvariants" 29 "go.chromium.org/luci/resultdb/internal/invocations" 30 "go.chromium.org/luci/resultdb/internal/spanutil" 31 "go.chromium.org/luci/resultdb/internal/testutil" 32 "go.chromium.org/luci/resultdb/internal/testutil/insert" 33 34 "go.chromium.org/luci/resultdb/pbutil" 35 pb "go.chromium.org/luci/resultdb/proto/v1" 36 "go.chromium.org/luci/server/span" 37 ) 38 39 func TestShouldMarkSubmitted(t *testing.T) { 40 Convey(`ShouldMarkSubmitted`, t, func() { 41 ctx := testutil.SpannerTestContext(t) 42 43 Convey(`FinalizedInvocation`, func() { 44 testutil.MustApply(ctx, testutil.CombineMutations( 45 insert.InvocationWithInclusions("a", pb.Invocation_FINALIZED, map[string]any{ 46 "BaselineId": "try:linux-rel", 47 }), 48 )...) 49 50 inv, err := invocations.Read(span.Single(ctx), "a") 51 So(err, ShouldBeNil) 52 53 err = shouldMarkSubmitted(inv) 54 So(err, ShouldBeNil) 55 }) 56 57 Convey(`Not Finalized Invocation`, func() { 58 testutil.MustApply(ctx, testutil.CombineMutations( 59 insert.InvocationWithInclusions("a", pb.Invocation_ACTIVE, map[string]any{ 60 "BaselineId": "try:linux-rel", 61 }), 62 )...) 63 64 inv, err := invocations.Read(span.Single(ctx), "a") 65 So(err, ShouldBeNil) 66 67 err = shouldMarkSubmitted(inv) 68 So(err, ShouldErrLike, `the invocation is not yet finalized`) 69 }) 70 71 Convey(`Non existent invocation`, func() { 72 _, err := invocations.Read(span.Single(ctx), "a") 73 So(err, ShouldErrLike, `invocations/a not found`) 74 }) 75 }) 76 } 77 78 func TestEnsureBaselineExists(t *testing.T) { 79 Convey(`EnsureBaselineExists`, t, func() { 80 ctx := testutil.SpannerTestContext(t) 81 82 Convey(`New Baseline`, func() { 83 inv := &pb.Invocation{ 84 Name: "invocations/a", 85 State: pb.Invocation_FINALIZED, 86 Realm: "testproject:testrealm", 87 BaselineId: "testrealm:linux-rel", 88 } 89 90 err := ensureBaselineExists(ctx, inv) 91 So(err, ShouldBeNil) 92 93 baseline, err := baselines.Read(span.Single(ctx), "testproject", "testrealm:linux-rel") 94 So(err, ShouldBeNil) 95 So(baseline.Project, ShouldEqual, "testproject") 96 So(baseline.BaselineID, ShouldEqual, "testrealm:linux-rel") 97 }) 98 99 Convey(`Existing baseline updated`, func() { 100 var twoHoursAgo = time.Now().UTC().Add(-time.Hour * 2) 101 row := map[string]any{ 102 "Project": "testproject", 103 "BaselineId": "try:linux-rel", 104 "LastUpdatedTime": twoHoursAgo, 105 "CreationTime": twoHoursAgo, 106 } 107 108 testutil.MustApply(ctx, 109 spanutil.InsertMap("Baselines", row), 110 ) 111 112 baseline, err := baselines.Read(span.Single(ctx), "testproject", "try:linux-rel") 113 So(err, ShouldBeNil) 114 So(baseline.LastUpdatedTime, ShouldEqual, twoHoursAgo) 115 116 inv := &pb.Invocation{ 117 Name: "invocations/a", 118 State: pb.Invocation_FINALIZED, 119 Realm: "testproject:testrealm", 120 BaselineId: "try:linux-rel", 121 } 122 123 // ensure baseline exists should update existing invocations' 124 // last updated time to now. 125 err = ensureBaselineExists(ctx, inv) 126 So(err, ShouldBeNil) 127 128 // re-read the baseline after ensuring it exists. the timestanp should 129 // be different from the original timestamp it was written into. 130 baseline, err = baselines.Read(span.Single(ctx), "testproject", "try:linux-rel") 131 So(err, ShouldBeNil) 132 So(baseline.LastUpdatedTime, ShouldNotEqual, twoHoursAgo) 133 }) 134 }) 135 } 136 137 func TestTryMarkInvocationSubmitted(t *testing.T) { 138 139 Convey(`e2e`, t, func() { 140 ctx := testutil.SpannerTestContext(t) 141 142 testutil.MustApply(ctx, testutil.CombineMutations( 143 insert.InvocationWithInclusions("inv1", pb.Invocation_FINALIZED, map[string]any{"BaselineId": "try:linux-rel"}, "inv2"), 144 insert.InvocationWithInclusions("inv2", pb.Invocation_FINALIZED, map[string]any{"BaselineId": "try:linux-rel"}), 145 )...) 146 147 // Split the test results between parent and included to ensure that the query 148 // fetches results for all included invs. 149 ms := make([]*spanner.Mutation, 0) 150 for i := 1; i <= 4000; i++ { 151 ms = append(ms, insert.TestResults("inv1", fmt.Sprintf("testId%d", i), pbutil.Variant("a", "b"), pb.TestStatus_PASS)...) 152 } 153 // TransactionLimit + 1 so we know there's overflow. 154 for i := 4001; i <= TransactionLimit+1; i++ { 155 ms = append(ms, insert.TestResults("inv2", fmt.Sprintf("testId%d", i), pbutil.Variant("a", "b"), pb.TestStatus_PASS)...) 156 } 157 158 testutil.MustApply(ctx, testutil.CombineMutations(ms)...) 159 err := tryMarkInvocationSubmitted(ctx, invocations.ID("inv1")) 160 So(err, ShouldBeNil) 161 162 // fetch the 8001 entry to ensure that test results from subinvocations 163 // that also exceed the 8000 limit are being processed 164 res, err := btv.Read(span.Single(ctx), "testproject", "try:linux-rel", "testId8001", pbutil.VariantHash(pbutil.Variant("a", "b"))) 165 So(err, ShouldBeNil) 166 So(res.Project, ShouldEqual, "testproject") 167 So(res.BaselineID, ShouldEqual, "try:linux-rel") 168 So(res.TestID, ShouldEqual, "testId8001") 169 170 // find the baseline in the baselines table 171 baseline, err := baselines.Read(span.Single(ctx), "testproject", "try:linux-rel") 172 So(err, ShouldBeNil) 173 So(res.Project, ShouldEqual, baseline.Project) 174 So(res.BaselineID, ShouldEqual, baseline.BaselineID) 175 176 Convey(`Missing baseline`, func() { 177 testutil.MustApply(ctx, testutil.CombineMutations( 178 insert.InvocationWithInclusions("a", pb.Invocation_FINALIZED, nil), 179 )...) 180 181 _, err := invocations.Read(span.Single(ctx), "a") 182 So(err, ShouldBeNil) 183 184 err = tryMarkInvocationSubmitted(ctx, invocations.ID("a")) 185 // invocations without a baseline specified will terminate early with no error. 186 So(err, ShouldBeNil) 187 }) 188 189 Convey(`Mark existing baseline test variant submitted`, func() { 190 testutil.MustApply(ctx, testutil.CombineMutations( 191 []*spanner.Mutation{insert.Invocation("inv3", pb.Invocation_FINALIZED, map[string]any{"BaselineId": "try:linux-rel"})}, 192 insert.TestResults("inv3", "testId8001", pbutil.Variant("a", "b"), pb.TestStatus_PASS), 193 )...) 194 195 err := tryMarkInvocationSubmitted(ctx, invocations.ID("inv3")) 196 So(err, ShouldBeNil) 197 198 exp := &btv.BaselineTestVariant{ 199 Project: "testproject", 200 BaselineID: "try:linux-rel", 201 TestID: "testId8001", 202 VariantHash: pbutil.VariantHash(pbutil.Variant("a", "b")), 203 } 204 205 res, err := btv.Read(span.Single(ctx), exp.Project, exp.BaselineID, exp.TestID, exp.VariantHash) 206 // zero out Timestamp 207 res.LastUpdated = time.Time{} 208 So(err, ShouldBeNil) 209 So(res, ShouldResemble, exp) 210 }) 211 212 Convey(`Mark skipped test should be skipped`, func() { 213 testutil.MustApply(ctx, testutil.CombineMutations( 214 []*spanner.Mutation{insert.Invocation("inv4", pb.Invocation_FINALIZED, map[string]any{"BaselineId": "try:linux-rel"})}, 215 insert.TestResults("inv4", "testId8002", pbutil.Variant("a", "b"), pb.TestStatus_SKIP), 216 )...) 217 218 err := tryMarkInvocationSubmitted(ctx, invocations.ID("inv4")) 219 So(err, ShouldBeNil) 220 221 exp := &btv.BaselineTestVariant{ 222 Project: "testproject", 223 BaselineID: "try:linux-rel", 224 TestID: "testId8002", 225 VariantHash: pbutil.VariantHash(pbutil.Variant("a", "b")), 226 } 227 228 _, err = btv.Read(span.Single(ctx), exp.Project, exp.BaselineID, exp.TestID, exp.VariantHash) 229 So(err, ShouldErrLike, btv.NotFound) 230 }) 231 }) 232 }