github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/pkg/gerrit/adapter/trigger_test.go (about)

     1  /*
     2  Copyright 2020 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 adapter
    18  
    19  import (
    20  	"reflect"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/andygrunwald/go-gerrit"
    25  	"github.com/sirupsen/logrus"
    26  	"k8s.io/apimachinery/pkg/api/equality"
    27  	"k8s.io/apimachinery/pkg/util/diff"
    28  	"k8s.io/apimachinery/pkg/util/sets"
    29  
    30  	"sigs.k8s.io/prow/pkg/config"
    31  	"sigs.k8s.io/prow/pkg/gerrit/client"
    32  )
    33  
    34  func TestPresubmitContexts(t *testing.T) {
    35  	jobs := func(names ...string) []config.Presubmit {
    36  		var presubmits []config.Presubmit
    37  		for _, n := range names {
    38  			var p config.Presubmit
    39  			p.Name = n
    40  			presubmits = append(presubmits, p)
    41  		}
    42  		return presubmits
    43  	}
    44  	cases := []struct {
    45  		name       string
    46  		presubmits []config.Presubmit
    47  		failing    sets.Set[string]
    48  		failed     sets.Set[string]
    49  		all        sets.Set[string]
    50  	}{
    51  		{
    52  			name: "basically works",
    53  		},
    54  		{
    55  			name:       "simple case works",
    56  			presubmits: jobs("hello-fail", "world"),
    57  			failing:    sets.New[string]("world"),
    58  			failed:     sets.New[string]("world"),
    59  			all:        sets.New[string]("hello-fail", "world"),
    60  		},
    61  		{
    62  			name:       "ignore failures from deleted jobs",
    63  			presubmits: jobs("failing", "passing"),
    64  			failing:    sets.New[string]("failing", "deleted"),
    65  			failed:     sets.New[string]("failing"),
    66  			all:        sets.New[string]("failing", "passing"),
    67  		},
    68  	}
    69  
    70  	for _, tc := range cases {
    71  		t.Run(tc.name, func(t *testing.T) {
    72  			gotFailed, gotAll := presubmitContexts(tc.failing, tc.presubmits, logrus.WithField("case", tc.name))
    73  			if !equality.Semantic.DeepEqual(tc.failed, gotFailed) {
    74  				t.Errorf("wrong failures:%s", diff.ObjectReflectDiff(tc.failed, gotFailed))
    75  			}
    76  			if !equality.Semantic.DeepEqual(tc.all, gotAll) {
    77  				t.Errorf("wrong all contexts:%s", diff.ObjectReflectDiff(tc.all, gotAll))
    78  			}
    79  		})
    80  	}
    81  }
    82  
    83  func stamp(t time.Time) gerrit.Timestamp {
    84  	return gerrit.Timestamp{Time: t}
    85  }
    86  
    87  func TestCurrentMessages(t *testing.T) {
    88  	now := time.Now()
    89  	before := now.Add(-time.Minute)
    90  	after := now.Add(time.Hour)
    91  	later := after.Add(time.Hour)
    92  
    93  	now3 := gerrit.ChangeMessageInfo{
    94  		RevisionNumber: 3,
    95  		Date:           stamp(now),
    96  		Message:        "now",
    97  	}
    98  	after3 := gerrit.ChangeMessageInfo{
    99  		RevisionNumber: 3,
   100  		Date:           stamp(after),
   101  		Message:        "after",
   102  	}
   103  	later3 := gerrit.ChangeMessageInfo{
   104  		RevisionNumber: 3,
   105  		Date:           stamp(later),
   106  		Message:        "later",
   107  	}
   108  	after4 := gerrit.ChangeMessageInfo{
   109  		RevisionNumber: 4,
   110  		Date:           stamp(after),
   111  		Message:        "4-after",
   112  	}
   113  
   114  	cases := []struct {
   115  		name   string
   116  		change gerrit.ChangeInfo
   117  		since  time.Time
   118  		want   []gerrit.ChangeMessageInfo
   119  	}{
   120  		{
   121  			name: "basically works",
   122  		},
   123  		{
   124  			name:  "simple case",
   125  			since: before,
   126  			change: gerrit.ChangeInfo{
   127  				Revisions: map[string]gerrit.RevisionInfo{
   128  					"3": {
   129  						Number: 3,
   130  					},
   131  				},
   132  				CurrentRevision: "3",
   133  				Messages:        []gerrit.ChangeMessageInfo{now3, after3, later3},
   134  			},
   135  			want: []gerrit.ChangeMessageInfo{now3, after3, later3},
   136  		},
   137  		{
   138  			name:  "reject old messages",
   139  			since: now,
   140  			change: gerrit.ChangeInfo{
   141  				Revisions: map[string]gerrit.RevisionInfo{
   142  					"3": {
   143  						Number: 3,
   144  					},
   145  				},
   146  				CurrentRevision: "3",
   147  				Messages:        []gerrit.ChangeMessageInfo{now3, after3, later3},
   148  			},
   149  			want: []gerrit.ChangeMessageInfo{after3, later3},
   150  		},
   151  		{
   152  			name:  "reject message from other revisions",
   153  			since: before,
   154  			change: gerrit.ChangeInfo{
   155  				Revisions: map[string]gerrit.RevisionInfo{
   156  					"3": {
   157  						Number: 3,
   158  					},
   159  				},
   160  				CurrentRevision: "3",
   161  				Messages:        []gerrit.ChangeMessageInfo{now3, after4, later3},
   162  			},
   163  			want: []gerrit.ChangeMessageInfo{now3, later3},
   164  		},
   165  	}
   166  
   167  	for _, tc := range cases {
   168  		t.Run(tc.name, func(t *testing.T) {
   169  			got := currentMessages(tc.change, tc.since)
   170  			if !reflect.DeepEqual(got, tc.want) {
   171  				t.Errorf("wrong messages:%s", diff.ObjectReflectDiff(got, tc.want))
   172  			}
   173  		})
   174  	}
   175  }
   176  
   177  func TestMessageFilter(t *testing.T) {
   178  	old := time.Now().Add(-1 * time.Hour)
   179  	older := old.Add(-1 * time.Hour)
   180  	job := func(name string, patch func(j *config.Presubmit)) config.Presubmit {
   181  		var presubmit config.Presubmit
   182  		presubmit.Name = name
   183  		presubmit.Context = name
   184  		presubmit.Trigger = config.DefaultTriggerFor(name)
   185  		presubmit.RerunCommand = config.DefaultRerunCommandFor(name)
   186  		presubmit.AlwaysRun = true
   187  		if patch != nil {
   188  			patch(&presubmit)
   189  		}
   190  		return presubmit
   191  	}
   192  	msg := func(content string, t time.Time) gerrit.ChangeMessageInfo {
   193  		return gerrit.ChangeMessageInfo{Message: content, Date: gerrit.Timestamp{Time: t}}
   194  	}
   195  	type check struct {
   196  		job             config.Presubmit
   197  		shouldRun       bool
   198  		forcedToRun     bool
   199  		defaultBehavior bool
   200  		triggered       time.Time
   201  	}
   202  	cases := []struct {
   203  		name     string
   204  		messages []gerrit.ChangeMessageInfo
   205  		failed   sets.Set[string]
   206  		all      sets.Set[string]
   207  		checks   []check
   208  	}{
   209  		{
   210  			name: "basically works",
   211  		},
   212  		{
   213  			name:     "/test foo works",
   214  			messages: []gerrit.ChangeMessageInfo{msg("/test foo", older), msg("/test bar", old)},
   215  			all:      sets.New[string]("foo", "bar", "ignored"),
   216  			checks: []check{
   217  				{
   218  					job:             job("foo", nil),
   219  					shouldRun:       true,
   220  					forcedToRun:     true,
   221  					defaultBehavior: true,
   222  					triggered:       older,
   223  				},
   224  				{
   225  					job:             job("bar", nil),
   226  					shouldRun:       true,
   227  					forcedToRun:     true,
   228  					defaultBehavior: true,
   229  					triggered:       old,
   230  				},
   231  				{
   232  					job:             job("ignored", nil),
   233  					shouldRun:       false,
   234  					forcedToRun:     false,
   235  					defaultBehavior: false,
   236  				},
   237  			},
   238  		},
   239  		{
   240  			name:     "/test all triggers multiple",
   241  			messages: []gerrit.ChangeMessageInfo{msg("/test all", old)},
   242  			all:      sets.New[string]("foo", "bar"),
   243  			checks: []check{
   244  				{
   245  					job:             job("foo", nil),
   246  					shouldRun:       true,
   247  					forcedToRun:     false,
   248  					defaultBehavior: false,
   249  					triggered:       old,
   250  				},
   251  				{
   252  					job:             job("bar", nil),
   253  					shouldRun:       true,
   254  					forcedToRun:     false,
   255  					defaultBehavior: false,
   256  					triggered:       old,
   257  				},
   258  			},
   259  		},
   260  		{
   261  			name:     "/retest triggers failures",
   262  			messages: []gerrit.ChangeMessageInfo{msg("/retest", old)},
   263  			failed:   sets.New[string]("failed"),
   264  			all:      sets.New[string]("foo", "bar", "failed"),
   265  			checks: []check{
   266  				{
   267  					job:             job("foo", nil),
   268  					shouldRun:       false,
   269  					forcedToRun:     false,
   270  					defaultBehavior: false,
   271  				},
   272  				{
   273  					job:             job("failed", nil),
   274  					shouldRun:       true,
   275  					forcedToRun:     false,
   276  					defaultBehavior: true,
   277  					triggered:       old,
   278  				},
   279  				{
   280  					job:             job("bar", nil),
   281  					shouldRun:       false,
   282  					forcedToRun:     false,
   283  					defaultBehavior: false,
   284  				},
   285  			},
   286  		},
   287  		{
   288  			name:     "draft->active by clicking `MARK AS ACTIVE` triggers multiple",
   289  			messages: []gerrit.ChangeMessageInfo{msg(client.ReadyForReviewMessageFixed, old)},
   290  			all:      sets.New[string]("foo", "bar"),
   291  			checks: []check{
   292  				{
   293  					job:             job("foo", nil),
   294  					shouldRun:       true,
   295  					forcedToRun:     false,
   296  					defaultBehavior: false,
   297  					triggered:       old,
   298  				},
   299  				{
   300  					job:             job("bar", nil),
   301  					shouldRun:       true,
   302  					forcedToRun:     false,
   303  					defaultBehavior: false,
   304  					triggered:       old,
   305  				},
   306  			},
   307  		},
   308  		{
   309  			name: "draft->active by clicking `SEND AND START REVIEW` triggers multiple",
   310  			messages: []gerrit.ChangeMessageInfo{msg(`Patch Set 1:
   311  
   312  			(1 comment)
   313  			
   314  			`+client.ReadyForReviewMessageCustomizable, old)},
   315  			all: sets.New[string]("foo", "bar"),
   316  			checks: []check{
   317  				{
   318  					job:             job("foo", nil),
   319  					shouldRun:       true,
   320  					forcedToRun:     false,
   321  					defaultBehavior: false,
   322  					triggered:       old,
   323  				},
   324  				{
   325  					job:             job("bar", nil),
   326  					shouldRun:       true,
   327  					forcedToRun:     false,
   328  					defaultBehavior: false,
   329  					triggered:       old,
   330  				},
   331  			},
   332  		},
   333  	}
   334  
   335  	for _, tc := range cases {
   336  		t.Run(tc.name, func(t *testing.T) {
   337  			logger := logrus.WithField("case", tc.name)
   338  			triggerTimes := map[string]time.Time{}
   339  			filt := messageFilter(tc.messages, tc.failed, tc.all, triggerTimes, logger)
   340  			for _, check := range tc.checks {
   341  				t.Run(check.job.Name, func(t *testing.T) {
   342  					fixed := []config.Presubmit{check.job}
   343  					config.SetPresubmitRegexes(fixed)
   344  					check.job = fixed[0]
   345  					shouldRun, forcedToRun, defaultBehavior := filt.ShouldRun(check.job)
   346  					if got, want := shouldRun, check.shouldRun; got != want {
   347  						t.Errorf("shouldRun: got %t, want %t", got, want)
   348  					}
   349  					if got, want := forcedToRun, check.forcedToRun; got != want {
   350  						t.Errorf("forcedToRun: got %t, want %t", got, want)
   351  					}
   352  					if got, want := defaultBehavior, check.defaultBehavior; got != want {
   353  						t.Errorf("defaultBehavior: got %t, want %t", got, want)
   354  					}
   355  				})
   356  			}
   357  			// Validate that triggerTimes was populated correctly after ShouldRun is called on every job.
   358  			for _, check := range tc.checks {
   359  				if !triggerTimes[check.job.Name].Equal(check.triggered) {
   360  					t.Errorf("expected job %q to have trigger time %v, but got %v", check.job.Name, check.triggered, triggerTimes[check.job.Name])
   361  				}
   362  			}
   363  		})
   364  	}
   365  }