...

Source file src/github.com/chaos-mesh/chaos-mesh/api/v1alpha1/workflow_webhook_test.go

Documentation: github.com/chaos-mesh/chaos-mesh/api/v1alpha1

     1  // Copyright 2021 Chaos Mesh Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  // http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  
    16  package v1alpha1
    17  
    18  import (
    19  	"fmt"
    20  	"reflect"
    21  	"testing"
    22  	"time"
    23  
    24  	corev1 "k8s.io/api/core/v1"
    25  	"k8s.io/apimachinery/pkg/util/validation/field"
    26  )
    27  
    28  func Test_entryMustExists(t *testing.T) {
    29  	entryPath := field.NewPath("spec", "entry")
    30  
    31  	type args struct {
    32  		path      *field.Path
    33  		entry     string
    34  		templates []Template
    35  	}
    36  	tests := []struct {
    37  		name string
    38  		args args
    39  		want field.ErrorList
    40  	}{
    41  		{
    42  			name: "entry is empty",
    43  			args: args{
    44  				path:      entryPath,
    45  				entry:     "",
    46  				templates: nil,
    47  			},
    48  			want: field.ErrorList{
    49  				field.Required(entryPath, "the entry of workflow is required"),
    50  				field.Invalid(entryPath, "", fmt.Sprintf("can not find a template with name %s", "")),
    51  			},
    52  		}, {
    53  			name: "entry does not exist in templates",
    54  			args: args{
    55  				path:  entryPath,
    56  				entry: "entry",
    57  				templates: []Template{
    58  					{
    59  						Name: "whatever is not entry",
    60  						Type: TypeSuspend,
    61  					},
    62  				},
    63  			},
    64  			want: field.ErrorList{
    65  				field.Invalid(entryPath, "entry", fmt.Sprintf("can not find a template with name %s", "entry")),
    66  			},
    67  		},
    68  	}
    69  	for _, tt := range tests {
    70  		t.Run(tt.name, func(t *testing.T) {
    71  			if got := entryMustExists(tt.args.path, tt.args.entry, tt.args.templates); !reflect.DeepEqual(got, tt.want) {
    72  				t.Errorf("entryMustExists() = %v, want %v", got, tt.want)
    73  			}
    74  		})
    75  	}
    76  }
    77  
    78  func Test_validateTemplates(t *testing.T) {
    79  	templatesPath := field.NewPath("spec", "templates")
    80  	var nilTemplates []Template
    81  	type args struct {
    82  		path      *field.Path
    83  		templates []Template
    84  	}
    85  	tests := []struct {
    86  		name string
    87  		args args
    88  		want field.ErrorList
    89  	}{
    90  		{
    91  			name: "templates is nil",
    92  			args: args{
    93  				path:      templatesPath,
    94  				templates: nil,
    95  			},
    96  			want: field.ErrorList{
    97  				field.Invalid(templatesPath, nilTemplates, "templates in workflow could not be empty"),
    98  			},
    99  		}, {
   100  			name: "templates is empty",
   101  			args: args{
   102  				path:      templatesPath,
   103  				templates: []Template{},
   104  			},
   105  			want: field.ErrorList{
   106  				field.Invalid(templatesPath, []Template{}, "templates in workflow could not be empty"),
   107  			},
   108  		},
   109  	}
   110  	for _, tt := range tests {
   111  		t.Run(tt.name, func(t *testing.T) {
   112  			if got := validateTemplates(tt.args.path, tt.args.templates); !reflect.DeepEqual(got, tt.want) {
   113  				t.Errorf("validateTemplates() = %v, want %v", got, tt.want)
   114  			}
   115  		})
   116  	}
   117  }
   118  
   119  func Test_shouldBeNoTask(t *testing.T) {
   120  	templatePath := field.NewPath("spec", "templates").Index(0)
   121  	mockTask := Task{
   122  		Container: &corev1.Container{Name: "fake-container"},
   123  	}
   124  	type args struct {
   125  		path     *field.Path
   126  		template Template
   127  	}
   128  	tests := []struct {
   129  		name string
   130  		args args
   131  		want field.ErrorList
   132  	}{
   133  		{
   134  			name: "contains unexpected task",
   135  			args: args{
   136  				path: templatePath,
   137  				template: Template{
   138  					Task: &mockTask,
   139  				},
   140  			},
   141  			want: field.ErrorList{
   142  				field.Invalid(templatePath, &mockTask, "this template should not contain Task"),
   143  			},
   144  		}, {
   145  			name: "does not contain task",
   146  			args: args{
   147  				path:     templatePath,
   148  				template: Template{},
   149  			},
   150  			want: nil,
   151  		},
   152  	}
   153  	for _, tt := range tests {
   154  		t.Run(tt.name, func(t *testing.T) {
   155  			if got := shouldBeNoTask(tt.args.path, tt.args.template); !reflect.DeepEqual(got, tt.want) {
   156  				t.Errorf("shouldBeNoTask() = %v, want %v", got, tt.want)
   157  			}
   158  		})
   159  	}
   160  }
   161  
   162  func Test_shouldBeNoChildren(t *testing.T) {
   163  	templatePath := field.NewPath("spec", "templates").Index(0)
   164  	mockChildren := []string{"child-a", "child-b"}
   165  	type args struct {
   166  		path     *field.Path
   167  		template Template
   168  	}
   169  	tests := []struct {
   170  		name string
   171  		args args
   172  		want field.ErrorList
   173  	}{
   174  		{
   175  			name: "contains unexpected children",
   176  			args: args{
   177  				path: templatePath,
   178  				template: Template{
   179  					Children: mockChildren,
   180  				},
   181  			},
   182  			want: field.ErrorList{
   183  				field.Invalid(templatePath, mockChildren, "this template should not contain Children"),
   184  			},
   185  		}, {
   186  			name: "does not contain children",
   187  			args: args{
   188  				path:     templatePath,
   189  				template: Template{},
   190  			},
   191  			want: nil,
   192  		}, {
   193  			name: "empty array is also valid",
   194  			args: args{
   195  				path: templatePath,
   196  				template: Template{
   197  					Children: []string{},
   198  				},
   199  			},
   200  			want: nil,
   201  		},
   202  	}
   203  	for _, tt := range tests {
   204  		t.Run(tt.name, func(t *testing.T) {
   205  			if got := shouldBeNoChildren(tt.args.path, tt.args.template); !reflect.DeepEqual(got, tt.want) {
   206  				t.Errorf("shouldBeNoChildren() = %v, want %v", got, tt.want)
   207  			}
   208  		})
   209  	}
   210  }
   211  
   212  func Test_shouldBeNoConditionalBranches(t *testing.T) {
   213  	templatePath := field.NewPath("spec", "templates").Index(0)
   214  	mockConditionalBranches := []ConditionalBranch{
   215  		{Target: "", Expression: ""},
   216  	}
   217  	type args struct {
   218  		path     *field.Path
   219  		template Template
   220  	}
   221  	tests := []struct {
   222  		name string
   223  		args args
   224  		want field.ErrorList
   225  	}{
   226  		{
   227  			name: "contains unexpected conditional branches",
   228  			args: args{
   229  				path: templatePath,
   230  				template: Template{
   231  					ConditionalBranches: mockConditionalBranches,
   232  				},
   233  			},
   234  			want: field.ErrorList{
   235  				field.Invalid(templatePath, mockConditionalBranches, "this template should not contain ConditionalBranches"),
   236  			},
   237  		}, {
   238  			name: "does not contain conditional branches",
   239  			args: args{
   240  				path:     templatePath,
   241  				template: Template{},
   242  			},
   243  			want: nil,
   244  		}, {
   245  			name: "empty array is also valid",
   246  			args: args{
   247  				path: templatePath,
   248  				template: Template{
   249  					ConditionalBranches: []ConditionalBranch{},
   250  				},
   251  			},
   252  			want: nil,
   253  		},
   254  	}
   255  	for _, tt := range tests {
   256  		t.Run(tt.name, func(t *testing.T) {
   257  			if got := shouldBeNoConditionalBranches(tt.args.path, tt.args.template); !reflect.DeepEqual(got, tt.want) {
   258  				t.Errorf("shouldBeNoConditionalBranches() = %v, want %v", got, tt.want)
   259  			}
   260  		})
   261  	}
   262  }
   263  
   264  func Test_shouldBeNoEmbedChaos(t *testing.T) {
   265  	templatePath := field.NewPath("spec", "templates").Index(0)
   266  	type args struct {
   267  		path     *field.Path
   268  		template Template
   269  	}
   270  	mockedEmbedChaos := &EmbedChaos{
   271  		PodChaos: &PodChaosSpec{
   272  			ContainerSelector: ContainerSelector{
   273  				PodSelector: PodSelector{
   274  					Selector: PodSelectorSpec{
   275  						GenericSelectorSpec: GenericSelectorSpec{
   276  							Namespaces: []string{"default"},
   277  						},
   278  					},
   279  				},
   280  			},
   281  			Action: PodKillAction,
   282  		},
   283  	}
   284  	tests := []struct {
   285  		name string
   286  		args args
   287  		want field.ErrorList
   288  	}{
   289  		{
   290  			name: "unexpected embedded chaos",
   291  			args: args{
   292  				path: templatePath,
   293  				template: Template{
   294  					EmbedChaos: mockedEmbedChaos,
   295  				},
   296  			},
   297  			want: field.ErrorList{
   298  				field.Invalid(templatePath, mockedEmbedChaos, "this template should not contain any Chaos"),
   299  			},
   300  		}, {
   301  			name: "only nil embedded chaos is valid",
   302  			args: args{
   303  				path: templatePath,
   304  				template: Template{
   305  					EmbedChaos: nil,
   306  				},
   307  			},
   308  			want: nil,
   309  		}, {
   310  			name: "an embedded chaos with all the nil fields is also INVALID",
   311  			args: args{
   312  				path: templatePath,
   313  				template: Template{
   314  					EmbedChaos: &EmbedChaos{},
   315  				},
   316  			},
   317  			want: field.ErrorList{
   318  				field.Invalid(templatePath, &EmbedChaos{}, "this template should not contain any Chaos"),
   319  			},
   320  		},
   321  	}
   322  	for _, tt := range tests {
   323  		t.Run(tt.name, func(t *testing.T) {
   324  			if got := shouldBeNoEmbedChaos(tt.args.path, tt.args.template); !reflect.DeepEqual(got, tt.want) {
   325  				t.Errorf("shouldBeNoEmbedChaos() = %v, want %v", got, tt.want)
   326  			}
   327  		})
   328  	}
   329  }
   330  
   331  func Test_shouldBeNoSchedule(t *testing.T) {
   332  	templatePath := field.NewPath("spec", "templates").Index(0)
   333  	type args struct {
   334  		path     *field.Path
   335  		template Template
   336  	}
   337  	mockedSchedule := &ChaosOnlyScheduleSpec{
   338  		Type: ScheduleTypePodChaos,
   339  	}
   340  	tests := []struct {
   341  		name string
   342  		args args
   343  		want field.ErrorList
   344  	}{
   345  		{
   346  			name: "unexpected schedule",
   347  			args: args{
   348  				path: templatePath,
   349  				template: Template{
   350  					Schedule: mockedSchedule,
   351  				},
   352  			},
   353  			want: field.ErrorList{
   354  				field.Invalid(templatePath, mockedSchedule, "this template should not contain Schedule"),
   355  			},
   356  		}, {
   357  			name: "no schedule",
   358  			args: args{
   359  				path: templatePath,
   360  				template: Template{
   361  					Schedule: nil,
   362  				},
   363  			},
   364  			want: nil,
   365  		},
   366  	}
   367  	for _, tt := range tests {
   368  		t.Run(tt.name, func(t *testing.T) {
   369  			if got := shouldBeNoSchedule(tt.args.path, tt.args.template); !reflect.DeepEqual(got, tt.want) {
   370  				t.Errorf("shouldBeNoSchedule() = %v, want %v", got, tt.want)
   371  			}
   372  		})
   373  	}
   374  }
   375  
   376  func Test_namesCouldNotBeDuplicated(t *testing.T) {
   377  	templatesPath := field.NewPath("spec", "templates")
   378  	type args struct {
   379  		templatesPath *field.Path
   380  		names         []string
   381  	}
   382  	tests := []struct {
   383  		name string
   384  		args args
   385  		want field.ErrorList
   386  	}{
   387  		{
   388  			name: "names could not be duplicated",
   389  			args: args{
   390  				templatesPath: templatesPath,
   391  				names:         []string{"template-a", "template-b", "template-c", "template-a", "template-b", "template-d"},
   392  			},
   393  			want: field.ErrorList{
   394  				field.Invalid(templatesPath, "", fmt.Sprintf("template name must be unique, duplicated names: %s", []string{"template-a", "template-b"})),
   395  			},
   396  		}, {
   397  			name: "names could not be duplicated",
   398  			args: args{
   399  				templatesPath: templatesPath,
   400  				names:         []string{"template-a", "template-b", "template-c", "template-d"},
   401  			},
   402  			want: nil,
   403  		},
   404  	}
   405  	for _, tt := range tests {
   406  		t.Run(tt.name, func(t *testing.T) {
   407  			if got := namesCouldNotBeDuplicated(tt.args.templatesPath, tt.args.names); !reflect.DeepEqual(got, tt.want) {
   408  				t.Errorf("namesCouldNotBeDuplicated() = %v, want %v", got, tt.want)
   409  			}
   410  		})
   411  	}
   412  }
   413  
   414  func Test_shouldNotSetupDurationInTheChaos(t *testing.T) {
   415  	templatesPath := field.NewPath("spec", "templates")
   416  	duration20sString := "20s"
   417  	duration20s := 20 * time.Second
   418  
   419  	type args struct {
   420  		path     *field.Path
   421  		template Template
   422  	}
   423  	tests := []struct {
   424  		name string
   425  		args args
   426  		want field.ErrorList
   427  	}{
   428  		{
   429  			name: "should return error when embed chaos is not set",
   430  			args: args{
   431  				path: templatesPath,
   432  				template: Template{
   433  					Name:       "invalid-pod-chaos",
   434  					Type:       TypePodChaos,
   435  					EmbedChaos: nil,
   436  				},
   437  			},
   438  			want: field.ErrorList{
   439  				field.Invalid(templatesPath.Child(string(TypePodChaos)), nil, fmt.Sprintf("the value of chaos %s is required", string(TypePodChaos))),
   440  			},
   441  		},
   442  		{
   443  			name: "should not set duration in the chaos",
   444  			args: args{
   445  				path: templatesPath,
   446  				template: Template{
   447  					Name: "invalid-pod-chaos",
   448  					Type: TypePodChaos,
   449  					EmbedChaos: &EmbedChaos{
   450  						PodChaos: &PodChaosSpec{
   451  							Duration: &duration20sString,
   452  						},
   453  					},
   454  				},
   455  			},
   456  			want: field.ErrorList{
   457  				field.Invalid(templatesPath, &duration20s, "should not define duration in chaos when using Workflow, use Template#Deadline instead."),
   458  			},
   459  		}, {
   460  			name: "should set duration in the template",
   461  			args: args{
   462  				path: templatesPath,
   463  				template: Template{
   464  					Name:     "invalid-pod-chaos",
   465  					Type:     TypePodChaos,
   466  					Deadline: &duration20sString,
   467  					EmbedChaos: &EmbedChaos{
   468  						PodChaos: &PodChaosSpec{},
   469  					},
   470  				},
   471  			},
   472  			want: nil,
   473  		},
   474  	}
   475  	for _, tt := range tests {
   476  		t.Run(tt.name, func(t *testing.T) {
   477  			if got := shouldNotSetupDurationInTheChaos(tt.args.path, tt.args.template); !reflect.DeepEqual(got, tt.want) {
   478  				t.Errorf("shouldNotSetupDurationInTheChaos() = %v, want %v", got, tt.want)
   479  			}
   480  		})
   481  	}
   482  }
   483