sigs.k8s.io/cluster-api-provider-aws@v1.5.5/api/v1beta1/tags_test.go (about)

     1  /*
     2  Copyright 2021 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 v1beta1
    18  
    19  import (
    20  	"sort"
    21  	"strings"
    22  	"testing"
    23  
    24  	"github.com/google/go-cmp/cmp"
    25  	"k8s.io/apimachinery/pkg/util/validation/field"
    26  )
    27  
    28  func TestTags_Merge(t *testing.T) {
    29  	tests := []struct {
    30  		name     string
    31  		other    Tags
    32  		expected Tags
    33  	}{
    34  		{
    35  			name:  "nil other",
    36  			other: nil,
    37  			expected: Tags{
    38  				"a": "b",
    39  				"c": "d",
    40  			},
    41  		},
    42  		{
    43  			name:  "empty other",
    44  			other: Tags{},
    45  			expected: Tags{
    46  				"a": "b",
    47  				"c": "d",
    48  			},
    49  		},
    50  		{
    51  			name: "disjoint",
    52  			other: Tags{
    53  				"1": "2",
    54  				"3": "4",
    55  			},
    56  			expected: Tags{
    57  				"a": "b",
    58  				"c": "d",
    59  				"1": "2",
    60  				"3": "4",
    61  			},
    62  		},
    63  		{
    64  			name: "overlapping, other wins",
    65  			other: Tags{
    66  				"1": "2",
    67  				"3": "4",
    68  				"a": "hello",
    69  			},
    70  			expected: Tags{
    71  				"a": "hello",
    72  				"c": "d",
    73  				"1": "2",
    74  				"3": "4",
    75  			},
    76  		},
    77  	}
    78  	for _, tc := range tests {
    79  		t.Run(tc.name, func(t *testing.T) {
    80  			tags := Tags{
    81  				"a": "b",
    82  				"c": "d",
    83  			}
    84  
    85  			tags.Merge(tc.other)
    86  			if e, a := tc.expected, tags; !cmp.Equal(e, a) {
    87  				t.Errorf("expected %#v, got %#v", e, a)
    88  			}
    89  		})
    90  	}
    91  }
    92  
    93  func TestTags_Difference(t *testing.T) {
    94  	tests := []struct {
    95  		name     string
    96  		self     Tags
    97  		input    Tags
    98  		expected Tags
    99  	}{
   100  		{
   101  			name:     "self and input are nil",
   102  			self:     nil,
   103  			input:    nil,
   104  			expected: Tags{},
   105  		},
   106  		{
   107  			name: "input is nil",
   108  			self: Tags{
   109  				"a": "b",
   110  				"c": "d",
   111  			},
   112  			input: nil,
   113  			expected: Tags{
   114  				"a": "b",
   115  				"c": "d",
   116  			},
   117  		},
   118  		{
   119  			name: "similar input",
   120  			self: Tags{
   121  				"a": "b",
   122  				"c": "d",
   123  			},
   124  			input: Tags{
   125  				"a": "b",
   126  				"c": "d",
   127  			},
   128  			expected: Tags{},
   129  		},
   130  		{
   131  			name: "input with extra tags",
   132  			self: Tags{
   133  				"a": "b",
   134  				"c": "d",
   135  			},
   136  			input: Tags{
   137  				"a": "b",
   138  				"c": "d",
   139  				"e": "f",
   140  			},
   141  			expected: Tags{},
   142  		},
   143  		{
   144  			name: "same keys, different values",
   145  			self: Tags{
   146  				"a": "b",
   147  				"c": "d",
   148  			},
   149  			input: Tags{
   150  				"a": "b1",
   151  				"c": "d",
   152  				"e": "f",
   153  			},
   154  			expected: Tags{
   155  				"a": "b",
   156  			},
   157  		},
   158  	}
   159  	for _, tc := range tests {
   160  		t.Run(tc.name, func(t *testing.T) {
   161  			out := tc.self.Difference(tc.input)
   162  			if e, a := tc.expected, out; !cmp.Equal(e, a) {
   163  				t.Errorf("expected %#v, got %#v", e, a)
   164  			}
   165  		})
   166  	}
   167  }
   168  
   169  func TestTags_Validate(t *testing.T) {
   170  	tests := []struct {
   171  		name     string
   172  		self     Tags
   173  		expected []*field.Error
   174  	}{
   175  		{
   176  			name: "no errors",
   177  			self: Tags{
   178  				"validKey": "validValue",
   179  			},
   180  			expected: nil,
   181  		},
   182  		{
   183  			name: "key cannot be empty",
   184  			self: Tags{
   185  				"": "validValue",
   186  			},
   187  			expected: []*field.Error{
   188  				{
   189  					Type:     field.ErrorTypeInvalid,
   190  					Detail:   "key cannot be empty",
   191  					Field:    "spec.additionalTags",
   192  					BadValue: "",
   193  				},
   194  			},
   195  		},
   196  		{
   197  			name: "key cannot be empty - second element",
   198  			self: Tags{
   199  				"validKey": "validValue",
   200  				"":         "secondValidValue",
   201  			},
   202  			expected: []*field.Error{
   203  				{
   204  					Type:     field.ErrorTypeInvalid,
   205  					Detail:   "key cannot be empty",
   206  					Field:    "spec.additionalTags",
   207  					BadValue: "",
   208  				},
   209  			},
   210  		},
   211  		{
   212  			name: "key with 128 characters is accepted",
   213  			self: Tags{
   214  				strings.Repeat("CAPI", 32): "validValue",
   215  			},
   216  			expected: nil,
   217  		},
   218  		{
   219  			name: "key too long",
   220  			self: Tags{
   221  				strings.Repeat("CAPI", 33): "validValue",
   222  			},
   223  			expected: []*field.Error{
   224  				{
   225  					Type:     field.ErrorTypeInvalid,
   226  					Detail:   "key cannot be longer than 128 characters",
   227  					Field:    "spec.additionalTags",
   228  					BadValue: strings.Repeat("CAPI", 33),
   229  				},
   230  			},
   231  		},
   232  		{
   233  			name: "value too long",
   234  			self: Tags{
   235  				"validKey": strings.Repeat("CAPI", 65),
   236  			},
   237  			expected: []*field.Error{
   238  				{
   239  					Type:     field.ErrorTypeInvalid,
   240  					Detail:   "value cannot be longer than 256 characters",
   241  					Field:    "spec.additionalTags",
   242  					BadValue: strings.Repeat("CAPI", 65),
   243  				},
   244  			},
   245  		},
   246  		{
   247  			name: "multiple errors are appended",
   248  			self: Tags{
   249  				"validKey":                 strings.Repeat("CAPI", 65),
   250  				strings.Repeat("CAPI", 33): "validValue",
   251  				"":                         "thirdValidValue",
   252  			},
   253  			expected: []*field.Error{
   254  				{
   255  					Type:     field.ErrorTypeInvalid,
   256  					Detail:   "value cannot be longer than 256 characters",
   257  					Field:    "spec.additionalTags",
   258  					BadValue: strings.Repeat("CAPI", 65),
   259  				},
   260  				{
   261  					Type:     field.ErrorTypeInvalid,
   262  					Detail:   "key cannot be longer than 128 characters",
   263  					Field:    "spec.additionalTags",
   264  					BadValue: strings.Repeat("CAPI", 33),
   265  				},
   266  				{
   267  					Type:     field.ErrorTypeInvalid,
   268  					Detail:   "key cannot be empty",
   269  					Field:    "spec.additionalTags",
   270  					BadValue: "",
   271  				},
   272  			},
   273  		},
   274  		{
   275  			name: "key has aws: prefix",
   276  			self: Tags{
   277  				"aws:key": "validValue",
   278  			},
   279  			expected: []*field.Error{
   280  				{
   281  					Type:     field.ErrorTypeInvalid,
   282  					Detail:   "user created tag's key cannot have prefix aws:",
   283  					Field:    "spec.additionalTags",
   284  					BadValue: "aws:key",
   285  				},
   286  			},
   287  		},
   288  		{
   289  			name: "key has wrong characters",
   290  			self: Tags{
   291  				"wrong*key": "validValue",
   292  			},
   293  			expected: []*field.Error{
   294  				{
   295  					Type:     field.ErrorTypeInvalid,
   296  					Detail:   "key cannot have characters other than alphabets, numbers, spaces and _ . : / = + - @ .",
   297  					Field:    "spec.additionalTags",
   298  					BadValue: "wrong*key",
   299  				},
   300  			},
   301  		},
   302  		{
   303  			name: "value has wrong characters",
   304  			self: Tags{
   305  				"validKey": "wrong*value",
   306  			},
   307  			expected: []*field.Error{
   308  				{
   309  					Type:     field.ErrorTypeInvalid,
   310  					Detail:   "value cannot have characters other than alphabets, numbers, spaces and _ . : / = + - @ .",
   311  					Field:    "spec.additionalTags",
   312  					BadValue: "wrong*value",
   313  				},
   314  			},
   315  		},
   316  		{
   317  			name: "value and key both has wrong characters",
   318  			self: Tags{
   319  				"wrong*key": "wrong*value",
   320  			},
   321  			expected: []*field.Error{
   322  				{
   323  					Type:     field.ErrorTypeInvalid,
   324  					Detail:   "key cannot have characters other than alphabets, numbers, spaces and _ . : / = + - @ .",
   325  					Field:    "spec.additionalTags",
   326  					BadValue: "wrong*key",
   327  				},
   328  				{
   329  					Type:     field.ErrorTypeInvalid,
   330  					Detail:   "value cannot have characters other than alphabets, numbers, spaces and _ . : / = + - @ .",
   331  					Field:    "spec.additionalTags",
   332  					BadValue: "wrong*value",
   333  				},
   334  			},
   335  		},
   336  	}
   337  	for _, tc := range tests {
   338  		t.Run(tc.name, func(t *testing.T) {
   339  			out := tc.self.Validate()
   340  			sort.Slice(out, getSortFieldErrorsFunc(out))
   341  			sort.Slice(tc.expected, getSortFieldErrorsFunc(tc.expected))
   342  
   343  			if !cmp.Equal(out, tc.expected) {
   344  				t.Errorf("expected %+v, got %+v", tc.expected, out)
   345  			}
   346  		})
   347  	}
   348  }
   349  
   350  func getSortFieldErrorsFunc(errs []*field.Error) func(i, j int) bool {
   351  	return func(i, j int) bool {
   352  		if errs[i].Detail != errs[j].Detail {
   353  			return errs[i].Detail < errs[j].Detail
   354  		}
   355  		iBV, ok := errs[i].BadValue.(string)
   356  		if !ok {
   357  			panic("unexpected error converting BadValue to string")
   358  		}
   359  		jBV, ok := errs[j].BadValue.(string)
   360  		if !ok {
   361  			panic("unexpected error converting BadValue to string")
   362  		}
   363  		return iBV < jBV
   364  	}
   365  }