volcano.sh/volcano@v1.9.0/pkg/controllers/job/job_state_test.go (about)

     1  /*
     2  Copyright 2019 The Volcano 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 job
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"reflect"
    23  	"testing"
    24  
    25  	"github.com/agiledragon/gomonkey/v2"
    26  	v1 "k8s.io/api/core/v1"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  
    29  	"volcano.sh/apis/pkg/apis/batch/v1alpha1"
    30  	busv1alpha1 "volcano.sh/apis/pkg/apis/bus/v1alpha1"
    31  	schedulingapi "volcano.sh/apis/pkg/apis/scheduling/v1beta1"
    32  	"volcano.sh/volcano/pkg/controllers/apis"
    33  	"volcano.sh/volcano/pkg/controllers/job/state"
    34  )
    35  
    36  func TestAbortedState_Execute(t *testing.T) {
    37  	namespace := "test"
    38  
    39  	testcases := []struct {
    40  		Name        string
    41  		JobInfo     *apis.JobInfo
    42  		Action      busv1alpha1.Action
    43  		ExpectedVal error
    44  	}{
    45  		{
    46  			Name: "AbortedState-ResumeAction case",
    47  			JobInfo: &apis.JobInfo{
    48  				Namespace: namespace,
    49  				Name:      "jobinfo1",
    50  				Job: &v1alpha1.Job{
    51  					ObjectMeta: metav1.ObjectMeta{
    52  						Name:            "Job1",
    53  						Namespace:       namespace,
    54  						ResourceVersion: "100",
    55  					},
    56  					Status: v1alpha1.JobStatus{
    57  						State: v1alpha1.JobState{
    58  							Phase: v1alpha1.Aborted,
    59  						},
    60  					},
    61  				},
    62  			},
    63  			Action:      busv1alpha1.ResumeJobAction,
    64  			ExpectedVal: nil,
    65  		},
    66  		{
    67  			Name: "AbortedState-AnyOtherAction case",
    68  			JobInfo: &apis.JobInfo{
    69  				Namespace: namespace,
    70  				Name:      "jobinfo1",
    71  				Job: &v1alpha1.Job{
    72  					ObjectMeta: metav1.ObjectMeta{
    73  						Name:            "Job1",
    74  						Namespace:       namespace,
    75  						ResourceVersion: "100",
    76  					},
    77  					Status: v1alpha1.JobStatus{
    78  						State: v1alpha1.JobState{
    79  							Phase: v1alpha1.Aborted,
    80  						},
    81  					},
    82  				},
    83  			},
    84  			Action:      busv1alpha1.RestartJobAction,
    85  			ExpectedVal: nil,
    86  		},
    87  	}
    88  
    89  	for _, testcase := range testcases {
    90  		t.Run(testcase.Name, func(t *testing.T) {
    91  			absState := state.NewState(testcase.JobInfo)
    92  
    93  			fakecontroller := newFakeController()
    94  			state.KillJob = fakecontroller.killJob
    95  
    96  			_, err := fakecontroller.vcClient.BatchV1alpha1().Jobs(namespace).Create(context.TODO(), testcase.JobInfo.Job, metav1.CreateOptions{})
    97  			if err != nil {
    98  				t.Error("Error while creating Job")
    99  			}
   100  
   101  			err = fakecontroller.cache.Add(testcase.JobInfo.Job)
   102  			if err != nil {
   103  				t.Error("Error while adding Job in cache")
   104  			}
   105  
   106  			err = absState.Execute(testcase.Action)
   107  			if err != nil {
   108  				t.Errorf("Expected Error not to occur but got: %s", err)
   109  			}
   110  			if testcase.Action == busv1alpha1.ResumeJobAction {
   111  				jobInfo, err := fakecontroller.cache.Get(fmt.Sprintf("%s/%s", testcase.JobInfo.Job.Namespace, testcase.JobInfo.Job.Name))
   112  				if err != nil {
   113  					t.Error("Error while retrieving value from Cache")
   114  				}
   115  
   116  				if jobInfo.Job.Status.State.Phase != v1alpha1.Restarting {
   117  					t.Error("Expected Phase to be equal to restarting phase")
   118  				}
   119  			}
   120  		})
   121  	}
   122  }
   123  
   124  func TestAbortingState_Execute(t *testing.T) {
   125  	namespace := "test"
   126  
   127  	testcases := []struct {
   128  		Name        string
   129  		JobInfo     *apis.JobInfo
   130  		Action      busv1alpha1.Action
   131  		ExpectedVal error
   132  	}{
   133  		{
   134  			Name: "AbortedState-ResumeAction case",
   135  			JobInfo: &apis.JobInfo{
   136  				Namespace: namespace,
   137  				Name:      "jobinfo1",
   138  				Job: &v1alpha1.Job{
   139  					ObjectMeta: metav1.ObjectMeta{
   140  						Name:            "Job1",
   141  						Namespace:       namespace,
   142  						ResourceVersion: "100",
   143  					},
   144  					Status: v1alpha1.JobStatus{
   145  						State: v1alpha1.JobState{
   146  							Phase: v1alpha1.Aborting,
   147  						},
   148  					},
   149  				},
   150  			},
   151  			Action:      busv1alpha1.ResumeJobAction,
   152  			ExpectedVal: nil,
   153  		},
   154  		{
   155  			Name: "AbortedState-AnyOtherAction case with pods count equal to 0",
   156  			JobInfo: &apis.JobInfo{
   157  				Namespace: namespace,
   158  				Name:      "jobinfo1",
   159  				Job: &v1alpha1.Job{
   160  					ObjectMeta: metav1.ObjectMeta{
   161  						Name:            "Job1",
   162  						Namespace:       namespace,
   163  						ResourceVersion: "100",
   164  					},
   165  					Status: v1alpha1.JobStatus{
   166  						State: v1alpha1.JobState{
   167  							Phase: v1alpha1.Aborting,
   168  						},
   169  					},
   170  				},
   171  			},
   172  			Action:      busv1alpha1.RestartJobAction,
   173  			ExpectedVal: nil,
   174  		},
   175  		{
   176  			Name: "AbortedState-AnyOtherAction case with Pods count not equal to 0",
   177  			JobInfo: &apis.JobInfo{
   178  				Namespace: namespace,
   179  				Name:      "jobinfo1",
   180  				Job: &v1alpha1.Job{
   181  					ObjectMeta: metav1.ObjectMeta{
   182  						Name:            "Job1",
   183  						Namespace:       namespace,
   184  						ResourceVersion: "100",
   185  					},
   186  					Status: v1alpha1.JobStatus{
   187  						Pending: 1,
   188  						State: v1alpha1.JobState{
   189  							Phase: v1alpha1.Aborting,
   190  						},
   191  					},
   192  				},
   193  				Pods: map[string]map[string]*v1.Pod{
   194  					"task1": {
   195  						"pod1": buildPod(namespace, "pod1", v1.PodPending, nil),
   196  					},
   197  				},
   198  			},
   199  			Action:      busv1alpha1.RestartJobAction,
   200  			ExpectedVal: nil,
   201  		},
   202  	}
   203  
   204  	for _, testcase := range testcases {
   205  		t.Run(testcase.Name, func(t *testing.T) {
   206  			absState := state.NewState(testcase.JobInfo)
   207  
   208  			fakecontroller := newFakeController()
   209  			state.KillJob = fakecontroller.killJob
   210  
   211  			_, err := fakecontroller.vcClient.BatchV1alpha1().Jobs(namespace).Create(context.TODO(), testcase.JobInfo.Job, metav1.CreateOptions{})
   212  			if err != nil {
   213  				t.Error("Error while creating Job")
   214  			}
   215  
   216  			err = fakecontroller.cache.Add(testcase.JobInfo.Job)
   217  			if err != nil {
   218  				t.Error("Error while adding Job in cache")
   219  			}
   220  
   221  			err = absState.Execute(testcase.Action)
   222  			if err != nil {
   223  				t.Errorf("Expected Error not to occur but got: %s", err)
   224  			}
   225  			if testcase.Action == busv1alpha1.ResumeJobAction {
   226  				jobInfo, err := fakecontroller.cache.Get(fmt.Sprintf("%s/%s", testcase.JobInfo.Job.Namespace, testcase.JobInfo.Job.Name))
   227  				if err != nil {
   228  					t.Error("Error while retrieving value from Cache")
   229  				}
   230  
   231  				if jobInfo.Job.Status.RetryCount == 0 {
   232  					t.Error("Retry Count should not be zero")
   233  				}
   234  			}
   235  
   236  			if testcase.Action != busv1alpha1.ResumeJobAction {
   237  				jobInfo, err := fakecontroller.cache.Get(fmt.Sprintf("%s/%s", testcase.JobInfo.Job.Namespace, testcase.JobInfo.Job.Name))
   238  				if err != nil {
   239  					t.Error("Error while retrieving value from Cache")
   240  				}
   241  
   242  				if testcase.JobInfo.Job.Status.Pending == 0 && testcase.JobInfo.Job.Status.Running == 0 && testcase.JobInfo.Job.Status.Terminating == 0 {
   243  					if jobInfo.Job.Status.State.Phase != v1alpha1.Aborted {
   244  						t.Error("Phase Should be aborted")
   245  					}
   246  				} else {
   247  					if jobInfo.Job.Status.State.Phase != v1alpha1.Aborting {
   248  						t.Error("Phase Should be aborted")
   249  					}
   250  				}
   251  			}
   252  		})
   253  	}
   254  }
   255  
   256  func TestCompletingState_Execute(t *testing.T) {
   257  
   258  	namespace := "test"
   259  
   260  	testcases := []struct {
   261  		Name        string
   262  		JobInfo     *apis.JobInfo
   263  		Action      busv1alpha1.Action
   264  		ExpectedVal error
   265  	}{
   266  		{
   267  			Name: "CompletingState- With pod count not equal to zero",
   268  			JobInfo: &apis.JobInfo{
   269  				Namespace: namespace,
   270  				Name:      "jobinfo1",
   271  				Job: &v1alpha1.Job{
   272  					ObjectMeta: metav1.ObjectMeta{
   273  						Name:            "Job1",
   274  						Namespace:       namespace,
   275  						ResourceVersion: "100",
   276  					},
   277  					Status: v1alpha1.JobStatus{
   278  						Running: 2,
   279  						State: v1alpha1.JobState{
   280  							Phase: v1alpha1.Completing,
   281  						},
   282  					},
   283  				},
   284  				Pods: map[string]map[string]*v1.Pod{
   285  					"task1": {
   286  						"pod1": buildPod(namespace, "pod1", v1.PodRunning, nil),
   287  						"pod2": buildPod(namespace, "pod2", v1.PodRunning, nil),
   288  					},
   289  				},
   290  			},
   291  			Action:      busv1alpha1.ResumeJobAction,
   292  			ExpectedVal: nil,
   293  		},
   294  		{
   295  			Name: "CompletingState- With pod count equal to zero",
   296  			JobInfo: &apis.JobInfo{
   297  				Namespace: namespace,
   298  				Name:      "jobinfo1",
   299  				Job: &v1alpha1.Job{
   300  					ObjectMeta: metav1.ObjectMeta{
   301  						Name:            "Job1",
   302  						Namespace:       namespace,
   303  						ResourceVersion: "100",
   304  					},
   305  					Status: v1alpha1.JobStatus{
   306  						State: v1alpha1.JobState{
   307  							Phase: v1alpha1.Completing,
   308  						},
   309  					},
   310  				},
   311  			},
   312  			Action:      busv1alpha1.ResumeJobAction,
   313  			ExpectedVal: nil,
   314  		},
   315  	}
   316  
   317  	for i, testcase := range testcases {
   318  		t.Run(testcase.Name, func(t *testing.T) {
   319  			testState := state.NewState(testcase.JobInfo)
   320  
   321  			fakecontroller := newFakeController()
   322  			state.KillJob = fakecontroller.killJob
   323  
   324  			_, err := fakecontroller.vcClient.BatchV1alpha1().Jobs(namespace).Create(context.TODO(), testcase.JobInfo.Job, metav1.CreateOptions{})
   325  			if err != nil {
   326  				t.Error("Error while creating Job")
   327  			}
   328  
   329  			err = fakecontroller.cache.Add(testcase.JobInfo.Job)
   330  			if err != nil {
   331  				t.Error("Error while adding Job in cache")
   332  			}
   333  
   334  			err = testState.Execute(testcase.Action)
   335  			if err != nil {
   336  				t.Errorf("Expected Error not to occur but got: %s", err)
   337  			}
   338  
   339  			jobInfo, err := fakecontroller.cache.Get(fmt.Sprintf("%s/%s", testcase.JobInfo.Job.Namespace, testcase.JobInfo.Job.Name))
   340  			if err != nil {
   341  				t.Error("Error while retrieving value from Cache")
   342  			}
   343  
   344  			if testcase.JobInfo.Job.Status.Running == 0 && testcase.JobInfo.Job.Status.Pending == 0 && testcase.JobInfo.Job.Status.Terminating == 0 {
   345  				if jobInfo.Job.Status.State.Phase != v1alpha1.Completed {
   346  					fmt.Println(jobInfo.Job.Status.State.Phase)
   347  					t.Errorf("Expected Phase to be Completed State in test case: %d", i)
   348  				}
   349  			} else {
   350  				if jobInfo.Job.Status.State.Phase != v1alpha1.Completing {
   351  					t.Errorf("Expected Phase to be completing state in test case: %d", i)
   352  				}
   353  			}
   354  		})
   355  	}
   356  }
   357  
   358  func TestFinishedState_Execute(t *testing.T) {
   359  	namespace := "test"
   360  
   361  	testcases := []struct {
   362  		Name        string
   363  		JobInfo     *apis.JobInfo
   364  		Action      busv1alpha1.Action
   365  		ExpectedVal error
   366  	}{
   367  		{
   368  			Name: "FinishedState Test Case",
   369  			JobInfo: &apis.JobInfo{
   370  				Namespace: namespace,
   371  				Name:      "jobinfo1",
   372  				Job: &v1alpha1.Job{
   373  					ObjectMeta: metav1.ObjectMeta{
   374  						Name:            "Job1",
   375  						Namespace:       namespace,
   376  						ResourceVersion: "100",
   377  					},
   378  					Status: v1alpha1.JobStatus{
   379  						State: v1alpha1.JobState{
   380  							Phase: v1alpha1.Completed,
   381  						},
   382  					},
   383  				},
   384  			},
   385  			Action:      busv1alpha1.ResumeJobAction,
   386  			ExpectedVal: nil,
   387  		},
   388  	}
   389  
   390  	for _, testcase := range testcases {
   391  		t.Run(testcase.Name, func(t *testing.T) {
   392  			testState := state.NewState(testcase.JobInfo)
   393  
   394  			fakecontroller := newFakeController()
   395  			state.KillJob = fakecontroller.killJob
   396  
   397  			_, err := fakecontroller.vcClient.BatchV1alpha1().Jobs(namespace).Create(context.TODO(), testcase.JobInfo.Job, metav1.CreateOptions{})
   398  			if err != nil {
   399  				t.Error("Error while creating Job")
   400  			}
   401  
   402  			err = fakecontroller.cache.Add(testcase.JobInfo.Job)
   403  			if err != nil {
   404  				t.Error("Error while adding Job in cache")
   405  			}
   406  
   407  			err = testState.Execute(testcase.Action)
   408  			if err != nil {
   409  				t.Errorf("Expected Error not to occur but got: %s", err)
   410  			}
   411  		})
   412  	}
   413  }
   414  
   415  func TestPendingState_Execute(t *testing.T) {
   416  	namespace := "test"
   417  
   418  	testcases := []struct {
   419  		Name        string
   420  		JobInfo     *apis.JobInfo
   421  		Action      busv1alpha1.Action
   422  		ExpectedVal error
   423  	}{
   424  		{
   425  			Name: "PendingState- RestartJobAction case With terminating pod count equal to zero",
   426  			JobInfo: &apis.JobInfo{
   427  				Namespace: namespace,
   428  				Name:      "jobinfo1",
   429  				Job: &v1alpha1.Job{
   430  					ObjectMeta: metav1.ObjectMeta{
   431  						Name:            "Job1",
   432  						Namespace:       namespace,
   433  						ResourceVersion: "100",
   434  					},
   435  					Status: v1alpha1.JobStatus{
   436  						State: v1alpha1.JobState{
   437  							Phase: v1alpha1.Pending,
   438  						},
   439  					},
   440  				},
   441  			},
   442  			Action:      busv1alpha1.RestartJobAction,
   443  			ExpectedVal: nil,
   444  		},
   445  		{
   446  			Name: "PendingState- RestartJobAction case With terminating pod count not equal to zero",
   447  			JobInfo: &apis.JobInfo{
   448  				Namespace: namespace,
   449  				Name:      "jobinfo1",
   450  				Job: &v1alpha1.Job{
   451  					ObjectMeta: metav1.ObjectMeta{
   452  						Name:            "Job1",
   453  						Namespace:       namespace,
   454  						ResourceVersion: "100",
   455  					},
   456  					Status: v1alpha1.JobStatus{
   457  						Terminating: 2,
   458  						State: v1alpha1.JobState{
   459  							Phase: v1alpha1.Pending,
   460  						},
   461  					},
   462  				},
   463  				Pods: map[string]map[string]*v1.Pod{
   464  					"task1": {
   465  						"pod1": buildPod(namespace, "pod1", v1.PodRunning, nil),
   466  						"pod2": buildPod(namespace, "pod2", v1.PodRunning, nil),
   467  					},
   468  				},
   469  			},
   470  			Action:      busv1alpha1.RestartJobAction,
   471  			ExpectedVal: nil,
   472  		},
   473  		{
   474  			Name: "PendingState- AbortJobAction case With terminating pod count equal to zero",
   475  			JobInfo: &apis.JobInfo{
   476  				Namespace: namespace,
   477  				Name:      "jobinfo1",
   478  				Job: &v1alpha1.Job{
   479  					ObjectMeta: metav1.ObjectMeta{
   480  						Name:            "Job1",
   481  						Namespace:       namespace,
   482  						ResourceVersion: "100",
   483  					},
   484  					Status: v1alpha1.JobStatus{
   485  						State: v1alpha1.JobState{
   486  							Phase: v1alpha1.Pending,
   487  						},
   488  					},
   489  				},
   490  			},
   491  			Action:      busv1alpha1.AbortJobAction,
   492  			ExpectedVal: nil,
   493  		},
   494  		{
   495  			Name: "PendingState- AbortJobAction case With terminating pod count not equal to zero",
   496  			JobInfo: &apis.JobInfo{
   497  				Namespace: namespace,
   498  				Name:      "jobinfo1",
   499  				Job: &v1alpha1.Job{
   500  					ObjectMeta: metav1.ObjectMeta{
   501  						Name:            "Job1",
   502  						Namespace:       namespace,
   503  						ResourceVersion: "100",
   504  					},
   505  					Status: v1alpha1.JobStatus{
   506  						Terminating: 2,
   507  						State: v1alpha1.JobState{
   508  							Phase: v1alpha1.Pending,
   509  						},
   510  					},
   511  				},
   512  				Pods: map[string]map[string]*v1.Pod{
   513  					"task1": {
   514  						"pod1": buildPod(namespace, "pod1", v1.PodRunning, nil),
   515  						"pod2": buildPod(namespace, "pod2", v1.PodRunning, nil),
   516  					},
   517  				},
   518  			},
   519  			Action:      busv1alpha1.AbortJobAction,
   520  			ExpectedVal: nil,
   521  		},
   522  		{
   523  			Name: "PendingState- TerminateJobAction case With terminating pod count not equal to zero",
   524  			JobInfo: &apis.JobInfo{
   525  				Namespace: namespace,
   526  				Name:      "jobinfo1",
   527  				Job: &v1alpha1.Job{
   528  					ObjectMeta: metav1.ObjectMeta{
   529  						Name:            "Job1",
   530  						Namespace:       namespace,
   531  						ResourceVersion: "100",
   532  					},
   533  					Status: v1alpha1.JobStatus{
   534  						Terminating: 2,
   535  						State: v1alpha1.JobState{
   536  							Phase: v1alpha1.Pending,
   537  						},
   538  					},
   539  				},
   540  				Pods: map[string]map[string]*v1.Pod{
   541  					"task1": {
   542  						"pod1": buildPod(namespace, "pod1", v1.PodRunning, nil),
   543  						"pod2": buildPod(namespace, "pod2", v1.PodRunning, nil),
   544  					},
   545  				},
   546  			},
   547  			Action:      busv1alpha1.TerminateJobAction,
   548  			ExpectedVal: nil,
   549  		},
   550  		{
   551  			Name: "PendingState- CompleteJobAction case With terminating pod count equal to zero",
   552  			JobInfo: &apis.JobInfo{
   553  				Namespace: namespace,
   554  				Name:      "jobinfo1",
   555  				Job: &v1alpha1.Job{
   556  					ObjectMeta: metav1.ObjectMeta{
   557  						Name:            "Job1",
   558  						Namespace:       namespace,
   559  						ResourceVersion: "100",
   560  					},
   561  					Status: v1alpha1.JobStatus{
   562  						State: v1alpha1.JobState{
   563  							Phase: v1alpha1.Pending,
   564  						},
   565  					},
   566  				},
   567  			},
   568  			Action:      busv1alpha1.CompleteJobAction,
   569  			ExpectedVal: nil,
   570  		},
   571  		{
   572  			Name: "PendingState- CompleteJobAction case With terminating pod count not equal to zero",
   573  			JobInfo: &apis.JobInfo{
   574  				Namespace: namespace,
   575  				Name:      "jobinfo1",
   576  				Job: &v1alpha1.Job{
   577  					ObjectMeta: metav1.ObjectMeta{
   578  						Name:            "Job1",
   579  						Namespace:       namespace,
   580  						ResourceVersion: "100",
   581  					},
   582  					Status: v1alpha1.JobStatus{
   583  						Terminating: 2,
   584  						State: v1alpha1.JobState{
   585  							Phase: v1alpha1.Pending,
   586  						},
   587  					},
   588  				},
   589  				Pods: map[string]map[string]*v1.Pod{
   590  					"task1": {
   591  						"pod1": buildPod(namespace, "pod1", v1.PodRunning, nil),
   592  						"pod2": buildPod(namespace, "pod2", v1.PodRunning, nil),
   593  					},
   594  				},
   595  			},
   596  			Action:      busv1alpha1.CompleteJobAction,
   597  			ExpectedVal: nil,
   598  		},
   599  		{
   600  			Name: "PendingState- EnqueueAction case With Min Available equal to running pods",
   601  			JobInfo: &apis.JobInfo{
   602  				Namespace: namespace,
   603  				Name:      "jobinfo1",
   604  				Job: &v1alpha1.Job{
   605  					ObjectMeta: metav1.ObjectMeta{
   606  						Name:            "Job1",
   607  						Namespace:       namespace,
   608  						ResourceVersion: "100",
   609  					},
   610  					Spec: v1alpha1.JobSpec{
   611  						MinAvailable: 3,
   612  					},
   613  					Status: v1alpha1.JobStatus{
   614  						Running: 3,
   615  						State: v1alpha1.JobState{
   616  							Phase: v1alpha1.Pending,
   617  						},
   618  					},
   619  				},
   620  				Pods: map[string]map[string]*v1.Pod{
   621  					"task1": {
   622  						"pod1": buildPod(namespace, "pod1", v1.PodRunning, nil),
   623  						"pod2": buildPod(namespace, "pod2", v1.PodRunning, nil),
   624  						"pod3": buildPod(namespace, "pod3", v1.PodRunning, nil),
   625  					},
   626  				},
   627  			},
   628  			Action:      busv1alpha1.EnqueueAction,
   629  			ExpectedVal: nil,
   630  		},
   631  		{
   632  			Name: "PendingState- EnqueueAction case With Min Available not equal to running pods",
   633  			JobInfo: &apis.JobInfo{
   634  				Namespace: namespace,
   635  				Name:      "jobinfo1",
   636  				Job: &v1alpha1.Job{
   637  					ObjectMeta: metav1.ObjectMeta{
   638  						Name:            "Job1",
   639  						Namespace:       namespace,
   640  						ResourceVersion: "100",
   641  					},
   642  					Spec: v1alpha1.JobSpec{
   643  						MinAvailable: 3,
   644  					},
   645  					Status: v1alpha1.JobStatus{
   646  						Running: 2,
   647  						State: v1alpha1.JobState{
   648  							Phase: v1alpha1.Pending,
   649  						},
   650  					},
   651  				},
   652  				Pods: map[string]map[string]*v1.Pod{
   653  					"task1": {
   654  						"pod1": buildPod(namespace, "pod1", v1.PodRunning, nil),
   655  						"pod2": buildPod(namespace, "pod2", v1.PodRunning, nil),
   656  					},
   657  				},
   658  			},
   659  			Action:      busv1alpha1.EnqueueAction,
   660  			ExpectedVal: nil,
   661  		},
   662  		{
   663  			Name: "PendingState- Default case With Min Available equal to running pods",
   664  			JobInfo: &apis.JobInfo{
   665  				Namespace: namespace,
   666  				Name:      "jobinfo1",
   667  				Job: &v1alpha1.Job{
   668  					ObjectMeta: metav1.ObjectMeta{
   669  						Name:            "Job1",
   670  						Namespace:       namespace,
   671  						ResourceVersion: "100",
   672  					},
   673  					Spec: v1alpha1.JobSpec{
   674  						MinAvailable: 3,
   675  					},
   676  					Status: v1alpha1.JobStatus{
   677  						Running: 2,
   678  						State: v1alpha1.JobState{
   679  							Phase: v1alpha1.Pending,
   680  						},
   681  					},
   682  				},
   683  				Pods: map[string]map[string]*v1.Pod{
   684  					"task1": {
   685  						"pod1": buildPod(namespace, "pod1", v1.PodRunning, nil),
   686  						"pod2": buildPod(namespace, "pod2", v1.PodRunning, nil),
   687  					},
   688  				},
   689  			},
   690  			Action:      busv1alpha1.SyncJobAction,
   691  			ExpectedVal: nil,
   692  		},
   693  	}
   694  
   695  	for i, testcase := range testcases {
   696  		t.Run(testcase.Name, func(t *testing.T) {
   697  			testState := state.NewState(testcase.JobInfo)
   698  
   699  			fakecontroller := newFakeController()
   700  			state.KillJob = fakecontroller.killJob
   701  
   702  			patches := gomonkey.ApplyMethod(reflect.TypeOf(fakecontroller), "GetQueueInfo", func(_ *jobcontroller, _ string) (*schedulingapi.Queue, error) {
   703  				return &schedulingapi.Queue{}, nil
   704  			})
   705  
   706  			defer patches.Reset()
   707  
   708  			_, err := fakecontroller.vcClient.BatchV1alpha1().Jobs(namespace).Create(context.TODO(), testcase.JobInfo.Job, metav1.CreateOptions{})
   709  			if err != nil {
   710  				t.Error("Error while creating Job")
   711  			}
   712  
   713  			err = fakecontroller.cache.Add(testcase.JobInfo.Job)
   714  			if err != nil {
   715  				t.Error("Error while adding Job in cache")
   716  			}
   717  
   718  			err = testState.Execute(testcase.Action)
   719  			if err != nil {
   720  				t.Errorf("Expected Error not to occur but got: %s", err)
   721  			}
   722  
   723  			jobInfo, err := fakecontroller.cache.Get(fmt.Sprintf("%s/%s", testcase.JobInfo.Job.Namespace, testcase.JobInfo.Job.Name))
   724  			if err != nil {
   725  				t.Error("Error while retrieving value from Cache")
   726  			}
   727  
   728  			if testcase.Action == busv1alpha1.RestartJobAction {
   729  				// always jump to restarting firstly
   730  				if jobInfo.Job.Status.State.Phase != v1alpha1.Restarting {
   731  					t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Restarting, jobInfo.Job.Status.State.Phase, i)
   732  				}
   733  			} else if testcase.Action == busv1alpha1.AbortJobAction {
   734  				// always jump to aborting firstly
   735  				if jobInfo.Job.Status.State.Phase != v1alpha1.Aborting {
   736  					t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Aborting, jobInfo.Job.Status.State.Phase, i)
   737  				}
   738  			} else if testcase.Action == busv1alpha1.TerminateJobAction {
   739  				// always jump to completing firstly
   740  				if jobInfo.Job.Status.State.Phase != v1alpha1.Terminating {
   741  					t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Terminating, jobInfo.Job.Status.State.Phase, i)
   742  				}
   743  			} else if testcase.Action == busv1alpha1.CompleteJobAction {
   744  				// always jump to completing firstly
   745  				if jobInfo.Job.Status.State.Phase != v1alpha1.Completing {
   746  					t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Completing, jobInfo.Job.Status.State.Phase, i)
   747  				}
   748  			} else if testcase.Action == busv1alpha1.EnqueueAction {
   749  				if jobInfo.Job.Spec.MinAvailable <= jobInfo.Job.Status.Running+jobInfo.Job.Status.Succeeded+jobInfo.Job.Status.Failed {
   750  					if jobInfo.Job.Status.State.Phase != v1alpha1.Running {
   751  						t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Running, jobInfo.Job.Status.State.Phase, i)
   752  					}
   753  				}
   754  			} else {
   755  				if jobInfo.Job.Status.State.Phase != v1alpha1.Pending {
   756  					t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Pending, jobInfo.Job.Status.State.Phase, i)
   757  				}
   758  			}
   759  		})
   760  	}
   761  }
   762  
   763  func TestRestartingState_Execute(t *testing.T) {
   764  	namespace := "test"
   765  
   766  	testcases := []struct {
   767  		Name        string
   768  		JobInfo     *apis.JobInfo
   769  		Action      busv1alpha1.Action
   770  		ExpectedVal error
   771  	}{
   772  		{
   773  			Name: "RestartingState- RetryCount is equal to or greater than MaxRetry",
   774  			JobInfo: &apis.JobInfo{
   775  				Namespace: namespace,
   776  				Name:      "jobinfo1",
   777  				Job: &v1alpha1.Job{
   778  					ObjectMeta: metav1.ObjectMeta{
   779  						Name:            "Job1",
   780  						Namespace:       namespace,
   781  						ResourceVersion: "100",
   782  					},
   783  					Spec: v1alpha1.JobSpec{
   784  						MaxRetry: 3,
   785  					},
   786  					Status: v1alpha1.JobStatus{
   787  						RetryCount: 3,
   788  						State: v1alpha1.JobState{
   789  							Phase: v1alpha1.Restarting,
   790  						},
   791  					},
   792  				},
   793  			},
   794  			Action:      busv1alpha1.RestartJobAction,
   795  			ExpectedVal: nil,
   796  		},
   797  		{
   798  			Name: "RestartingState- RetryCount is less than MaxRetry",
   799  			JobInfo: &apis.JobInfo{
   800  				Namespace: namespace,
   801  				Name:      "jobinfo1",
   802  				Job: &v1alpha1.Job{
   803  					ObjectMeta: metav1.ObjectMeta{
   804  						Name:            "Job1",
   805  						Namespace:       namespace,
   806  						ResourceVersion: "100",
   807  					},
   808  					Spec: v1alpha1.JobSpec{
   809  						MaxRetry: 3,
   810  						Tasks: []v1alpha1.TaskSpec{
   811  							{
   812  								Name:     "task1",
   813  								Replicas: 1,
   814  							},
   815  							{
   816  								Name:     "task2",
   817  								Replicas: 1,
   818  							},
   819  						},
   820  					},
   821  					Status: v1alpha1.JobStatus{
   822  						RetryCount:   1,
   823  						MinAvailable: 1,
   824  						Terminating:  0,
   825  						State: v1alpha1.JobState{
   826  							Phase: v1alpha1.Restarting,
   827  						},
   828  					},
   829  				},
   830  			},
   831  			Action:      busv1alpha1.RestartJobAction,
   832  			ExpectedVal: nil,
   833  		},
   834  	}
   835  
   836  	for i, testcase := range testcases {
   837  		t.Run(testcase.Name, func(t *testing.T) {
   838  			testState := state.NewState(testcase.JobInfo)
   839  
   840  			fakecontroller := newFakeController()
   841  			state.KillJob = fakecontroller.killJob
   842  
   843  			_, err := fakecontroller.vcClient.BatchV1alpha1().Jobs(namespace).Create(context.TODO(), testcase.JobInfo.Job, metav1.CreateOptions{})
   844  			if err != nil {
   845  				t.Error("Error while creating Job")
   846  			}
   847  
   848  			err = fakecontroller.cache.Add(testcase.JobInfo.Job)
   849  			if err != nil {
   850  				t.Error("Error while adding Job in cache")
   851  			}
   852  
   853  			err = testState.Execute(testcase.Action)
   854  			if err != nil {
   855  				t.Errorf("Expected Error not to occur but got: %s", err)
   856  			}
   857  
   858  			jobInfo, err := fakecontroller.cache.Get(fmt.Sprintf("%s/%s", testcase.JobInfo.Job.Namespace, testcase.JobInfo.Job.Name))
   859  			if err != nil {
   860  				t.Error("Error while retrieving value from Cache")
   861  			}
   862  
   863  			if testcase.JobInfo.Job.Spec.MaxRetry <= testcase.JobInfo.Job.Status.RetryCount {
   864  				if jobInfo.Job.Status.State.Phase != v1alpha1.Failed {
   865  					t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Failed, jobInfo.Job.Status.State.Phase, i)
   866  				}
   867  			} else {
   868  				if jobInfo.Job.Status.State.Phase != v1alpha1.Pending {
   869  					t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Pending, jobInfo.Job.Status.State.Phase, i)
   870  				}
   871  			}
   872  		})
   873  	}
   874  }
   875  
   876  func TestRunningState_Execute(t *testing.T) {
   877  	namespace := "test"
   878  
   879  	testcases := []struct {
   880  		Name        string
   881  		JobInfo     *apis.JobInfo
   882  		Action      busv1alpha1.Action
   883  		ExpectedVal error
   884  	}{
   885  		{
   886  			Name: "RunningState- RestartJobAction case and Terminating Pods not equal to 0",
   887  			JobInfo: &apis.JobInfo{
   888  				Namespace: namespace,
   889  				Name:      "jobinfo1",
   890  				Job: &v1alpha1.Job{
   891  					ObjectMeta: metav1.ObjectMeta{
   892  						Name:            "Job1",
   893  						Namespace:       namespace,
   894  						ResourceVersion: "100",
   895  					},
   896  					Spec: v1alpha1.JobSpec{},
   897  					Status: v1alpha1.JobStatus{
   898  						Terminating: 2,
   899  						State: v1alpha1.JobState{
   900  							Phase: v1alpha1.Running,
   901  						},
   902  					},
   903  				},
   904  				Pods: map[string]map[string]*v1.Pod{
   905  					"task1": {
   906  						"pod1": buildPod(namespace, "pod1", v1.PodRunning, nil),
   907  						"pod2": buildPod(namespace, "pod2", v1.PodRunning, nil),
   908  					},
   909  				},
   910  			},
   911  			Action:      busv1alpha1.RestartJobAction,
   912  			ExpectedVal: nil,
   913  		},
   914  		{
   915  			Name: "RunningState- RestartJobAction case and Terminating Pods equal to 0",
   916  			JobInfo: &apis.JobInfo{
   917  				Namespace: namespace,
   918  				Name:      "jobinfo1",
   919  				Job: &v1alpha1.Job{
   920  					ObjectMeta: metav1.ObjectMeta{
   921  						Name:            "Job1",
   922  						Namespace:       namespace,
   923  						ResourceVersion: "100",
   924  					},
   925  					Spec: v1alpha1.JobSpec{},
   926  					Status: v1alpha1.JobStatus{
   927  						Terminating: 0,
   928  						State: v1alpha1.JobState{
   929  							Phase: v1alpha1.Running,
   930  						},
   931  					},
   932  				},
   933  			},
   934  			Action:      busv1alpha1.RestartJobAction,
   935  			ExpectedVal: nil,
   936  		},
   937  		{
   938  			Name: "RunningState- AbortAction case and Terminating Pods equal to 0",
   939  			JobInfo: &apis.JobInfo{
   940  				Namespace: namespace,
   941  				Name:      "jobinfo1",
   942  				Job: &v1alpha1.Job{
   943  					ObjectMeta: metav1.ObjectMeta{
   944  						Name:            "Job1",
   945  						Namespace:       namespace,
   946  						ResourceVersion: "100",
   947  					},
   948  					Spec: v1alpha1.JobSpec{},
   949  					Status: v1alpha1.JobStatus{
   950  						Terminating: 0,
   951  						State: v1alpha1.JobState{
   952  							Phase: v1alpha1.Running,
   953  						},
   954  					},
   955  				},
   956  			},
   957  			Action:      busv1alpha1.AbortJobAction,
   958  			ExpectedVal: nil,
   959  		},
   960  		{
   961  			Name: "RunningState- AbortAction case and Terminating Pods not equal to 0",
   962  			JobInfo: &apis.JobInfo{
   963  				Namespace: namespace,
   964  				Name:      "jobinfo1",
   965  				Job: &v1alpha1.Job{
   966  					ObjectMeta: metav1.ObjectMeta{
   967  						Name:            "Job1",
   968  						Namespace:       namespace,
   969  						ResourceVersion: "100",
   970  					},
   971  					Spec: v1alpha1.JobSpec{},
   972  					Status: v1alpha1.JobStatus{
   973  						Terminating: 2,
   974  						State: v1alpha1.JobState{
   975  							Phase: v1alpha1.Running,
   976  						},
   977  					},
   978  				},
   979  				Pods: map[string]map[string]*v1.Pod{
   980  					"task1": {
   981  						"pod1": buildPod(namespace, "pod1", v1.PodRunning, nil),
   982  						"pod2": buildPod(namespace, "pod2", v1.PodRunning, nil),
   983  					},
   984  				},
   985  			},
   986  			Action:      busv1alpha1.AbortJobAction,
   987  			ExpectedVal: nil,
   988  		},
   989  		{
   990  			Name: "RunningState- TerminateJobAction case and Terminating Pods equal to 0",
   991  			JobInfo: &apis.JobInfo{
   992  				Namespace: namespace,
   993  				Name:      "jobinfo1",
   994  				Job: &v1alpha1.Job{
   995  					ObjectMeta: metav1.ObjectMeta{
   996  						Name:            "Job1",
   997  						Namespace:       namespace,
   998  						ResourceVersion: "100",
   999  					},
  1000  					Spec: v1alpha1.JobSpec{},
  1001  					Status: v1alpha1.JobStatus{
  1002  						Terminating: 0,
  1003  						State: v1alpha1.JobState{
  1004  							Phase: v1alpha1.Running,
  1005  						},
  1006  					},
  1007  				},
  1008  			},
  1009  			Action:      busv1alpha1.TerminateJobAction,
  1010  			ExpectedVal: nil,
  1011  		},
  1012  		{
  1013  			Name: "RunningState- TerminateJobAction case and Terminating Pods not equal to 0",
  1014  			JobInfo: &apis.JobInfo{
  1015  				Namespace: namespace,
  1016  				Name:      "jobinfo1",
  1017  				Job: &v1alpha1.Job{
  1018  					ObjectMeta: metav1.ObjectMeta{
  1019  						Name:            "Job1",
  1020  						Namespace:       namespace,
  1021  						ResourceVersion: "100",
  1022  					},
  1023  					Spec: v1alpha1.JobSpec{},
  1024  					Status: v1alpha1.JobStatus{
  1025  						Terminating: 2,
  1026  						State: v1alpha1.JobState{
  1027  							Phase: v1alpha1.Running,
  1028  						},
  1029  					},
  1030  				},
  1031  				Pods: map[string]map[string]*v1.Pod{
  1032  					"task1": {
  1033  						"pod1": buildPod(namespace, "pod1", v1.PodRunning, nil),
  1034  						"pod2": buildPod(namespace, "pod2", v1.PodRunning, nil),
  1035  					},
  1036  				},
  1037  			},
  1038  			Action:      busv1alpha1.TerminateJobAction,
  1039  			ExpectedVal: nil,
  1040  		},
  1041  		{
  1042  			Name: "RunningState- CompleteJobAction case and Terminating Pods equal to 0",
  1043  			JobInfo: &apis.JobInfo{
  1044  				Namespace: namespace,
  1045  				Name:      "jobinfo1",
  1046  				Job: &v1alpha1.Job{
  1047  					ObjectMeta: metav1.ObjectMeta{
  1048  						Name:            "Job1",
  1049  						Namespace:       namespace,
  1050  						ResourceVersion: "100",
  1051  					},
  1052  					Spec: v1alpha1.JobSpec{},
  1053  					Status: v1alpha1.JobStatus{
  1054  						Terminating: 0,
  1055  						State: v1alpha1.JobState{
  1056  							Phase: v1alpha1.Running,
  1057  						},
  1058  					},
  1059  				},
  1060  			},
  1061  			Action:      busv1alpha1.CompleteJobAction,
  1062  			ExpectedVal: nil,
  1063  		},
  1064  		{
  1065  			Name: "RunningState- CompleteJobAction case and Terminating Pods not equal to 0",
  1066  			JobInfo: &apis.JobInfo{
  1067  				Namespace: namespace,
  1068  				Name:      "jobinfo1",
  1069  				Job: &v1alpha1.Job{
  1070  					ObjectMeta: metav1.ObjectMeta{
  1071  						Name:            "Job1",
  1072  						Namespace:       namespace,
  1073  						ResourceVersion: "100",
  1074  					},
  1075  					Spec: v1alpha1.JobSpec{},
  1076  					Status: v1alpha1.JobStatus{
  1077  						Terminating: 2,
  1078  						State: v1alpha1.JobState{
  1079  							Phase: v1alpha1.Running,
  1080  						},
  1081  					},
  1082  				},
  1083  				Pods: map[string]map[string]*v1.Pod{
  1084  					"task1": {
  1085  						"pod1": buildPod(namespace, "pod1", v1.PodRunning, nil),
  1086  						"pod2": buildPod(namespace, "pod2", v1.PodRunning, nil),
  1087  					},
  1088  				},
  1089  			},
  1090  			Action:      busv1alpha1.CompleteJobAction,
  1091  			ExpectedVal: nil,
  1092  		},
  1093  		{
  1094  			Name: "RunningState- Default case and Total is equal to failed+succeeded",
  1095  			JobInfo: &apis.JobInfo{
  1096  				Namespace: namespace,
  1097  				Name:      "jobinfo1",
  1098  				Job: &v1alpha1.Job{
  1099  					ObjectMeta: metav1.ObjectMeta{
  1100  						Name:            "job1",
  1101  						Namespace:       namespace,
  1102  						ResourceVersion: "100",
  1103  					},
  1104  					Spec: v1alpha1.JobSpec{
  1105  						Tasks: []v1alpha1.TaskSpec{
  1106  							{
  1107  								Name:     "task1",
  1108  								Replicas: 2,
  1109  								Template: v1.PodTemplateSpec{
  1110  									ObjectMeta: metav1.ObjectMeta{
  1111  										Name: "task1",
  1112  									},
  1113  								},
  1114  							},
  1115  						},
  1116  					},
  1117  					Status: v1alpha1.JobStatus{
  1118  						Succeeded: 2,
  1119  						State: v1alpha1.JobState{
  1120  							Phase: v1alpha1.Running,
  1121  						},
  1122  					},
  1123  				},
  1124  				Pods: map[string]map[string]*v1.Pod{
  1125  					"task1": {
  1126  						"job1-task1-0": buildPod(namespace, "pod1", v1.PodSucceeded, nil),
  1127  						"job1-task1-1": buildPod(namespace, "pod2", v1.PodSucceeded, nil),
  1128  					},
  1129  				},
  1130  			},
  1131  			Action:      busv1alpha1.SyncJobAction,
  1132  			ExpectedVal: nil,
  1133  		},
  1134  		{
  1135  			Name: "RunningState- Default case and Total is not equal to failed+succeeded",
  1136  			JobInfo: &apis.JobInfo{
  1137  				Namespace: namespace,
  1138  				Name:      "jobinfo1",
  1139  				Job: &v1alpha1.Job{
  1140  					ObjectMeta: metav1.ObjectMeta{
  1141  						Name:            "job1",
  1142  						Namespace:       namespace,
  1143  						ResourceVersion: "100",
  1144  					},
  1145  					Spec: v1alpha1.JobSpec{
  1146  						Tasks: []v1alpha1.TaskSpec{
  1147  							{
  1148  								Name:     "task1",
  1149  								Replicas: 2,
  1150  								Template: v1.PodTemplateSpec{
  1151  									ObjectMeta: metav1.ObjectMeta{
  1152  										Name: "task1",
  1153  									},
  1154  								},
  1155  							},
  1156  						},
  1157  					},
  1158  					Status: v1alpha1.JobStatus{
  1159  						Succeeded: 1,
  1160  						State: v1alpha1.JobState{
  1161  							Phase: v1alpha1.Running,
  1162  						},
  1163  					},
  1164  				},
  1165  				Pods: map[string]map[string]*v1.Pod{
  1166  					"task1": {
  1167  						"job1-task1-0": buildPod(namespace, "pod1", v1.PodSucceeded, nil),
  1168  					},
  1169  				},
  1170  			},
  1171  			Action:      busv1alpha1.SyncJobAction,
  1172  			ExpectedVal: nil,
  1173  		},
  1174  		{
  1175  			Name: "RunningState- Default case and running back to pending When pending equal to total",
  1176  			JobInfo: &apis.JobInfo{
  1177  				Namespace: namespace,
  1178  				Name:      "jobinfo1",
  1179  				Job: &v1alpha1.Job{
  1180  					ObjectMeta: metav1.ObjectMeta{
  1181  						Name:            "job1",
  1182  						Namespace:       namespace,
  1183  						ResourceVersion: "100",
  1184  					},
  1185  					Spec: v1alpha1.JobSpec{
  1186  						MinAvailable: 3,
  1187  						Tasks: []v1alpha1.TaskSpec{
  1188  							{
  1189  								Name:     "task1",
  1190  								Replicas: 5,
  1191  								Template: v1.PodTemplateSpec{
  1192  									ObjectMeta: metav1.ObjectMeta{
  1193  										Name: "task1",
  1194  									},
  1195  								},
  1196  							},
  1197  						},
  1198  					},
  1199  					Status: v1alpha1.JobStatus{
  1200  						Pending: 5,
  1201  						State: v1alpha1.JobState{
  1202  							Phase: v1alpha1.Running,
  1203  						},
  1204  					},
  1205  				},
  1206  				Pods: map[string]map[string]*v1.Pod{
  1207  					"task1": {
  1208  						"job1-task1-0": buildPod(namespace, "pod1", v1.PodPending, nil),
  1209  						"job1-task1-1": buildPod(namespace, "pod2", v1.PodPending, nil),
  1210  						"job1-task1-2": buildPod(namespace, "pod3", v1.PodPending, nil),
  1211  						"job1-task1-3": buildPod(namespace, "pod4", v1.PodPending, nil),
  1212  						"job1-task1-4": buildPod(namespace, "pod5", v1.PodPending, nil),
  1213  					},
  1214  				},
  1215  			},
  1216  			Action:      busv1alpha1.SyncJobAction,
  1217  			ExpectedVal: nil,
  1218  		},
  1219  		{
  1220  			Name: "RunningState- Default case and running back to pending When pods status pending>(total-minAvailable)",
  1221  			JobInfo: &apis.JobInfo{
  1222  				Namespace: namespace,
  1223  				Name:      "jobinfo1",
  1224  				Job: &v1alpha1.Job{
  1225  					ObjectMeta: metav1.ObjectMeta{
  1226  						Name:            "job1",
  1227  						Namespace:       namespace,
  1228  						ResourceVersion: "100",
  1229  					},
  1230  					Spec: v1alpha1.JobSpec{
  1231  						MinAvailable: 3,
  1232  						Tasks: []v1alpha1.TaskSpec{
  1233  							{
  1234  								Name:     "task1",
  1235  								Replicas: 5,
  1236  								Template: v1.PodTemplateSpec{
  1237  									ObjectMeta: metav1.ObjectMeta{
  1238  										Name: "task1",
  1239  									},
  1240  								},
  1241  							},
  1242  						},
  1243  					},
  1244  					Status: v1alpha1.JobStatus{
  1245  						Pending: 3,
  1246  						Running: 2,
  1247  						State: v1alpha1.JobState{
  1248  							Phase: v1alpha1.Running,
  1249  						},
  1250  					},
  1251  				},
  1252  				Pods: map[string]map[string]*v1.Pod{
  1253  					"task1": {
  1254  						"job1-task1-0": buildPod(namespace, "pod1", v1.PodRunning, nil),
  1255  						"job1-task1-1": buildPod(namespace, "pod2", v1.PodRunning, nil),
  1256  						"job1-task1-2": buildPod(namespace, "pod3", v1.PodPending, nil),
  1257  						"job1-task1-3": buildPod(namespace, "pod4", v1.PodPending, nil),
  1258  						"job1-task1-4": buildPod(namespace, "pod5", v1.PodPending, nil),
  1259  					},
  1260  				},
  1261  			},
  1262  			Action:      busv1alpha1.SyncJobAction,
  1263  			ExpectedVal: nil,
  1264  		},
  1265  		{
  1266  			Name: "RunningState- Default case and keep running When pods status pending<=(total-minAvailable)",
  1267  			JobInfo: &apis.JobInfo{
  1268  				Namespace: namespace,
  1269  				Name:      "jobinfo1",
  1270  				Job: &v1alpha1.Job{
  1271  					ObjectMeta: metav1.ObjectMeta{
  1272  						Name:            "job1",
  1273  						Namespace:       namespace,
  1274  						ResourceVersion: "100",
  1275  					},
  1276  					Spec: v1alpha1.JobSpec{
  1277  						MinAvailable: 3,
  1278  						Tasks: []v1alpha1.TaskSpec{
  1279  							{
  1280  								Name:     "task1",
  1281  								Replicas: 5,
  1282  								Template: v1.PodTemplateSpec{
  1283  									ObjectMeta: metav1.ObjectMeta{
  1284  										Name: "task1",
  1285  									},
  1286  								},
  1287  							},
  1288  						},
  1289  					},
  1290  					Status: v1alpha1.JobStatus{
  1291  						Pending:   2,
  1292  						Running:   1,
  1293  						Succeeded: 1,
  1294  						Failed:    1,
  1295  						State: v1alpha1.JobState{
  1296  							Phase: v1alpha1.Running,
  1297  						},
  1298  					},
  1299  				},
  1300  				Pods: map[string]map[string]*v1.Pod{
  1301  					"task1": {
  1302  						"job1-task1-0": buildPod(namespace, "pod1", v1.PodRunning, nil),
  1303  						"job1-task1-1": buildPod(namespace, "pod2", v1.PodSucceeded, nil),
  1304  						"job1-task1-2": buildPod(namespace, "pod3", v1.PodFailed, nil),
  1305  						"job1-task1-3": buildPod(namespace, "pod4", v1.PodPending, nil),
  1306  						"job1-task1-4": buildPod(namespace, "pod5", v1.PodPending, nil),
  1307  					},
  1308  				},
  1309  			},
  1310  			Action:      busv1alpha1.SyncJobAction,
  1311  			ExpectedVal: nil,
  1312  		},
  1313  	}
  1314  
  1315  	for i, testcase := range testcases {
  1316  		t.Run(testcase.Name, func(t *testing.T) {
  1317  			testState := state.NewState(testcase.JobInfo)
  1318  
  1319  			fakecontroller := newFakeController()
  1320  			state.KillJob = fakecontroller.killJob
  1321  
  1322  			patches := gomonkey.ApplyMethod(reflect.TypeOf(fakecontroller), "GetQueueInfo", func(_ *jobcontroller, _ string) (*schedulingapi.Queue, error) {
  1323  				return &schedulingapi.Queue{}, nil
  1324  			})
  1325  
  1326  			defer patches.Reset()
  1327  
  1328  			_, err := fakecontroller.vcClient.BatchV1alpha1().Jobs(namespace).Create(context.TODO(), testcase.JobInfo.Job, metav1.CreateOptions{})
  1329  			if err != nil {
  1330  				t.Error("Error while creating Job")
  1331  			}
  1332  
  1333  			err = fakecontroller.cache.Add(testcase.JobInfo.Job)
  1334  			if err != nil {
  1335  				t.Error("Error while adding Job in cache")
  1336  			}
  1337  
  1338  			err = testState.Execute(testcase.Action)
  1339  			if err != nil {
  1340  				t.Errorf("Expected Error not to occur but got: %s", err)
  1341  			}
  1342  
  1343  			jobInfo, err := fakecontroller.cache.Get(fmt.Sprintf("%s/%s", testcase.JobInfo.Job.Namespace, testcase.JobInfo.Job.Name))
  1344  			if err != nil {
  1345  				t.Error("Error while retrieving value from Cache")
  1346  			}
  1347  
  1348  			if testcase.Action == busv1alpha1.RestartJobAction {
  1349  				// always jump to restarting firstly
  1350  				if jobInfo.Job.Status.State.Phase != v1alpha1.Restarting {
  1351  					t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Restarting, jobInfo.Job.Status.State.Phase, i)
  1352  				}
  1353  			} else if testcase.Action == busv1alpha1.AbortJobAction {
  1354  				// always jump to aborting firstly
  1355  				if jobInfo.Job.Status.State.Phase != v1alpha1.Aborting {
  1356  					t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Restarting, jobInfo.Job.Status.State.Phase, i)
  1357  				}
  1358  			} else if testcase.Action == busv1alpha1.TerminateJobAction {
  1359  				// always jump to terminating firstly
  1360  				if jobInfo.Job.Status.State.Phase != v1alpha1.Terminating {
  1361  					t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Terminating, jobInfo.Job.Status.State.Phase, i)
  1362  				}
  1363  			} else if testcase.Action == busv1alpha1.CompleteJobAction {
  1364  				// always jump to completing firstly
  1365  				if jobInfo.Job.Status.State.Phase != v1alpha1.Completing {
  1366  					t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Restarting, jobInfo.Job.Status.State.Phase, i)
  1367  				}
  1368  			} else {
  1369  				total := state.TotalTasks(testcase.JobInfo.Job)
  1370  				if total == testcase.JobInfo.Job.Status.Succeeded+testcase.JobInfo.Job.Status.Failed {
  1371  					if jobInfo.Job.Status.State.Phase != v1alpha1.Completed {
  1372  						t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Completed, jobInfo.Job.Status.State.Phase, i)
  1373  					}
  1374  				} else if testcase.JobInfo.Job.Status.Pending > total-testcase.JobInfo.Job.Spec.MinAvailable {
  1375  					if jobInfo.Job.Status.State.Phase != v1alpha1.Pending {
  1376  						t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Pending, jobInfo.Job.Status.State.Phase, i)
  1377  					}
  1378  				} else {
  1379  					if jobInfo.Job.Status.State.Phase != v1alpha1.Running {
  1380  						t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Running, jobInfo.Job.Status.State.Phase, i)
  1381  					}
  1382  				}
  1383  			}
  1384  		})
  1385  	}
  1386  }
  1387  
  1388  func TestTerminatingState_Execute(t *testing.T) {
  1389  	namespace := "test"
  1390  
  1391  	testcases := []struct {
  1392  		Name        string
  1393  		JobInfo     *apis.JobInfo
  1394  		Action      busv1alpha1.Action
  1395  		ExpectedVal error
  1396  	}{
  1397  		{
  1398  			Name: "TerminatingState- With pod count not equal to zero",
  1399  			JobInfo: &apis.JobInfo{
  1400  				Namespace: namespace,
  1401  				Name:      "jobinfo1",
  1402  				Job: &v1alpha1.Job{
  1403  					ObjectMeta: metav1.ObjectMeta{
  1404  						Name:            "Job1",
  1405  						Namespace:       namespace,
  1406  						ResourceVersion: "100",
  1407  					},
  1408  					Status: v1alpha1.JobStatus{
  1409  						Running: 2,
  1410  						State: v1alpha1.JobState{
  1411  							Phase: v1alpha1.Terminating,
  1412  						},
  1413  					},
  1414  				},
  1415  				Pods: map[string]map[string]*v1.Pod{
  1416  					"task1": {
  1417  						"pod1": buildPod(namespace, "pod1", v1.PodRunning, nil),
  1418  						"pod2": buildPod(namespace, "pod2", v1.PodRunning, nil),
  1419  					},
  1420  				},
  1421  			},
  1422  			Action:      busv1alpha1.TerminateJobAction,
  1423  			ExpectedVal: nil,
  1424  		},
  1425  		{
  1426  			Name: "TerminatingState- With pod count not equal to zero",
  1427  			JobInfo: &apis.JobInfo{
  1428  				Namespace: namespace,
  1429  				Name:      "jobinfo1",
  1430  				Job: &v1alpha1.Job{
  1431  					ObjectMeta: metav1.ObjectMeta{
  1432  						Name:            "Job1",
  1433  						Namespace:       namespace,
  1434  						ResourceVersion: "100",
  1435  					},
  1436  					Status: v1alpha1.JobStatus{
  1437  						State: v1alpha1.JobState{
  1438  							Phase: v1alpha1.Terminating,
  1439  						},
  1440  					},
  1441  				},
  1442  			},
  1443  			Action:      busv1alpha1.TerminateJobAction,
  1444  			ExpectedVal: nil,
  1445  		},
  1446  	}
  1447  
  1448  	for i, testcase := range testcases {
  1449  		t.Run(testcase.Name, func(t *testing.T) {
  1450  			testState := state.NewState(testcase.JobInfo)
  1451  
  1452  			fakecontroller := newFakeController()
  1453  			state.KillJob = fakecontroller.killJob
  1454  
  1455  			_, err := fakecontroller.vcClient.BatchV1alpha1().Jobs(namespace).Create(context.TODO(), testcase.JobInfo.Job, metav1.CreateOptions{})
  1456  			if err != nil {
  1457  				t.Error("Error while creating Job")
  1458  			}
  1459  
  1460  			err = fakecontroller.cache.Add(testcase.JobInfo.Job)
  1461  			if err != nil {
  1462  				t.Error("Error while adding Job in cache")
  1463  			}
  1464  
  1465  			err = testState.Execute(testcase.Action)
  1466  			if err != nil {
  1467  				t.Errorf("Expected Error not to occur but got: %s", err)
  1468  			}
  1469  
  1470  			jobInfo, err := fakecontroller.cache.Get(fmt.Sprintf("%s/%s", testcase.JobInfo.Job.Namespace, testcase.JobInfo.Job.Name))
  1471  			if err != nil {
  1472  				t.Error("Error while retrieving value from Cache")
  1473  			}
  1474  
  1475  			if testcase.JobInfo.Job.Status.Running == 0 && testcase.JobInfo.Job.Status.Pending == 0 && testcase.JobInfo.Job.Status.Terminating == 0 {
  1476  
  1477  				if jobInfo.Job.Status.State.Phase != v1alpha1.Terminated {
  1478  					fmt.Println(jobInfo.Job.Status.State.Phase)
  1479  					t.Errorf("Expected Phase to be Terminated State in test case: %d", i)
  1480  				}
  1481  			} else {
  1482  				if jobInfo.Job.Status.State.Phase != v1alpha1.Terminating {
  1483  					t.Errorf("Expected Phase to be Terminating state in test case: %d", i)
  1484  				}
  1485  			}
  1486  		})
  1487  	}
  1488  }