go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/luci_notify/notify/commits_test.go (about)

     1  // Copyright 2017 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 notify
    16  
    17  import (
    18  	"context"
    19  	"testing"
    20  
    21  	buildbucketpb "go.chromium.org/luci/buildbucket/proto"
    22  	"go.chromium.org/luci/buildbucket/protoutil"
    23  	"go.chromium.org/luci/common/data/stringset"
    24  	gitpb "go.chromium.org/luci/common/proto/git"
    25  
    26  	notifypb "go.chromium.org/luci/luci_notify/api/config"
    27  
    28  	. "github.com/smartystreets/goconvey/convey"
    29  	. "go.chromium.org/luci/common/testing/assertions"
    30  )
    31  
    32  var (
    33  	// These defaults match up with the "basic" project config.
    34  	defaultGitilesHost    = "chromium.googlesource.com"
    35  	defaultGitilesProject = "chromium/src"
    36  
    37  	// Test revisions.
    38  	rev1 = "deadbeef"
    39  	rev2 = "badcoffe"
    40  
    41  	// Test email addresses for commits.
    42  	commitEmail1 = "example1@google.com"
    43  	commitEmail2 = "example2@google.com"
    44  
    45  	// Test commits, ordered from new to old.
    46  	testCommits = []*gitpb.Commit{
    47  		{
    48  			Author: &gitpb.Commit_User{
    49  				Email: commitEmail2,
    50  			},
    51  			Id: rev2,
    52  		},
    53  		{
    54  			Author: &gitpb.Commit_User{
    55  				Email: commitEmail1,
    56  			},
    57  			Id: rev1,
    58  		},
    59  	}
    60  	revTestCommits = []*gitpb.Commit{
    61  		{
    62  			Author: &gitpb.Commit_User{
    63  				Email: commitEmail1,
    64  			},
    65  			Id: rev2,
    66  		},
    67  		{
    68  			Author: &gitpb.Commit_User{
    69  				Email: commitEmail2,
    70  			},
    71  			Id: rev1,
    72  		},
    73  	}
    74  )
    75  
    76  func TestCheckout(t *testing.T) {
    77  	gitilesCheckout := &notifypb.GitilesCommits{
    78  		Commits: []*buildbucketpb.GitilesCommit{
    79  			{
    80  				Host:    defaultGitilesHost,
    81  				Project: defaultGitilesProject,
    82  				Id:      rev1,
    83  			},
    84  			{
    85  				Host:    defaultGitilesHost,
    86  				Project: "third_party/hello",
    87  				Id:      rev2,
    88  			},
    89  		},
    90  	}
    91  	Convey(`Conversion with GitilesCommits Empty`, t, func() {
    92  		checkout := NewCheckout(&notifypb.GitilesCommits{})
    93  		So(checkout, ShouldHaveLength, 0)
    94  		result := checkout.ToGitilesCommits()
    95  		So(result, ShouldBeNil)
    96  	})
    97  
    98  	Convey(`Conversion with GitilesCommits Non-Empty`, t, func() {
    99  		checkout := NewCheckout(gitilesCheckout)
   100  		So(checkout, ShouldResemble, Checkout{
   101  			protoutil.GitilesRepoURL(gitilesCheckout.Commits[0]): rev1,
   102  			protoutil.GitilesRepoURL(gitilesCheckout.Commits[1]): rev2,
   103  		})
   104  		result := checkout.ToGitilesCommits()
   105  		So(result, ShouldResembleProto, gitilesCheckout)
   106  	})
   107  
   108  	Convey(`Filter repositories from allowlist`, t, func() {
   109  		checkout := NewCheckout(gitilesCheckout)
   110  		repoURL := protoutil.GitilesRepoURL(gitilesCheckout.Commits[0])
   111  		filteredCheckout := checkout.Filter(stringset.NewFromSlice([]string{repoURL}...))
   112  		So(filteredCheckout, ShouldResemble, Checkout{repoURL: rev1})
   113  	})
   114  }
   115  
   116  func TestLogs(t *testing.T) {
   117  	Convey(`ComputeLogs`, t, func() {
   118  		ctx := context.Background()
   119  		history := mockHistoryFunc(map[string][]*gitpb.Commit{
   120  			"chromium/src":      testCommits,
   121  			"third_party/hello": testCommits,
   122  			"third_party/what":  testCommits,
   123  		})
   124  		checkout1Old := Checkout{
   125  			"https://chromium.googlesource.com/chromium/src":      rev1,
   126  			"https://chromium.googlesource.com/third_party/hello": rev1,
   127  		}
   128  		checkout1New := Checkout{
   129  			"https://chromium.googlesource.com/chromium/src":      rev2,
   130  			"https://chromium.googlesource.com/third_party/hello": rev2,
   131  		}
   132  		checkout2 := Checkout{
   133  			"https://chromium.googlesource.com/chromium/src":     rev2,
   134  			"https://chromium.googlesource.com/third_party/what": rev2,
   135  		}
   136  		checkout3 := Checkout{
   137  			"https://chromium.googlesource.com/third_party/what": rev2,
   138  		}
   139  		Convey(`Both empty`, func() {
   140  			logs, err := ComputeLogs(ctx, "luci-proj", nil, nil, history)
   141  			So(err, ShouldBeNil)
   142  			So(logs, ShouldHaveLength, 0)
   143  		})
   144  		Convey(`One empty`, func() {
   145  			logs1, err := ComputeLogs(ctx, "luci-proj", checkout1Old, nil, history)
   146  			So(err, ShouldBeNil)
   147  			So(logs1, ShouldHaveLength, 0)
   148  
   149  			logs2, err := ComputeLogs(ctx, "luci-proj", nil, checkout1Old, history)
   150  			So(err, ShouldBeNil)
   151  			So(logs2, ShouldHaveLength, 0)
   152  		})
   153  		Convey(`Both valid, full overlap`, func() {
   154  			logs, err := ComputeLogs(ctx, "luci-proj", checkout1Old, checkout1New, history)
   155  			So(err, ShouldBeNil)
   156  			So(logs["https://chromium.googlesource.com/chromium/src"], ShouldResembleProto, testCommits[:1])
   157  			So(logs["https://chromium.googlesource.com/third_party/hello"], ShouldResembleProto, testCommits[:1])
   158  		})
   159  		Convey(`Both valid, partial overlap`, func() {
   160  			logs, err := ComputeLogs(ctx, "luci-proj", checkout1Old, checkout2, history)
   161  			So(err, ShouldBeNil)
   162  			So(logs["https://chromium.googlesource.com/chromium/src"], ShouldResembleProto, testCommits[:1])
   163  		})
   164  		Convey(`Both valid, no overlap`, func() {
   165  			logs, err := ComputeLogs(ctx, "luci-proj", checkout1Old, checkout3, history)
   166  			So(err, ShouldBeNil)
   167  			So(logs, ShouldHaveLength, 0)
   168  		})
   169  	})
   170  
   171  	testLogs := Logs{
   172  		"https://chromium.googlesource.com/chromium/src":      testCommits,
   173  		"https://chromium.googlesource.com/third_party/hello": testCommits,
   174  	}
   175  
   176  	Convey(`Filter repositories from allowlist`, t, func() {
   177  		filteredLogs := testLogs.Filter(stringset.NewFromSlice([]string{
   178  			"https://chromium.googlesource.com/chromium/src",
   179  		}...))
   180  		So(filteredLogs["https://chromium.googlesource.com/chromium/src"], ShouldResembleProto, testCommits)
   181  	})
   182  
   183  	Convey(`Blamelist`, t, func() {
   184  		blamelist := testLogs.Blamelist("default")
   185  		So(blamelist, ShouldResemble, []EmailNotify{
   186  			{
   187  				Email:    "example1@google.com",
   188  				Template: "default",
   189  			},
   190  			{
   191  				Email:    "example2@google.com",
   192  				Template: "default",
   193  			},
   194  		})
   195  	})
   196  }
   197  
   198  // mockHistoryFunc returns a mock HistoryFunc that gets its history from
   199  // a given list of gitpb.Commit.
   200  //
   201  // mockCommits should be ordered from newest to oldest, to mimic the ordering
   202  // returned by Gitiles.
   203  func mockHistoryFunc(projectCommits map[string][]*gitpb.Commit) HistoryFunc {
   204  	return func(_ context.Context, _, _, project, oldRevision, newRevision string) ([]*gitpb.Commit, error) {
   205  		mockCommits := projectCommits[project]
   206  		oldCommit := -1
   207  		newCommit := -1
   208  		for i, c := range mockCommits {
   209  			if c.Id == oldRevision {
   210  				oldCommit = i
   211  			}
   212  			if c.Id == newRevision {
   213  				newCommit = i
   214  			}
   215  		}
   216  		if oldCommit == -1 || newCommit == -1 || newCommit > oldCommit {
   217  			return nil, nil
   218  		}
   219  		commits := make([]*gitpb.Commit, oldCommit-newCommit+1)
   220  		copy(commits, mockCommits[newCommit:oldCommit+1])
   221  		return commits, nil
   222  	}
   223  }