github.com/grpc-ecosystem/grpc-gateway/v2@v2.19.1/internal/descriptor/registry_test.go (about) 1 package descriptor 2 3 import ( 4 "testing" 5 6 "github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/openapiconfig" 7 "google.golang.org/protobuf/compiler/protogen" 8 "google.golang.org/protobuf/encoding/prototext" 9 "google.golang.org/protobuf/proto" 10 "google.golang.org/protobuf/types/descriptorpb" 11 "google.golang.org/protobuf/types/pluginpb" 12 ) 13 14 func newGeneratorFromSources(req *pluginpb.CodeGeneratorRequest, sources ...string) (*protogen.Plugin, error) { 15 for _, src := range sources { 16 var fd descriptorpb.FileDescriptorProto 17 if err := prototext.Unmarshal([]byte(src), &fd); err != nil { 18 return nil, err 19 } 20 req.FileToGenerate = append(req.FileToGenerate, fd.GetName()) 21 req.ProtoFile = append(req.ProtoFile, &fd) 22 } 23 return protogen.Options{}.New(req) 24 } 25 26 func loadFileWithCodeGeneratorRequest(t *testing.T, reg *Registry, req *pluginpb.CodeGeneratorRequest, sources ...string) []*descriptorpb.FileDescriptorProto { 27 t.Helper() 28 plugin, err := newGeneratorFromSources(req, sources...) 29 if err != nil { 30 t.Fatalf("failed to create a generator: %v", err) 31 } 32 err = reg.LoadFromPlugin(plugin) 33 if err != nil { 34 t.Fatalf("failed to Registry.LoadFromPlugin(): %v", err) 35 } 36 return plugin.Request.ProtoFile 37 } 38 39 func loadFile(t *testing.T, reg *Registry, src string) *descriptorpb.FileDescriptorProto { 40 t.Helper() 41 fds := loadFileWithCodeGeneratorRequest(t, reg, &pluginpb.CodeGeneratorRequest{}, src) 42 return fds[0] 43 } 44 45 func TestLoadFile(t *testing.T) { 46 reg := NewRegistry() 47 fd := loadFile(t, reg, ` 48 name: 'example.proto' 49 package: 'example' 50 options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example' > 51 message_type < 52 name: 'ExampleMessage' 53 field < 54 name: 'str' 55 label: LABEL_OPTIONAL 56 type: TYPE_STRING 57 number: 1 58 > 59 > 60 `) 61 62 file := reg.files["example.proto"] 63 if file == nil { 64 t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto") 65 return 66 } 67 wantPkg := GoPackage{Path: "github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example", Name: "example"} 68 if got, want := file.GoPkg, wantPkg; got != want { 69 t.Errorf("file.GoPkg = %#v; want %#v", got, want) 70 } 71 72 msg, err := reg.LookupMsg("", ".example.ExampleMessage") 73 if err != nil { 74 t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", "", ".example.ExampleMessage", err) 75 return 76 } 77 if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want { 78 t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", "", ".example.ExampleMessage", got, want) 79 } 80 if got, want := msg.File, file; got != want { 81 t.Errorf("msg.File = %v; want %v", got, want) 82 } 83 if got := msg.Outers; got != nil { 84 t.Errorf("msg.Outers = %v; want %v", got, nil) 85 } 86 if got, want := len(msg.Fields), 1; got != want { 87 t.Errorf("len(msg.Fields) = %d; want %d", got, want) 88 } else if got, want := msg.Fields[0].FieldDescriptorProto, fd.MessageType[0].Field[0]; got != want { 89 t.Errorf("msg.Fields[0].FieldDescriptorProto = %v; want %v", got, want) 90 } else if got, want := msg.Fields[0].Message, msg; got != want { 91 t.Errorf("msg.Fields[0].Message = %v; want %v", got, want) 92 } 93 94 if got, want := len(file.Messages), 1; got != want { 95 t.Errorf("file.Meeesages = %#v; want %#v", file.Messages, []*Message{msg}) 96 } 97 if got, want := file.Messages[0], msg; got != want { 98 t.Errorf("file.Meeesages[0] = %v; want %v", got, want) 99 } 100 } 101 102 func TestLoadFileNestedPackage(t *testing.T) { 103 reg := NewRegistry() 104 loadFile(t, reg, ` 105 name: 'example.proto' 106 package: 'example.nested.nested2' 107 options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example.nested.nested2' > 108 `) 109 110 file := reg.files["example.proto"] 111 if file == nil { 112 t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto") 113 return 114 } 115 wantPkg := GoPackage{Path: "github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example.nested.nested2", Name: "example_nested_nested2"} 116 if got, want := file.GoPkg, wantPkg; got != want { 117 t.Errorf("file.GoPkg = %#v; want %#v", got, want) 118 } 119 } 120 121 func TestLoadFileWithDir(t *testing.T) { 122 reg := NewRegistry() 123 loadFile(t, reg, ` 124 name: 'path/to/example.proto' 125 package: 'example' 126 options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example' > 127 `) 128 129 file := reg.files["path/to/example.proto"] 130 if file == nil { 131 t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto") 132 return 133 } 134 wantPkg := GoPackage{Path: "github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example", Name: "example"} 135 if got, want := file.GoPkg, wantPkg; got != want { 136 t.Errorf("file.GoPkg = %#v; want %#v", got, want) 137 } 138 } 139 140 func TestLoadFileWithoutPackage(t *testing.T) { 141 reg := NewRegistry() 142 loadFile(t, reg, ` 143 name: 'path/to/example_file.proto' 144 options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example_file' > 145 `) 146 147 file := reg.files["path/to/example_file.proto"] 148 if file == nil { 149 t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto") 150 return 151 } 152 wantPkg := GoPackage{Path: "github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example_file", Name: "example_file"} 153 if got, want := file.GoPkg, wantPkg; got != want { 154 t.Errorf("file.GoPkg = %#v; want %#v", got, want) 155 } 156 } 157 158 func TestLoadFileWithMapping(t *testing.T) { 159 reg := NewRegistry() 160 loadFileWithCodeGeneratorRequest(t, reg, &pluginpb.CodeGeneratorRequest{ 161 Parameter: proto.String("Mpath/to/example.proto=example.com/proj/example/proto"), 162 }, ` 163 name: 'path/to/example.proto' 164 package: 'example' 165 options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example' > 166 `) 167 168 file := reg.files["path/to/example.proto"] 169 if file == nil { 170 t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto") 171 return 172 } 173 wantPkg := GoPackage{Path: "example.com/proj/example/proto", Name: "example"} 174 if got, want := file.GoPkg, wantPkg; got != want { 175 t.Errorf("file.GoPkg = %#v; want %#v", got, want) 176 } 177 } 178 179 func TestLoadFileWithPackageNameCollision(t *testing.T) { 180 reg := NewRegistry() 181 loadFile(t, reg, ` 182 name: 'path/to/another.proto' 183 package: 'example' 184 options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example' > 185 `) 186 loadFile(t, reg, ` 187 name: 'path/to/example.proto' 188 package: 'example' 189 options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example' > 190 `) 191 if err := reg.ReserveGoPackageAlias("ioutil", "io/ioutil"); err != nil { 192 t.Fatalf("reg.ReserveGoPackageAlias(%q) failed with %v; want success", "ioutil", err) 193 } 194 loadFile(t, reg, ` 195 name: 'path/to/ioutil.proto' 196 package: 'ioutil' 197 options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/ioutil' > 198 `) 199 200 file := reg.files["path/to/another.proto"] 201 if file == nil { 202 t.Errorf("reg.files[%q] = nil; want non-nil", "path/to/another.proto") 203 return 204 } 205 wantPkg := GoPackage{Path: "github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example", Name: "example"} 206 if got, want := file.GoPkg, wantPkg; got != want { 207 t.Errorf("file.GoPkg = %#v; want %#v", got, want) 208 } 209 210 file = reg.files["path/to/example.proto"] 211 if file == nil { 212 t.Errorf("reg.files[%q] = nil; want non-nil", "path/to/example.proto") 213 return 214 } 215 wantPkg = GoPackage{Path: "github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example", Name: "example", Alias: ""} 216 if got, want := file.GoPkg, wantPkg; got != want { 217 t.Errorf("file.GoPkg = %#v; want %#v", got, want) 218 } 219 220 file = reg.files["path/to/ioutil.proto"] 221 if file == nil { 222 t.Errorf("reg.files[%q] = nil; want non-nil", "path/to/ioutil.proto") 223 return 224 } 225 wantPkg = GoPackage{Path: "github.com/grpc-ecosystem/grpc-gateway/runtime/internal/ioutil", Name: "ioutil", Alias: "ioutil_0"} 226 if got, want := file.GoPkg, wantPkg; got != want { 227 t.Errorf("file.GoPkg = %#v; want %#v", got, want) 228 } 229 } 230 231 func TestLoadFileWithIdenticalGoPkg(t *testing.T) { 232 reg := NewRegistry() 233 loadFileWithCodeGeneratorRequest(t, reg, &pluginpb.CodeGeneratorRequest{ 234 Parameter: proto.String("Mpath/to/another.proto=example.com/example,Mpath/to/example.proto=example.com/example"), 235 }, ` 236 name: 'path/to/another.proto' 237 package: 'example' 238 options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example' > 239 `, ` 240 name: 'path/to/example.proto' 241 package: 'example' 242 options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example' > 243 `) 244 245 file := reg.files["path/to/example.proto"] 246 if file == nil { 247 t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto") 248 return 249 } 250 wantPkg := GoPackage{Path: "example.com/example", Name: "example"} 251 if got, want := file.GoPkg, wantPkg; got != want { 252 t.Errorf("file.GoPkg = %#v; want %#v", got, want) 253 } 254 255 file = reg.files["path/to/another.proto"] 256 if file == nil { 257 t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto") 258 return 259 } 260 wantPkg = GoPackage{Path: "example.com/example", Name: "example"} 261 if got, want := file.GoPkg, wantPkg; got != want { 262 t.Errorf("file.GoPkg = %#v; want %#v", got, want) 263 } 264 } 265 266 // TestLookupMsgWithoutPackage tests a case when there is no "package" directive. 267 // In Go, it is required to have a generated package so we rely on 268 // google.golang.org/protobuf/compiler/protogen to provide it. 269 func TestLookupMsgWithoutPackage(t *testing.T) { 270 reg := NewRegistry() 271 fd := loadFile(t, reg, ` 272 name: 'example.proto' 273 options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example' > 274 message_type < 275 name: 'ExampleMessage' 276 field < 277 name: 'str' 278 label: LABEL_OPTIONAL 279 type: TYPE_STRING 280 number: 1 281 > 282 > 283 `) 284 285 msg, err := reg.LookupMsg("", ".ExampleMessage") 286 if err != nil { 287 t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", "", ".ExampleMessage", err) 288 return 289 } 290 if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want { 291 t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", "", ".ExampleMessage", got, want) 292 } 293 } 294 295 func TestLookupMsgWithNestedPackage(t *testing.T) { 296 reg := NewRegistry() 297 fd := loadFile(t, reg, ` 298 name: 'example.proto' 299 package: 'nested.nested2.mypackage' 300 options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example' > 301 message_type < 302 name: 'ExampleMessage' 303 field < 304 name: 'str' 305 label: LABEL_OPTIONAL 306 type: TYPE_STRING 307 number: 1 308 > 309 > 310 `) 311 312 for _, name := range []string{ 313 "nested.nested2.mypackage.ExampleMessage", 314 "nested2.mypackage.ExampleMessage", 315 "mypackage.ExampleMessage", 316 "ExampleMessage", 317 } { 318 msg, err := reg.LookupMsg("nested.nested2.mypackage", name) 319 if err != nil { 320 t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", ".nested.nested2.mypackage", name, err) 321 return 322 } 323 if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want { 324 t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", ".nested.nested2.mypackage", name, got, want) 325 } 326 } 327 328 for _, loc := range []string{ 329 ".nested.nested2.mypackage", 330 "nested.nested2.mypackage", 331 ".nested.nested2", 332 "nested.nested2", 333 ".nested", 334 "nested", 335 ".", 336 "", 337 "somewhere.else", 338 } { 339 name := "nested.nested2.mypackage.ExampleMessage" 340 msg, err := reg.LookupMsg(loc, name) 341 if err != nil { 342 t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", loc, name, err) 343 return 344 } 345 if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want { 346 t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", loc, name, got, want) 347 } 348 } 349 350 for _, loc := range []string{ 351 ".nested.nested2.mypackage", 352 "nested.nested2.mypackage", 353 ".nested.nested2", 354 "nested.nested2", 355 ".nested", 356 "nested", 357 } { 358 name := "nested2.mypackage.ExampleMessage" 359 msg, err := reg.LookupMsg(loc, name) 360 if err != nil { 361 t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", loc, name, err) 362 return 363 } 364 if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want { 365 t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", loc, name, got, want) 366 } 367 } 368 } 369 370 func TestLoadWithInconsistentTargetPackage(t *testing.T) { 371 for _, spec := range []struct { 372 req string 373 consistent bool 374 }{ 375 // root package, explicit go package 376 { 377 req: ` 378 file_to_generate: 'a.proto' 379 file_to_generate: 'b.proto' 380 proto_file < 381 name: 'a.proto' 382 options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example.foo' > 383 message_type < name: 'A' > 384 service < 385 name: "AService" 386 method < 387 name: "Meth" 388 input_type: "A" 389 output_type: "A" 390 options < 391 [google.api.http] < post: "/v1/a" body: "*" > 392 > 393 > 394 > 395 > 396 proto_file < 397 name: 'b.proto' 398 options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example.foo' > 399 message_type < name: 'B' > 400 service < 401 name: "BService" 402 method < 403 name: "Meth" 404 input_type: "B" 405 output_type: "B" 406 options < 407 [google.api.http] < post: "/v1/b" body: "*" > 408 > 409 > 410 > 411 > 412 `, 413 consistent: true, 414 }, 415 // named package, explicit go package 416 { 417 req: ` 418 file_to_generate: 'a.proto' 419 file_to_generate: 'b.proto' 420 proto_file < 421 name: 'a.proto' 422 package: 'example.foo' 423 options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example.foo' > 424 message_type < name: 'A' > 425 service < 426 name: "AService" 427 method < 428 name: "Meth" 429 input_type: "A" 430 output_type: "A" 431 options < 432 [google.api.http] < post: "/v1/a" body: "*" > 433 > 434 > 435 > 436 > 437 proto_file < 438 name: 'b.proto' 439 package: 'example.foo' 440 options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example.foo' > 441 message_type < name: 'B' > 442 service < 443 name: "BService" 444 method < 445 name: "Meth" 446 input_type: "B" 447 output_type: "B" 448 options < 449 [google.api.http] < post: "/v1/b" body: "*" > 450 > 451 > 452 > 453 > 454 `, 455 consistent: true, 456 }, 457 } { 458 var req pluginpb.CodeGeneratorRequest 459 if err := prototext.Unmarshal([]byte(spec.req), &req); err != nil { 460 t.Fatalf("proto.UnmarshalText(%s, &file) failed with %v; want success", spec.req, err) 461 } 462 _, err := newGeneratorFromSources(&req) 463 if got, want := err == nil, spec.consistent; got != want { 464 if want { 465 t.Errorf("reg.Load(%s) failed with %v; want success", spec.req, err) 466 continue 467 } 468 t.Errorf("reg.Load(%s) succeeded; want an package inconsistency error", spec.req) 469 } 470 } 471 } 472 473 func TestLoadOverriddenPackageName(t *testing.T) { 474 reg := NewRegistry() 475 loadFile(t, reg, ` 476 name: 'example.proto' 477 package: 'example' 478 options < go_package: 'example.com/xyz;pb' > 479 `) 480 file := reg.files["example.proto"] 481 if file == nil { 482 t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto") 483 return 484 } 485 wantPkg := GoPackage{Path: "example.com/xyz", Name: "pb"} 486 if got, want := file.GoPkg, wantPkg; got != want { 487 t.Errorf("file.GoPkg = %#v; want %#v", got, want) 488 } 489 } 490 491 func TestLoadWithStandalone(t *testing.T) { 492 reg := NewRegistry() 493 reg.SetStandalone(true) 494 loadFile(t, reg, ` 495 name: 'example.proto' 496 package: 'example' 497 options < go_package: 'example.com/xyz;pb' > 498 `) 499 file := reg.files["example.proto"] 500 if file == nil { 501 t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto") 502 return 503 } 504 wantPkg := GoPackage{Path: "example.com/xyz", Name: "pb", Alias: "extPb"} 505 if got, want := file.GoPkg, wantPkg; got != want { 506 t.Errorf("file.GoPkg = %#v; want %#v", got, want) 507 } 508 } 509 510 func TestUnboundExternalHTTPRules(t *testing.T) { 511 reg := NewRegistry() 512 methodName := ".example.ExampleService.Echo" 513 reg.AddExternalHTTPRule(methodName, nil) 514 assertStringSlice(t, "unbound external HTTP rules", reg.UnboundExternalHTTPRules(), []string{methodName}) 515 loadFile(t, reg, ` 516 name: "path/to/example.proto", 517 package: "example" 518 options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example' > 519 message_type < 520 name: "StringMessage" 521 field < 522 name: "string" 523 number: 1 524 label: LABEL_OPTIONAL 525 type: TYPE_STRING 526 > 527 > 528 service < 529 name: "ExampleService" 530 method < 531 name: "Echo" 532 input_type: "StringMessage" 533 output_type: "StringMessage" 534 > 535 > 536 `) 537 assertStringSlice(t, "unbound external HTTP rules", reg.UnboundExternalHTTPRules(), []string{}) 538 } 539 540 func TestRegisterOpenAPIOptions(t *testing.T) { 541 codeReqText := `file_to_generate: 'a.proto' 542 proto_file < 543 name: 'a.proto' 544 package: 'example.foo' 545 options < go_package: 'github.com/grpc-ecosystem/grpc-gateway/runtime/internal/example' > 546 message_type < 547 name: 'ExampleMessage' 548 field < 549 name: 'str' 550 label: LABEL_OPTIONAL 551 type: TYPE_STRING 552 number: 1 553 > 554 > 555 service < 556 name: "AService" 557 method < 558 name: "Meth" 559 input_type: "ExampleMessage" 560 output_type: "ExampleMessage" 561 options < 562 [google.api.http] < post: "/v1/a" body: "*" > 563 > 564 > 565 > 566 > 567 ` 568 var codeReq pluginpb.CodeGeneratorRequest 569 if err := prototext.Unmarshal([]byte(codeReqText), &codeReq); err != nil { 570 t.Fatalf("proto.UnmarshalText(%s, &file) failed with %v; want success", codeReqText, err) 571 } 572 573 for _, tcase := range []struct { 574 options *openapiconfig.OpenAPIOptions 575 shouldErr bool 576 desc string 577 }{ 578 { 579 desc: "handle nil options", 580 }, 581 { 582 desc: "successfully add options if referenced entity exists", 583 options: &openapiconfig.OpenAPIOptions{ 584 File: []*openapiconfig.OpenAPIFileOption{ 585 { 586 File: "a.proto", 587 }, 588 }, 589 Method: []*openapiconfig.OpenAPIMethodOption{ 590 { 591 Method: "example.foo.AService.Meth", 592 }, 593 }, 594 Message: []*openapiconfig.OpenAPIMessageOption{ 595 { 596 Message: "example.foo.ExampleMessage", 597 }, 598 }, 599 Service: []*openapiconfig.OpenAPIServiceOption{ 600 { 601 Service: "example.foo.AService", 602 }, 603 }, 604 Field: []*openapiconfig.OpenAPIFieldOption{ 605 { 606 Field: "example.foo.ExampleMessage.str", 607 }, 608 }, 609 }, 610 }, 611 { 612 desc: "reject fully qualified names with leading \".\"", 613 options: &openapiconfig.OpenAPIOptions{ 614 File: []*openapiconfig.OpenAPIFileOption{ 615 { 616 File: "a.proto", 617 }, 618 }, 619 Method: []*openapiconfig.OpenAPIMethodOption{ 620 { 621 Method: ".example.foo.AService.Meth", 622 }, 623 }, 624 Message: []*openapiconfig.OpenAPIMessageOption{ 625 { 626 Message: ".example.foo.ExampleMessage", 627 }, 628 }, 629 Service: []*openapiconfig.OpenAPIServiceOption{ 630 { 631 Service: ".example.foo.AService", 632 }, 633 }, 634 Field: []*openapiconfig.OpenAPIFieldOption{ 635 { 636 Field: ".example.foo.ExampleMessage.str", 637 }, 638 }, 639 }, 640 shouldErr: true, 641 }, 642 { 643 desc: "error if file does not exist", 644 options: &openapiconfig.OpenAPIOptions{ 645 File: []*openapiconfig.OpenAPIFileOption{ 646 { 647 File: "b.proto", 648 }, 649 }, 650 }, 651 shouldErr: true, 652 }, 653 { 654 desc: "error if method does not exist", 655 options: &openapiconfig.OpenAPIOptions{ 656 Method: []*openapiconfig.OpenAPIMethodOption{ 657 { 658 Method: "example.foo.AService.Meth2", 659 }, 660 }, 661 }, 662 shouldErr: true, 663 }, 664 { 665 desc: "error if message does not exist", 666 options: &openapiconfig.OpenAPIOptions{ 667 Message: []*openapiconfig.OpenAPIMessageOption{ 668 { 669 Message: "example.foo.NonexistentMessage", 670 }, 671 }, 672 }, 673 shouldErr: true, 674 }, 675 { 676 desc: "error if service does not exist", 677 options: &openapiconfig.OpenAPIOptions{ 678 Service: []*openapiconfig.OpenAPIServiceOption{ 679 { 680 Service: "example.foo.AService1", 681 }, 682 }, 683 }, 684 shouldErr: true, 685 }, 686 { 687 desc: "error if field does not exist", 688 options: &openapiconfig.OpenAPIOptions{ 689 Field: []*openapiconfig.OpenAPIFieldOption{ 690 { 691 Field: "example.foo.ExampleMessage.str1", 692 }, 693 }, 694 }, 695 shouldErr: true, 696 }, 697 } { 698 t.Run(tcase.desc, func(t *testing.T) { 699 reg := NewRegistry() 700 loadFileWithCodeGeneratorRequest(t, reg, &codeReq) 701 err := reg.RegisterOpenAPIOptions(tcase.options) 702 if (err != nil) != tcase.shouldErr { 703 t.Fatalf("got unexpected error: %s", err) 704 } 705 }) 706 } 707 } 708 709 func assertStringSlice(t *testing.T, message string, got, want []string) { 710 if len(got) != len(want) { 711 t.Errorf("%s = %#v len(%d); want %#v len(%d)", message, got, len(got), want, len(want)) 712 } 713 for i := range want { 714 if got[i] != want[i] { 715 t.Errorf("%s[%d] = %#v; want %#v", message, i, got[i], want[i]) 716 } 717 } 718 }