github.com/abayer/test-infra@v0.0.5/prow/github/fakegithub/fakegithub.go (about) 1 /* 2 Copyright 2016 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 fakegithub 18 19 import ( 20 "fmt" 21 "regexp" 22 23 "k8s.io/test-infra/prow/github" 24 ) 25 26 const botName = "k8s-ci-robot" 27 const Bot = botName 28 29 // FakeClient is like client, but fake. 30 type FakeClient struct { 31 Issues []github.Issue 32 OrgMembers map[string][]string 33 Collaborators []string 34 IssueComments map[int][]github.IssueComment 35 IssueCommentID int 36 PullRequests map[int]*github.PullRequest 37 PullRequestChanges map[int][]github.PullRequestChange 38 PullRequestComments map[int][]github.ReviewComment 39 ReviewID int 40 Reviews map[int][]github.Review 41 CombinedStatuses map[string]*github.CombinedStatus 42 CreatedStatuses map[string][]github.Status 43 IssueEvents map[int][]github.ListedIssueEvent 44 45 //All Labels That Exist In The Repo 46 ExistingLabels []string 47 // org/repo#number:label 48 LabelsAdded []string 49 LabelsRemoved []string 50 51 // org/repo#number:body 52 IssueCommentsAdded []string 53 // org/repo#issuecommentid 54 IssueCommentsDeleted []string 55 56 // org/repo#issuecommentid:reaction 57 IssueReactionsAdded []string 58 CommentReactionsAdded []string 59 60 // org/repo#number:assignee 61 AssigneesAdded []string 62 63 // org/repo#number:milestone (represents the milestone for a specific issue) 64 Milestone int 65 MilestoneMap map[string]int 66 67 // Fake remote git storage. File name are keys 68 // and values map SHA to content 69 RemoteFiles map[string]map[string]string 70 } 71 72 // BotName returns authenticated login. 73 func (f *FakeClient) BotName() (string, error) { 74 return botName, nil 75 } 76 77 // IsMember returns true if user is in org. 78 func (f *FakeClient) IsMember(org, user string) (bool, error) { 79 for _, m := range f.OrgMembers[org] { 80 if m == user { 81 return true, nil 82 } 83 } 84 return false, nil 85 } 86 87 // ListIssueComments returns comments. 88 func (f *FakeClient) ListIssueComments(owner, repo string, number int) ([]github.IssueComment, error) { 89 return append([]github.IssueComment{}, f.IssueComments[number]...), nil 90 } 91 92 // ListPullRequestComments returns review comments. 93 func (f *FakeClient) ListPullRequestComments(owner, repo string, number int) ([]github.ReviewComment, error) { 94 return append([]github.ReviewComment{}, f.PullRequestComments[number]...), nil 95 } 96 97 // ListReviews returns reviews. 98 func (f *FakeClient) ListReviews(owner, repo string, number int) ([]github.Review, error) { 99 return append([]github.Review{}, f.Reviews[number]...), nil 100 } 101 102 // ListIssueEvents returns issue events 103 func (f *FakeClient) ListIssueEvents(owner, repo string, number int) ([]github.ListedIssueEvent, error) { 104 return append([]github.ListedIssueEvent{}, f.IssueEvents[number]...), nil 105 } 106 107 // CreateComment adds a comment to a PR 108 func (f *FakeClient) CreateComment(owner, repo string, number int, comment string) error { 109 f.IssueCommentsAdded = append(f.IssueCommentsAdded, fmt.Sprintf("%s/%s#%d:%s", owner, repo, number, comment)) 110 f.IssueComments[number] = append(f.IssueComments[number], github.IssueComment{ 111 ID: f.IssueCommentID, 112 Body: comment, 113 User: github.User{Login: botName}, 114 }) 115 f.IssueCommentID++ 116 return nil 117 } 118 119 // CreateReview adds a review to a PR 120 func (f *FakeClient) CreateReview(org, repo string, number int, r github.DraftReview) error { 121 f.Reviews[number] = append(f.Reviews[number], github.Review{ 122 ID: f.ReviewID, 123 User: github.User{Login: botName}, 124 Body: r.Body, 125 }) 126 f.ReviewID++ 127 return nil 128 } 129 130 // CreateCommentReaction adds emoji to a comment. 131 func (f *FakeClient) CreateCommentReaction(org, repo string, ID int, reaction string) error { 132 f.CommentReactionsAdded = append(f.CommentReactionsAdded, fmt.Sprintf("%s/%s#%d:%s", org, repo, ID, reaction)) 133 return nil 134 } 135 136 // CreateIssueReaction adds an emoji to an issue. 137 func (f *FakeClient) CreateIssueReaction(org, repo string, ID int, reaction string) error { 138 f.IssueReactionsAdded = append(f.IssueReactionsAdded, fmt.Sprintf("%s/%s#%d:%s", org, repo, ID, reaction)) 139 return nil 140 } 141 142 // DeleteComment deletes a comment. 143 func (f *FakeClient) DeleteComment(owner, repo string, ID int) error { 144 f.IssueCommentsDeleted = append(f.IssueCommentsDeleted, fmt.Sprintf("%s/%s#%d", owner, repo, ID)) 145 for num, ics := range f.IssueComments { 146 for i, ic := range ics { 147 if ic.ID == ID { 148 f.IssueComments[num] = append(ics[:i], ics[i+1:]...) 149 return nil 150 } 151 } 152 } 153 return fmt.Errorf("could not find issue comment %d", ID) 154 } 155 156 // DeleteStaleComments deletes comments flagged by isStale. 157 func (f *FakeClient) DeleteStaleComments(org, repo string, number int, comments []github.IssueComment, isStale func(github.IssueComment) bool) error { 158 if comments == nil { 159 comments, _ = f.ListIssueComments(org, repo, number) 160 } 161 for _, comment := range comments { 162 if isStale(comment) { 163 if err := f.DeleteComment(org, repo, comment.ID); err != nil { 164 return fmt.Errorf("failed to delete stale comment with ID '%d'", comment.ID) 165 } 166 } 167 } 168 return nil 169 } 170 171 // GetPullRequest returns details about the PR. 172 func (f *FakeClient) GetPullRequest(owner, repo string, number int) (*github.PullRequest, error) { 173 return f.PullRequests[number], nil 174 } 175 176 // GetPullRequestChanges returns the file modifications in a PR. 177 func (f *FakeClient) GetPullRequestChanges(org, repo string, number int) ([]github.PullRequestChange, error) { 178 return f.PullRequestChanges[number], nil 179 } 180 181 // GetRef returns the hash of a ref. 182 func (f *FakeClient) GetRef(owner, repo, ref string) (string, error) { 183 return "abcde", nil 184 } 185 186 // CreateStatus adds a status context to a commit. 187 func (f *FakeClient) CreateStatus(owner, repo, sha string, s github.Status) error { 188 if f.CreatedStatuses == nil { 189 f.CreatedStatuses = make(map[string][]github.Status) 190 } 191 statuses := f.CreatedStatuses[sha] 192 var updated bool 193 for i := range statuses { 194 if statuses[i].Context == s.Context { 195 statuses[i] = s 196 updated = true 197 } 198 } 199 if !updated { 200 statuses = append(statuses, s) 201 } 202 f.CreatedStatuses[sha] = statuses 203 return nil 204 } 205 206 // ListStatuses returns individual status contexts on a commit. 207 func (f *FakeClient) ListStatuses(org, repo, ref string) ([]github.Status, error) { 208 return f.CreatedStatuses[ref], nil 209 } 210 211 // GetCombinedStatus returns the overall status for a commit. 212 func (f *FakeClient) GetCombinedStatus(owner, repo, ref string) (*github.CombinedStatus, error) { 213 return f.CombinedStatuses[ref], nil 214 } 215 216 // GetRepoLabels gets labels in a repo. 217 func (f *FakeClient) GetRepoLabels(owner, repo string) ([]github.Label, error) { 218 la := []github.Label{} 219 for _, l := range f.ExistingLabels { 220 la = append(la, github.Label{Name: l}) 221 } 222 return la, nil 223 } 224 225 // GetIssueLabels gets labels on an issue 226 func (f *FakeClient) GetIssueLabels(owner, repo string, number int) ([]github.Label, error) { 227 // Only labels added to an issue are considered. Removals are ignored by this fake. 228 re := regexp.MustCompile(fmt.Sprintf(`^%s/%s#%d:(.*)$`, owner, repo, number)) 229 la := []github.Label{} 230 for _, l := range f.LabelsAdded { 231 groups := re.FindStringSubmatch(l) 232 if groups != nil { 233 la = append(la, github.Label{Name: groups[1]}) 234 } 235 } 236 return la, nil 237 } 238 239 // AddLabel adds a label 240 func (f *FakeClient) AddLabel(owner, repo string, number int, label string) error { 241 if f.ExistingLabels == nil { 242 f.LabelsAdded = append(f.LabelsAdded, fmt.Sprintf("%s/%s#%d:%s", owner, repo, number, label)) 243 return nil 244 } 245 for _, l := range f.ExistingLabels { 246 if label == l { 247 f.LabelsAdded = append(f.LabelsAdded, fmt.Sprintf("%s/%s#%d:%s", owner, repo, number, label)) 248 return nil 249 } 250 } 251 return fmt.Errorf("cannot add %v to %s/%s/#%d", label, owner, repo, number) 252 } 253 254 // RemoveLabel removes a label 255 func (f *FakeClient) RemoveLabel(owner, repo string, number int, label string) error { 256 f.LabelsRemoved = append(f.LabelsRemoved, fmt.Sprintf("%s/%s#%d:%s", owner, repo, number, label)) 257 return nil 258 } 259 260 // FindIssues returns f.Issues 261 func (f *FakeClient) FindIssues(query, sort string, asc bool) ([]github.Issue, error) { 262 return f.Issues, nil 263 } 264 265 // AssignIssue adds assignees. 266 func (f *FakeClient) AssignIssue(owner, repo string, number int, assignees []string) error { 267 var m github.MissingUsers 268 for _, a := range assignees { 269 if a == "not-in-the-org" { 270 m.Users = append(m.Users, a) 271 continue 272 } 273 f.AssigneesAdded = append(f.AssigneesAdded, fmt.Sprintf("%s/%s#%d:%s", owner, repo, number, a)) 274 } 275 if m.Users == nil { 276 return nil 277 } 278 return m 279 } 280 281 // GetFile returns the bytes of the file. 282 func (f *FakeClient) GetFile(org, repo, file, commit string) ([]byte, error) { 283 contents, ok := f.RemoteFiles[file] 284 if !ok { 285 return nil, fmt.Errorf("could not find file %s", file) 286 } 287 if commit == "" { 288 if master, ok := contents["master"]; ok { 289 return []byte(master), nil 290 } 291 292 return nil, fmt.Errorf("could not find file %s in master", file) 293 } 294 295 if content, ok := contents[commit]; ok { 296 return []byte(content), nil 297 } 298 299 return nil, fmt.Errorf("could not find file %s with ref %s", file, commit) 300 } 301 302 // ListTeamMembers return a fake team with a single "sig-lead" Github teammember 303 func (f *FakeClient) ListTeamMembers(teamID int, role string) ([]github.TeamMember, error) { 304 if role != github.RoleAll { 305 return nil, fmt.Errorf("unsupport role %v (only all supported)", role) 306 } 307 teams := map[int][]github.TeamMember{ 308 0: {{Login: "default-sig-lead"}}, 309 42: {{Login: "sig-lead"}}, 310 } 311 members, ok := teams[teamID] 312 if !ok { 313 return []github.TeamMember{}, nil 314 } 315 return members, nil 316 } 317 318 // IsCollaborator returns true if the user is a collaborator of the repo. 319 func (f *FakeClient) IsCollaborator(org, repo, login string) (bool, error) { 320 normed := github.NormLogin(login) 321 for _, collab := range f.Collaborators { 322 if github.NormLogin(collab) == normed { 323 return true, nil 324 } 325 } 326 return false, nil 327 } 328 329 // ListCollaborators lists the collaborators. 330 func (f *FakeClient) ListCollaborators(org, repo string) ([]github.User, error) { 331 result := make([]github.User, 0, len(f.Collaborators)) 332 for _, login := range f.Collaborators { 333 result = append(result, github.User{Login: login}) 334 } 335 return result, nil 336 } 337 338 // ClearMilestone removes the milestone 339 func (f *FakeClient) ClearMilestone(org, repo string, issueNum int) error { 340 f.Milestone = 0 341 return nil 342 } 343 344 // SetMilestone sets the milestone. 345 func (f *FakeClient) SetMilestone(org, repo string, issueNum, milestoneNum int) error { 346 if milestoneNum < 0 { 347 return fmt.Errorf("Milestone Numbers Cannot Be Negative") 348 } 349 f.Milestone = milestoneNum 350 return nil 351 } 352 353 // ListMilestones lists milestones. 354 func (f *FakeClient) ListMilestones(org, repo string) ([]github.Milestone, error) { 355 milestones := []github.Milestone{} 356 for k, v := range f.MilestoneMap { 357 milestones = append(milestones, github.Milestone{Title: k, Number: v}) 358 } 359 return milestones, nil 360 }