go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/resultdb/internal/services/purger/purger_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 purger 16 17 import ( 18 "context" 19 "fmt" 20 "strconv" 21 "testing" 22 "time" 23 24 "cloud.google.com/go/spanner" 25 durpb "google.golang.org/protobuf/types/known/durationpb" 26 27 "go.chromium.org/luci/common/clock" 28 "go.chromium.org/luci/server/span" 29 30 "go.chromium.org/luci/resultdb/internal/artifacts" 31 "go.chromium.org/luci/resultdb/internal/invocations" 32 "go.chromium.org/luci/resultdb/internal/spanutil" 33 "go.chromium.org/luci/resultdb/internal/testutil" 34 "go.chromium.org/luci/resultdb/internal/testutil/insert" 35 "go.chromium.org/luci/resultdb/pbutil" 36 pb "go.chromium.org/luci/resultdb/proto/v1" 37 38 . "github.com/smartystreets/goconvey/convey" 39 ) 40 41 // makeTestResultsWithVariants creates test results with a number of passing/failing variants. 42 // 43 // There'll be two rows for each variant. If the variant is a passing variant, both results 44 // will have a passing status, otherwise the first will be passing, and the second faled. 45 func makeTestResultsWithVariants(invID, testID string, nPassingVariants, nFailedVariants int) []*pb.TestResult { 46 nVariants := nPassingVariants + nFailedVariants 47 // For every variant we'll generate two rows. 48 statuses := []pb.TestStatus{pb.TestStatus_PASS, pb.TestStatus_PASS} 49 trs := make([]*pb.TestResult, nVariants*len(statuses)) 50 for v := 0; v < nVariants; v++ { 51 // We'll generate all passing variants first, so we only have to change the 52 // list of statuses once. 53 if v == nPassingVariants { 54 statuses[len(statuses)-1] = pb.TestStatus_FAIL 55 } 56 for s, status := range statuses { 57 rIndex := v*len(statuses) + s 58 resultID := fmt.Sprintf("%d", rIndex) 59 trs[rIndex] = &pb.TestResult{ 60 Name: pbutil.TestResultName(invID, testID, resultID), 61 TestId: testID, 62 ResultId: resultID, 63 Variant: pbutil.Variant("k1", "v1", "k2", fmt.Sprintf("v%d", v)), 64 Expected: status == pb.TestStatus_PASS, 65 Status: status, 66 Duration: &durpb.Duration{Seconds: int64(rIndex), Nanos: 234567000}, 67 } 68 } 69 } 70 return trs 71 } 72 73 func insertInvocation(ctx context.Context, invID invocations.ID, nTests, nPassingVariants, nFailedVariants, nArtifactsPerResult int) invocations.ID { 74 now := clock.Now(ctx).UTC() 75 76 // Insert an invocation, 77 testutil.MustApply(ctx, insert.Invocation(invID, pb.Invocation_FINALIZED, map[string]any{ 78 "ExpectedTestResultsExpirationTime": now.Add(-time.Minute), 79 "CreateTime": now.Add(-time.Hour), 80 "FinalizeTime": now.Add(-time.Hour), 81 })) 82 83 // Insert test results and artifacts. 84 inserts := []*spanner.Mutation{} 85 for i := 0; i < nTests; i++ { 86 results := makeTestResultsWithVariants(string(invID), fmt.Sprintf("Test%d", i), nPassingVariants, nFailedVariants) 87 inserts = append(inserts, insert.TestResultMessages(results)...) 88 for _, res := range results { 89 for j := 0; j < nArtifactsPerResult; j++ { 90 inserts = append(inserts, spanutil.InsertMap("Artifacts", map[string]any{ 91 "InvocationId": invID, 92 "ParentId": artifacts.ParentID(res.TestId, res.ResultId), 93 "ArtifactId": strconv.Itoa(j), 94 })) 95 } 96 } 97 } 98 testutil.MustApply(ctx, testutil.CombineMutations(inserts)...) 99 return invID 100 } 101 102 func countRows(ctx context.Context, invID invocations.ID) (testResults, artifacts int64) { 103 st := spanner.NewStatement(` 104 SELECT 105 (SELECT COUNT(*) FROM TestResults WHERE InvocationId = @invocationId), 106 (SELECT COUNT(*) FROM Artifacts WHERE InvocationId = @invocationId), 107 `) 108 st.Params["invocationId"] = spanutil.ToSpanner(invID) 109 So(spanutil.QueryFirstRow(span.Single(ctx), st, &testResults, &artifacts), ShouldBeNil) 110 return 111 } 112 113 func TestPurgeExpiredResults(t *testing.T) { 114 Convey(`TestPurgeExpiredResults`, t, func() { 115 ctx := testutil.SpannerTestContext(t) 116 117 Convey(`Some results are unexpected`, func() { 118 inv := insertInvocation(ctx, "inv-some-unexpected", 10, 9, 1, 0) 119 120 err := purgeOneShard(ctx, 0) 121 So(err, ShouldBeNil) 122 123 // 10 tests * 1 variant with unexpected results * 2 results per test variant. 124 testResults, _ := countRows(ctx, inv) 125 So(testResults, ShouldEqual, 20) 126 }) 127 128 Convey(`No unexpected results`, func() { 129 inv := insertInvocation(ctx, "inv-no-unexpected", 10, 10, 0, 0) 130 131 err := purgeOneShard(ctx, 0) 132 So(err, ShouldBeNil) 133 134 // 10 tests * 0 variants with unexpected results * 2 results per test variant. 135 testResults, _ := countRows(ctx, inv) 136 So(testResults, ShouldEqual, 0) 137 }) 138 139 Convey(`Purge artifacts`, func() { 140 inv := insertInvocation(ctx, "inv", 10, 9, 1, 10) 141 142 err := purgeOneShard(ctx, 0) 143 So(err, ShouldBeNil) 144 145 // 10 tests * 1 variant with unexpected results * 2 results per test variant * 10 artifacts.. 146 _, artifacts := countRows(ctx, inv) 147 So(artifacts, ShouldEqual, 200) 148 }) 149 }) 150 }