github.com/googleapis/api-linter@v1.65.2/lint/config_test.go (about) 1 // Copyright 2019 Google LLC 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 // https://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 package lint 16 17 import ( 18 "errors" 19 "os" 20 "path/filepath" 21 "reflect" 22 "strings" 23 "testing" 24 ) 25 26 func TestRuleConfigs_IsRuleEnabled(t *testing.T) { 27 enabled := true 28 disabled := false 29 30 tests := []struct { 31 name string 32 configs Configs 33 path string 34 rule string 35 want bool 36 }{ 37 {"EmptyConfig", nil, "a", "b", enabled}, 38 { 39 "NoConfigMatched_Enabled", 40 Configs{ 41 { 42 IncludedPaths: []string{"a.proto"}, 43 DisabledRules: []string{"testrule"}, 44 }, 45 }, 46 "b.proto", 47 "testrule", 48 enabled, 49 }, 50 { 51 "PathMatched_DisabledRulesNotMatch_Enabled", 52 Configs{ 53 { 54 IncludedPaths: []string{"a.proto"}, 55 DisabledRules: []string{"somerule"}, 56 }, 57 }, 58 "a.proto", 59 "testrule", 60 enabled, 61 }, 62 { 63 "PathExactMatched_DisabledRulesMatched_Disabled", 64 Configs{ 65 { 66 IncludedPaths: []string{"a.proto"}, 67 DisabledRules: []string{"somerule", "testrule"}, 68 }, 69 }, 70 "a.proto", 71 "testrule", 72 disabled, 73 }, 74 { 75 "PathExactMatched_DisabledRulesMatchedAll_Disabled", 76 Configs{ 77 { 78 IncludedPaths: []string{"a.proto"}, 79 DisabledRules: []string{"all"}, 80 }, 81 }, 82 "a.proto", 83 "testrule", 84 disabled, 85 }, 86 { 87 "PathDoubleStartMatched_DisabledRulesMatched_Disabled", 88 Configs{ 89 { 90 IncludedPaths: []string{"a/**/*.proto"}, 91 DisabledRules: []string{"somerule", "testrule"}, 92 }, 93 }, 94 "a/with/long/sub/dir/etc/ory/e.proto", 95 "testrule", 96 disabled, 97 }, 98 { 99 "PathMatched_DisabledRulesPrefixMatched_Disabled", 100 Configs{ 101 { 102 IncludedPaths: []string{"a/b/c.proto"}, 103 DisabledRules: []string{"parent"}, 104 }, 105 }, 106 "a/b/c.proto", 107 "parent::test_rule", 108 disabled, 109 }, 110 { 111 "PathMatched_DisabledRulesSuffixMatched_Disabled", 112 Configs{ 113 { 114 IncludedPaths: []string{"a/b/c.proto"}, 115 DisabledRules: []string{"child"}, 116 }, 117 }, 118 "a/b/c.proto", 119 "parent::child", 120 disabled, 121 }, 122 { 123 "PathMatched_DisabledRulesMiddleMatched_Disabled", 124 Configs{ 125 { 126 IncludedPaths: []string{"a/b/c.proto"}, 127 DisabledRules: []string{"middle"}, 128 }, 129 }, 130 "a/b/c.proto", 131 "parent::middle::child", 132 disabled, 133 }, 134 { 135 "EmptyIncludePath_ConfigMatched_DisabledRulesMatched_Disabled", 136 Configs{ 137 { 138 DisabledRules: []string{"testrule"}, 139 }, 140 }, 141 "a.proto", 142 "testrule", 143 disabled, 144 }, 145 { 146 "ExcludedPathMatch_ConfigNotMatched_DisabledRulesMatched_Enabled", 147 Configs{ 148 { 149 ExcludedPaths: []string{"a.proto"}, 150 DisabledRules: []string{"testrule"}, 151 }, 152 }, 153 "a.proto", 154 "testrule", 155 enabled, 156 }, 157 { 158 "TwoConfigs_Override_Enabled", 159 Configs{ 160 { 161 DisabledRules: []string{"testrule"}, 162 }, 163 { 164 EnabledRules: []string{"testrule::a"}, 165 }, 166 }, 167 "a.proto", 168 "testrule::a", 169 enabled, 170 }, 171 { 172 "TwoConfigs_Override_Disabled", 173 Configs{ 174 { 175 EnabledRules: []string{"testrule"}, 176 }, 177 { 178 DisabledRules: []string{"testrule::a"}, 179 }, 180 }, 181 "a.proto", 182 "testrule::a", 183 disabled, 184 }, 185 { 186 "TwoConfigs_DoubleEnable_Enabled", 187 Configs{ 188 { 189 EnabledRules: []string{"testrule"}, 190 }, 191 { 192 EnabledRules: []string{"testrule::a"}, 193 }, 194 }, 195 "a.proto", 196 "testrule::a", 197 enabled, 198 }, 199 { 200 "TwoConfigs_DoubleDisabled_Disabled", 201 Configs{ 202 { 203 DisabledRules: []string{"testrule"}, 204 }, 205 { 206 DisabledRules: []string{"testrule::a"}, 207 }, 208 }, 209 "a.proto", 210 "testrule::a", 211 disabled, 212 }, 213 { 214 "NoConfigMatched_DefaultDisabled", 215 Configs{ 216 { 217 IncludedPaths: []string{"a.proto"}, 218 DisabledRules: []string{"testrule"}, 219 }, 220 }, 221 "b.proto", 222 "cloud::25164::generic-fields", 223 disabled, 224 }, 225 { 226 "ConfigMatched_DefaultDisabled_Enabled", 227 Configs{ 228 { 229 IncludedPaths: []string{"a.proto"}, 230 EnabledRules: []string{"cloud"}, 231 }, 232 }, 233 "a.proto", 234 "cloud::25164::generic-fields", 235 enabled, 236 }, 237 } 238 for _, test := range tests { 239 t.Run(test.name, func(t *testing.T) { 240 got := test.configs.IsRuleEnabled(test.rule, test.path) 241 242 if got != test.want { 243 t.Errorf("IsRuleEnabled: got %t, but want %t", got, test.want) 244 } 245 }) 246 } 247 } 248 249 type errReader int 250 251 func (errReader) Read(p []byte) (int, error) { 252 return 0, errors.New("test error") 253 } 254 255 func TestReadConfigsJSONReaderError(t *testing.T) { 256 if _, err := ReadConfigsJSON(errReader(0)); err == nil { 257 t.Error("ReadConfigsJSON expects an error") 258 } 259 } 260 261 func TestReadConfigsJSONFormatError(t *testing.T) { 262 invalidJSON := ` 263 [ 264 { 265 "included_paths": ["path_a"], 266 "excluded_paths": ["path_b"], 267 "disabled_rules": ["rule_a", "rule_b"], 268 "enabled_rules": ["rule_c", "rule_d"] 269 } 270 ` 271 272 if _, err := ReadConfigsJSON(strings.NewReader(invalidJSON)); err == nil { 273 t.Error("ReadConfigsJSON expects an error") 274 } 275 } 276 277 func TestReadConfigsJSON(t *testing.T) { 278 content := ` 279 [ 280 { 281 "included_paths": ["path_a"], 282 "excluded_paths": ["path_b"], 283 "disabled_rules": ["rule_a", "rule_b"], 284 "enabled_rules": ["rule_c", "rule_d"] 285 } 286 ] 287 ` 288 289 configs, err := ReadConfigsJSON(strings.NewReader(content)) 290 if err != nil { 291 t.Errorf("ReadConfigsJSON returns error: %v", err) 292 } 293 294 expected := Configs{ 295 { 296 IncludedPaths: []string{"path_a"}, 297 ExcludedPaths: []string{"path_b"}, 298 DisabledRules: []string{"rule_a", "rule_b"}, 299 EnabledRules: []string{"rule_c", "rule_d"}, 300 }, 301 } 302 if !reflect.DeepEqual(configs, expected) { 303 t.Errorf("ReadConfigsJSON returns %v, but want %v", configs, expected) 304 } 305 } 306 307 func TestReadConfigsYAMLReaderError(t *testing.T) { 308 if _, err := ReadConfigsYAML(errReader(0)); err == nil { 309 t.Error("ReadConfigsYAML expects an error") 310 } 311 } 312 313 func TestReadConfigsYAMLFormatError(t *testing.T) { 314 invalidYAML := ` 315 [ 316 { 317 "included_paths": ["path_a"], 318 "excluded_paths": ["path_b"], 319 "disabled_rules": ["rule_a", "rule_b"], 320 "enabled_rules": ["rule_c", "rule_d"] 321 } 322 ` 323 324 if _, err := ReadConfigsYAML(strings.NewReader(invalidYAML)); err == nil { 325 t.Error("ReadConfigsYAML expects an error") 326 } 327 } 328 329 func TestReadConfigsYAML(t *testing.T) { 330 content := ` 331 --- 332 - included_paths: 333 - 'path_a' 334 excluded_paths: 335 - 'path_b' 336 disabled_rules: 337 - 'rule_a' 338 - 'rule_b' 339 enabled_rules: 340 - 'rule_c' 341 - 'rule_d' 342 ` 343 344 configs, err := ReadConfigsYAML(strings.NewReader(content)) 345 if err != nil { 346 t.Errorf("ReadConfigsYAML returns error: %v", err) 347 } 348 349 expected := Configs{ 350 { 351 IncludedPaths: []string{"path_a"}, 352 ExcludedPaths: []string{"path_b"}, 353 DisabledRules: []string{"rule_a", "rule_b"}, 354 EnabledRules: []string{"rule_c", "rule_d"}, 355 }, 356 } 357 if !reflect.DeepEqual(configs, expected) { 358 t.Errorf("ReadConfigsYAML returns %v, but want %v", configs, expected) 359 } 360 } 361 362 func TestReadConfigsFromFile(t *testing.T) { 363 expectedConfigs := Configs{ 364 { 365 IncludedPaths: []string{"path_a"}, 366 ExcludedPaths: []string{"path_b"}, 367 DisabledRules: []string{"rule_a", "rule_b"}, 368 EnabledRules: []string{"rule_c", "rule_d"}, 369 }, 370 } 371 372 jsonConfigsText := ` 373 [ 374 { 375 "included_paths": ["path_a"], 376 "excluded_paths": ["path_b"], 377 "disabled_rules": ["rule_a", "rule_b"], 378 "enabled_rules": ["rule_c", "rule_d"] 379 } 380 ] 381 ` 382 jsonConfigsFile := createTempFile(t, "test.json", jsonConfigsText) 383 defer os.Remove(jsonConfigsFile) 384 385 yamlConfigsText := ` 386 --- 387 - included_paths: 388 - 'path_a' 389 excluded_paths: 390 - 'path_b' 391 disabled_rules: 392 - 'rule_a' 393 - 'rule_b' 394 enabled_rules: 395 - 'rule_c' 396 - 'rule_d' 397 ` 398 yamlConfigsFile := createTempFile(t, "test.yaml", yamlConfigsText) 399 defer os.Remove(yamlConfigsFile) 400 401 tests := []struct { 402 name string 403 filePath string 404 configs Configs 405 hasErr bool 406 }{ 407 { 408 "JSON file", 409 jsonConfigsFile, 410 expectedConfigs, 411 false, 412 }, 413 { 414 "YAML file", 415 yamlConfigsFile, 416 expectedConfigs, 417 false, 418 }, 419 { 420 "Invalid file extension", 421 "test.abc", 422 nil, 423 true, 424 }, 425 { 426 "File not existed", 427 "not-existed-file.json", 428 nil, 429 true, 430 }, 431 } 432 for _, test := range tests { 433 t.Run(test.name, func(t *testing.T) { 434 configs, err := ReadConfigsFromFile(test.filePath) 435 if (err != nil) != test.hasErr { 436 t.Errorf("ReadConfigsFromFile got error %v, but want %v", err, test.hasErr) 437 } 438 if err != nil { 439 if !reflect.DeepEqual(configs, test.configs) { 440 t.Errorf("ReadConfigsFromFile got configs %v, but want %v", configs, test.configs) 441 } 442 } 443 }) 444 } 445 } 446 447 func createTempFile(t *testing.T, name, content string) string { 448 dir, err := os.MkdirTemp("", "config_tests") 449 if err != nil { 450 t.Fatal(err) 451 } 452 filePath := filepath.Join(dir, name) 453 if err := os.WriteFile(filePath, []byte(content), 0o644); err != nil { 454 t.Fatal(err) 455 } 456 return filePath 457 }