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  }