github.com/abayer/test-infra@v0.0.5/robots/issue-creator/sources/flakyjob-reporter_test.go (about)

     1  /*
     2  Copyright 2017 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 sources
    18  
    19  import (
    20  	"testing"
    21  	"time"
    22  
    23  	"k8s.io/test-infra/robots/issue-creator/creator"
    24  
    25  	githubapi "github.com/google/go-github/github"
    26  )
    27  
    28  var (
    29  	sampleFlakyJobJSON = []byte(`
    30  	{
    31  	  "ci-kubernetes-e2e-non-cri-gce-etcd3": {
    32  	    "consistency": 0.863,
    33  	    "flakes": 43,
    34  	    "flakiest": {
    35  	      "[k8s.io] Volumes [Volume] [k8s.io] PD should be mountable": 24,
    36  	      "[k8s.io] Services should preserve source pod IP for traffic thru service cluster IP": 7
    37  	    }
    38  	  },
    39  	  "pr:pull-kubernetes-e2e-gce-non-cri": {
    40  	    "consistency": 0.929,
    41  	    "flakes": 62,
    42  	    "flakiest": {
    43  	      "[k8s.io] Volumes [Volume] [k8s.io] PD should be mountable": 28
    44  	    }
    45  	  },
    46  	  "ci-kubernetes-e2e-non-cri-gce": {
    47  	    "consistency": 0.864,
    48  	    "flakes": 42,
    49  	    "flakiest": {
    50  	      "[k8s.io] Volumes [Volume] [k8s.io] PD should be mountable": 24,
    51  	      "[k8s.io] Services should preserve source pod IP for traffic thru service cluster IP": 10
    52  	    }
    53  	  },
    54  	  "ci-kubernetes-e2e-gce-container-vm": {
    55  	    "consistency": 0.863,
    56  	    "flakes": 42,
    57  	    "flakiest": {
    58  	      "[k8s.io] Volumes [Volume] [k8s.io] PD should be mountable": 10,
    59  	      "[k8s.io] Services should preserve source pod IP for traffic thru service cluster IP": 8
    60  	    }
    61  	  },
    62  	  "ci-kubernetes-e2e-non-cri-gce-proto": {
    63  	    "consistency": 0.865,
    64  	    "flakes": 41,
    65  	    "flakiest": {
    66  	      "[k8s.io] Volumes [Volume] [k8s.io] PD should be mountable": 22
    67  	    }
    68  	  },
    69  	  "ci-kubernetes-e2e-gce-gci-ci-master": {
    70  	    "consistency": 0.868,
    71  	    "flakes": 41,
    72  	    "flakiest": {
    73  	      "[k8s.io] Volumes [Volume] [k8s.io] PD should be mountable": 16,
    74  	      "[k8s.io] Services should preserve source pod IP for traffic thru service cluster IP": 8
    75  	    }
    76  	  }
    77  	}`)
    78  )
    79  
    80  func TestFJParseFlakyJobs(t *testing.T) {
    81  	reporter := &FlakyJobReporter{creator: &creator.IssueCreator{}}
    82  	jobs, err := reporter.parseFlakyJobs(sampleFlakyJobJSON)
    83  	if err != nil {
    84  		t.Fatalf("Error parsing flaky jobs: %v\n", err)
    85  	}
    86  
    87  	if len(jobs) != 6 {
    88  		t.Fatalf("ParseFlakyJobs parsed the wrong number of jobs.  Expected 6, got %d.\n", len(jobs))
    89  	}
    90  
    91  	if !checkFlakyJobsSorted(jobs) {
    92  		t.Fatal("The slice of *FlakyJob that was returned by parseFlakyJobs was not sorted.\n")
    93  	}
    94  	if jobs[0].Name != "pr:pull-kubernetes-e2e-gce-non-cri" {
    95  		t.Fatalf("The name of the top flaking job should be 'pr:pull-kubernetes-e2e-gce-non-cri' but is '%s'\n", jobs[0].Name)
    96  	}
    97  	if *jobs[0].Consistency != 0.929 {
    98  		t.Fatalf("The consistency of the top flaking job should be 0.926 but is '%v'\n", *jobs[0].Consistency)
    99  	}
   100  	if *jobs[0].FlakeCount != 62 {
   101  		t.Fatalf("The flake count of the top flaking job should be 63 but is '%d'\n", *jobs[0].FlakeCount)
   102  	}
   103  	if len(jobs[0].FlakyTests) != 1 || jobs[0].FlakyTests["[k8s.io] Volumes [Volume] [k8s.io] PD should be mountable"] != 28 {
   104  		t.Fatal("The dictionary of flaky tests for the top flaking job is invalid.\n")
   105  	}
   106  	for _, job := range jobs {
   107  		if job.reporter == nil {
   108  			t.Errorf("FlakyJob with name: '%s' does not have reporter set.\n", job.Name)
   109  		}
   110  	}
   111  }
   112  
   113  // TestFJPrevCloseInWindow checks that FlakyJob issues will abort issue creation by returning an
   114  // empty body if there is a closed issue for the same flaky job that was closed in the past week.
   115  func TestFJPrevCloseInWindow(t *testing.T) {
   116  	reporter := &FlakyJobReporter{creator: &creator.IssueCreator{}}
   117  	fjs, err := reporter.parseFlakyJobs(sampleFlakyJobJSON)
   118  	if err != nil {
   119  		t.Fatalf("Error parsing flaky jobs: %v\n", err)
   120  	}
   121  
   122  	lastWeek := time.Now().AddDate(0, 0, -8)
   123  	yesterday := time.Now().AddDate(0, 0, -1)
   124  	num := 1
   125  	// Only need to populate the ClosedAt and Number fields of the Issue.
   126  	prevIssues := []*githubapi.Issue{{ClosedAt: &yesterday, Number: &num}}
   127  	if fjs[0].Body(prevIssues) != "" {
   128  		t.Errorf("FlakyJob returned an issue body when there was a recently closed issue for the job.")
   129  	}
   130  
   131  	prevIssues = []*githubapi.Issue{{ClosedAt: &lastWeek, Number: &num}}
   132  	if fjs[0].Body(prevIssues) == "" {
   133  		t.Errorf("FlakyJob returned an empty issue body when it should have returned a valid body.")
   134  	}
   135  }
   136  
   137  func checkFlakyJobsSorted(jobs []*FlakyJob) bool {
   138  	for i := 1; i < len(jobs); i++ {
   139  		if *jobs[i-1].FlakeCount < *jobs[i].FlakeCount {
   140  			return false
   141  		}
   142  	}
   143  	return true
   144  }