go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/analysis/internal/ingestion/control/testutils.go (about) 1 // Copyright 2022 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 control 16 17 import ( 18 "context" 19 "fmt" 20 "strings" 21 "time" 22 23 "cloud.google.com/go/spanner" 24 "google.golang.org/protobuf/types/known/timestamppb" 25 26 buildbucketpb "go.chromium.org/luci/buildbucket/proto" 27 "go.chromium.org/luci/server/span" 28 29 controlpb "go.chromium.org/luci/analysis/internal/ingestion/control/proto" 30 spanutil "go.chromium.org/luci/analysis/internal/span" 31 "go.chromium.org/luci/analysis/internal/testutil" 32 pb "go.chromium.org/luci/analysis/proto/v1" 33 ) 34 35 // EntryBuilder provides methods to build ingestion control records. 36 type EntryBuilder struct { 37 record *Entry 38 } 39 40 // NewEntry starts building a new Entry. 41 func NewEntry(uniqifier int) *EntryBuilder { 42 return &EntryBuilder{ 43 record: &Entry{ 44 BuildID: fmt.Sprintf("buildbucket-host/%v", uniqifier), 45 BuildProject: "build-project", 46 BuildResult: &controlpb.BuildResult{ 47 Host: "buildbucket-host", 48 Id: int64(uniqifier), 49 CreationTime: timestamppb.New(time.Date(2025, time.December, 1, 1, 2, 3, uniqifier*1000, time.UTC)), 50 Project: "myproject", 51 Builder: "builder", 52 Status: pb.BuildStatus_BUILD_STATUS_SUCCESS, 53 Changelists: []*pb.Changelist{ 54 { 55 Host: "myhost-review.googlesource.com", 56 Change: 123456, 57 Patchset: 123, 58 OwnerKind: pb.ChangelistOwnerKind_AUTOMATION, 59 }, 60 }, 61 Commit: &buildbucketpb.GitilesCommit{ 62 Host: "myproject.googlesource.com", 63 Project: "someproject/src", 64 Id: strings.Repeat("0a", 20), 65 Ref: "refs/heads/mybranch", 66 Position: 111888, 67 }, 68 HasInvocation: true, 69 ResultdbHost: "resultdb_host", 70 IsIncludedByAncestor: true, 71 }, 72 BuildJoinedTime: time.Date(2020, time.December, 11, 1, 1, 1, uniqifier*1000, time.UTC), 73 HasInvocation: true, 74 InvocationProject: "invocation-project", 75 InvocationResult: &controlpb.InvocationResult{}, 76 InvocationJoinedTime: time.Date(2020, time.December, 12, 1, 1, 1, uniqifier*1000, time.UTC), 77 IsPresubmit: true, 78 PresubmitProject: "presubmit-project", 79 PresubmitResult: &controlpb.PresubmitResult{ 80 PresubmitRunId: &pb.PresubmitRunId{ 81 System: "luci-cv", 82 Id: fmt.Sprintf("%s/123123-%v", "presubmit-project", uniqifier), 83 }, 84 Status: pb.PresubmitRunStatus_PRESUBMIT_RUN_STATUS_SUCCEEDED, 85 Mode: pb.PresubmitRunMode_QUICK_DRY_RUN, 86 Owner: "automation", 87 CreationTime: timestamppb.New(time.Date(2026, time.December, 1, 1, 2, 3, uniqifier*1000, time.UTC)), 88 }, 89 PresubmitJoinedTime: time.Date(2020, time.December, 13, 1, 1, 1, uniqifier*1000, time.UTC), 90 LastUpdated: time.Date(2020, time.December, 14, 1, 1, 1, uniqifier*1000, time.UTC), 91 TaskCount: int64(uniqifier), 92 }, 93 } 94 } 95 96 // WithBuildID specifies the build ID to use on the ingestion control record. 97 func (b *EntryBuilder) WithBuildID(id string) *EntryBuilder { 98 b.record.BuildID = id 99 return b 100 } 101 102 // WithBuildProject specifies the build project to use on the ingestion control record. 103 func (b *EntryBuilder) WithBuildProject(project string) *EntryBuilder { 104 b.record.BuildProject = project 105 return b 106 } 107 108 // WithBuildResult specifies the build result for the entry. 109 func (b *EntryBuilder) WithBuildResult(value *controlpb.BuildResult) *EntryBuilder { 110 b.record.BuildResult = value 111 return b 112 } 113 114 // WithBuildJoinedTime specifies the time the build result was populated. 115 func (b *EntryBuilder) WithBuildJoinedTime(value time.Time) *EntryBuilder { 116 b.record.BuildJoinedTime = value 117 return b 118 } 119 120 // WithHasInvocation specifies whether the build that is the subject of the ingestion 121 // has a ResultDB invocation. 122 func (b *EntryBuilder) WithHasInvocation(hasInvocation bool) *EntryBuilder { 123 b.record.HasInvocation = hasInvocation 124 return b 125 } 126 127 // WithInvocationProject specifies the invocation project to use on the ingestion control record. 128 func (b *EntryBuilder) WithInvocationProject(project string) *EntryBuilder { 129 b.record.InvocationProject = project 130 return b 131 } 132 133 // WithInvocationResult specifies the invocation result for the entry. 134 func (b *EntryBuilder) WithInvocationResult(value *controlpb.InvocationResult) *EntryBuilder { 135 b.record.InvocationResult = value 136 return b 137 } 138 139 // WithInvocationJoinedTime specifies the time the invocation result was populated. 140 func (b *EntryBuilder) WithInvocationJoinedTime(value time.Time) *EntryBuilder { 141 b.record.InvocationJoinedTime = value 142 return b 143 } 144 145 // WithIsPresubmit specifies whether the ingestion relates to a presubmit run. 146 func (b *EntryBuilder) WithIsPresubmit(isPresubmit bool) *EntryBuilder { 147 b.record.IsPresubmit = isPresubmit 148 return b 149 } 150 151 // WithPresubmitProject specifies the presubmit project to use on the ingestion control record. 152 func (b *EntryBuilder) WithPresubmitProject(project string) *EntryBuilder { 153 b.record.PresubmitProject = project 154 return b 155 } 156 157 // WithPresubmitResult specifies the build result for the entry. 158 func (b *EntryBuilder) WithPresubmitResult(value *controlpb.PresubmitResult) *EntryBuilder { 159 b.record.PresubmitResult = value 160 return b 161 } 162 163 // WithPresubmitJoinedTime specifies the time the presubmit result was populated. 164 func (b *EntryBuilder) WithPresubmitJoinedTime(lastUpdated time.Time) *EntryBuilder { 165 b.record.PresubmitJoinedTime = lastUpdated 166 return b 167 } 168 169 func (b *EntryBuilder) WithTaskCount(taskCount int64) *EntryBuilder { 170 b.record.TaskCount = taskCount 171 return b 172 } 173 174 // Build constructs the entry. 175 func (b *EntryBuilder) Build() *Entry { 176 return b.record 177 } 178 179 // SetEntriesForTesting replaces the set of stored entries to match the given set. 180 func SetEntriesForTesting(ctx context.Context, es ...*Entry) (time.Time, error) { 181 testutil.MustApply(ctx, 182 spanner.Delete("Ingestions", spanner.AllKeys())) 183 // Insert some Ingestion records. 184 commitTime, err := span.ReadWriteTransaction(ctx, func(ctx context.Context) error { 185 for _, r := range es { 186 ms := spanutil.InsertMap("Ingestions", map[string]any{ 187 "BuildId": r.BuildID, 188 "BuildProject": r.BuildProject, 189 "BuildResult": r.BuildResult, 190 "BuildJoinedTime": r.BuildJoinedTime, 191 "HasInvocation": r.HasInvocation, 192 "InvocationProject": r.InvocationProject, 193 "InvocationResult": r.InvocationResult, 194 "InvocationJoinedTime": r.InvocationJoinedTime, 195 "IsPresubmit": r.IsPresubmit, 196 "PresubmitProject": r.PresubmitProject, 197 "PresubmitResult": r.PresubmitResult, 198 "PresubmitJoinedTime": r.PresubmitJoinedTime, 199 "LastUpdated": r.LastUpdated, 200 "TaskCount": r.TaskCount, 201 }) 202 span.BufferWrite(ctx, ms) 203 } 204 return nil 205 }) 206 return commitTime.In(time.UTC), err 207 }