github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/pkg/config/inrepoconfig_test.go (about)

     1  /*
     2  Copyright 2019 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 config
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"os"
    23  	"path"
    24  	"testing"
    25  
    26  	"github.com/google/go-cmp/cmp"
    27  	"sigs.k8s.io/prow/pkg/git/localgit"
    28  	"sigs.k8s.io/prow/pkg/git/v2"
    29  	"sigs.k8s.io/prow/pkg/kube"
    30  )
    31  
    32  var defaultBranch = localgit.DefaultBranch("")
    33  
    34  func TestDefaultProwYAMLGetterV2(t *testing.T) {
    35  	testDefaultProwYAMLGetter(localgit.NewV2, t)
    36  }
    37  
    38  func testDefaultProwYAMLGetter(clients localgit.Clients, t *testing.T) {
    39  	org, defaultRepo := "org", "repo"
    40  	testCases := []struct {
    41  		name              string
    42  		baseContent       map[string][]byte
    43  		headContent       map[string][]byte
    44  		config            *Config
    45  		dontPassGitClient bool
    46  		validate          func(*ProwYAML, error) error
    47  		repo              string
    48  	}{
    49  		// presubmits
    50  		{
    51  			name: "Basic happy path (presubmits)",
    52  			baseContent: map[string][]byte{
    53  				".prow.yaml": []byte(`presubmits: [{"name": "hans", "annotations": {"foo.bar": "foobar"}, "spec": {"containers": [{}]}}]`),
    54  			},
    55  			validate: func(p *ProwYAML, err error) error {
    56  				if err != nil {
    57  					return fmt.Errorf("unexpected error: %w", err)
    58  				}
    59  				if n := len(p.Presubmits); n != 1 || p.Presubmits[0].Name != "hans" {
    60  					return fmt.Errorf(`expected exactly one presubmit with name "hans", got %v`, p.Presubmits)
    61  				}
    62  				if diff := cmp.Diff(p.Presubmits[0].Annotations, map[string]string{"foo.bar": "foobar"}); diff != "" {
    63  					return errors.New(diff)
    64  				}
    65  				return nil
    66  			},
    67  		},
    68  		{
    69  			name: "Merging is executed (presubmits)",
    70  			headContent: map[string][]byte{
    71  				".prow.yaml": []byte(`presubmits: [{"name": "hans", "spec": {"containers": [{}]}}]`),
    72  			},
    73  			validate: func(p *ProwYAML, err error) error {
    74  				if err != nil {
    75  					return fmt.Errorf("unexpected error: %w", err)
    76  				}
    77  				if n := len(p.Presubmits); n != 1 || p.Presubmits[0].Name != "hans" {
    78  					return fmt.Errorf(`expected exactly one presubmit with name "hans", got %v`, p.Presubmits)
    79  				}
    80  				return nil
    81  			},
    82  		},
    83  		{
    84  			name: "Presubmit defaulting is executed",
    85  			baseContent: map[string][]byte{
    86  				".prow.yaml": []byte(`presubmits: [{"name": "hans", "spec": {"containers": [{}]}}]`),
    87  			},
    88  			validate: func(p *ProwYAML, err error) error {
    89  				if err != nil {
    90  					return fmt.Errorf("unexpected error: %w", err)
    91  				}
    92  				if n := len(p.Presubmits); n != 1 || p.Presubmits[0].Name != "hans" {
    93  					return fmt.Errorf(`expected exactly one presubmit with name "hans", got %v`, p.Presubmits)
    94  				}
    95  				if p.Presubmits[0].Context != "hans" {
    96  					return fmt.Errorf(`expected defaulting to set context to "hans", was %q`, p.Presubmits[0].Context)
    97  				}
    98  				return nil
    99  			},
   100  		},
   101  		{
   102  			name: "Presubmit validation is executed",
   103  			baseContent: map[string][]byte{
   104  				".prow.yaml": []byte(`presubmits: [{"name": "hans", "spec": {"containers": [{}]}},{"name": "hans", "spec": {"containers": [{}]}}]`),
   105  			},
   106  			validate: func(_ *ProwYAML, err error) error {
   107  				if err == nil {
   108  					return errors.New("error is nil")
   109  				}
   110  				expectedErrMsg := "duplicated presubmit jobs (consider both inrepo and central config): [hans]"
   111  				if err.Error() != expectedErrMsg {
   112  					return fmt.Errorf("expected error message to be %q, was %q", expectedErrMsg, err.Error())
   113  				}
   114  				return nil
   115  			},
   116  		},
   117  		{
   118  			name: "Presubmit validation includes static presubmits",
   119  			baseContent: map[string][]byte{
   120  				".prow.yaml": []byte(`presubmits: [{"name": "hans", "spec": {"containers": [{}]}}]`),
   121  			},
   122  			config: &Config{JobConfig: JobConfig{
   123  				PresubmitsStatic: map[string][]Presubmit{
   124  					org + "/" + defaultRepo: {{Reporter: Reporter{Context: "hans"}, JobBase: JobBase{Name: "hans"}}},
   125  				},
   126  			}},
   127  			validate: func(_ *ProwYAML, err error) error {
   128  				if err == nil {
   129  					return errors.New("error is nil")
   130  				}
   131  				expectedErrMsg := "duplicated presubmit jobs (consider both inrepo and central config): [hans]"
   132  				if err.Error() != expectedErrMsg {
   133  					return fmt.Errorf("expected error message to be %q, was %q", expectedErrMsg, err.Error())
   134  				}
   135  				return nil
   136  			},
   137  		},
   138  		{
   139  			name: "Branchconfig on presubmit is allowed",
   140  			baseContent: map[string][]byte{
   141  				".prow.yaml": []byte(`presubmits: [{"name": "hans", "spec": {"containers": [{}]}, "branches":["master"]}]`),
   142  			},
   143  			validate: func(p *ProwYAML, err error) error {
   144  				if err != nil {
   145  					return fmt.Errorf("unexpected error: %w", err)
   146  				}
   147  				if n := len(p.Presubmits); n != 1 || p.Presubmits[0].Name != "hans" {
   148  					return fmt.Errorf(`expected exactly one postsubmit with name "hans", got %v`, p.Presubmits)
   149  				}
   150  				if n := len(p.Presubmits[0].Branches); n != 1 || p.Presubmits[0].Branches[0] != "master" {
   151  					return fmt.Errorf(`expected exactly one postsubmit branch with name "master", got %v`, p.Presubmits[0].Branches)
   152  				}
   153  				return nil
   154  			},
   155  		},
   156  		{
   157  			name: "Not allowed cluster is rejected (presubmits)",
   158  			baseContent: map[string][]byte{
   159  				".prow.yaml": []byte(`presubmits: [{"name": "hans", "cluster": "privileged", "spec": {"containers": [{}]}}]`),
   160  			},
   161  			validate: func(_ *ProwYAML, err error) error {
   162  				if err == nil {
   163  					return errors.New("error is nil")
   164  				}
   165  				expectedErrMsg := "cluster \"privileged\" is not allowed for repository \"org/repo\""
   166  				if err.Error() != expectedErrMsg {
   167  					return fmt.Errorf("expected error message to be %q, was %q", expectedErrMsg, err.Error())
   168  				}
   169  				return nil
   170  			},
   171  		},
   172  		// postsubmits
   173  		{
   174  			name: "Basic happy path (postsubmits)",
   175  			baseContent: map[string][]byte{
   176  				".prow.yaml": []byte(`postsubmits: [{"name": "hans", "spec": {"containers": [{}]}}]`),
   177  			},
   178  			validate: func(p *ProwYAML, err error) error {
   179  				if err != nil {
   180  					return fmt.Errorf("unexpected error: %w", err)
   181  				}
   182  				if n := len(p.Postsubmits); n != 1 || p.Postsubmits[0].Name != "hans" {
   183  					return fmt.Errorf(`expected exactly one postsubmit with name "hans", got %v`, p.Postsubmits)
   184  				}
   185  				return nil
   186  			},
   187  		},
   188  		{
   189  			name: "Postsubmit defaulting is executed",
   190  			baseContent: map[string][]byte{
   191  				".prow.yaml": []byte(`postsubmits: [{"name": "hans", "spec": {"containers": [{}]}}]`),
   192  			},
   193  			validate: func(p *ProwYAML, err error) error {
   194  				if err != nil {
   195  					return fmt.Errorf("unexpected error: %w", err)
   196  				}
   197  				if n := len(p.Postsubmits); n != 1 || p.Postsubmits[0].Name != "hans" {
   198  					return fmt.Errorf(`expected exactly one postsubmit with name "hans", got %v`, p.Postsubmits)
   199  				}
   200  				if p.Postsubmits[0].Context != "hans" {
   201  					return fmt.Errorf(`expected defaulting to set context to "hans", was %q`, p.Postsubmits[0].Context)
   202  				}
   203  				return nil
   204  			},
   205  		},
   206  		{
   207  			name: "Postsubmit validation is executed",
   208  			baseContent: map[string][]byte{
   209  				".prow.yaml": []byte(`postsubmits: [{"name": "hans", "spec": {"containers": [{}]}},{"name": "hans", "spec": {"containers": [{}]}}]`),
   210  			},
   211  			validate: func(_ *ProwYAML, err error) error {
   212  				if err == nil {
   213  					return errors.New("error is nil")
   214  				}
   215  				expectedErrMsg := "duplicated postsubmit jobs (consider both inrepo and central config): [hans]"
   216  				if err.Error() != expectedErrMsg {
   217  					return fmt.Errorf("expected error message to be %q, was %q", expectedErrMsg, err.Error())
   218  				}
   219  				return nil
   220  			},
   221  		},
   222  		{
   223  			name: "Postsubmit validation includes static postsubmits",
   224  			baseContent: map[string][]byte{
   225  				".prow.yaml": []byte(`postsubmits: [{"name": "hans", "spec": {"containers": [{}]}}]`),
   226  			},
   227  			config: &Config{JobConfig: JobConfig{
   228  				PostsubmitsStatic: map[string][]Postsubmit{
   229  					org + "/" + defaultRepo: {{Reporter: Reporter{Context: "hans"}, JobBase: JobBase{Name: "hans"}}},
   230  				},
   231  			}},
   232  			validate: func(_ *ProwYAML, err error) error {
   233  				if err == nil {
   234  					return errors.New("error is nil")
   235  				}
   236  				expectedErrMsg := "duplicated postsubmit jobs (consider both inrepo and central config): [hans]"
   237  				if err.Error() != expectedErrMsg {
   238  					return fmt.Errorf("expected error message to be %q, was %q", expectedErrMsg, err.Error())
   239  				}
   240  				return nil
   241  			},
   242  		},
   243  		{
   244  			name: "Branchconfig on postsubmit is allowed",
   245  			baseContent: map[string][]byte{
   246  				".prow.yaml": []byte(`postsubmits: [{"name": "hans", "spec": {"containers": [{}]}, "branches":["master"]}]`),
   247  			},
   248  			validate: func(p *ProwYAML, err error) error {
   249  				if err != nil {
   250  					return fmt.Errorf("unexpected error: %w", err)
   251  				}
   252  				if n := len(p.Postsubmits); n != 1 || p.Postsubmits[0].Name != "hans" {
   253  					return fmt.Errorf(`expected exactly one postsubmit with name "hans", got %v`, p.Postsubmits)
   254  				}
   255  				if n := len(p.Postsubmits[0].Branches); n != 1 || p.Postsubmits[0].Branches[0] != "master" {
   256  					return fmt.Errorf(`expected exactly one postsubmit branch with name "master", got %v`, p.Postsubmits[0].Branches)
   257  				}
   258  				return nil
   259  			},
   260  		},
   261  		// prowyaml
   262  		{
   263  			name: "Not allowed cluster is rejected",
   264  			baseContent: map[string][]byte{
   265  				".prow.yaml": []byte(`postsubmits: [{"name": "hans", "cluster": "privileged", "spec": {"containers": [{}]}}]`),
   266  			},
   267  			validate: func(_ *ProwYAML, err error) error {
   268  				if err == nil {
   269  					return errors.New("error is nil")
   270  				}
   271  				expectedErrMsg := "cluster \"privileged\" is not allowed for repository \"org/repo\""
   272  				if err.Error() != expectedErrMsg {
   273  					return fmt.Errorf("expected error message to be %q, was %q", expectedErrMsg, err.Error())
   274  				}
   275  				return nil
   276  			},
   277  		},
   278  		{
   279  			name: "No prow.yaml, no error, no nullpointer",
   280  			validate: func(p *ProwYAML, err error) error {
   281  				if err != nil {
   282  					return fmt.Errorf("unexpected error: %w", err)
   283  				}
   284  				if p == nil {
   285  					return errors.New("prowYAML is nil")
   286  				}
   287  				if n := len(p.Presubmits); n != 0 {
   288  					return fmt.Errorf("expected to get zero presubmits, got %d", n)
   289  				}
   290  				return nil
   291  			},
   292  		},
   293  		{
   294  			name: "Yaml unmarshaling is not strict",
   295  			baseContent: map[string][]byte{
   296  				".prow.yaml": []byte(`postsubmits: [{"name": "hans", "undef_attr": true, "spec": {"containers": [{}]}}]`),
   297  			},
   298  			validate: func(p *ProwYAML, err error) error {
   299  				if err != nil {
   300  					return fmt.Errorf("unexpected error: %w", err)
   301  				}
   302  				if n := len(p.Postsubmits); n != 1 || p.Postsubmits[0].Name != "hans" {
   303  					return fmt.Errorf(`expected exactly one postsubmit with name "hans", got %v`, p.Postsubmits)
   304  				}
   305  				return nil
   306  			},
   307  		},
   308  		// git client
   309  		{
   310  			name:              "No panic on nil gitClient",
   311  			dontPassGitClient: true,
   312  			validate: func(_ *ProwYAML, err error) error {
   313  				if err == nil || err.Error() != "gitClient is nil" {
   314  					return fmt.Errorf(`expected error to be "gitClient is nil", was %v`, err)
   315  				}
   316  				return nil
   317  			},
   318  		},
   319  		// .prow directory
   320  		{
   321  			name: "Basic happy path (.prow directory, single file)",
   322  			baseContent: map[string][]byte{
   323  				".prow/base.yaml": []byte(`presubmits: [{"name": "hans", "spec": {"containers": [{}]}}]`),
   324  			},
   325  			validate: func(p *ProwYAML, err error) error {
   326  				if err != nil {
   327  					return fmt.Errorf("unexpected error: %w", err)
   328  				}
   329  				if n := len(p.Presubmits); n != 1 || p.Presubmits[0].Name != "hans" {
   330  					return fmt.Errorf(`expected exactly one presubmit with name "hans", got %v`, p.Presubmits)
   331  				}
   332  				return nil
   333  			},
   334  		},
   335  		{
   336  			name: "Prefer .prow directory over .prow.yaml file",
   337  			baseContent: map[string][]byte{
   338  				".prow.yaml":      []byte(`presubmits: [{"name": "hans", "spec": {"containers": [{}]}}]`),
   339  				".prow/base.yaml": []byte(`presubmits: [{"name": "kurt", "spec": {"containers": [{}]}}]`),
   340  			},
   341  			validate: func(p *ProwYAML, err error) error {
   342  				if err != nil {
   343  					return fmt.Errorf("unexpected error: %w", err)
   344  				}
   345  				if n := len(p.Presubmits); n != 1 || p.Presubmits[0].Name != "kurt" {
   346  					return fmt.Errorf(`expected exactly one presubmit with name "kurt", got %v`, p.Presubmits)
   347  				}
   348  				return nil
   349  			},
   350  		},
   351  		{
   352  			name: "Merge presubmits under .prow directory",
   353  			baseContent: map[string][]byte{
   354  				".prow/one.yaml": []byte(`presubmits: [{"name": "hans", "spec": {"containers": [{}]}}]`),
   355  				".prow/two.yaml": []byte(`presubmits: [{"name": "kurt", "spec": {"containers": [{}]}}]`),
   356  			},
   357  			validate: func(p *ProwYAML, err error) error {
   358  				if err != nil {
   359  					return fmt.Errorf("unexpected error: %w", err)
   360  				}
   361  				if n := len(p.Presubmits); n != 2 ||
   362  					p.Presubmits[0].Name != "hans" ||
   363  					p.Presubmits[1].Name != "kurt" {
   364  					return fmt.Errorf(`expected exactly two presubmit with name "hans" and "kurt", got %v`, p.Presubmits)
   365  				}
   366  				return nil
   367  			},
   368  		},
   369  		{
   370  			name: "Merge presubmits several levels under .prow directory",
   371  			baseContent: map[string][]byte{
   372  				".prow/sub1/sub2/one.yaml": []byte(`presubmits: [{"name": "hans", "spec": {"containers": [{}]}}]`),
   373  				".prow/sub3/two.yaml":      []byte(`presubmits: [{"name": "kurt", "spec": {"containers": [{}]}}]`),
   374  			},
   375  			validate: func(p *ProwYAML, err error) error {
   376  				if err != nil {
   377  					return fmt.Errorf("unexpected error: %w", err)
   378  				}
   379  				if n := len(p.Presubmits); n != 2 ||
   380  					p.Presubmits[0].Name != "hans" ||
   381  					p.Presubmits[1].Name != "kurt" {
   382  					return fmt.Errorf(`expected exactly two presubmit with name "hans" and "kurt", got %v`, p.Presubmits)
   383  				}
   384  				return nil
   385  			},
   386  		},
   387  		{
   388  			name: "Merge postsubmits under .prow directory",
   389  			baseContent: map[string][]byte{
   390  				".prow/one.yaml": []byte(`postsubmits: [{"name": "hans", "spec": {"containers": [{}]}}]`),
   391  				".prow/two.yaml": []byte(`postsubmits: [{"name": "kurt", "spec": {"containers": [{}]}}]`),
   392  			},
   393  			validate: func(p *ProwYAML, err error) error {
   394  				if err != nil {
   395  					return fmt.Errorf("unexpected error: %w", err)
   396  				}
   397  				if n := len(p.Postsubmits); n != 2 ||
   398  					p.Postsubmits[0].Name != "hans" ||
   399  					p.Postsubmits[1].Name != "kurt" {
   400  					return fmt.Errorf(`expected exactly two postsubmit with name "hans" and "kurt", got %v`, p.Postsubmits)
   401  				}
   402  				return nil
   403  			},
   404  		},
   405  		{
   406  			name: "Merge postsubmits several levels under .prow directory",
   407  			baseContent: map[string][]byte{
   408  				".prow/sub1/sub2/one.yaml": []byte(`postsubmits: [{"name": "hans", "spec": {"containers": [{}]}}]`),
   409  				".prow/sub3/two.yaml":      []byte(`postsubmits: [{"name": "kurt", "spec": {"containers": [{}]}}]`),
   410  			},
   411  			validate: func(p *ProwYAML, err error) error {
   412  				if err != nil {
   413  					return fmt.Errorf("unexpected error: %w", err)
   414  				}
   415  				if n := len(p.Postsubmits); n != 2 ||
   416  					p.Postsubmits[0].Name != "hans" ||
   417  					p.Postsubmits[1].Name != "kurt" {
   418  					return fmt.Errorf(`expected exactly two postsubmit with names "hans" and "kurt", got %v`, p.Postsubmits)
   419  				}
   420  				return nil
   421  			},
   422  		},
   423  		{
   424  			name: "Merge presets under .prow directory",
   425  			baseContent: map[string][]byte{
   426  				".prow/one.yaml": []byte(`presets: [{"labels": {"hans": "hansValue"}}]`),
   427  				".prow/two.yaml": []byte(`presets: [{"labels": {"kurt": "kurtValue"}}]`),
   428  			},
   429  			validate: func(p *ProwYAML, err error) error {
   430  				if err != nil {
   431  					return fmt.Errorf("unexpected error: %w", err)
   432  				}
   433  				if n := len(p.Presets); n != 2 ||
   434  					p.Presets[0].Labels["hans"] != "hansValue" ||
   435  					p.Presets[1].Labels["kurt"] != "kurtValue" {
   436  					return fmt.Errorf(`expected exactly two presets with labels "hans": "hansValue" and "kurt": "kurtValue", got %v`, p.Presets)
   437  				}
   438  				return nil
   439  			},
   440  		},
   441  		{
   442  			name: "Merge presets several levels under .prow directory",
   443  			baseContent: map[string][]byte{
   444  				".prow/sub1/sub2/one.yaml": []byte(`presets: [{"labels": {"hans": "hansValue"}}]`),
   445  				".prow/sub3/two.yaml":      []byte(`presets: [{"labels": {"kurt": "kurtValue"}}]`),
   446  			},
   447  			validate: func(p *ProwYAML, err error) error {
   448  				if err != nil {
   449  					return fmt.Errorf("unexpected error: %w", err)
   450  				}
   451  				if n := len(p.Presets); n != 2 ||
   452  					p.Presets[0].Labels["hans"] != "hansValue" ||
   453  					p.Presets[1].Labels["kurt"] != "kurtValue" {
   454  					return fmt.Errorf(`expected exactly two presets with labels "hans": "hansValue" and "kurt": "kurtValue", got %v`, p.Presets)
   455  				}
   456  				return nil
   457  			},
   458  		},
   459  		{
   460  			name: "Merge presubmits, postsubmits and presets several levels under .prow directory",
   461  			baseContent: map[string][]byte{
   462  				".prow/sub1/sub2/one.yaml": []byte(`presubmits: [{"name": "hans", "spec": {"containers": [{}]}}]
   463  postsubmits: [{"name": "karl", "spec": {"containers": [{}]}}]
   464  presets: [{"labels": {"karl": "karlValue"}}]
   465  `),
   466  				".prow/sub3/two.yaml": []byte(`presubmits: [{"name": "kurt", "spec": {"containers": [{}]}}]
   467  postsubmits: [{"name": "oli", "spec": {"containers": [{}]}}]`),
   468  				".prow/sub4//sub5/sub6/three.yaml": []byte(`presets: [{"labels": {"henning": "henningValue"}}]`),
   469  			},
   470  			validate: func(p *ProwYAML, err error) error {
   471  				if err != nil {
   472  					return fmt.Errorf("unexpected error: %w", err)
   473  				}
   474  				if n := len(p.Presubmits); n != 2 ||
   475  					p.Presubmits[0].Name != "hans" ||
   476  					p.Presubmits[1].Name != "kurt" {
   477  					return fmt.Errorf(`expected exactly two presubmits with names "hans" and "kurt" got %v`, p.Presubmits)
   478  				}
   479  				if n := len(p.Postsubmits); n != 2 ||
   480  					p.Postsubmits[0].Name != "karl" ||
   481  					p.Postsubmits[1].Name != "oli" {
   482  					return fmt.Errorf(`expected exactly two postsubmits with names "karl" and "oli", got %v`, p.Postsubmits)
   483  				}
   484  				if n := len(p.Presets); n != 2 ||
   485  					p.Presets[0].Labels["karl"] != "karlValue" ||
   486  					p.Presets[1].Labels["henning"] != "henningValue" {
   487  					return fmt.Errorf(`expected exactly two presets with labels "karl": "karlValue" and "henning": "henningValue", got %v`, p.Presets)
   488  				}
   489  				return nil
   490  			},
   491  		},
   492  		{
   493  			name: "Non-.yaml files under .prow directory are allowed",
   494  			baseContent: map[string][]byte{
   495  				".prow/one.yaml":     []byte(`presubmits: [{"name": "hans", "spec": {"containers": [{}]}}]`),
   496  				".prow/OWNERS":       []byte(`approvers: [approver1, approver2]`),
   497  				".prow/sub/two.yaml": []byte(`presubmits: [{"name": "kurt", "spec": {"containers": [{}]}}]`),
   498  				".prow/sub/OWNERS":   []byte(`approvers: [approver3, approver4]`),
   499  			},
   500  			validate: func(p *ProwYAML, err error) error {
   501  				if err != nil {
   502  					return fmt.Errorf("unexpected error: %w", err)
   503  				}
   504  				if n := len(p.Presubmits); n != 2 ||
   505  					p.Presubmits[0].Name != "hans" ||
   506  					p.Presubmits[1].Name != "kurt" {
   507  					return fmt.Errorf(`expected exactly two presubmit with name "hans" and "kurt", got %v`, p.Presubmits)
   508  				}
   509  				return nil
   510  			},
   511  		},
   512  		{
   513  			name: "Both .yaml and .yml files are allowed under .prow directory)",
   514  			baseContent: map[string][]byte{
   515  				".prow/one.yaml": []byte(`presubmits: [{"name": "hans", "spec": {"containers": [{}]}}]`),
   516  				".prow/two.yml":  []byte(`presubmits: [{"name": "kurt", "spec": {"containers": [{}]}}]`),
   517  			},
   518  			validate: func(p *ProwYAML, err error) error {
   519  				if err != nil {
   520  					return fmt.Errorf("unexpected error: %w", err)
   521  				}
   522  				if n := len(p.Presubmits); n != 2 ||
   523  					p.Presubmits[0].Name != "hans" ||
   524  					p.Presubmits[1].Name != "kurt" {
   525  					return fmt.Errorf(`expected exactly two presubmit with name "hans" and "kurt", got %v`, p.Presubmits)
   526  				}
   527  				return nil
   528  			},
   529  		},
   530  		{
   531  			name: "Basic happy path (presubmits, gerrit repo)",
   532  			baseContent: map[string][]byte{
   533  				".prow.yaml": []byte(`presubmits: [{"name": "hans", "spec": {"containers": [{}]}}]`),
   534  			},
   535  			validate: func(p *ProwYAML, err error) error {
   536  				if err != nil {
   537  					return fmt.Errorf("unexpected error: %w", err)
   538  				}
   539  				if n := len(p.Presubmits); n != 1 || p.Presubmits[0].Name != "hans" {
   540  					return fmt.Errorf(`expected exactly one presubmit with name "hans", got %v`, p.Presubmits)
   541  				}
   542  				return nil
   543  			},
   544  			repo: "repo/name",
   545  		},
   546  	}
   547  
   548  	for idx := range testCases {
   549  		tc := testCases[idx]
   550  		t.Run(tc.name, func(t *testing.T) {
   551  			t.Parallel()
   552  
   553  			repo := defaultRepo
   554  			if len(tc.repo) > 0 {
   555  				repo = tc.repo
   556  			}
   557  
   558  			lg, gc, err := clients()
   559  			if err != nil {
   560  				t.Fatalf("Making local git repo: %v", err)
   561  			}
   562  			defer func() {
   563  				if err := lg.Clean(); err != nil {
   564  					t.Errorf("Error cleaning LocalGit: %v", err)
   565  				}
   566  				if err := gc.Clean(); err != nil {
   567  					t.Errorf("Error cleaning Client: %v", err)
   568  				}
   569  			}()
   570  
   571  			if err := lg.MakeFakeRepo(org, repo); err != nil {
   572  				t.Fatalf("Making fake repo: %v", err)
   573  			}
   574  			if tc.baseContent != nil {
   575  				if err := lg.AddCommit(org, repo, tc.baseContent); err != nil {
   576  					t.Fatalf("failed to commit baseContent: %v", err)
   577  				}
   578  			}
   579  			if tc.headContent != nil {
   580  				if err := lg.CheckoutNewBranch(org, repo, "can-I-haz-pulled"); err != nil {
   581  					t.Fatalf("failed to create new branch: %v", err)
   582  				}
   583  				if err := lg.AddCommit(org, repo, tc.headContent); err != nil {
   584  					t.Fatalf("failed to add head commit: %v", err)
   585  				}
   586  			}
   587  
   588  			baseSHA, err := lg.RevParse(org, repo, defaultBranch)
   589  			if err != nil {
   590  				t.Fatalf("failed to get baseSHA: %v", err)
   591  			}
   592  			headSHA, err := lg.RevParse(org, repo, "HEAD")
   593  			if err != nil {
   594  				t.Fatalf("failed to head headSHA: %v", err)
   595  			}
   596  
   597  			if tc.config == nil {
   598  				tc.config = &Config{
   599  					ProwConfig: ProwConfig{
   600  						InRepoConfig: InRepoConfig{
   601  							AllowedClusters: map[string][]string{"*": {kube.DefaultClusterAlias}},
   602  						},
   603  					},
   604  				}
   605  			}
   606  			// Validation fails when no NS is provided
   607  			tc.config.PodNamespace = "my-ns"
   608  
   609  			testGC := gc
   610  			if tc.dontPassGitClient {
   611  				testGC = nil
   612  			}
   613  
   614  			var p *ProwYAML
   615  			if headSHA == baseSHA {
   616  				p, err = prowYAMLGetterWithDefaults(tc.config, testGC, org+"/"+repo, "main", baseSHA)
   617  			} else {
   618  				p, err = prowYAMLGetterWithDefaults(tc.config, testGC, org+"/"+repo, "main", baseSHA, headSHA)
   619  			}
   620  
   621  			if err := tc.validate(p, err); err != nil {
   622  				t.Fatal(err)
   623  			}
   624  
   625  			// Empty base branch string shouldn't affect how we can fetch the configs.
   626  			if headSHA == baseSHA {
   627  				p, err = prowYAMLGetterWithDefaults(tc.config, testGC, org+"/"+repo, "", baseSHA)
   628  			} else {
   629  				p, err = prowYAMLGetterWithDefaults(tc.config, testGC, org+"/"+repo, "", baseSHA, headSHA)
   630  			}
   631  
   632  			if err := tc.validate(p, err); err != nil {
   633  				t.Fatal(err)
   634  			}
   635  
   636  		})
   637  	}
   638  }
   639  
   640  func TestDefaultProwYAMLGetter_RejectsJustOrgV2(t *testing.T) {
   641  	testDefaultProwYAMLGetter_RejectsJustOrg(localgit.NewV2, t)
   642  }
   643  
   644  func testDefaultProwYAMLGetter_RejectsJustOrg(clients localgit.Clients, t *testing.T) {
   645  	lg, gc, err := clients()
   646  	if err != nil {
   647  		t.Fatalf("Making local git repo: %v", err)
   648  	}
   649  	defer func() {
   650  		if err := lg.Clean(); err != nil {
   651  			t.Errorf("Error cleaning LocalGit: %v", err)
   652  		}
   653  		if err := gc.Clean(); err != nil {
   654  			t.Errorf("Error cleaning Client: %v", err)
   655  		}
   656  	}()
   657  
   658  	identifier := "my-repo"
   659  	if err := lg.MakeFakeRepo(identifier, ""); err != nil {
   660  		t.Fatalf("Making fake repo: %v", err)
   661  	}
   662  	expectedErrMsg := `didn't get two results when splitting repo identifier "my-repo"`
   663  	if _, err := prowYAMLGetterWithDefaults(&Config{}, gc, identifier, "", ""); err == nil || err.Error() != expectedErrMsg {
   664  		t.Errorf("Error %v does not have expected message %s", err, expectedErrMsg)
   665  	}
   666  }
   667  
   668  type testClientFactory struct {
   669  	git.ClientFactory // This will be nil during testing, we override the functions that are used.
   670  	rcMap             map[string]git.RepoClient
   671  	clientsCreated    int
   672  }
   673  
   674  func (cf *testClientFactory) ClientFor(org, repo string) (git.RepoClient, error) {
   675  	cf.clientsCreated++
   676  	// Returning this RepoClient ensures that only Fetch() is called and that Close() is not.
   677  
   678  	return &fetchOnlyNoCleanRepoClient{cf.rcMap[repo]}, nil
   679  }
   680  
   681  func (cf *testClientFactory) ClientForWithRepoOpts(org, repo string, repoOpts git.RepoOpts) (git.RepoClient, error) {
   682  	cf.clientsCreated++
   683  	// Returning this RepoClient ensures that only Fetch() is called and that Close() is not.
   684  
   685  	return &fetchOnlyNoCleanRepoClient{cf.rcMap[repo]}, nil
   686  }
   687  
   688  type fetchOnlyNoCleanRepoClient struct {
   689  	git.RepoClient // This will be nil during testing, we override the functions that are allowed to be used.
   690  }
   691  
   692  func (rc *fetchOnlyNoCleanRepoClient) Fetch(arg ...string) error {
   693  	return nil
   694  }
   695  
   696  // Override Close to make sure when Close is called it would error out
   697  func (rc *fetchOnlyNoCleanRepoClient) Close() error {
   698  	panic("This is not supposed to be called")
   699  }
   700  
   701  func TestInRepoConfigClean(t *testing.T) {
   702  	t.Parallel()
   703  	org, repo := "org", "repo"
   704  
   705  	lg, c, _ := localgit.NewV2()
   706  	rcMap := make(map[string]git.RepoClient)
   707  	if err := lg.MakeFakeRepo(org, repo); err != nil {
   708  		t.Fatal(err)
   709  	}
   710  	rc, err := c.ClientFor(org, repo)
   711  	if err != nil {
   712  		t.Fatal(err)
   713  	}
   714  	rcMap[repo] = rc
   715  
   716  	cf := &testClientFactory{
   717  		rcMap: rcMap,
   718  	}
   719  
   720  	// First time clone should work
   721  	repoClient, err := cf.ClientFor(org, repo)
   722  	if err != nil {
   723  		t.Fatalf("Unexpected error getting repo client for thread 1: %v.", err)
   724  	}
   725  
   726  	// Now dirty the repo
   727  	dir := repoClient.Directory()
   728  	f := path.Join(dir, "new-file")
   729  	if err := os.WriteFile(f, []byte("something"), 0644); err != nil {
   730  		t.Fatal(err)
   731  	}
   732  	repoClient.Clean()
   733  
   734  	// Second time should be none dirty
   735  	repoClient, err = cf.ClientFor(org, repo)
   736  	if err != nil {
   737  		t.Fatalf("Unexpected error getting repo client for thread 1: %v.", err)
   738  	}
   739  	repoClient.Clean()
   740  
   741  	_, err = os.Stat(f)
   742  	if err == nil || !os.IsNotExist(err) {
   743  		t.Fatalf("%s should have been deleted", f)
   744  	}
   745  }