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 }