go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/resultdb/internal/testutil/insert/insert.go (about) 1 // Copyright 2019 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 insert implements functions to insert rows for testing purposes. 16 package insert 17 18 import ( 19 "fmt" 20 "strconv" 21 "time" 22 23 "cloud.google.com/go/spanner" 24 . "github.com/smartystreets/goconvey/convey" 25 "google.golang.org/protobuf/proto" 26 durpb "google.golang.org/protobuf/types/known/durationpb" 27 28 "go.chromium.org/luci/resultdb/internal/invocations" 29 "go.chromium.org/luci/resultdb/internal/spanutil" 30 "go.chromium.org/luci/resultdb/internal/testmetadata" 31 "go.chromium.org/luci/resultdb/pbutil" 32 pb "go.chromium.org/luci/resultdb/proto/v1" 33 ) 34 35 // TestRealm is the default realm used for invocation mutations returned by Invocation(). 36 const TestRealm = "testproject:testrealm" 37 38 func updateDict(dest, source map[string]any) { 39 for k, v := range source { 40 dest[k] = v 41 } 42 } 43 44 // Invocation returns a spanner mutation that inserts an invocation. 45 func Invocation(id invocations.ID, state pb.Invocation_State, extraValues map[string]any) *spanner.Mutation { 46 future := time.Date(2050, 1, 1, 0, 0, 0, 0, time.UTC) 47 values := map[string]any{ 48 "InvocationId": id, 49 "ShardId": 0, 50 "State": state, 51 "Realm": TestRealm, 52 "InvocationExpirationTime": future, 53 "ExpectedTestResultsExpirationTime": future, 54 "CreateTime": spanner.CommitTimestamp, 55 "Deadline": future, 56 } 57 58 if state == pb.Invocation_FINALIZED { 59 values["FinalizeTime"] = spanner.CommitTimestamp 60 } 61 updateDict(values, extraValues) 62 return spanutil.InsertMap("Invocations", values) 63 } 64 65 // FinalizedInvocationWithInclusions returns mutations to insert a finalized invocation with inclusions. 66 func FinalizedInvocationWithInclusions(id invocations.ID, extraValues map[string]any, included ...invocations.ID) []*spanner.Mutation { 67 return InvocationWithInclusions(id, pb.Invocation_FINALIZED, extraValues, included...) 68 } 69 70 // InvocationWithInclusions returns mutations to insert an invocation with inclusions. 71 func InvocationWithInclusions(id invocations.ID, state pb.Invocation_State, extraValues map[string]any, included ...invocations.ID) []*spanner.Mutation { 72 ms := []*spanner.Mutation{Invocation(id, state, extraValues)} 73 for _, incl := range included { 74 ms = append(ms, Inclusion(id, incl)) 75 } 76 return ms 77 } 78 79 // Inclusion returns a spanner mutation that inserts an inclusion. 80 func Inclusion(including, included invocations.ID) *spanner.Mutation { 81 return spanutil.InsertMap("IncludedInvocations", map[string]any{ 82 "InvocationId": including, 83 "IncludedInvocationId": included, 84 }) 85 } 86 87 // TestResults returns spanner mutations to insert test results 88 func TestResults(invID, testID string, v *pb.Variant, statuses ...pb.TestStatus) []*spanner.Mutation { 89 return TestResultMessages(MakeTestResults(invID, testID, v, statuses...)) 90 } 91 92 // TestResultMessages returns spanner mutations to insert test results 93 func TestResultMessages(trs []*pb.TestResult) []*spanner.Mutation { 94 ms := make([]*spanner.Mutation, len(trs)) 95 for i, tr := range trs { 96 invID, testID, resultID, err := pbutil.ParseTestResultName(tr.Name) 97 So(err, ShouldBeNil) 98 mutMap := map[string]any{ 99 "InvocationId": invocations.ID(invID), 100 "TestId": testID, 101 "ResultId": resultID, 102 "Variant": trs[i].Variant, 103 "VariantHash": pbutil.VariantHash(trs[i].Variant), 104 "CommitTimestamp": spanner.CommitTimestamp, 105 "Status": tr.Status, 106 "RunDurationUsec": 1e6*i + 234567, 107 "SummaryHtml": spanutil.Compressed("SummaryHtml"), 108 } 109 if tr.SkipReason != pb.SkipReason_SKIP_REASON_UNSPECIFIED { 110 mutMap["SkipReason"] = tr.SkipReason 111 } 112 if !trs[i].Expected { 113 mutMap["IsUnexpected"] = true 114 } 115 if tr.TestMetadata != nil { 116 tmdBytes, err := proto.Marshal(tr.TestMetadata) 117 So(err, ShouldBeNil) 118 mutMap["TestMetadata"] = spanutil.Compressed(tmdBytes) 119 } 120 if tr.FailureReason != nil { 121 frBytes, err := proto.Marshal(tr.FailureReason) 122 So(err, ShouldBeNil) 123 mutMap["FailureReason"] = spanutil.Compressed(frBytes) 124 } 125 if tr.Properties != nil { 126 propertiesBytes, err := proto.Marshal(tr.Properties) 127 So(err, ShouldBeNil) 128 mutMap["Properties"] = spanutil.Compressed(propertiesBytes) 129 } 130 131 ms[i] = spanutil.InsertMap("TestResults", mutMap) 132 } 133 return ms 134 } 135 136 // TestExonerations returns Spanner mutations to insert test exonerations. 137 func TestExonerations(invID invocations.ID, testID string, variant *pb.Variant, reasons ...pb.ExonerationReason) []*spanner.Mutation { 138 ms := make([]*spanner.Mutation, len(reasons)) 139 for i := 0; i < len(reasons); i++ { 140 ms[i] = spanutil.InsertMap("TestExonerations", map[string]any{ 141 "InvocationId": invID, 142 "TestId": testID, 143 "ExonerationId": strconv.Itoa(i), 144 "Variant": variant, 145 "VariantHash": pbutil.VariantHash(variant), 146 "ExplanationHTML": spanutil.Compressed(fmt.Sprintf("explanation %d", i)), 147 "Reason": reasons[i], 148 }) 149 } 150 return ms 151 } 152 153 // Artifact returns a Spanner mutation to insert an artifact. 154 func Artifact(invID invocations.ID, parentID, artID string, extraValues map[string]any) *spanner.Mutation { 155 values := map[string]any{ 156 "InvocationId": invID, 157 "ParentID": parentID, 158 "ArtifactId": artID, 159 } 160 updateDict(values, extraValues) 161 return spanutil.InsertMap("Artifacts", values) 162 } 163 164 func TestMetadataRows(rows []*testmetadata.TestMetadataRow) []*spanner.Mutation { 165 ms := make([]*spanner.Mutation, len(rows)) 166 for i, row := range rows { 167 mutMap := map[string]any{ 168 "Project": row.Project, 169 "TestId": row.TestID, 170 "SubRealm": row.SubRealm, 171 "RefHash": row.RefHash, 172 "LastUpdated": row.LastUpdated, 173 "TestMetadata": spanutil.Compressed(pbutil.MustMarshal(row.TestMetadata)), 174 "SourceRef": spanutil.Compressed(pbutil.MustMarshal(row.SourceRef)), 175 "Position": int64(row.Position), 176 } 177 ms[i] = spanutil.InsertMap("TestMetadata", mutMap) 178 } 179 return ms 180 } 181 182 func MakeTestMetadataRow(project, testID, subRealm string, refHash []byte) *testmetadata.TestMetadataRow { 183 return &testmetadata.TestMetadataRow{ 184 Project: project, 185 TestID: testID, 186 RefHash: refHash, 187 SubRealm: subRealm, 188 LastUpdated: time.Time{}, 189 TestMetadata: &pb.TestMetadata{ 190 Name: project + "\n" + testID + "\n" + subRealm + "\n" + string(refHash), 191 Location: &pb.TestLocation{ 192 Repo: "testRepo", 193 FileName: "testFile", 194 Line: 0, 195 }, 196 BugComponent: &pb.BugComponent{}, 197 }, 198 SourceRef: &pb.SourceRef{ 199 System: &pb.SourceRef_Gitiles{ 200 Gitiles: &pb.GitilesRef{Host: "testHost"}, 201 }, 202 }, 203 Position: 0, 204 } 205 } 206 207 // MakeTestResults creates test results. 208 func MakeTestResults(invID, testID string, v *pb.Variant, statuses ...pb.TestStatus) []*pb.TestResult { 209 trs := make([]*pb.TestResult, len(statuses)) 210 for i, status := range statuses { 211 resultID := fmt.Sprintf("%d", i) 212 trs[i] = &pb.TestResult{ 213 Name: pbutil.TestResultName(invID, testID, resultID), 214 TestId: testID, 215 ResultId: resultID, 216 Variant: v, 217 VariantHash: pbutil.VariantHash(v), 218 Expected: status == pb.TestStatus_PASS, 219 Status: status, 220 Duration: &durpb.Duration{Seconds: int64(i), Nanos: 234567000}, 221 SummaryHtml: "SummaryHtml", 222 } 223 } 224 return trs 225 }