sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/apis/prowjobs/v1/types_test.go (about)

     1  /*
     2  Copyright 2018 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 v1
    18  
    19  import (
    20  	"reflect"
    21  	"strconv"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/google/go-cmp/cmp"
    26  	"github.com/google/go-cmp/cmp/cmpopts"
    27  	fuzz "github.com/google/gofuzz"
    28  	pipelinev1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
    29  )
    30  
    31  func pStr(str string) *string {
    32  	return &str
    33  }
    34  
    35  // TODO(mpherman): Add more tests when ProwJobDefaults have more than 1 field
    36  func TestProwJobDefaulting(t *testing.T) {
    37  	var testCases = []struct {
    38  		name     string
    39  		provided *ProwJobDefault
    40  		def      *ProwJobDefault
    41  		expected *ProwJobDefault
    42  	}{
    43  		{
    44  			name:     "nothing provided",
    45  			provided: &ProwJobDefault{},
    46  			def:      &ProwJobDefault{},
    47  			expected: &ProwJobDefault{},
    48  		},
    49  		{
    50  			name: "All provided, no default",
    51  			provided: &ProwJobDefault{
    52  				ResultStoreConfig: &ResultStoreConfig{
    53  					ProjectID: "project",
    54  				},
    55  				TenantID: "tenant",
    56  			},
    57  			def: &ProwJobDefault{},
    58  			expected: &ProwJobDefault{
    59  				ResultStoreConfig: &ResultStoreConfig{
    60  					ProjectID: "project",
    61  				},
    62  				TenantID: "tenant",
    63  			},
    64  		},
    65  		{
    66  			name: "All provided, no override",
    67  			provided: &ProwJobDefault{
    68  				ResultStoreConfig: &ResultStoreConfig{
    69  					ProjectID: "project",
    70  				},
    71  				TenantID: "tenant",
    72  			},
    73  			def: &ProwJobDefault{
    74  				ResultStoreConfig: &ResultStoreConfig{
    75  					ProjectID: "default-project",
    76  				},
    77  				TenantID: "default-tenant",
    78  			},
    79  			expected: &ProwJobDefault{
    80  				ResultStoreConfig: &ResultStoreConfig{
    81  					ProjectID: "project",
    82  				},
    83  				TenantID: "tenant",
    84  			},
    85  		},
    86  		{
    87  			name:     "Empty provided, no default",
    88  			provided: &ProwJobDefault{},
    89  			def:      &ProwJobDefault{},
    90  			expected: &ProwJobDefault{},
    91  		},
    92  		{
    93  			name:     "Empty provided, use default",
    94  			provided: &ProwJobDefault{},
    95  			def: &ProwJobDefault{
    96  				ResultStoreConfig: &ResultStoreConfig{
    97  					ProjectID: "default-project",
    98  				},
    99  				TenantID: "default-tenant",
   100  			},
   101  			expected: &ProwJobDefault{
   102  				ResultStoreConfig: &ResultStoreConfig{
   103  					ProjectID: "default-project",
   104  				},
   105  				TenantID: "default-tenant",
   106  			},
   107  		},
   108  		{
   109  			name:     "Nil provided, empty default",
   110  			provided: nil,
   111  			def:      &ProwJobDefault{},
   112  			expected: &ProwJobDefault{},
   113  		},
   114  		{
   115  			name:     "Nil provided, use default",
   116  			provided: nil,
   117  			def: &ProwJobDefault{
   118  				ResultStoreConfig: &ResultStoreConfig{
   119  					ProjectID: "default-project",
   120  				},
   121  				TenantID: "default-tenant",
   122  			},
   123  			expected: &ProwJobDefault{
   124  				ResultStoreConfig: &ResultStoreConfig{
   125  					ProjectID: "default-project",
   126  				},
   127  				TenantID: "default-tenant",
   128  			},
   129  		},
   130  		{
   131  			name:     "Nil provided, nil default",
   132  			provided: nil,
   133  			def:      nil,
   134  			expected: nil,
   135  		},
   136  		{
   137  			name: "This provided, that default",
   138  			provided: &ProwJobDefault{
   139  				ResultStoreConfig: &ResultStoreConfig{
   140  					ProjectID: "project",
   141  				},
   142  			},
   143  			def: &ProwJobDefault{
   144  				TenantID: "default-tenant",
   145  			},
   146  			expected: &ProwJobDefault{
   147  				ResultStoreConfig: &ResultStoreConfig{
   148  					ProjectID: "project",
   149  				},
   150  				TenantID: "default-tenant",
   151  			},
   152  		},
   153  	}
   154  	for _, testCase := range testCases {
   155  		tc := testCase
   156  		t.Run(tc.name, func(t *testing.T) {
   157  			t.Parallel()
   158  			actual := tc.provided.ApplyDefault(tc.def)
   159  			if diff := cmp.Diff(actual, tc.expected, cmpopts.EquateEmpty()); diff != "" {
   160  				t.Errorf("expected defaulted config but got diff %v", diff)
   161  			}
   162  		})
   163  	}
   164  }
   165  
   166  func TestDecorationDefaultingDoesntOverwrite(t *testing.T) {
   167  	truth := true
   168  	lies := false
   169  
   170  	var testCases = []struct {
   171  		name     string
   172  		provided *DecorationConfig
   173  		// Note: def is a copy of the defaults and may be modified.
   174  		expected func(orig, def *DecorationConfig) *DecorationConfig
   175  	}{
   176  		{
   177  			name:     "nothing provided",
   178  			provided: &DecorationConfig{},
   179  			expected: func(orig, def *DecorationConfig) *DecorationConfig {
   180  				return def
   181  			},
   182  		},
   183  		{
   184  			name: "timeout provided",
   185  			provided: &DecorationConfig{
   186  				Timeout: &Duration{Duration: 10 * time.Minute},
   187  			},
   188  			expected: func(orig, def *DecorationConfig) *DecorationConfig {
   189  				def.Timeout = orig.Timeout
   190  				return def
   191  			},
   192  		},
   193  		{
   194  			name: "grace period provided",
   195  			provided: &DecorationConfig{
   196  				GracePeriod: &Duration{Duration: 10 * time.Hour},
   197  			},
   198  			expected: func(orig, def *DecorationConfig) *DecorationConfig {
   199  				def.GracePeriod = orig.GracePeriod
   200  				return def
   201  			},
   202  		},
   203  		{
   204  			name: "utility images provided",
   205  			provided: &DecorationConfig{
   206  				UtilityImages: &UtilityImages{
   207  					CloneRefs:  "clonerefs-special",
   208  					InitUpload: "initupload-special",
   209  					Entrypoint: "entrypoint-special",
   210  					Sidecar:    "sidecar-special",
   211  				},
   212  			},
   213  			expected: func(orig, def *DecorationConfig) *DecorationConfig {
   214  				def.UtilityImages = orig.UtilityImages
   215  				return def
   216  			},
   217  		},
   218  		{
   219  			name: "gcs configuration provided",
   220  			provided: &DecorationConfig{
   221  				GCSConfiguration: &GCSConfiguration{
   222  					Bucket:            "bucket-1",
   223  					PathPrefix:        "prefix-2",
   224  					PathStrategy:      PathStrategyExplicit,
   225  					DefaultOrg:        "org2",
   226  					DefaultRepo:       "repo2",
   227  					CompressFileTypes: []string{"txt", "json"},
   228  				},
   229  			},
   230  			expected: func(orig, def *DecorationConfig) *DecorationConfig {
   231  				def.GCSConfiguration = orig.GCSConfiguration
   232  				return def
   233  			},
   234  		},
   235  		{
   236  			name: "gcs secret name provided",
   237  			provided: &DecorationConfig{
   238  				GCSCredentialsSecret: pStr("somethingSecret"),
   239  			},
   240  			expected: func(orig, def *DecorationConfig) *DecorationConfig {
   241  				def.GCSCredentialsSecret = orig.GCSCredentialsSecret
   242  				return def
   243  			},
   244  		},
   245  		{
   246  			name: "gcs secret name unset",
   247  			provided: &DecorationConfig{
   248  				GCSCredentialsSecret: pStr(""),
   249  			},
   250  			expected: func(orig, def *DecorationConfig) *DecorationConfig {
   251  				def.GCSCredentialsSecret = orig.GCSCredentialsSecret
   252  				return def
   253  			},
   254  		},
   255  		{
   256  			name: "s3 secret name provided",
   257  			provided: &DecorationConfig{
   258  				S3CredentialsSecret: pStr("overwritten"),
   259  			},
   260  			expected: func(orig, def *DecorationConfig) *DecorationConfig {
   261  				def.S3CredentialsSecret = orig.S3CredentialsSecret
   262  				return def
   263  			},
   264  		},
   265  		{
   266  			name: "s3 secret name unset",
   267  			provided: &DecorationConfig{
   268  				S3CredentialsSecret: pStr(""),
   269  			},
   270  			expected: func(orig, def *DecorationConfig) *DecorationConfig {
   271  				def.S3CredentialsSecret = orig.S3CredentialsSecret
   272  				return def
   273  			},
   274  		},
   275  		{
   276  			name: "default service account name provided",
   277  			provided: &DecorationConfig{
   278  				DefaultServiceAccountName: pStr("gcs-upload-sa"),
   279  			},
   280  			expected: func(orig, def *DecorationConfig) *DecorationConfig {
   281  				def.DefaultServiceAccountName = orig.DefaultServiceAccountName
   282  				return def
   283  			},
   284  		},
   285  		{
   286  			name: "ssh secrets provided",
   287  			provided: &DecorationConfig{
   288  				SSHKeySecrets: []string{"my", "special"},
   289  			},
   290  			expected: func(orig, def *DecorationConfig) *DecorationConfig {
   291  				def.SSHKeySecrets = orig.SSHKeySecrets
   292  				return def
   293  			},
   294  		},
   295  
   296  		{
   297  			name: "utility images partially provided",
   298  			provided: &DecorationConfig{
   299  				UtilityImages: &UtilityImages{
   300  					CloneRefs:  "clonerefs-special",
   301  					InitUpload: "initupload-special",
   302  				},
   303  			},
   304  			expected: func(orig, def *DecorationConfig) *DecorationConfig {
   305  				def.UtilityImages.CloneRefs = orig.UtilityImages.CloneRefs
   306  				def.UtilityImages.InitUpload = orig.UtilityImages.InitUpload
   307  				return def
   308  			},
   309  		},
   310  		{
   311  			name: "gcs configuration partially provided",
   312  			provided: &DecorationConfig{
   313  				GCSConfiguration: &GCSConfiguration{
   314  					Bucket: "bucket-1",
   315  				},
   316  			},
   317  			expected: func(orig, def *DecorationConfig) *DecorationConfig {
   318  				def.GCSConfiguration.Bucket = orig.GCSConfiguration.Bucket
   319  				return def
   320  			},
   321  		},
   322  		{
   323  			name: "skip_cloning provided",
   324  			provided: &DecorationConfig{
   325  				SkipCloning: &lies,
   326  			},
   327  			expected: func(orig, def *DecorationConfig) *DecorationConfig {
   328  				def.SkipCloning = orig.SkipCloning
   329  				return def
   330  			},
   331  		},
   332  		{
   333  			name: "ssh host fingerprints provided",
   334  			provided: &DecorationConfig{
   335  				SSHHostFingerprints: []string{"unique", "print"},
   336  			},
   337  			expected: func(orig, def *DecorationConfig) *DecorationConfig {
   338  				def.SSHHostFingerprints = orig.SSHHostFingerprints
   339  				return def
   340  			},
   341  		},
   342  		{
   343  			name: "ingnore interrupts set",
   344  			provided: &DecorationConfig{
   345  				UploadIgnoresInterrupts: &truth,
   346  			},
   347  			expected: func(orig, def *DecorationConfig) *DecorationConfig {
   348  				def.UploadIgnoresInterrupts = orig.UploadIgnoresInterrupts
   349  				return def
   350  			},
   351  		},
   352  		{
   353  			name: "do not ingnore interrupts ",
   354  			provided: &DecorationConfig{
   355  				UploadIgnoresInterrupts: &lies,
   356  			},
   357  			expected: func(orig, def *DecorationConfig) *DecorationConfig {
   358  				def.UploadIgnoresInterrupts = orig.UploadIgnoresInterrupts
   359  				return def
   360  			},
   361  		},
   362  	}
   363  
   364  	for _, testCase := range testCases {
   365  		tc := testCase
   366  		t.Run(tc.name, func(t *testing.T) {
   367  			t.Parallel()
   368  			defaults := &DecorationConfig{
   369  				Timeout:     &Duration{Duration: 1 * time.Minute},
   370  				GracePeriod: &Duration{Duration: 10 * time.Second},
   371  				UtilityImages: &UtilityImages{
   372  					CloneRefs:  "clonerefs",
   373  					InitUpload: "initupload",
   374  					Entrypoint: "entrypoint",
   375  					Sidecar:    "sidecar",
   376  				},
   377  				GCSConfiguration: &GCSConfiguration{
   378  					Bucket:       "bucket",
   379  					PathPrefix:   "prefix",
   380  					PathStrategy: PathStrategyLegacy,
   381  					DefaultOrg:   "org",
   382  					DefaultRepo:  "repo",
   383  				},
   384  				GCSCredentialsSecret: pStr("secretName"),
   385  				S3CredentialsSecret:  pStr("s3-secret"),
   386  				SSHKeySecrets:        []string{"first", "second"},
   387  				SSHHostFingerprints:  []string{"primero", "segundo"},
   388  				SkipCloning:          &truth,
   389  			}
   390  
   391  			expected := tc.expected(tc.provided, defaults)
   392  			actual := tc.provided.ApplyDefault(defaults)
   393  			if diff := cmp.Diff(actual, expected, cmpopts.EquateEmpty()); diff != "" {
   394  				t.Errorf("expected defaulted config but got diff %v", diff)
   395  			}
   396  		})
   397  	}
   398  }
   399  
   400  func TestApplyDefaultsAppliesDefaultsForAllFields(t *testing.T) {
   401  	t.Parallel()
   402  	seed := time.Now().UnixNano()
   403  	// Print the seed so failures can easily be reproduced
   404  	t.Logf("Seed: %d", seed)
   405  	fuzzer := fuzz.NewWithSeed(seed)
   406  	for i := 0; i < 100; i++ {
   407  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   408  			def := &DecorationConfig{}
   409  			fuzzer.Fuzz(def)
   410  
   411  			// Each of those three has its own DeepCopy and in case it is nil,
   412  			// we just call that and return. In order to make this test verify
   413  			// that copying of their fields also works, we have to set them to
   414  			// something non-nil.
   415  			toDefault := &DecorationConfig{
   416  				UtilityImages:    &UtilityImages{},
   417  				Resources:        &Resources{},
   418  				GCSConfiguration: &GCSConfiguration{},
   419  			}
   420  			if def.UtilityImages == nil {
   421  				def.UtilityImages = &UtilityImages{}
   422  			}
   423  			if def.Resources == nil {
   424  				def.Resources = &Resources{}
   425  			}
   426  			if def.GCSConfiguration == nil {
   427  				def.GCSConfiguration = &GCSConfiguration{}
   428  			}
   429  			defaulted := toDefault.ApplyDefault(def)
   430  
   431  			if diff := cmp.Diff(def, defaulted); diff != "" {
   432  				t.Errorf("defaulted decoration config didn't get all fields defaulted: %s", diff)
   433  			}
   434  		})
   435  	}
   436  }
   437  
   438  func TestSlackConfigApplyDefaultsAppliesDefaultsForAllFields(t *testing.T) {
   439  	t.Parallel()
   440  	seed := time.Now().UnixNano()
   441  	// Print the seed so failures can easily be reproduced
   442  	t.Logf("Seed: %d", seed)
   443  	fuzzer := fuzz.NewWithSeed(seed)
   444  	for i := 0; i < 100; i++ {
   445  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   446  			def := &SlackReporterConfig{}
   447  			fuzzer.Fuzz(def)
   448  
   449  			// Each of those three has its own DeepCopy and in case it is nil,
   450  			// we just call that and return. In order to make this test verify
   451  			// that copying of their fields also works, we have to set them to
   452  			// something non-nil.
   453  			toDefault := &SlackReporterConfig{
   454  				Host:              "",
   455  				Channel:           "",
   456  				JobStatesToReport: nil,
   457  				ReportTemplate:    "",
   458  			}
   459  			defaulted := toDefault.ApplyDefault(def)
   460  
   461  			if diff := cmp.Diff(def, defaulted); diff != "" {
   462  				t.Errorf("defaulted decoration config didn't get all fields defaulted: %s", diff)
   463  			}
   464  		})
   465  	}
   466  }
   467  
   468  func TestRefsToString(t *testing.T) {
   469  	var tests = []struct {
   470  		name     string
   471  		ref      Refs
   472  		expected string
   473  	}{
   474  		{
   475  			name: "Refs with Pull",
   476  			ref: Refs{
   477  				BaseRef: "master",
   478  				BaseSHA: "deadbeef",
   479  				Pulls: []Pull{
   480  					{
   481  						Number: 123,
   482  						SHA:    "abcd1234",
   483  					},
   484  				},
   485  			},
   486  			expected: "master:deadbeef,123:abcd1234",
   487  		},
   488  		{
   489  			name: "Refs with multiple Pulls",
   490  			ref: Refs{
   491  				BaseRef: "master",
   492  				BaseSHA: "deadbeef",
   493  				Pulls: []Pull{
   494  					{
   495  						Number: 123,
   496  						SHA:    "abcd1234",
   497  					},
   498  					{
   499  						Number: 456,
   500  						SHA:    "dcba4321",
   501  					},
   502  				},
   503  			},
   504  			expected: "master:deadbeef,123:abcd1234,456:dcba4321",
   505  		},
   506  		{
   507  			name: "Refs with BaseRef only",
   508  			ref: Refs{
   509  				BaseRef: "master",
   510  			},
   511  			expected: "master",
   512  		},
   513  		{
   514  			name: "Refs with BaseRef and BaseSHA",
   515  			ref: Refs{
   516  				BaseRef: "master",
   517  				BaseSHA: "deadbeef",
   518  			},
   519  			expected: "master:deadbeef",
   520  		},
   521  	}
   522  
   523  	for _, test := range tests {
   524  		actual, expected := test.ref.String(), test.expected
   525  		if actual != expected {
   526  			t.Errorf("%s: got ref string: %s, but expected: %s", test.name, actual, expected)
   527  		}
   528  	}
   529  }
   530  
   531  func TestRerunAuthConfigValidate(t *testing.T) {
   532  	var testCases = []struct {
   533  		name        string
   534  		config      *RerunAuthConfig
   535  		errExpected bool
   536  	}{
   537  		{
   538  			name:        "disallow all",
   539  			config:      &RerunAuthConfig{AllowAnyone: false},
   540  			errExpected: false,
   541  		},
   542  		{
   543  			name:        "no restrictions",
   544  			config:      &RerunAuthConfig{},
   545  			errExpected: false,
   546  		},
   547  		{
   548  			name:        "allow any",
   549  			config:      &RerunAuthConfig{AllowAnyone: true},
   550  			errExpected: false,
   551  		},
   552  		{
   553  			name:        "restrict orgs",
   554  			config:      &RerunAuthConfig{GitHubOrgs: []string{"istio"}},
   555  			errExpected: false,
   556  		},
   557  		{
   558  			name:        "restrict orgs and users",
   559  			config:      &RerunAuthConfig{GitHubOrgs: []string{"istio", "kubernetes"}, GitHubUsers: []string{"clarketm", "scoobydoo"}},
   560  			errExpected: false,
   561  		},
   562  		{
   563  			name:        "allow any and has restriction",
   564  			config:      &RerunAuthConfig{AllowAnyone: true, GitHubOrgs: []string{"istio"}},
   565  			errExpected: true,
   566  		},
   567  	}
   568  
   569  	for _, tc := range testCases {
   570  		t.Run(tc.name, func(t *testing.T) {
   571  
   572  			if err := tc.config.Validate(); (err != nil) != tc.errExpected {
   573  				t.Errorf("Expected error %v, got %v", tc.errExpected, err)
   574  			}
   575  		})
   576  	}
   577  }
   578  
   579  func TestRerunAuthConfigIsAuthorized(t *testing.T) {
   580  	var testCases = []struct {
   581  		name       string
   582  		user       string
   583  		config     *RerunAuthConfig
   584  		authorized bool
   585  	}{
   586  		{
   587  			name:       "authorized - AllowAnyone is true",
   588  			user:       "gumby",
   589  			config:     &RerunAuthConfig{AllowAnyone: true},
   590  			authorized: true,
   591  		},
   592  		{
   593  			name:       "authorized - user in GitHubUsers",
   594  			user:       "gumby",
   595  			config:     &RerunAuthConfig{GitHubUsers: []string{"gumby"}},
   596  			authorized: true,
   597  		},
   598  		{
   599  			name:       "unauthorized - RerunAuthConfig is nil",
   600  			user:       "gumby",
   601  			config:     nil,
   602  			authorized: false,
   603  		},
   604  		{
   605  			name:       "unauthorized - cli is nil",
   606  			user:       "gumby",
   607  			config:     &RerunAuthConfig{},
   608  			authorized: false,
   609  		},
   610  	}
   611  
   612  	for _, tc := range testCases {
   613  		t.Run(tc.name, func(t *testing.T) {
   614  
   615  			if actual, _ := tc.config.IsAuthorized("", tc.user, nil); actual != tc.authorized {
   616  				t.Errorf("Expected %v, got %v", tc.authorized, actual)
   617  			}
   618  		})
   619  	}
   620  }
   621  
   622  func TestRerunAuthConfigIsAllowAnyone(t *testing.T) {
   623  	var testCases = []struct {
   624  		name     string
   625  		config   *RerunAuthConfig
   626  		expected bool
   627  	}{
   628  		{
   629  			name:     "AllowAnyone is true",
   630  			config:   &RerunAuthConfig{AllowAnyone: true},
   631  			expected: true,
   632  		},
   633  		{
   634  			name:     "AllowAnyone is false",
   635  			config:   &RerunAuthConfig{AllowAnyone: false},
   636  			expected: false,
   637  		},
   638  		{
   639  			name:     "AllowAnyone is unset",
   640  			config:   &RerunAuthConfig{},
   641  			expected: false,
   642  		},
   643  		{
   644  			name:     "RerunAuthConfig is nil",
   645  			config:   nil,
   646  			expected: false,
   647  		},
   648  	}
   649  
   650  	for _, tc := range testCases {
   651  		t.Run(tc.name, func(t *testing.T) {
   652  
   653  			if actual := tc.config.IsAllowAnyone(); actual != tc.expected {
   654  				t.Errorf("Expected %v, got %v", tc.expected, actual)
   655  			}
   656  		})
   657  	}
   658  }
   659  
   660  func TestParsePath(t *testing.T) {
   661  	type args struct {
   662  		bucket string
   663  	}
   664  	tests := []struct {
   665  		name                string
   666  		args                args
   667  		wantStorageProvider string
   668  		wantBucket          string
   669  		wantFullPath        string
   670  		wantPath            string
   671  		wantErr             string
   672  	}{
   673  		{
   674  			name: "valid gcs bucket",
   675  			args: args{
   676  				bucket: "prow-artifacts",
   677  			},
   678  			wantStorageProvider: "gs",
   679  			wantBucket:          "prow-artifacts",
   680  			wantFullPath:        "prow-artifacts",
   681  			wantPath:            "",
   682  		},
   683  		{
   684  			name: "valid gcs bucket with storage provider prefix",
   685  			args: args{
   686  				bucket: "gs://prow-artifacts",
   687  			},
   688  			wantStorageProvider: "gs",
   689  			wantBucket:          "prow-artifacts",
   690  			wantFullPath:        "prow-artifacts",
   691  			wantPath:            "",
   692  		},
   693  		{
   694  			name: "valid gcs bucket with multiple separator with storage provider prefix",
   695  			args: args{
   696  				bucket: "gs://my-floppy-backup/a://doom2.wad.006",
   697  			},
   698  			wantStorageProvider: "gs",
   699  			wantBucket:          "my-floppy-backup",
   700  			wantFullPath:        "my-floppy-backup/a://doom2.wad.006",
   701  			wantPath:            "/a://doom2.wad.006",
   702  		},
   703  		{
   704  			name: "valid s3 bucket with storage provider prefix",
   705  			args: args{
   706  				bucket: "s3://prow-artifacts",
   707  			},
   708  			wantStorageProvider: "s3",
   709  			wantBucket:          "prow-artifacts",
   710  			wantFullPath:        "prow-artifacts",
   711  			wantPath:            "",
   712  		},
   713  	}
   714  	for _, tt := range tests {
   715  		t.Run(tt.name, func(t *testing.T) {
   716  			prowPath, err := ParsePath(tt.args.bucket)
   717  			var gotErr string
   718  			if err != nil {
   719  				gotErr = err.Error()
   720  			}
   721  			if gotErr != tt.wantErr {
   722  				t.Errorf("ParsePath() error = %v, wantErr %v", err, tt.wantErr)
   723  				return
   724  			}
   725  			if prowPath.StorageProvider() != tt.wantStorageProvider {
   726  				t.Errorf("ParsePath() gotStorageProvider = %v, wantStorageProvider %v", prowPath.StorageProvider(), tt.wantStorageProvider)
   727  			}
   728  			if got, want := prowPath.Bucket(), tt.wantBucket; got != want {
   729  				t.Errorf("ParsePath() gotBucket = %v, wantBucket %v", got, want)
   730  			}
   731  			if got, want := prowPath.FullPath(), tt.wantFullPath; got != want {
   732  				t.Errorf("ParsePath() gotFullPath = %v, wantFullPath %v", got, want)
   733  			}
   734  			if got, want := prowPath.Path, tt.wantPath; got != want {
   735  				t.Errorf("ParsePath() gotPath = %v, wantPath %v", got, want)
   736  			}
   737  		})
   738  	}
   739  }
   740  
   741  func TestProwJobSpec_HasPipelineRunSpec(t *testing.T) {
   742  	type fields struct {
   743  		PipelineRunSpec       *pipelinev1beta1.PipelineRunSpec
   744  		TektonPipelineRunSpec *TektonPipelineRunSpec
   745  	}
   746  	tests := []struct {
   747  		name   string
   748  		fields fields
   749  		want   bool
   750  	}{{
   751  		name: "none set",
   752  		want: false,
   753  	}, {
   754  		name: "PipelineRunSpec set",
   755  		fields: fields{
   756  			PipelineRunSpec: &pipelinev1beta1.PipelineRunSpec{},
   757  		},
   758  		want: true,
   759  	}, {
   760  		name: "TektonPipelineRunSpec set",
   761  		fields: fields{
   762  			TektonPipelineRunSpec: &TektonPipelineRunSpec{},
   763  		},
   764  		want: false,
   765  	}, {
   766  		name: "TektonPipelineRunSpec.V1VBeta1 set",
   767  		fields: fields{
   768  			TektonPipelineRunSpec: &TektonPipelineRunSpec{
   769  				V1Beta1: &pipelinev1beta1.PipelineRunSpec{},
   770  			},
   771  		},
   772  		want: true,
   773  	}, {
   774  		name: "both set",
   775  		fields: fields{
   776  			PipelineRunSpec: &pipelinev1beta1.PipelineRunSpec{},
   777  			TektonPipelineRunSpec: &TektonPipelineRunSpec{
   778  				V1Beta1: &pipelinev1beta1.PipelineRunSpec{},
   779  			},
   780  		},
   781  		want: true,
   782  	}}
   783  	for _, tt := range tests {
   784  		t.Run(tt.name, func(t *testing.T) {
   785  			pjs := ProwJobSpec{
   786  				PipelineRunSpec:       tt.fields.PipelineRunSpec,
   787  				TektonPipelineRunSpec: tt.fields.TektonPipelineRunSpec,
   788  			}
   789  			if got := pjs.HasPipelineRunSpec(); got != tt.want {
   790  				t.Errorf("ProwJobSpec.HasPipelineRunSpec() = %v, want %v", got, tt.want)
   791  			}
   792  		})
   793  	}
   794  }
   795  
   796  func TestProwJobSpec_GetPipelineRunSpec(t *testing.T) {
   797  	type fields struct {
   798  		PipelineRunSpec       *pipelinev1beta1.PipelineRunSpec
   799  		TektonPipelineRunSpec *TektonPipelineRunSpec
   800  	}
   801  	tests := []struct {
   802  		name    string
   803  		fields  fields
   804  		want    *pipelinev1beta1.PipelineRunSpec
   805  		wantErr bool
   806  	}{
   807  		{
   808  			name: "none set",
   809  			fields: fields{
   810  				PipelineRunSpec:       nil,
   811  				TektonPipelineRunSpec: nil,
   812  			},
   813  			wantErr: true,
   814  		},
   815  		{
   816  			name: "only PipelineRunSpec set",
   817  			fields: fields{
   818  				PipelineRunSpec: &pipelinev1beta1.PipelineRunSpec{
   819  					ServiceAccountName: "robot",
   820  					PipelineSpec: &pipelinev1beta1.PipelineSpec{
   821  						Tasks: []pipelinev1beta1.PipelineTask{{Name: "implicit git resource", TaskRef: &pipelinev1beta1.TaskRef{Name: "abc"}}},
   822  					},
   823  				},
   824  				TektonPipelineRunSpec: nil,
   825  			},
   826  			want: &pipelinev1beta1.PipelineRunSpec{
   827  				ServiceAccountName: "robot",
   828  				PipelineSpec: &pipelinev1beta1.PipelineSpec{
   829  					Tasks: []pipelinev1beta1.PipelineTask{{Name: "implicit git resource", TaskRef: &pipelinev1beta1.TaskRef{Name: "abc"}}},
   830  				},
   831  			},
   832  		},
   833  		{
   834  			name: "only TektonPipelineRunSpec set",
   835  			fields: fields{
   836  				PipelineRunSpec: nil,
   837  				TektonPipelineRunSpec: &TektonPipelineRunSpec{
   838  					V1Beta1: &pipelinev1beta1.PipelineRunSpec{
   839  						ServiceAccountName: "robot",
   840  						PipelineSpec: &pipelinev1beta1.PipelineSpec{
   841  							Tasks: []pipelinev1beta1.PipelineTask{{Name: "implicit git resource", TaskRef: &pipelinev1beta1.TaskRef{Name: "abc"}}},
   842  						},
   843  					},
   844  				},
   845  			},
   846  			want: &pipelinev1beta1.PipelineRunSpec{
   847  				ServiceAccountName: "robot",
   848  				PipelineSpec: &pipelinev1beta1.PipelineSpec{
   849  					Tasks: []pipelinev1beta1.PipelineTask{{Name: "implicit git resource", TaskRef: &pipelinev1beta1.TaskRef{Name: "abc"}}},
   850  				},
   851  			},
   852  		},
   853  		{
   854  			name: "PipelineRunSpec and TektonPipelineRunSpec set",
   855  			fields: fields{
   856  				PipelineRunSpec: &pipelinev1beta1.PipelineRunSpec{
   857  					ServiceAccountName: "robot",
   858  					PipelineSpec: &pipelinev1beta1.PipelineSpec{
   859  						Tasks: []pipelinev1beta1.PipelineTask{{Name: "implicit git resource", TaskRef: &pipelinev1beta1.TaskRef{Name: "abc"}}},
   860  					},
   861  				},
   862  				TektonPipelineRunSpec: &TektonPipelineRunSpec{
   863  					V1Beta1: &pipelinev1beta1.PipelineRunSpec{
   864  						ServiceAccountName: "robot",
   865  						PipelineSpec: &pipelinev1beta1.PipelineSpec{
   866  							Tasks: []pipelinev1beta1.PipelineTask{{Name: "implicit git resource", TaskRef: &pipelinev1beta1.TaskRef{Name: "def"}}},
   867  						},
   868  					},
   869  				},
   870  			},
   871  			want: &pipelinev1beta1.PipelineRunSpec{
   872  				ServiceAccountName: "robot",
   873  				PipelineSpec: &pipelinev1beta1.PipelineSpec{
   874  					Tasks: []pipelinev1beta1.PipelineTask{{Name: "implicit git resource", TaskRef: &pipelinev1beta1.TaskRef{Name: "def"}}},
   875  				},
   876  			},
   877  		},
   878  	}
   879  	for _, tt := range tests {
   880  		t.Run(tt.name, func(t *testing.T) {
   881  			pjs := ProwJobSpec{
   882  				PipelineRunSpec:       tt.fields.PipelineRunSpec,
   883  				TektonPipelineRunSpec: tt.fields.TektonPipelineRunSpec,
   884  			}
   885  			got, err := pjs.GetPipelineRunSpec()
   886  			if (err != nil) != tt.wantErr {
   887  				t.Errorf("ProwJobSpec.GetPipelineRunSpec() error = %v, wantErr %v", err, tt.wantErr)
   888  				return
   889  			}
   890  			if !reflect.DeepEqual(got, tt.want) {
   891  				t.Errorf("ProwJobSpec.GetPipelineRunSpec() = %v, want %v", got, tt.want)
   892  			}
   893  		})
   894  	}
   895  }