istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/validation/virtualservice_test.go (about) 1 // Copyright 2010 Istio 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 package validation 16 17 import ( 18 "testing" 19 20 "google.golang.org/protobuf/proto" 21 "google.golang.org/protobuf/types/known/wrapperspb" 22 23 networking "istio.io/api/networking/v1alpha3" 24 "istio.io/istio/pkg/config" 25 ) 26 27 func TestValidateChainingVirtualService(t *testing.T) { 28 testCases := []struct { 29 name string 30 in proto.Message 31 valid bool 32 }{ 33 { 34 name: "root simple", 35 in: &networking.VirtualService{ 36 Hosts: []string{"foo.bar"}, 37 Gateways: []string{"test-gateway"}, 38 Http: []*networking.HTTPRoute{{ 39 Delegate: &networking.Delegate{ 40 Name: "test", 41 Namespace: "test", 42 }, 43 }}, 44 }, 45 valid: true, 46 }, 47 { 48 name: "root applies to sidecar with delegate", 49 in: &networking.VirtualService{ 50 Hosts: []string{"foo.bar"}, 51 Http: []*networking.HTTPRoute{{ 52 Delegate: &networking.Delegate{ 53 Name: "test", 54 Namespace: "test", 55 }, 56 }}, 57 }, 58 valid: true, 59 }, 60 { 61 name: "root with delegate and destination in one route", 62 in: &networking.VirtualService{ 63 Hosts: []string{"foo.bar"}, 64 Http: []*networking.HTTPRoute{{ 65 Delegate: &networking.Delegate{ 66 Name: "test", 67 Namespace: "test", 68 }, 69 Route: []*networking.HTTPRouteDestination{{ 70 Destination: &networking.Destination{Host: "foo.baz"}, 71 }}, 72 }}, 73 }, 74 valid: false, 75 }, 76 { 77 name: "delegate with no destination", 78 in: &networking.VirtualService{ 79 Hosts: []string{}, 80 Http: []*networking.HTTPRoute{{}}, 81 }, 82 valid: false, 83 }, 84 { 85 name: "delegate with delegate", 86 in: &networking.VirtualService{ 87 Hosts: []string{}, 88 Http: []*networking.HTTPRoute{ 89 { 90 Delegate: &networking.Delegate{ 91 Name: "test", 92 Namespace: "test", 93 }, 94 }, 95 }, 96 }, 97 valid: false, 98 }, 99 { 100 name: "delegate with TCP route", 101 in: &networking.VirtualService{ 102 Hosts: []string{}, 103 Tcp: []*networking.TCPRoute{{ 104 Route: []*networking.RouteDestination{{ 105 Destination: &networking.Destination{Host: "foo.baz"}, 106 }}, 107 }}, 108 }, 109 valid: false, 110 }, 111 { 112 name: "delegate with gateway", 113 in: &networking.VirtualService{ 114 Hosts: []string{}, 115 Gateways: []string{"test"}, 116 Http: []*networking.HTTPRoute{{ 117 Route: []*networking.HTTPRouteDestination{{ 118 Destination: &networking.Destination{Host: "foo.baz"}, 119 }}, 120 }}, 121 }, 122 valid: false, 123 }, 124 { 125 name: "delegate with sourceNamespace", 126 in: &networking.VirtualService{ 127 Hosts: []string{}, 128 Http: []*networking.HTTPRoute{{ 129 Match: []*networking.HTTPMatchRequest{ 130 { 131 SourceNamespace: "test", 132 }, 133 }, 134 Route: []*networking.HTTPRouteDestination{{ 135 Destination: &networking.Destination{Host: "foo.baz"}, 136 }}, 137 }}, 138 }, 139 valid: true, 140 }, 141 } 142 143 for _, tc := range testCases { 144 t.Run(tc.name, func(t *testing.T) { 145 if _, err := ValidateVirtualService(config.Config{Spec: tc.in}); (err == nil) != tc.valid { 146 t.Fatalf("got valid=%v but wanted valid=%v: %v", err == nil, tc.valid, err) 147 } 148 }) 149 } 150 } 151 152 func TestValidateRootHTTPRoute(t *testing.T) { 153 testCases := []struct { 154 name string 155 route *networking.HTTPRoute 156 valid bool 157 }{ 158 { 159 name: "simple", 160 route: &networking.HTTPRoute{ 161 Delegate: &networking.Delegate{ 162 Name: "test", 163 Namespace: "test", 164 }, 165 }, 166 valid: true, 167 }, 168 { 169 name: "route and delegate conflict", 170 route: &networking.HTTPRoute{ 171 Route: []*networking.HTTPRouteDestination{{ 172 Destination: &networking.Destination{Host: "foo.baz"}, 173 }}, 174 Delegate: &networking.Delegate{ 175 Name: "test", 176 Namespace: "test", 177 }, 178 }, 179 valid: false, 180 }, 181 { 182 name: "redirect and delegate conflict", 183 route: &networking.HTTPRoute{ 184 Redirect: &networking.HTTPRedirect{ 185 Uri: "/lerp", 186 Authority: "foo.biz", 187 }, 188 Delegate: &networking.Delegate{ 189 Name: "test", 190 Namespace: "test", 191 }, 192 }, 193 valid: false, 194 }, 195 { 196 name: "null header match", route: &networking.HTTPRoute{ 197 Delegate: &networking.Delegate{ 198 Name: "test", 199 Namespace: "test", 200 }, 201 Match: []*networking.HTTPMatchRequest{{ 202 Headers: map[string]*networking.StringMatch{ 203 "header": nil, 204 }, 205 }}, 206 }, valid: false, 207 }, 208 { 209 name: "prefix header match", route: &networking.HTTPRoute{ 210 Delegate: &networking.Delegate{ 211 Name: "test", 212 Namespace: "test", 213 }, 214 Match: []*networking.HTTPMatchRequest{{ 215 Headers: map[string]*networking.StringMatch{ 216 "header": { 217 MatchType: &networking.StringMatch_Prefix{Prefix: "test"}, 218 }, 219 }, 220 }}, 221 }, valid: true, 222 }, 223 { 224 name: "empty prefix header match (delegate)", route: &networking.HTTPRoute{ 225 Delegate: &networking.Delegate{ 226 Name: "test", 227 Namespace: "test", 228 }, 229 Match: []*networking.HTTPMatchRequest{{ 230 Headers: map[string]*networking.StringMatch{ 231 "header": { 232 MatchType: &networking.StringMatch_Prefix{Prefix: ""}, 233 }, 234 }, 235 }}, 236 }, valid: false, 237 }, 238 { 239 name: "exact header match", route: &networking.HTTPRoute{ 240 Delegate: &networking.Delegate{ 241 Name: "test", 242 Namespace: "test", 243 }, 244 Match: []*networking.HTTPMatchRequest{{ 245 Headers: map[string]*networking.StringMatch{ 246 "header": { 247 MatchType: &networking.StringMatch_Exact{Exact: "test"}, 248 }, 249 }, 250 }}, 251 }, valid: true, 252 }, 253 {name: "regex header match", route: &networking.HTTPRoute{ 254 Delegate: &networking.Delegate{ 255 Name: "test", 256 Namespace: "test", 257 }, 258 Match: []*networking.HTTPMatchRequest{{ 259 Headers: map[string]*networking.StringMatch{ 260 "header": { 261 MatchType: &networking.StringMatch_Regex{Regex: "test"}, 262 }, 263 }, 264 }}, 265 }, valid: true}, 266 {name: "exact without headers match", route: &networking.HTTPRoute{ 267 Redirect: &networking.HTTPRedirect{ 268 Uri: "/", 269 Authority: "foo.biz", 270 }, 271 Match: []*networking.HTTPMatchRequest{{ 272 WithoutHeaders: map[string]*networking.StringMatch{ 273 "header": { 274 MatchType: &networking.StringMatch_Exact{Exact: "test"}, 275 }, 276 }, 277 }}, 278 }, valid: true}, 279 {name: "regex without headers match", route: &networking.HTTPRoute{ 280 Redirect: &networking.HTTPRedirect{ 281 Uri: "/", 282 Authority: "foo.biz", 283 }, 284 Match: []*networking.HTTPMatchRequest{{ 285 WithoutHeaders: map[string]*networking.StringMatch{ 286 "header": { 287 MatchType: &networking.StringMatch_Regex{Regex: "test"}, 288 }, 289 }, 290 }}, 291 }, valid: true}, 292 {name: "regex without headers match ?", route: &networking.HTTPRoute{ 293 Redirect: &networking.HTTPRedirect{ 294 Uri: "/", 295 Authority: "foo.biz", 296 }, 297 Match: []*networking.HTTPMatchRequest{{ 298 WithoutHeaders: map[string]*networking.StringMatch{ 299 "header": { 300 MatchType: &networking.StringMatch_Regex{Regex: "?"}, 301 }, 302 }, 303 }}, 304 }, valid: false}, 305 {name: "regex without headers match *", route: &networking.HTTPRoute{ 306 Redirect: &networking.HTTPRedirect{ 307 Uri: "/", 308 Authority: "foo.biz", 309 }, 310 Match: []*networking.HTTPMatchRequest{{ 311 WithoutHeaders: map[string]*networking.StringMatch{ 312 "header": { 313 MatchType: &networking.StringMatch_Regex{Regex: "*"}, 314 }, 315 }, 316 }}, 317 }, valid: true}, 318 {name: "regex uri match", route: &networking.HTTPRoute{ 319 Delegate: &networking.Delegate{ 320 Name: "test", 321 Namespace: "test", 322 }, 323 Match: []*networking.HTTPMatchRequest{{ 324 Uri: &networking.StringMatch{ 325 MatchType: &networking.StringMatch_Regex{Regex: "test"}, 326 }, 327 }}, 328 }, valid: true}, 329 {name: "prefix uri match", route: &networking.HTTPRoute{ 330 Delegate: &networking.Delegate{ 331 Name: "test", 332 Namespace: "test", 333 }, 334 Match: []*networking.HTTPMatchRequest{{ 335 Uri: &networking.StringMatch{ 336 MatchType: &networking.StringMatch_Prefix{Prefix: "test"}, 337 }, 338 }}, 339 }, valid: true}, 340 {name: "exact uri match", route: &networking.HTTPRoute{ 341 Delegate: &networking.Delegate{ 342 Name: "test", 343 Namespace: "test", 344 }, 345 Match: []*networking.HTTPMatchRequest{{ 346 Uri: &networking.StringMatch{ 347 MatchType: &networking.StringMatch_Exact{Exact: "test"}, 348 }, 349 }}, 350 }, valid: true}, 351 { 352 name: "prefix queryParams match", route: &networking.HTTPRoute{ 353 Delegate: &networking.Delegate{ 354 Name: "test", 355 Namespace: "test", 356 }, 357 Match: []*networking.HTTPMatchRequest{{ 358 QueryParams: map[string]*networking.StringMatch{ 359 "q": { 360 MatchType: &networking.StringMatch_Prefix{Prefix: "test"}, 361 }, 362 }, 363 }}, 364 }, valid: true, 365 }, 366 { 367 name: "exact queryParams match", route: &networking.HTTPRoute{ 368 Delegate: &networking.Delegate{ 369 Name: "test", 370 Namespace: "test", 371 }, 372 Match: []*networking.HTTPMatchRequest{{ 373 QueryParams: map[string]*networking.StringMatch{ 374 "q": { 375 MatchType: &networking.StringMatch_Exact{Exact: "test"}, 376 }, 377 }, 378 }}, 379 }, valid: true, 380 }, 381 {name: "regex queryParams match", route: &networking.HTTPRoute{ 382 Delegate: &networking.Delegate{ 383 Name: "test", 384 Namespace: "test", 385 }, 386 Match: []*networking.HTTPMatchRequest{{ 387 Headers: map[string]*networking.StringMatch{ 388 "q": { 389 MatchType: &networking.StringMatch_Regex{Regex: "test"}, 390 }, 391 }, 392 }}, 393 }, valid: true}, 394 {name: "empty prefix match in method", route: &networking.HTTPRoute{ 395 Match: []*networking.HTTPMatchRequest{{ 396 Method: &networking.StringMatch{ 397 MatchType: &networking.StringMatch_Prefix{Prefix: ""}, 398 }, 399 }}, 400 Redirect: &networking.HTTPRedirect{ 401 Uri: "/", 402 Authority: "foo.biz", 403 }, 404 }, valid: false}, 405 {name: "empty regex match in method", route: &networking.HTTPRoute{ 406 Match: []*networking.HTTPMatchRequest{{ 407 Method: &networking.StringMatch{ 408 MatchType: &networking.StringMatch_Regex{Regex: ""}, 409 }, 410 }}, 411 Redirect: &networking.HTTPRedirect{ 412 Uri: "/", 413 Authority: "foo.biz", 414 }, 415 }, valid: false}, 416 {name: "empty prefix match in uri", route: &networking.HTTPRoute{ 417 Match: []*networking.HTTPMatchRequest{{ 418 Uri: &networking.StringMatch{ 419 MatchType: &networking.StringMatch_Prefix{Prefix: ""}, 420 }, 421 }}, 422 Redirect: &networking.HTTPRedirect{ 423 Uri: "/", 424 Authority: "foo.biz", 425 }, 426 }, valid: true}, 427 {name: "empty regex match in uri", route: &networking.HTTPRoute{ 428 Match: []*networking.HTTPMatchRequest{{ 429 Uri: &networking.StringMatch{ 430 MatchType: &networking.StringMatch_Regex{Regex: ""}, 431 }, 432 }}, 433 Redirect: &networking.HTTPRedirect{ 434 Uri: "/", 435 Authority: "foo.biz", 436 }, 437 }, valid: false}, 438 {name: "empty regex match in query", route: &networking.HTTPRoute{ 439 Match: []*networking.HTTPMatchRequest{{ 440 QueryParams: map[string]*networking.StringMatch{ 441 "q": { 442 MatchType: &networking.StringMatch_Regex{Regex: ""}, 443 }, 444 }, 445 }}, 446 Redirect: &networking.HTTPRedirect{ 447 Uri: "/", 448 Authority: "foo.biz", 449 }, 450 }, valid: false}, 451 {name: "empty regex match in scheme", route: &networking.HTTPRoute{ 452 Match: []*networking.HTTPMatchRequest{{ 453 Scheme: &networking.StringMatch{ 454 MatchType: &networking.StringMatch_Regex{Regex: ""}, 455 }, 456 }}, 457 Redirect: &networking.HTTPRedirect{ 458 Uri: "/", 459 Authority: "foo.biz", 460 }, 461 }, valid: false}, 462 {name: "empty prefix match in scheme", route: &networking.HTTPRoute{ 463 Match: []*networking.HTTPMatchRequest{{ 464 Scheme: &networking.StringMatch{ 465 MatchType: &networking.StringMatch_Prefix{Prefix: ""}, 466 }, 467 }}, 468 Redirect: &networking.HTTPRedirect{ 469 Uri: "/", 470 Authority: "foo.biz", 471 }, 472 }, valid: false}, 473 {name: "empty prefix header match", route: &networking.HTTPRoute{ 474 Match: []*networking.HTTPMatchRequest{{ 475 Headers: map[string]*networking.StringMatch{ 476 "header": { 477 MatchType: &networking.StringMatch_Prefix{Prefix: ""}, 478 }, 479 }, 480 }}, 481 }, valid: false}, 482 } 483 484 for _, tc := range testCases { 485 t.Run(tc.name, func(t *testing.T) { 486 if err := validateHTTPRoute(tc.route, false, false); (err.Err == nil) != tc.valid { 487 t.Fatalf("got valid=%v but wanted valid=%v: %v", err.Err == nil, tc.valid, err) 488 } 489 }) 490 } 491 } 492 493 func TestValidateDelegateHTTPRoute(t *testing.T) { 494 testCases := []struct { 495 name string 496 route *networking.HTTPRoute 497 valid bool 498 }{ 499 {name: "empty", route: &networking.HTTPRoute{ // nothing 500 }, valid: false}, 501 {name: "simple", route: &networking.HTTPRoute{ 502 Route: []*networking.HTTPRouteDestination{{ 503 Destination: &networking.Destination{Host: "foo.baz"}, 504 }}, 505 }, valid: true}, 506 {name: "no destination", route: &networking.HTTPRoute{ 507 Route: []*networking.HTTPRouteDestination{{ 508 Destination: nil, 509 }}, 510 }, valid: false}, 511 {name: "weighted", route: &networking.HTTPRoute{ 512 Route: []*networking.HTTPRouteDestination{{ 513 Destination: &networking.Destination{Host: "foo.baz.south"}, 514 Weight: 25, 515 }, { 516 Destination: &networking.Destination{Host: "foo.baz.east"}, 517 Weight: 75, 518 }}, 519 }, valid: true}, 520 {name: "total weight > 100", route: &networking.HTTPRoute{ 521 Route: []*networking.HTTPRouteDestination{{ 522 Destination: &networking.Destination{Host: "foo.baz.south"}, 523 Weight: 55, 524 }, { 525 Destination: &networking.Destination{Host: "foo.baz.east"}, 526 Weight: 50, 527 }}, 528 }, valid: true}, 529 {name: "total weight < 100", route: &networking.HTTPRoute{ 530 Route: []*networking.HTTPRouteDestination{{ 531 Destination: &networking.Destination{Host: "foo.baz.south"}, 532 Weight: 49, 533 }, { 534 Destination: &networking.Destination{Host: "foo.baz.east"}, 535 Weight: 50, 536 }}, 537 }, valid: true}, 538 {name: "simple redirect", route: &networking.HTTPRoute{ 539 Redirect: &networking.HTTPRedirect{ 540 Uri: "/lerp", 541 Authority: "foo.biz", 542 }, 543 }, valid: true}, 544 {name: "conflicting redirect and route", route: &networking.HTTPRoute{ 545 Route: []*networking.HTTPRouteDestination{{ 546 Destination: &networking.Destination{Host: "foo.baz"}, 547 }}, 548 Redirect: &networking.HTTPRedirect{ 549 Uri: "/lerp", 550 Authority: "foo.biz", 551 }, 552 }, valid: false}, 553 {name: "request response headers", route: &networking.HTTPRoute{ 554 Route: []*networking.HTTPRouteDestination{{ 555 Destination: &networking.Destination{Host: "foo.baz"}, 556 }}, 557 }, valid: true}, 558 {name: "valid headers", route: &networking.HTTPRoute{ 559 Route: []*networking.HTTPRouteDestination{{ 560 Destination: &networking.Destination{Host: "foo.baz"}, 561 Headers: &networking.Headers{ 562 Request: &networking.Headers_HeaderOperations{ 563 Add: map[string]string{ 564 "name": "", 565 }, 566 Set: map[string]string{ 567 "name": "", 568 }, 569 Remove: []string{ 570 "name", 571 }, 572 }, 573 Response: &networking.Headers_HeaderOperations{ 574 Add: map[string]string{ 575 "name": "", 576 }, 577 Set: map[string]string{ 578 "name": "", 579 }, 580 Remove: []string{ 581 "name", 582 }, 583 }, 584 }, 585 }}, 586 }, valid: true}, 587 {name: "empty header name - request add", route: &networking.HTTPRoute{ 588 Route: []*networking.HTTPRouteDestination{{ 589 Destination: &networking.Destination{Host: "foo.baz"}, 590 Headers: &networking.Headers{ 591 Request: &networking.Headers_HeaderOperations{ 592 Add: map[string]string{ 593 "": "value", 594 }, 595 }, 596 }, 597 }}, 598 }, valid: false}, 599 {name: "empty header name - request set", route: &networking.HTTPRoute{ 600 Route: []*networking.HTTPRouteDestination{{ 601 Destination: &networking.Destination{Host: "foo.baz"}, 602 Headers: &networking.Headers{ 603 Request: &networking.Headers_HeaderOperations{ 604 Set: map[string]string{ 605 "": "value", 606 }, 607 }, 608 }, 609 }}, 610 }, valid: false}, 611 {name: "empty header name - request remove", route: &networking.HTTPRoute{ 612 Route: []*networking.HTTPRouteDestination{{ 613 Destination: &networking.Destination{Host: "foo.baz"}, 614 Headers: &networking.Headers{ 615 Request: &networking.Headers_HeaderOperations{ 616 Remove: []string{ 617 "", 618 }, 619 }, 620 }, 621 }}, 622 }, valid: false}, 623 {name: "empty header name - response add", route: &networking.HTTPRoute{ 624 Route: []*networking.HTTPRouteDestination{{ 625 Destination: &networking.Destination{Host: "foo.baz"}, 626 Headers: &networking.Headers{ 627 Response: &networking.Headers_HeaderOperations{ 628 Add: map[string]string{ 629 "": "value", 630 }, 631 }, 632 }, 633 }}, 634 }, valid: false}, 635 {name: "empty header name - response set", route: &networking.HTTPRoute{ 636 Route: []*networking.HTTPRouteDestination{{ 637 Destination: &networking.Destination{Host: "foo.baz"}, 638 Headers: &networking.Headers{ 639 Response: &networking.Headers_HeaderOperations{ 640 Set: map[string]string{ 641 "": "value", 642 }, 643 }, 644 }, 645 }}, 646 }, valid: false}, 647 {name: "empty header name - response remove", route: &networking.HTTPRoute{ 648 Route: []*networking.HTTPRouteDestination{{ 649 Destination: &networking.Destination{Host: "foo.baz"}, 650 Headers: &networking.Headers{ 651 Response: &networking.Headers_HeaderOperations{ 652 Remove: []string{ 653 "", 654 }, 655 }, 656 }, 657 }}, 658 }, valid: false}, 659 {name: "null header match", route: &networking.HTTPRoute{ 660 Route: []*networking.HTTPRouteDestination{{ 661 Destination: &networking.Destination{Host: "foo.bar"}, 662 }}, 663 Match: []*networking.HTTPMatchRequest{{ 664 Headers: map[string]*networking.StringMatch{ 665 "header": nil, 666 }, 667 }}, 668 }, valid: false}, 669 {name: "nil match", route: &networking.HTTPRoute{ 670 Route: []*networking.HTTPRouteDestination{{ 671 Destination: &networking.Destination{Host: "foo.bar"}, 672 }}, 673 Match: nil, 674 }, valid: true}, 675 {name: "match with nil element", route: &networking.HTTPRoute{ 676 Route: []*networking.HTTPRouteDestination{{ 677 Destination: &networking.Destination{Host: "foo.bar"}, 678 }}, 679 Match: []*networking.HTTPMatchRequest{nil}, 680 }, valid: true}, 681 {name: "invalid mirror percent", route: &networking.HTTPRoute{ 682 MirrorPercent: &wrapperspb.UInt32Value{Value: 101}, 683 Route: []*networking.HTTPRouteDestination{{ 684 Destination: &networking.Destination{Host: "foo.bar"}, 685 }}, 686 Match: []*networking.HTTPMatchRequest{nil}, 687 }, valid: false}, 688 {name: "invalid mirror percentage", route: &networking.HTTPRoute{ 689 MirrorPercentage: &networking.Percent{ 690 Value: 101, 691 }, 692 Route: []*networking.HTTPRouteDestination{{ 693 Destination: &networking.Destination{Host: "foo.bar"}, 694 }}, 695 Match: []*networking.HTTPMatchRequest{nil}, 696 }, valid: false}, 697 {name: "valid mirror percentage", route: &networking.HTTPRoute{ 698 MirrorPercentage: &networking.Percent{ 699 Value: 1, 700 }, 701 Route: []*networking.HTTPRouteDestination{{ 702 Destination: &networking.Destination{Host: "foo.bar"}, 703 }}, 704 Match: []*networking.HTTPMatchRequest{nil}, 705 }, valid: true}, 706 {name: "negative mirror percentage", route: &networking.HTTPRoute{ 707 MirrorPercentage: &networking.Percent{ 708 Value: -1, 709 }, 710 Route: []*networking.HTTPRouteDestination{{ 711 Destination: &networking.Destination{Host: "foo.bar"}, 712 }}, 713 Match: []*networking.HTTPMatchRequest{nil}, 714 }, valid: false}, 715 {name: "delegate route with delegate", route: &networking.HTTPRoute{ 716 Delegate: &networking.Delegate{ 717 Name: "test", 718 Namespace: "test", 719 }, 720 }, valid: false}, 721 } 722 723 for _, tc := range testCases { 724 t.Run(tc.name, func(t *testing.T) { 725 if err := validateHTTPRoute(tc.route, true, false); (err.Err == nil) != tc.valid { 726 t.Fatalf("got valid=%v but wanted valid=%v: %v", err.Err == nil, tc.valid, err) 727 } 728 }) 729 } 730 }