sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/bugzilla/fake.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package bugzilla 18 19 import ( 20 "errors" 21 "fmt" 22 "net/http" 23 24 "github.com/sirupsen/logrus" 25 "k8s.io/apimachinery/pkg/util/sets" 26 ) 27 28 // Fake is a fake Bugzilla client with injectable fields 29 type Fake struct { 30 EndpointString string 31 Bugs map[int]Bug 32 BugComments map[int][]Comment 33 BugErrors sets.Set[int] 34 BugErrorMessages map[int]string 35 BugCreateErrors sets.Set[string] 36 ExternalBugs map[int][]ExternalBug 37 SubComponents map[int]map[string][]string 38 SearchedBugs []*Bug 39 } 40 41 // Endpoint returns the endpoint for this fake 42 func (c *Fake) Endpoint() string { 43 return c.EndpointString 44 } 45 46 func (c *Fake) bugErrorMsg(bug int, def string) error { 47 if c.BugErrorMessages[bug] != "" { 48 return errors.New(c.BugErrorMessages[bug]) 49 } 50 return errors.New(def) 51 } 52 53 // GetBug retrieves the bug, if registered, or an error, if set, 54 // or responds with an error that matches IsNotFound 55 func (c *Fake) GetBug(id int) (*Bug, error) { 56 if c.BugErrors.Has(id) { 57 return nil, c.bugErrorMsg(id, "injected error getting bug") 58 } 59 if bug, exists := c.Bugs[id]; exists { 60 return &bug, nil 61 } 62 return nil, &requestError{statusCode: http.StatusNotFound, message: "bug not registered in the fake"} 63 } 64 65 // GetBug retrieves the external bugs for the Bugzilla bug, 66 // if registered, or an error, if set, or responds with an 67 // error that matches IsNotFound 68 func (c *Fake) GetExternalBugPRsOnBug(id int) ([]ExternalBug, error) { 69 if c.BugErrors.Has(id) { 70 return nil, c.bugErrorMsg(id, "injected error adding external bug to bug") 71 } 72 if _, exists := c.Bugs[id]; exists { 73 return c.ExternalBugs[id], nil 74 } 75 return nil, &requestError{statusCode: http.StatusNotFound, message: "bug not registered in the fake"} 76 } 77 78 // UpdateBug updates the bug, if registered, or an error, if set, 79 // or responds with an error that matches IsNotFound 80 func (c *Fake) UpdateBug(id int, update BugUpdate) error { 81 if c.BugErrors.Has(id) { 82 return c.bugErrorMsg(id, "injected error updating bug") 83 } 84 bug, exists := c.Bugs[id] 85 if !exists { 86 return &requestError{statusCode: http.StatusNotFound, message: "bug not registered in the fake"} 87 } 88 bug.Status = update.Status 89 bug.Resolution = update.Resolution 90 if update.Version != "" { 91 bug.Version = []string{update.Version} 92 } 93 if update.TargetRelease != nil { 94 bug.TargetRelease = update.TargetRelease 95 } 96 if update.DependsOn != nil { 97 if len(update.DependsOn.Set) > 0 { 98 bug.DependsOn = update.DependsOn.Set 99 } else { 100 bug.DependsOn = sets.List(sets.New[int](bug.DependsOn...).Insert(update.DependsOn.Add...).Delete(update.DependsOn.Remove...)) 101 } 102 for _, blockerID := range bug.DependsOn { 103 blockerBug := c.Bugs[blockerID] 104 blockerBug.Blocks = append(blockerBug.Blocks, id) 105 c.Bugs[blockerID] = blockerBug 106 } 107 } 108 if update.Blocks != nil { 109 if len(update.Blocks.Set) > 0 { 110 bug.Blocks = update.Blocks.Set 111 } else { 112 bug.Blocks = sets.List(sets.New[int](bug.Blocks...).Insert(update.Blocks.Add...).Delete(update.Blocks.Remove...)) 113 } 114 for _, blockerID := range bug.Blocks { 115 blockerBug := c.Bugs[blockerID] 116 blockerBug.DependsOn = append(blockerBug.DependsOn, id) 117 c.Bugs[blockerID] = blockerBug 118 } 119 } 120 c.Bugs[id] = bug 121 return nil 122 } 123 124 // AddPullRequestAsExternalBug adds an external bug to the Bugzilla bug, 125 // if registered, or an error, if set, or responds with an error that 126 // matches IsNotFound 127 func (c *Fake) AddPullRequestAsExternalBug(id int, org, repo string, num int) (bool, error) { 128 if c.BugErrors.Has(id) { 129 return false, c.bugErrorMsg(id, "injected error adding external bug to bug") 130 } 131 if _, exists := c.Bugs[id]; exists { 132 pullIdentifier := IdentifierForPull(org, repo, num) 133 for _, bug := range c.ExternalBugs[id] { 134 if bug.BugzillaBugID == id && bug.ExternalBugID == pullIdentifier { 135 return false, nil 136 } 137 } 138 c.ExternalBugs[id] = append(c.ExternalBugs[id], ExternalBug{ 139 BugzillaBugID: id, 140 ExternalBugID: pullIdentifier, 141 }) 142 return true, nil 143 } 144 return false, &requestError{statusCode: http.StatusNotFound, message: "bug not registered in the fake"} 145 } 146 147 // RemovePullRequestAsExternalBug removes an external bug from the Bugzilla bug, 148 // if registered, or an error, if set, or responds with an error that 149 // matches IsNotFound 150 func (c *Fake) RemovePullRequestAsExternalBug(id int, org, repo string, num int) (bool, error) { 151 if c.BugErrors.Has(id) { 152 return false, c.bugErrorMsg(id, "injected error removing external bug from bug") 153 } 154 if _, exists := c.Bugs[id]; exists { 155 pullIdentifier := IdentifierForPull(org, repo, num) 156 toRemove := -1 157 for i, bug := range c.ExternalBugs[id] { 158 if bug.BugzillaBugID == id && bug.ExternalBugID == pullIdentifier { 159 toRemove = i 160 break 161 } 162 } 163 if toRemove != -1 { 164 c.ExternalBugs[id] = append(c.ExternalBugs[id][:toRemove], c.ExternalBugs[id][toRemove+1:]...) 165 return true, nil 166 } 167 return false, nil 168 } 169 return false, &requestError{statusCode: http.StatusNotFound, message: "bug not registered in the fake"} 170 } 171 172 // CreateBug creates a new bug and associated description comment given a BugCreate or and error 173 // if description is in BugCreateErrors set 174 func (c *Fake) CreateBug(bug *BugCreate) (int, error) { 175 if c.BugCreateErrors.Has(bug.Description) { 176 return 0, errors.New("injected error creating new bug") 177 } 178 // add new bug one ID newer than highest existing BugID 179 newID := 0 180 for k := range c.Bugs { 181 if k >= newID { 182 newID = k + 1 183 } 184 } 185 newBug := Bug{ 186 Alias: bug.Alias, 187 AssignedTo: bug.AssignedTo, 188 CC: bug.CC, 189 Component: bug.Component, 190 Flags: bug.Flags, 191 Groups: bug.Groups, 192 ID: newID, 193 Keywords: bug.Keywords, 194 OperatingSystem: bug.OperatingSystem, 195 Platform: bug.Platform, 196 Priority: bug.Priority, 197 Product: bug.Product, 198 QAContact: bug.QAContact, 199 Resolution: bug.Resolution, 200 Severity: bug.Severity, 201 Status: bug.Status, 202 Summary: bug.Summary, 203 TargetMilestone: bug.TargetMilestone, 204 Version: bug.Version, 205 TargetRelease: bug.TargetRelease, 206 } 207 c.Bugs[newID] = newBug 208 // add new comment one ID newer than highest existing CommentID 209 newCommentID := 0 210 for _, comments := range c.BugComments { 211 for _, comment := range comments { 212 if comment.ID >= newCommentID { 213 newCommentID = comment.ID + 1 214 } 215 } 216 } 217 newComments := []Comment{{ 218 ID: newCommentID, 219 BugID: newID, 220 Count: 0, 221 Text: bug.Description, 222 IsMarkdown: bug.IsMarkdown, 223 IsPrivate: bug.CommentIsPrivate, 224 Tags: bug.CommentTags, 225 }} 226 c.BugComments[newID] = newComments 227 if bug.SubComponents != nil { 228 c.SubComponents[newID] = bug.SubComponents 229 } 230 return newID, nil 231 } 232 233 func (c *Fake) CreateComment(comment *CommentCreate) (int, error) { 234 // add new comment one ID newer than highest existing CommentID 235 newCommentID := 0 236 for _, comments := range c.BugComments { 237 for _, comment := range comments { 238 if comment.ID >= newCommentID { 239 newCommentID = comment.ID + 1 240 } 241 } 242 } 243 newComment := Comment{ 244 ID: newCommentID, 245 BugID: comment.ID, 246 Count: len(c.BugComments[comment.ID]), 247 Text: comment.Comment, 248 IsPrivate: comment.IsPrivate, 249 } 250 c.BugComments[comment.ID] = append(c.BugComments[comment.ID], newComment) 251 return newCommentID, nil 252 } 253 254 // GetComments retrieves the bug comments, if registered, or an error, if set, 255 // or responds with an error that matches IsNotFound 256 func (c *Fake) GetComments(id int) ([]Comment, error) { 257 if c.BugErrors.Has(id) { 258 return nil, c.bugErrorMsg(id, "injected error getting bug comments") 259 } 260 if comments, exists := c.BugComments[id]; exists { 261 return comments, nil 262 } 263 return nil, &requestError{statusCode: http.StatusNotFound, message: fmt.Sprintf("bug comments for id %d not registered in the fake", id)} 264 } 265 266 // CloneBug clones a bug by creating a new bug with the same fields, copying the description, and updating the bug to depend on the original bug 267 func (c *Fake) CloneBug(bug *Bug, mutations ...func(bug *BugCreate)) (int, error) { 268 return clone(c, bug, mutations) 269 } 270 271 func (c *Fake) GetSubComponentsOnBug(id int) (map[string][]string, error) { 272 if c.BugErrors.Has(id) { 273 return nil, errors.New("injected error getting bug subcomponents") 274 } 275 return c.SubComponents[id], nil 276 } 277 278 func (c *Fake) GetClones(bug *Bug) ([]*Bug, error) { 279 if c.BugErrors.Has(bug.ID) { 280 return nil, errors.New("injected error getting subcomponents") 281 } 282 return getClones(c, bug) 283 } 284 285 // GetAllClones gets all clones including its parents and children spanning multiple levels 286 func (c *Fake) GetAllClones(bug *Bug) ([]*Bug, error) { 287 if c.BugErrors.Has(bug.ID) { 288 return nil, errors.New("injected error getting subcomponents") 289 } 290 bugCache := newBugDetailsCache() 291 return getAllClones(c, bug, bugCache) 292 } 293 294 // GetRootForClone gets the original bug. 295 func (c *Fake) GetRootForClone(bug *Bug) (*Bug, error) { 296 if c.BugErrors.Has(bug.ID) { 297 return nil, errors.New("injected error getting bug") 298 } 299 return getRootForClone(c, bug) 300 } 301 302 func (c *Fake) SearchBugs(filters map[string]string) ([]*Bug, error) { 303 return c.SearchedBugs, nil 304 } 305 306 // SetRoundTripper sets the Transport in http.Client to a custom RoundTripper 307 func (c *Fake) SetRoundTripper(t http.RoundTripper) { 308 // Do nothing here 309 } 310 311 func (c *Fake) ForPlugin(plugin string) Client { return c } 312 func (c *Fake) ForSubcomponent(subcomponent string) Client { return c } 313 func (c *Fake) WithFields(fields logrus.Fields) Client { return c } 314 func (c *Fake) Used() bool { return true } 315 316 // the Fake is a Client 317 var _ Client = &Fake{}