1/* 2Copyright 2018 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package rules 18 19import ( 20 "reflect" 21 "testing" 22 23 "k8s.io/gengo/types" 24) 25 26func TestNamesMatch(t *testing.T) { 27 tcs := []struct { 28 // name of test case 29 name string 30 t *types.Type 31 32 // expected list of violation fields 33 expected []string 34 }{ 35 // The comments are in format of {goName, jsonName, match}, 36 // {"PodSpec", "podSpec", true}, 37 { 38 name: "simple", 39 t: &types.Type{ 40 Kind: types.Struct, 41 Members: []types.Member{ 42 types.Member{ 43 Name: "PodSpec", 44 Tags: `json:"podSpec"`, 45 }, 46 }, 47 }, 48 expected: []string{}, 49 }, 50 // {"PodSpec", "podSpec", true}, 51 { 52 name: "multiple_json_tags", 53 t: &types.Type{ 54 Kind: types.Struct, 55 Members: []types.Member{ 56 types.Member{ 57 Name: "PodSpec", 58 Tags: `json:"podSpec,omitempty"`, 59 }, 60 }, 61 }, 62 expected: []string{}, 63 }, 64 // {"PodSpec", "podSpec", true}, 65 { 66 name: "protobuf_tag", 67 t: &types.Type{ 68 Kind: types.Struct, 69 Members: []types.Member{ 70 types.Member{ 71 Name: "PodSpec", 72 Tags: `json:"podSpec,omitempty" protobuf:"bytes,1,opt,name=podSpec"`, 73 }, 74 }, 75 }, 76 expected: []string{}, 77 }, 78 // {"", "podSpec", false}, 79 { 80 name: "empty", 81 t: &types.Type{ 82 Kind: types.Struct, 83 Members: []types.Member{ 84 types.Member{ 85 Name: "", 86 Tags: `json:"podSpec"`, 87 }, 88 }, 89 }, 90 expected: []string{""}, 91 }, 92 // {"PodSpec", "PodSpec", false}, 93 { 94 name: "CamelCase_CamelCase", 95 t: &types.Type{ 96 Kind: types.Struct, 97 Members: []types.Member{ 98 types.Member{ 99 Name: "PodSpec", 100 Tags: `json:"PodSpec"`, 101 }, 102 }, 103 }, 104 expected: []string{"PodSpec"}, 105 }, 106 // {"podSpec", "podSpec", false}, 107 { 108 name: "camelCase_camelCase", 109 t: &types.Type{ 110 Kind: types.Struct, 111 Members: []types.Member{ 112 types.Member{ 113 Name: "podSpec", 114 Tags: `json:"podSpec"`, 115 }, 116 }, 117 }, 118 expected: []string{"podSpec"}, 119 }, 120 // {"PodSpec", "spec", false}, 121 { 122 name: "short_json_name", 123 t: &types.Type{ 124 Kind: types.Struct, 125 Members: []types.Member{ 126 types.Member{ 127 Name: "PodSpec", 128 Tags: `json:"spec"`, 129 }, 130 }, 131 }, 132 expected: []string{"PodSpec"}, 133 }, 134 // {"Spec", "podSpec", false}, 135 { 136 name: "long_json_name", 137 t: &types.Type{ 138 Kind: types.Struct, 139 Members: []types.Member{ 140 types.Member{ 141 Name: "Spec", 142 Tags: `json:"podSpec"`, 143 }, 144 }, 145 }, 146 expected: []string{"Spec"}, 147 }, 148 // {"JSONSpec", "jsonSpec", true}, 149 { 150 name: "acronym", 151 t: &types.Type{ 152 Kind: types.Struct, 153 Members: []types.Member{ 154 types.Member{ 155 Name: "JSONSpec", 156 Tags: `json:"jsonSpec"`, 157 }, 158 }, 159 }, 160 expected: []string{}, 161 }, 162 // {"JSONSpec", "jsonspec", false}, 163 { 164 name: "acronym_invalid", 165 t: &types.Type{ 166 Kind: types.Struct, 167 Members: []types.Member{ 168 types.Member{ 169 Name: "JSONSpec", 170 Tags: `json:"jsonspec"`, 171 }, 172 }, 173 }, 174 expected: []string{"JSONSpec"}, 175 }, 176 // {"HTTPJSONSpec", "httpJSONSpec", true}, 177 { 178 name: "multiple_acronym", 179 t: &types.Type{ 180 Kind: types.Struct, 181 Members: []types.Member{ 182 types.Member{ 183 Name: "HTTPJSONSpec", 184 Tags: `json:"httpJSONSpec"`, 185 }, 186 }, 187 }, 188 expected: []string{}, 189 }, 190 // // NOTE: this validator cannot tell two sequential all-capital words from one word, 191 // // therefore the case below is also considered matched. 192 // {"HTTPJSONSpec", "httpjsonSpec", true}, 193 { 194 name: "multiple_acronym_as_one", 195 t: &types.Type{ 196 Kind: types.Struct, 197 Members: []types.Member{ 198 types.Member{ 199 Name: "HTTPJSONSpec", 200 Tags: `json:"httpjsonSpec"`, 201 }, 202 }, 203 }, 204 expected: []string{}, 205 }, 206 // NOTE: JSON tags in jsonTagBlacklist should skip evaluation 207 { 208 name: "blacklist_tag_dash", 209 t: &types.Type{ 210 Kind: types.Struct, 211 Members: []types.Member{ 212 types.Member{ 213 Name: "podSpec", 214 Tags: `json:"-"`, 215 }, 216 }, 217 }, 218 expected: []string{}, 219 }, 220 // {"PodSpec", "-", false}, 221 { 222 name: "invalid_json_name_dash", 223 t: &types.Type{ 224 Kind: types.Struct, 225 Members: []types.Member{ 226 types.Member{ 227 Name: "PodSpec", 228 Tags: `json:"-,"`, 229 }, 230 }, 231 }, 232 expected: []string{"PodSpec"}, 233 }, 234 // NOTE: JSON names in jsonNameBlacklist should skip evaluation 235 // {"", "", true}, 236 { 237 name: "unspecified", 238 t: &types.Type{ 239 Kind: types.Struct, 240 Members: []types.Member{ 241 types.Member{ 242 Name: "", 243 Tags: `json:""`, 244 }, 245 }, 246 }, 247 expected: []string{}, 248 }, 249 // {"podSpec", "", true}, 250 { 251 name: "blacklist_empty", 252 t: &types.Type{ 253 Kind: types.Struct, 254 Members: []types.Member{ 255 types.Member{ 256 Name: "podSpec", 257 Tags: `json:""`, 258 }, 259 }, 260 }, 261 expected: []string{}, 262 }, 263 // {"podSpec", "metadata", true}, 264 { 265 name: "blacklist_metadata", 266 t: &types.Type{ 267 Kind: types.Struct, 268 Members: []types.Member{ 269 types.Member{ 270 Name: "podSpec", 271 Tags: `json:"metadata"`, 272 }, 273 }, 274 }, 275 expected: []string{}, 276 }, 277 { 278 name: "non_struct", 279 t: &types.Type{ 280 Kind: types.Map, 281 }, 282 expected: []string{}, 283 }, 284 { 285 name: "no_json_tag", 286 t: &types.Type{ 287 Kind: types.Struct, 288 Members: []types.Member{ 289 types.Member{ 290 Name: "PodSpec", 291 Tags: `podSpec`, 292 }, 293 }, 294 }, 295 expected: []string{"PodSpec"}, 296 }, 297 // NOTE: this is to expand test coverage 298 // {"S", "s", true}, 299 { 300 name: "single_character", 301 t: &types.Type{ 302 Kind: types.Struct, 303 Members: []types.Member{ 304 types.Member{ 305 Name: "S", 306 Tags: `json:"s"`, 307 }, 308 }, 309 }, 310 expected: []string{}, 311 }, 312 // NOTE: names with disallowed substrings should fail evaluation 313 // {"Pod-Spec", "pod-Spec", false}, 314 { 315 name: "disallowed_substring_dash", 316 t: &types.Type{ 317 Kind: types.Struct, 318 Members: []types.Member{ 319 types.Member{ 320 Name: "Pod-Spec", 321 Tags: `json:"pod-Spec"`, 322 }, 323 }, 324 }, 325 expected: []string{"Pod-Spec"}, 326 }, 327 // {"Pod_Spec", "pod_Spec", false}, 328 { 329 name: "disallowed_substring_underscore", 330 t: &types.Type{ 331 Kind: types.Struct, 332 Members: []types.Member{ 333 types.Member{ 334 Name: "Pod_Spec", 335 Tags: `json:"pod_Spec"`, 336 }, 337 }, 338 }, 339 expected: []string{"Pod_Spec"}, 340 }, 341 } 342 343 n := &NamesMatch{} 344 for _, tc := range tcs { 345 if violations, _ := n.Validate(tc.t); !reflect.DeepEqual(violations, tc.expected) { 346 t.Errorf("unexpected validation result: test name %v, want: %v, got: %v", 347 tc.name, tc.expected, violations) 348 } 349 } 350} 351 352// TestRuleName tests the Name of API rule. This is to expand test coverage 353func TestRuleName(t *testing.T) { 354 ruleName := "names_match" 355 n := &NamesMatch{} 356 if n.Name() != ruleName { 357 t.Errorf("unexpected API rule name: want: %v, got: %v", ruleName, n.Name()) 358 } 359} 360