blob: c274edd531654d3de3a3e0e5f44e8d7de9acd3a8 [file] [log] [blame]
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"context"
"net/http"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/google/go-github/github"
"golang.org/x/build/devapp/owners"
"golang.org/x/build/maintner"
)
func TestLabelCommandsFromComments(t *testing.T) {
created := time.Now()
testCases := []struct {
desc string
body string
cmds []labelCommand
}{
{
"basic add/remove",
"We should fix this issue, but we need help\n\n@gopherbot please add help wanted, needsfix and remove needsinvestigation",
[]labelCommand{
{action: "add", label: "help wanted", created: created},
{action: "add", label: "needsfix", created: created},
{action: "remove", label: "needsinvestigation", created: created},
},
},
{
"no please",
"@gopherbot add NeedsFix",
[]labelCommand{
{action: "add", label: "needsfix", created: created},
},
},
{
"with comma",
"@gopherbot, NeedsFix",
[]labelCommand{
{action: "add", label: "needsfix", created: created},
},
},
{
"with semicolons",
"@gopherbot NeedsFix;help wanted; remove needsinvestigation",
[]labelCommand{
{action: "add", label: "needsfix", created: created},
{action: "add", label: "help wanted", created: created},
{action: "remove", label: "needsinvestigation", created: created},
},
},
{
"case insensitive",
"@gopherbot please add HelP WanteD",
[]labelCommand{
{action: "add", label: "help wanted", created: created},
},
},
{
"fun input",
"@gopherbot please add help wanted,;needsfix;",
[]labelCommand{
{action: "add", label: "help wanted", created: created},
{action: "add", label: "needsfix", created: created},
},
},
{
"with hyphen",
"@gopherbot please add label OS-macOS",
[]labelCommand{
{action: "add", label: "os-macos", created: created},
},
},
{
"unlabel keyword",
"@gopherbot please unlabel needsinvestigation, NeedsDecision",
[]labelCommand{
{action: "remove", label: "needsinvestigation", created: created},
{action: "remove", label: "needsdecision", created: created},
},
},
{
"with label[s] keyword",
"@gopherbot please add label help wanted and remove labels needsinvestigation, NeedsDecision",
[]labelCommand{
{action: "add", label: "help wanted", created: created},
{action: "remove", label: "needsinvestigation", created: created},
{action: "remove", label: "needsdecision", created: created},
},
},
{
"no label commands",
"The cake was a lie",
nil,
},
}
for _, tc := range testCases {
cmds := labelCommandsFromBody(tc.body, created)
if diff := cmp.Diff(cmds, tc.cmds, cmp.AllowUnexported(labelCommand{})); diff != "" {
t.Errorf("%s: commands differ: (-got +want)\n%s", tc.desc, diff)
}
}
}
func TestLabelMutations(t *testing.T) {
testCases := []struct {
desc string
cmds []labelCommand
add []string
remove []string
}{
{
"basic",
[]labelCommand{
{action: "add", label: "foo"},
{action: "remove", label: "baz"},
},
[]string{"foo"},
[]string{"baz"},
},
{
"add/remove of same label",
[]labelCommand{
{action: "add", label: "foo"},
{action: "remove", label: "foo"},
{action: "remove", label: "bar"},
{action: "add", label: "bar"},
},
nil,
nil,
},
{
"deduplication of labels",
[]labelCommand{
{action: "add", label: "foo"},
{action: "add", label: "foo"},
{action: "remove", label: "bar"},
{action: "remove", label: "bar"},
},
[]string{"foo"},
[]string{"bar"},
},
{
"forbidden actions",
[]labelCommand{
{action: "add", label: "Proposal-Accepted"},
{action: "add", label: "CherryPickApproved"},
{action: "add", label: "cla: yes"},
{action: "remove", label: "Security"},
},
nil,
nil,
},
{
"can add Security",
[]labelCommand{
{action: "add", label: "Security"},
},
[]string{"Security"},
nil,
},
}
for _, tc := range testCases {
add, remove := mutationsFromCommands(tc.cmds)
if diff := cmp.Diff(add, tc.add); diff != "" {
t.Errorf("%s: label additions differ: (-got, +want)\n%s", tc.desc, diff)
}
if diff := cmp.Diff(remove, tc.remove); diff != "" {
t.Errorf("%s: label removals differ: (-got, +want)\n%s", tc.desc, diff)
}
}
}
func TestAddLabels(t *testing.T) {
oldFunc := addLabelsToIssue
defer func() { addLabelsToIssue = oldFunc }()
var added []string
addLabelsToIssue = func(_ context.Context, _ *github.IssuesService, _ int, labels []string) error {
added = labels
return nil
}
testCases := []struct {
desc string
gi *maintner.GitHubIssue
labels []string
added []string
}{
{
"basic add",
&maintner.GitHubIssue{},
[]string{"foo"},
[]string{"foo"},
},
{
"some labels already present in maintner",
&maintner.GitHubIssue{
Labels: map[int64]*maintner.GitHubLabel{
0: {Name: "NeedsDecision"},
},
},
[]string{"foo", "NeedsDecision"},
[]string{"foo"},
},
{
"all labels already present in maintner",
&maintner.GitHubIssue{
Labels: map[int64]*maintner.GitHubLabel{
0: {Name: "NeedsDecision"},
},
},
[]string{"NeedsDecision"},
nil,
},
}
b := &gopherbot{ghc: github.NewClient(http.DefaultClient)}
for _, tc := range testCases {
// Clear any previous state from stubbed addLabelsToIssue function above
// since some test cases may skip calls to it.
added = nil
if err := b.addLabels(context.Background(), tc.gi, tc.labels); err != nil {
t.Errorf("%s: b.addLabels got unexpected error: %v", tc.desc, err)
continue
}
if diff := cmp.Diff(added, tc.added); diff != "" {
t.Errorf("%s: labels added differ: (-got, +want)\n%s", tc.desc, diff)
}
}
}
func TestRemoveLabels(t *testing.T) {
oldLabelsForIssue := labelsForIssue
defer func() { labelsForIssue = oldLabelsForIssue }()
labelsForIssue = func(_ context.Context, _ *github.IssuesService, _ int) ([]string, error) {
return []string{"help wanted", "NeedsFix"}, nil
}
oldRemoveLabelFromIssue := removeLabelFromIssue
defer func() { removeLabelFromIssue = oldRemoveLabelFromIssue }()
var removed []string
removeLabelFromIssue = func(_ context.Context, _ *github.IssuesService, _ int, label string) error {
removed = append(removed, label)
return nil
}
testCases := []struct {
desc string
gi *maintner.GitHubIssue
toRemove []string
removed []string
}{
{
"basic remove",
&maintner.GitHubIssue{
Labels: map[int64]*maintner.GitHubLabel{
0: {Name: "NeedsFix"},
},
},
[]string{"NeedsFix"},
[]string{"NeedsFix"},
},
{
"label not present in maintner",
&maintner.GitHubIssue{},
[]string{"NeedsFix"},
nil,
},
{
"label not present in GitHub",
&maintner.GitHubIssue{
Labels: map[int64]*maintner.GitHubLabel{
0: {Name: "foo"},
},
},
[]string{"foo"},
nil,
},
}
b := &gopherbot{ghc: github.NewClient(http.DefaultClient)}
for _, tc := range testCases {
// Clear any previous state from stubbed removeLabelFromIssue function above
// since some test cases may skip calls to it.
removed = nil
if err := b.removeLabels(context.Background(), tc.gi, tc.toRemove); err != nil {
t.Errorf("%s: b.addLabels got unexpected error: %v", tc.desc, err)
continue
}
if diff := cmp.Diff(removed, tc.removed); diff != "" {
t.Errorf("%s: labels removed differ: (-got, +want)\n%s", tc.desc, diff)
}
}
}
func TestHumanReviewersInMetas(t *testing.T) {
testCases := []struct {
commitMsg string
hasHuman bool
}{
{`Patch-set: 6
Reviewer: Andrew Bonventre <22285@62eb7196-b449-3ce5-99f1-c037f21e1705>
`,
true,
},
{`Patch-set: 6
CC: Andrew Bonventre <22285@62eb7196-b449-3ce5-99f1-c037f21e1705>
`,
true,
},
{`Patch-set: 6
Reviewer: Gobot Gobot <5976@62eb7196-b449-3ce5-99f1-c037f21e1705>
`,
false,
},
{`Patch-set: 6
Reviewer: Gobot Gobot <5976@62eb7196-b449-3ce5-99f1-c037f21e1705>
CC: Andrew Bonventre <22285@62eb7196-b449-3ce5-99f1-c037f21e1705>
`,
true,
},
{`Patch-set: 6
Reviewer: Gobot Gobot <5976@62eb7196-b449-3ce5-99f1-c037f21e1705>
Reviewer: Andrew Bonventre <22285@62eb7196-b449-3ce5-99f1-c037f21e1705>
`,
true,
},
}
for _, tc := range testCases {
metas := []*maintner.GerritMeta{
{Commit: &maintner.GitCommit{Msg: tc.commitMsg}},
}
if got, want := humanReviewersInMetas(metas), tc.hasHuman; got != want {
t.Errorf("Unexpected result for meta commit message: got %v; want %v for\n%s", got, want, tc.commitMsg)
}
}
}
func TestMergeOwnersEntries(t *testing.T) {
var (
andybons = owners.Owner{GitHubUsername: "andybons", GerritEmail: "andybons@golang.org"}
bradfitz = owners.Owner{GitHubUsername: "bradfitz", GerritEmail: "bradfitz@golang.org"}
filippo = owners.Owner{GitHubUsername: "filippo", GerritEmail: "filippo@golang.org"}
)
testCases := []struct {
desc string
entries []*owners.Entry
authorEmail string
result *owners.Entry
}{
{
"no entries",
nil,
"",
&owners.Entry{},
},
{
"primary merge",
[]*owners.Entry{
{Primary: []owners.Owner{andybons}},
{Primary: []owners.Owner{bradfitz}},
},
"",
&owners.Entry{
Primary: []owners.Owner{andybons, bradfitz},
},
},
{
"secondary merge",
[]*owners.Entry{
{Secondary: []owners.Owner{andybons}},
{Secondary: []owners.Owner{filippo}},
},
"",
&owners.Entry{
Secondary: []owners.Owner{andybons, filippo},
},
},
{
"promote from secondary to primary",
[]*owners.Entry{
{Primary: []owners.Owner{andybons, filippo}},
{Secondary: []owners.Owner{filippo}},
},
"",
&owners.Entry{
Primary: []owners.Owner{andybons, filippo},
},
},
{
"primary filter",
[]*owners.Entry{
{Primary: []owners.Owner{filippo, andybons}},
},
filippo.GerritEmail,
&owners.Entry{
Primary: []owners.Owner{andybons},
},
},
{
"secondary filter",
[]*owners.Entry{
{Secondary: []owners.Owner{filippo, andybons}},
},
filippo.GerritEmail,
&owners.Entry{
Secondary: []owners.Owner{andybons},
},
},
}
cmpFn := func(a, b owners.Owner) bool {
return a.GitHubUsername < b.GitHubUsername
}
for _, tc := range testCases {
got := mergeOwnersEntries(tc.entries, tc.authorEmail)
if diff := cmp.Diff(got, tc.result, cmpopts.SortSlices(cmpFn)); diff != "" {
t.Errorf("%s: final entry results differ: (-got, +want)\n%s", tc.desc, diff)
}
}
}