github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/pkg/deck/jobs/jobs_test.go (about)

     1  /*
     2  Copyright 2016 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 jobs
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"sort"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/google/go-cmp/cmp"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/labels"
    30  	"k8s.io/apimachinery/pkg/runtime"
    31  	"k8s.io/apimachinery/pkg/util/sets"
    32  	ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client"
    33  	fakectrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
    34  
    35  	prowapi "sigs.k8s.io/prow/pkg/apis/prowjobs/v1"
    36  	"sigs.k8s.io/prow/pkg/config"
    37  	"sigs.k8s.io/prow/pkg/kube"
    38  )
    39  
    40  func createTime(layout string, timeString string) metav1.Time {
    41  	t, _ := time.Parse(layout, timeString)
    42  	return metav1.NewTime(t)
    43  }
    44  
    45  type fkc []prowapi.ProwJob
    46  
    47  func (f fkc) ListProwJobs(s string) ([]prowapi.ProwJob, error) {
    48  	return f, nil
    49  }
    50  
    51  type fpkc string
    52  
    53  func (f fpkc) GetLogs(name, container string) ([]byte, error) {
    54  	if name == "wowowow" || name == "powowow" {
    55  		return []byte(fmt.Sprintf("%s.%s", f, container)), nil
    56  	}
    57  	return nil, fmt.Errorf("pod not found: %s", name)
    58  }
    59  
    60  func TestGetLog(t *testing.T) {
    61  	kc := fkc{
    62  		prowapi.ProwJob{
    63  			Spec: prowapi.ProwJobSpec{
    64  				Agent: prowapi.KubernetesAgent,
    65  				Job:   "job",
    66  			},
    67  			Status: prowapi.ProwJobStatus{
    68  				PodName: "wowowow",
    69  				BuildID: "123",
    70  			},
    71  		},
    72  		prowapi.ProwJob{
    73  			Spec: prowapi.ProwJobSpec{
    74  				Agent:   prowapi.KubernetesAgent,
    75  				Job:     "jib",
    76  				Cluster: "trusted",
    77  			},
    78  			Status: prowapi.ProwJobStatus{
    79  				PodName: "powowow",
    80  				BuildID: "123",
    81  			},
    82  		},
    83  	}
    84  	ja := &JobAgent{
    85  		kc:   kc,
    86  		pkcs: map[string]PodLogClient{kube.DefaultClusterAlias: fpkc("clusterA"), "trusted": fpkc("clusterB")},
    87  	}
    88  	if err := ja.update(); err != nil {
    89  		t.Fatalf("Updating: %v", err)
    90  	}
    91  	if res, err := ja.GetJobLog("job", "123", kube.TestContainerName); err != nil {
    92  		t.Fatalf("Failed to get log: %v", err)
    93  	} else if got, expect := string(res), fmt.Sprintf("clusterA.%s", kube.TestContainerName); got != expect {
    94  		t.Errorf("Unexpected result getting logs for job 'job'. Expected %q, but got %q.", expect, got)
    95  	}
    96  
    97  	if res, err := ja.GetJobLog("jib", "123", kube.TestContainerName); err != nil {
    98  		t.Fatalf("Failed to get log: %v", err)
    99  	} else if got, expect := string(res), fmt.Sprintf("clusterB.%s", kube.TestContainerName); got != expect {
   100  		t.Errorf("Unexpected result getting logs for job 'job'. Expected %q, but got %q.", expect, got)
   101  	}
   102  
   103  	customContainerName := "custom-container-name"
   104  	if res, err := ja.GetJobLog("jib", "123", customContainerName); err != nil {
   105  		t.Fatalf("Failed to get log: %v", err)
   106  	} else if got, expect := string(res), fmt.Sprintf("clusterB.%s", customContainerName); got != expect {
   107  		t.Errorf("Unexpected result getting logs for job 'job'. Expected %q, but got %q.", expect, got)
   108  	}
   109  }
   110  
   111  func TestProwJobs(t *testing.T) {
   112  	kc := fkc{
   113  		prowapi.ProwJob{
   114  			Spec: prowapi.ProwJobSpec{
   115  				Agent: prowapi.KubernetesAgent,
   116  				Job:   "jobFirst",
   117  				Refs: &prowapi.Refs{
   118  					Org:  "kubernetes",
   119  					Repo: "test-infra",
   120  				},
   121  			},
   122  			Status: prowapi.ProwJobStatus{
   123  				PodName:   "newpod",
   124  				BuildID:   "1236",
   125  				StartTime: createTime(time.RFC3339, "2008-01-02T15:04:05.999Z"),
   126  			},
   127  		},
   128  		prowapi.ProwJob{
   129  			Spec: prowapi.ProwJobSpec{
   130  				Agent: prowapi.KubernetesAgent,
   131  				Job:   "jobThird",
   132  				Refs: &prowapi.Refs{
   133  					Org:  "kubernetes",
   134  					Repo: "test-infra",
   135  				},
   136  			},
   137  			Status: prowapi.ProwJobStatus{
   138  				PodName:   "wowowow",
   139  				BuildID:   "1234",
   140  				StartTime: createTime(time.RFC3339, "2006-01-02T15:04:05.999Z"),
   141  			},
   142  		},
   143  		prowapi.ProwJob{
   144  			Spec: prowapi.ProwJobSpec{
   145  				Agent: prowapi.KubernetesAgent,
   146  				Job:   "jobSecond",
   147  				Refs: &prowapi.Refs{
   148  					Org:  "kubernetes",
   149  					Repo: "test-infra",
   150  				},
   151  			},
   152  			Status: prowapi.ProwJobStatus{
   153  				PodName:   "wowowow",
   154  				BuildID:   "1235",
   155  				StartTime: createTime(time.RFC3339, "2007-01-02T15:04:05.999Z"),
   156  			},
   157  		},
   158  	}
   159  	ja := &JobAgent{
   160  		kc:   kc,
   161  		pkcs: map[string]PodLogClient{kube.DefaultClusterAlias: fpkc("")},
   162  	}
   163  	if err := ja.update(); err != nil {
   164  		t.Fatalf("Updating: %v", err)
   165  	}
   166  
   167  	pjs := ja.ProwJobs()
   168  	if expect, got := 3, len(pjs); expect != got {
   169  		t.Fatalf("Expected %d prowjobs, but got %d.", expect, got)
   170  	}
   171  	if expect, got := "kubernetes", pjs[0].Spec.Refs.Org; expect != got {
   172  		t.Errorf("Expected prowjob to have org %q, but got %q.", expect, got)
   173  	}
   174  	if expect, got := "jobFirst", pjs[0].Spec.Job; expect != got {
   175  		t.Errorf("Expected first prowjob to have job name %q, but got %q.", expect, got)
   176  	}
   177  	if expect, got := "jobSecond", pjs[1].Spec.Job; expect != got {
   178  		t.Errorf("Expected second prowjob to have job name %q, but got %q.", expect, got)
   179  	}
   180  	if expect, got := "jobThird", pjs[2].Spec.Job; expect != got {
   181  		t.Errorf("Expected third prowjob to have job name %q, but got %q.", expect, got)
   182  	}
   183  }
   184  
   185  func TestJobs(t *testing.T) {
   186  	kc := fkc{
   187  		prowapi.ProwJob{
   188  			Spec: prowapi.ProwJobSpec{
   189  				Agent: prowapi.KubernetesAgent,
   190  				Job:   "jobFirst",
   191  				Refs: &prowapi.Refs{
   192  					Org:  "kubernetes",
   193  					Repo: "test-infra",
   194  				},
   195  			},
   196  			Status: prowapi.ProwJobStatus{
   197  				PodName:   "newpod",
   198  				BuildID:   "1236",
   199  				StartTime: createTime(time.RFC3339, "2008-01-02T15:04:05.999Z"),
   200  			},
   201  		},
   202  		prowapi.ProwJob{
   203  			Spec: prowapi.ProwJobSpec{
   204  				Agent: prowapi.KubernetesAgent,
   205  				Job:   "jobThird",
   206  				Refs: &prowapi.Refs{
   207  					Org:  "kubernetes",
   208  					Repo: "test-infra",
   209  				},
   210  			},
   211  			Status: prowapi.ProwJobStatus{
   212  				PodName:   "wowowow",
   213  				BuildID:   "1234",
   214  				StartTime: createTime(time.RFC3339, "2006-01-02T15:04:05.999Z"),
   215  			},
   216  		},
   217  		prowapi.ProwJob{
   218  			Spec: prowapi.ProwJobSpec{
   219  				Agent: prowapi.KubernetesAgent,
   220  				Job:   "jobSecond",
   221  				Refs: &prowapi.Refs{
   222  					Org:  "kubernetes",
   223  					Repo: "test-infra",
   224  				},
   225  			},
   226  			Status: prowapi.ProwJobStatus{
   227  				PodName:   "wowowow",
   228  				BuildID:   "1235",
   229  				StartTime: createTime(time.RFC3339, "2007-01-02T15:04:05.999Z"),
   230  			},
   231  		},
   232  	}
   233  	ja := &JobAgent{
   234  		kc:   kc,
   235  		pkcs: map[string]PodLogClient{kube.DefaultClusterAlias: fpkc("")},
   236  	}
   237  	if err := ja.update(); err != nil {
   238  		t.Fatalf("Updating: %v", err)
   239  	}
   240  
   241  	jobs := ja.Jobs()
   242  	if expect, got := 3, len(jobs); expect != got {
   243  		t.Fatalf("Expected %d jobs, but got %d.", expect, got)
   244  	}
   245  	if expect, got := "kubernetes", jobs[0].Refs.Org; expect != got {
   246  		t.Errorf("Expected jobs to have org %q, but got %q.", expect, got)
   247  	}
   248  	if expect, got := "jobFirst", jobs[0].Job; expect != got {
   249  		t.Errorf("Expected first job to have job name %q, but got %q.", expect, got)
   250  	}
   251  	if expect, got := "jobSecond", jobs[1].Job; expect != got {
   252  		t.Errorf("Expected second job to have job name %q, but got %q.", expect, got)
   253  	}
   254  	if expect, got := "jobThird", jobs[2].Job; expect != got {
   255  		t.Errorf("Expected third job to have job name %q, but got %q.", expect, got)
   256  	}
   257  }
   258  
   259  func TestListProwJobs(t *testing.T) {
   260  	templateJob := &prowapi.ProwJob{
   261  		ObjectMeta: metav1.ObjectMeta{
   262  			Namespace: "prowjobs",
   263  		},
   264  	}
   265  
   266  	var testCases = []struct {
   267  		name        string
   268  		selector    string
   269  		prowJobs    []func(*prowapi.ProwJob) runtime.Object
   270  		listErr     bool
   271  		hiddenRepos sets.Set[string]
   272  		hiddenOnly  bool
   273  		showHidden  bool
   274  		expected    sets.Set[string]
   275  		expectedErr bool
   276  		tenantIDs   []string
   277  	}{
   278  		{
   279  			name:        "list error results in filter error",
   280  			listErr:     true,
   281  			expectedErr: true,
   282  		},
   283  		{
   284  			name:     "no hidden repos returns all prowjobs",
   285  			selector: labels.Everything().String(),
   286  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   287  				func(in *prowapi.ProwJob) runtime.Object {
   288  					in.Name = "first"
   289  					return in
   290  				},
   291  			},
   292  			expected: sets.New[string]("first"),
   293  		},
   294  		{
   295  			name:     "no hidden repos returns all prowjobs except those not matching label selector",
   296  			selector: "foo=bar",
   297  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   298  				func(in *prowapi.ProwJob) runtime.Object {
   299  					in.Name = "first"
   300  					return in
   301  				},
   302  				func(in *prowapi.ProwJob) runtime.Object {
   303  					in.Name = "second"
   304  					in.Labels = map[string]string{"foo": "bar"}
   305  					return in
   306  				},
   307  			},
   308  			expected: sets.New[string]("second"),
   309  		},
   310  		{
   311  			name:     "hidden repos excludes prowjobs from those repos",
   312  			selector: labels.Everything().String(),
   313  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   314  				func(in *prowapi.ProwJob) runtime.Object {
   315  					in.Name = "first"
   316  					return in
   317  				},
   318  				func(in *prowapi.ProwJob) runtime.Object {
   319  					in.Name = "second"
   320  					in.Spec.Refs = &prowapi.Refs{
   321  						Org:  "org",
   322  						Repo: "repo",
   323  					}
   324  					return in
   325  				},
   326  			},
   327  			hiddenRepos: sets.New[string]("org/repo"),
   328  			expected:    sets.New[string]("first"),
   329  		},
   330  		{
   331  			name:     "hidden repos doesn't exclude prowjobs from other repos",
   332  			selector: labels.Everything().String(),
   333  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   334  				func(in *prowapi.ProwJob) runtime.Object {
   335  					in.Name = "first"
   336  					return in
   337  				},
   338  				func(in *prowapi.ProwJob) runtime.Object {
   339  					in.Name = "second"
   340  					in.Spec.Refs = &prowapi.Refs{
   341  						Org:  "org",
   342  						Repo: "other",
   343  					}
   344  					return in
   345  				},
   346  			},
   347  			hiddenRepos: sets.New[string]("org/repo"),
   348  			expected:    sets.New[string]("first", "second"),
   349  		},
   350  		{
   351  			name:     "hidden orgs excludes prowjobs from those orgs",
   352  			selector: labels.Everything().String(),
   353  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   354  				func(in *prowapi.ProwJob) runtime.Object {
   355  					in.Name = "first"
   356  					return in
   357  				},
   358  				func(in *prowapi.ProwJob) runtime.Object {
   359  					in.Name = "second"
   360  					in.Spec.Refs = &prowapi.Refs{
   361  						Org:  "org",
   362  						Repo: "other",
   363  					}
   364  					return in
   365  				},
   366  			},
   367  			hiddenRepos: sets.New[string]("org"),
   368  			expected:    sets.New[string]("first"),
   369  		},
   370  		{
   371  			name:     "hidden orgs doesn't exclude prowjobs from other orgs",
   372  			selector: labels.Everything().String(),
   373  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   374  				func(in *prowapi.ProwJob) runtime.Object {
   375  					in.Name = "first"
   376  					return in
   377  				},
   378  				func(in *prowapi.ProwJob) runtime.Object {
   379  					in.Name = "second"
   380  					in.Spec.Refs = &prowapi.Refs{
   381  						Org:  "other",
   382  						Repo: "other",
   383  					}
   384  					return in
   385  				},
   386  			},
   387  			hiddenRepos: sets.New[string]("org"),
   388  			expected:    sets.New[string]("first", "second"),
   389  		},
   390  		{
   391  			name:     "hidden repos excludes prowjobs from those repos even by extra_refs",
   392  			selector: labels.Everything().String(),
   393  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   394  				func(in *prowapi.ProwJob) runtime.Object {
   395  					in.Name = "first"
   396  					in.Spec.ExtraRefs = []prowapi.Refs{{Org: "org", Repo: "repo"}}
   397  					return in
   398  				},
   399  			},
   400  			hiddenRepos: sets.New[string]("org/repo"),
   401  			expected:    sets.New[string](),
   402  		},
   403  		{
   404  			name:     "hidden orgs excludes prowjobs from those orgs even by extra_refs",
   405  			selector: labels.Everything().String(),
   406  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   407  				func(in *prowapi.ProwJob) runtime.Object {
   408  					in.Name = "first"
   409  					in.Spec.ExtraRefs = []prowapi.Refs{{Org: "org", Repo: "repo"}}
   410  					return in
   411  				},
   412  			},
   413  			hiddenRepos: sets.New[string]("org"),
   414  			expected:    sets.New[string](),
   415  		},
   416  		{
   417  			name:     "prowjobs without refs are returned even with hidden repos filtering",
   418  			selector: labels.Everything().String(),
   419  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   420  				func(in *prowapi.ProwJob) runtime.Object {
   421  					in.Name = "first"
   422  					return in
   423  				},
   424  			},
   425  			hiddenRepos: sets.New[string]("org/repo"),
   426  			expected:    sets.New[string]("first"),
   427  		},
   428  		{
   429  			name:     "all prowjobs are returned when showHidden is true",
   430  			selector: labels.Everything().String(),
   431  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   432  				func(in *prowapi.ProwJob) runtime.Object {
   433  					in.Name = "first"
   434  					in.Spec.ExtraRefs = []prowapi.Refs{{Org: "org", Repo: "repo"}}
   435  					return in
   436  				},
   437  				func(in *prowapi.ProwJob) runtime.Object {
   438  					in.Name = "second"
   439  					return in
   440  				},
   441  			},
   442  			hiddenRepos: sets.New[string]("org/repo"),
   443  			expected:    sets.New[string]("first", "second"),
   444  			showHidden:  true,
   445  		},
   446  		{
   447  			name: "setting pj.Spec.Hidden hides it",
   448  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   449  				func(in *prowapi.ProwJob) runtime.Object {
   450  					in.Name = "hidden"
   451  					in.Spec.Hidden = true
   452  					return in
   453  				},
   454  				func(in *prowapi.ProwJob) runtime.Object {
   455  					in.Name = "shown"
   456  					return in
   457  				},
   458  			},
   459  			expected: sets.New[string]("shown"),
   460  		},
   461  		{
   462  			name: "hidden repo or org in extra_refs hides it",
   463  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   464  				func(in *prowapi.ProwJob) runtime.Object {
   465  					in.Name = "hidden-repo"
   466  					in.Spec.ExtraRefs = []prowapi.Refs{{Org: "hide", Repo: "me"}}
   467  					return in
   468  				},
   469  				func(in *prowapi.ProwJob) runtime.Object {
   470  					in.Name = "hidden-org"
   471  					in.Spec.ExtraRefs = []prowapi.Refs{{Org: "hidden-org"}}
   472  					return in
   473  				},
   474  			},
   475  			hiddenRepos: sets.New[string]("hide/me", "hidden-org"),
   476  		},
   477  		{
   478  			name: "tenantID on lister will not show jobs without id",
   479  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   480  				func(in *prowapi.ProwJob) runtime.Object {
   481  					in.Name = "no ID"
   482  					return in
   483  				},
   484  				func(in *prowapi.ProwJob) runtime.Object {
   485  					in.Name = "no ID hidden"
   486  					in.Spec.Hidden = true
   487  					return in
   488  				},
   489  			},
   490  			expected:  sets.New[string](),
   491  			tenantIDs: []string{"ID"},
   492  		},
   493  		{
   494  			name: "tenantID ID on lister will show jobs with id",
   495  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   496  				func(in *prowapi.ProwJob) runtime.Object {
   497  					in.Name = "no ID"
   498  					return in
   499  				},
   500  				func(in *prowapi.ProwJob) runtime.Object {
   501  					in.Name = "ID"
   502  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: "ID"}
   503  					return in
   504  				},
   505  			},
   506  			expected:  sets.New[string]("ID"),
   507  			tenantIDs: []string{"ID"},
   508  		},
   509  		{
   510  			name: "tenantID on lister will not show jobs with different id",
   511  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   512  				func(in *prowapi.ProwJob) runtime.Object {
   513  					in.Name = "Wrong ID"
   514  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: "Wrong ID"}
   515  					return in
   516  				},
   517  				func(in *prowapi.ProwJob) runtime.Object {
   518  					in.Name = "ID"
   519  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: "ID"}
   520  					return in
   521  				},
   522  			},
   523  			expected:  sets.New[string]("ID"),
   524  			tenantIDs: []string{"ID"},
   525  		},
   526  		{
   527  			name: "tenantIDs on lister will show all matching jobs",
   528  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   529  				func(in *prowapi.ProwJob) runtime.Object {
   530  					in.Name = "Wrong ID"
   531  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: "Wrong ID"}
   532  					return in
   533  				},
   534  				func(in *prowapi.ProwJob) runtime.Object {
   535  					in.Name = "ID"
   536  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: "ID"}
   537  					return in
   538  				},
   539  				func(in *prowapi.ProwJob) runtime.Object {
   540  					in.Name = "Other ID"
   541  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: "Other ID"}
   542  					return in
   543  				},
   544  			},
   545  			expected:  sets.New[string]("ID", "Other ID"),
   546  			tenantIDs: []string{"ID", "Other ID"},
   547  		},
   548  		{
   549  			name: "tenantIDs on lister will show hidden jobs with correct ID",
   550  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   551  				func(in *prowapi.ProwJob) runtime.Object {
   552  					in.Name = "Wrong ID"
   553  					in.Spec.Hidden = true
   554  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: "Wrong ID"}
   555  					return in
   556  				},
   557  				func(in *prowapi.ProwJob) runtime.Object {
   558  					in.Name = "ID"
   559  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: "ID"}
   560  					in.Spec.Hidden = true
   561  					return in
   562  				},
   563  				func(in *prowapi.ProwJob) runtime.Object {
   564  					in.Name = "Other ID"
   565  					in.Spec.Hidden = true
   566  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: "Other ID"}
   567  					return in
   568  				},
   569  			},
   570  			expected:  sets.New[string]("ID", "Other ID"),
   571  			tenantIDs: []string{"ID", "Other ID"},
   572  		},
   573  		{
   574  			name: "hidden repo with correct ID still shows if matching tenantIDs",
   575  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   576  				func(in *prowapi.ProwJob) runtime.Object {
   577  					in.Name = "hidden-repo"
   578  					in.Spec.ExtraRefs = []prowapi.Refs{{Org: "hide", Repo: "me"}}
   579  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: "ID"}
   580  					return in
   581  				},
   582  				func(in *prowapi.ProwJob) runtime.Object {
   583  					in.Name = "hidden-org"
   584  					in.Spec.ExtraRefs = []prowapi.Refs{{Org: "hidden-org"}}
   585  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: "ID"}
   586  					return in
   587  				},
   588  			},
   589  			expected:    sets.New[string]("hidden-repo", "hidden-org"),
   590  			hiddenRepos: sets.New[string]("hide/me", "hidden-org"),
   591  			tenantIDs:   []string{"ID"},
   592  		},
   593  		{
   594  			name: "hidden repo with tenantIDs will still show if show hidden",
   595  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   596  				func(in *prowapi.ProwJob) runtime.Object {
   597  					in.Name = "hidden-repo"
   598  					in.Spec.ExtraRefs = []prowapi.Refs{{Org: "hide", Repo: "me"}}
   599  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: "ID"}
   600  					return in
   601  				},
   602  				func(in *prowapi.ProwJob) runtime.Object {
   603  					in.Name = "hidden-org"
   604  					in.Spec.ExtraRefs = []prowapi.Refs{{Org: "hidden-org"}}
   605  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: "ID"}
   606  					return in
   607  				},
   608  			},
   609  			expected:    sets.New[string]("hidden-repo", "hidden-org"),
   610  			hiddenRepos: sets.New[string]("hide/me", "hidden-org"),
   611  			showHidden:  true,
   612  		},
   613  		{
   614  			name: "hidden repo with tenantIDs will still show if hidden only",
   615  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   616  				func(in *prowapi.ProwJob) runtime.Object {
   617  					in.Name = "hidden-repo"
   618  					in.Spec.ExtraRefs = []prowapi.Refs{{Org: "hide", Repo: "me"}}
   619  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: "ID"}
   620  					return in
   621  				},
   622  				func(in *prowapi.ProwJob) runtime.Object {
   623  					in.Name = "hidden-org"
   624  					in.Spec.ExtraRefs = []prowapi.Refs{{Org: "hidden-org"}}
   625  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: "ID"}
   626  					return in
   627  				},
   628  			},
   629  			expected:    sets.New[string]("hidden-repo", "hidden-org"),
   630  			hiddenRepos: sets.New[string]("hide/me", "hidden-org"),
   631  			hiddenOnly:  true,
   632  		},
   633  		{
   634  			name: "pjs with tenantIDs marked hidden will still show up if showHidden is true",
   635  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   636  				func(in *prowapi.ProwJob) runtime.Object {
   637  					in.Name = "ID"
   638  					in.Spec.Hidden = true
   639  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: "ID"}
   640  					return in
   641  				},
   642  				func(in *prowapi.ProwJob) runtime.Object {
   643  					in.Name = "Other ID"
   644  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: "Other ID"}
   645  					return in
   646  				},
   647  			},
   648  			expected:   sets.New[string]("ID"),
   649  			showHidden: true,
   650  		},
   651  		{
   652  			name: "pjs with tenantIDs will show up on Deck with hiddenOnly",
   653  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   654  				func(in *prowapi.ProwJob) runtime.Object {
   655  					in.Name = "Hidden ID"
   656  					in.Spec.Hidden = true
   657  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: "ID"}
   658  					return in
   659  				},
   660  				func(in *prowapi.ProwJob) runtime.Object {
   661  					in.Name = "Other ID"
   662  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: "Other ID"}
   663  					return in
   664  				},
   665  			},
   666  			expected:   sets.New[string]("Hidden ID"),
   667  			hiddenOnly: true,
   668  		},
   669  		{
   670  			name: "pjs with tenantIDs will not show up on Deck with no tenantID",
   671  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   672  				func(in *prowapi.ProwJob) runtime.Object {
   673  					in.Name = "tenantedID"
   674  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: "ID"}
   675  					return in
   676  				},
   677  				func(in *prowapi.ProwJob) runtime.Object {
   678  					in.Name = "Other ID"
   679  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: "Other ID"}
   680  					return in
   681  				},
   682  			},
   683  			expected: sets.New[string](),
   684  		},
   685  		{
   686  			name: "pjs with Default ID will  show up on Deck with no tenantID",
   687  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   688  				func(in *prowapi.ProwJob) runtime.Object {
   689  					in.Name = "tenantedID"
   690  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: config.DefaultTenantID}
   691  					return in
   692  				},
   693  				func(in *prowapi.ProwJob) runtime.Object {
   694  					in.Name = "Other ID"
   695  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: "Other ID"}
   696  					return in
   697  				},
   698  			},
   699  			expected: sets.New[string]("tenantedID"),
   700  		},
   701  		{
   702  			name: "empty tenantID counts as no tenantID",
   703  			prowJobs: []func(*prowapi.ProwJob) runtime.Object{
   704  				func(in *prowapi.ProwJob) runtime.Object {
   705  					in.Name = "empty tenant id"
   706  					in.Spec.ProwJobDefault = &prowapi.ProwJobDefault{TenantID: ""}
   707  					return in
   708  				},
   709  				func(in *prowapi.ProwJob) runtime.Object {
   710  					in.Name = "No ProwJobDefault"
   711  					return in
   712  				},
   713  			},
   714  			expected: sets.New[string]("empty tenant id", "No ProwJobDefault"),
   715  		},
   716  	}
   717  
   718  	for _, testCase := range testCases {
   719  		builder := fakectrlruntimeclient.NewClientBuilder()
   720  		for _, generator := range testCase.prowJobs {
   721  			builder.WithRuntimeObjects(generator(templateJob.DeepCopy()))
   722  		}
   723  		fakeProwJobClient := &possiblyErroringFakeCtrlRuntimeClient{
   724  			Client:      builder.Build(),
   725  			shouldError: testCase.listErr,
   726  		}
   727  		lister := filteringProwJobLister{
   728  			client: fakeProwJobClient,
   729  			hiddenRepos: func() sets.Set[string] {
   730  				return testCase.hiddenRepos
   731  			},
   732  			hiddenOnly: testCase.hiddenOnly,
   733  			showHidden: testCase.showHidden,
   734  			tenantIDs:  testCase.tenantIDs,
   735  			cfg:        func() *config.Config { return &config.Config{} },
   736  		}
   737  
   738  		filtered, err := lister.ListProwJobs(testCase.selector)
   739  		if err == nil && testCase.expectedErr {
   740  			t.Errorf("%s: expected an error but got none", testCase.name)
   741  		}
   742  		if err != nil && !testCase.expectedErr {
   743  			t.Errorf("%s: expected no error but got one: %v", testCase.name, err)
   744  		}
   745  
   746  		filteredNames := sets.New[string]()
   747  		for _, prowJob := range filtered {
   748  			filteredNames.Insert(prowJob.Name)
   749  		}
   750  
   751  		if missing := testCase.expected.Difference(filteredNames); missing.Len() > 0 {
   752  			t.Errorf("%s: did not get expected jobs in filtered list: %v", testCase.name, sets.List(missing))
   753  		}
   754  		if extra := filteredNames.Difference(testCase.expected); extra.Len() > 0 {
   755  			t.Errorf("%s: got unexpected jobs in filtered list: %v", testCase.name, sets.List(extra))
   756  		}
   757  	}
   758  }
   759  
   760  type possiblyErroringFakeCtrlRuntimeClient struct {
   761  	ctrlruntimeclient.Client
   762  	shouldError bool
   763  }
   764  
   765  func (p *possiblyErroringFakeCtrlRuntimeClient) List(
   766  	ctx context.Context,
   767  	pjl *prowapi.ProwJobList,
   768  	opts ...ctrlruntimeclient.ListOption) error {
   769  	if p.shouldError {
   770  		return errors.New("could not list ProwJobs")
   771  	}
   772  	return p.Client.List(ctx, pjl, opts...)
   773  }
   774  
   775  func TestByPJStartTime(t *testing.T) {
   776  	now := metav1.Now()
   777  	in := []prowapi.ProwJob{
   778  		{Spec: prowapi.ProwJobSpec{Job: "foo"}},
   779  		{Spec: prowapi.ProwJobSpec{Job: "bar"}, Status: prowapi.ProwJobStatus{StartTime: now}},
   780  		{Spec: prowapi.ProwJobSpec{Job: "bar"}},
   781  	}
   782  	expected := []prowapi.ProwJob{
   783  		{Spec: prowapi.ProwJobSpec{Job: "bar"}, Status: prowapi.ProwJobStatus{StartTime: now}},
   784  		{Spec: prowapi.ProwJobSpec{Job: "bar"}},
   785  		{Spec: prowapi.ProwJobSpec{Job: "foo"}},
   786  	}
   787  
   788  	sort.Sort(byPJStartTime(in))
   789  
   790  	if diff := cmp.Diff(in, expected); diff != "" {
   791  		t.Errorf("actual differs from expected: %s", diff)
   792  	}
   793  }