diff --git a/rules/aip0131/http_body_test.go b/rules/aip0131/http_body_test.go index a0de1e316..b3592102f 100644 --- a/rules/aip0131/http_body_test.go +++ b/rules/aip0131/http_body_test.go @@ -15,55 +15,42 @@ package aip0131 import ( - "strings" "testing" - dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/jhump/protoreflect/desc/builder" - "google.golang.org/genproto/googleapis/api/annotations" - "google.golang.org/protobuf/proto" + "github.com/googleapis/api-linter/rules/internal/testutils" ) func TestHttpBody(t *testing.T) { tests := []struct { testName string - body string - methodName string - msg string + Body string + MethodName string + problems testutils.Problems }{ - {"Valid", "", "GetBook", ""}, - {"Invalid", "*", "GetBook", "HTTP body"}, - {"Irrelevant", "*", "AcquireBook", ""}, + {"Valid", "", "GetBook", nil}, + {"Invalid", "*", "GetBook", testutils.Problems{{Message: "HTTP body"}}}, + {"Irrelevant", "*", "AcquireBook", nil}, } for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a MethodOptions with the annotation set. - opts := &dpb.MethodOptions{} - httpRule := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Get{ - Get: "/v1/{name=publishers/*/books/*}", - }, - Body: test.body, - } - proto.SetExtension(opts, annotations.E_Http, httpRule) - - // Create a minimal service with a AIP-131 Get method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage("GetBookRequest"), false), - builder.RpcTypeMessage(builder.NewMessage("Book"), false), - ).SetOptions(opts)).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the method, ensure we get what we expect. - problems := httpBody.Lint(service.GetFile()) - if test.msg == "" && len(problems) > 0 { - t.Errorf("Got %v, expected no problems.", problems) - } else if test.msg != "" && !strings.Contains(problems[0].Message, test.msg) { - t.Errorf("Got %q, expected message containing %q", problems[0].Message, test.msg) + file := testutils.ParseProto3Tmpl(t, ` + import "google/api/annotations.proto"; + service Library { + rpc {{.MethodName}}({{.MethodName}}Request) returns (Book) { + option (google.api.http) = { + get: "/v1/{name=publishers/*/book/*}" + body: "{{.Body}}" + }; + } + } + message Book {} + message {{.MethodName}}Request {} + `, test) + method := file.GetServices()[0].GetMethods()[0] + problems := httpBody.Lint(file) + if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" { + t.Error(diff) } }) } diff --git a/rules/aip0131/http_method_test.go b/rules/aip0131/http_method_test.go index 5dbe69522..4252f32be 100644 --- a/rules/aip0131/http_method_test.go +++ b/rules/aip0131/http_method_test.go @@ -15,63 +15,43 @@ package aip0131 import ( - "strings" "testing" - dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/jhump/protoreflect/desc/builder" - "google.golang.org/genproto/googleapis/api/annotations" - "google.golang.org/protobuf/proto" + "github.com/googleapis/api-linter/rules/internal/testutils" ) func TestHttpMethod(t *testing.T) { - // Set up GET and POST HTTP annotations. - httpGet := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Get{ - Get: "/v1/{name=publishers/*/books/*}", - }, - } - httpPost := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Post{ - Post: "/v1/{name=publishers/*/books/*}", - }, - } - // Set up testing permutations. tests := []struct { testName string - httpRule *annotations.HttpRule - methodName string - msg string + Method string + MethodName string + problems testutils.Problems }{ - {"Valid", httpGet, "GetBook", ""}, - {"Invalid", httpPost, "GetBook", "HTTP GET"}, - {"Irrelevant", httpPost, "AcquireBook", ""}, + {"Valid", "get", "GetBook", nil}, + {"Invalid", "post", "GetBook", testutils.Problems{{Message: "HTTP GET"}}}, + {"Irrelevant", "post", "AcquireBook", nil}, } // Run each test. for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a MethodOptions with the annotation set. - opts := &dpb.MethodOptions{} - proto.SetExtension(opts, annotations.E_Http, test.httpRule) - - // Create a minimal service with a AIP-131 Get method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage("GetBookRequest"), false), - builder.RpcTypeMessage(builder.NewMessage("Book"), false), - ).SetOptions(opts)).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the method, ensure we get what we expect. - problems := httpMethod.Lint(service.GetFile()) - if test.msg == "" && len(problems) > 0 { - t.Errorf("Got %v, expected no problems.", problems) - } else if test.msg != "" && !strings.Contains(problems[0].Message, test.msg) { - t.Errorf("Got %q, expected message containing %q", problems[0].Message, test.msg) + file := testutils.ParseProto3Tmpl(t, ` + import "google/api/annotations.proto"; + service Library { + rpc {{.MethodName}}({{.MethodName}}Request) returns (Book) { + option (google.api.http) = { + {{.Method}}: "/v1/{name=publishers/*/books/*}" + }; + } + } + message Book {} + message {{.MethodName}}Request {} + `, test) + method := file.GetServices()[0].GetMethods()[0] + problems := httpMethod.Lint(file) + if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" { + t.Error(diff) } }) } diff --git a/rules/aip0131/request_message_name_test.go b/rules/aip0131/request_message_name_test.go index 5f7886426..5fd568b1c 100644 --- a/rules/aip0131/request_message_name_test.go +++ b/rules/aip0131/request_message_name_test.go @@ -18,15 +18,14 @@ import ( "testing" "github.com/googleapis/api-linter/rules/internal/testutils" - "github.com/jhump/protoreflect/desc/builder" ) func TestRequestMessageName(t *testing.T) { // Set up the testing permutations. tests := []struct { testName string - methodName string - reqMessageName string + MethodName string + ReqMessageName string problems testutils.Problems }{ {"Valid", "GetBook", "GetBookRequest", testutils.Problems{}}, @@ -38,19 +37,17 @@ func TestRequestMessageName(t *testing.T) { // Run each test individually. for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a minimal service with a AIP-131 Get method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage(test.reqMessageName), false), - builder.RpcTypeMessage(builder.NewMessage("Book"), false), - )).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the lint rule, and establish that it returns the expected problems. - problems := requestMessageName.Lint(service.GetFile()) - if diff := test.problems.SetDescriptor(service.GetMethods()[0]).Diff(problems); diff != "" { + f := testutils.ParseProto3Tmpl(t, ` + service Library { + rpc {{.MethodName}}({{.ReqMessageName}}) returns (Book) {} + } + message {{.ReqMessageName}} {} + {{if ne .ReqMessageName "Book"}} + message Book {} + {{end}} + `, test) + m := f.GetServices()[0].GetMethods()[0] + if diff := test.problems.SetDescriptor(m).Diff(requestMessageName.Lint(f)); diff != "" { t.Errorf(diff) } }) diff --git a/rules/aip0131/response_message_name_test.go b/rules/aip0131/response_message_name_test.go index fc902eabc..612e1b17a 100644 --- a/rules/aip0131/response_message_name_test.go +++ b/rules/aip0131/response_message_name_test.go @@ -18,15 +18,14 @@ import ( "testing" "github.com/googleapis/api-linter/rules/internal/testutils" - "github.com/jhump/protoreflect/desc/builder" ) func TestResponseMessageName(t *testing.T) { // Set up the testing permutations. tests := []struct { testName string - methodName string - respMessageName string + MethodName string + RespMessageName string problems testutils.Problems }{ {"Valid", "GetBook", "Book", testutils.Problems{}}, @@ -37,21 +36,17 @@ func TestResponseMessageName(t *testing.T) { // Run each test individually. for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a minimal service with a AIP-131 Get method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage("GetBookRequest"), false), - builder.RpcTypeMessage(builder.NewMessage(test.respMessageName), false), - )).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the lint rule, and establish that it returns the correct - // number of problems. - problems := responseMessageName.Lint(service.GetFile()) - if diff := test.problems.SetDescriptor(service.GetMethods()[0]).Diff(problems); diff != "" { - t.Errorf(diff) + file := testutils.ParseProto3Tmpl(t, ` + service Library { + rpc {{.MethodName}}({{.MethodName}}Request) returns ({{.RespMessageName}}) {} + } + message {{.MethodName}}Request {} + message {{.RespMessageName}} {} + `, test) + method := file.GetServices()[0].GetMethods()[0] + problems := responseMessageName.Lint(file) + if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" { + t.Error(diff) } }) } diff --git a/rules/aip0132/request_message_name_test.go b/rules/aip0132/request_message_name_test.go index c89664072..453053d29 100644 --- a/rules/aip0132/request_message_name_test.go +++ b/rules/aip0132/request_message_name_test.go @@ -18,15 +18,14 @@ import ( "testing" "github.com/googleapis/api-linter/rules/internal/testutils" - "github.com/jhump/protoreflect/desc/builder" ) func TestRequestMessageName(t *testing.T) { // Set up the testing permutations. tests := []struct { testName string - methodName string - reqMessageName string + MethodName string + ReqMessageName string problems testutils.Problems }{ {"Valid", "ListBooks", "ListBooksRequest", testutils.Problems{}}, @@ -37,20 +36,15 @@ func TestRequestMessageName(t *testing.T) { // Run each test individually. for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a minimal service with a AIP-131 Get method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage(test.reqMessageName), false), - builder.RpcTypeMessage(builder.NewMessage("ListBooksResponse"), false), - )).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the lint rule, and establish that it returns the correct - // number of problems. - problems := requestMessageName.Lint(service.GetFile()) - if diff := test.problems.SetDescriptor(service.GetMethods()[0]).Diff(problems); diff != "" { + f := testutils.ParseProto3Tmpl(t, ` + service Library { + rpc {{.MethodName}}({{.ReqMessageName}}) returns (ListBooksResponse) {} + } + message {{.ReqMessageName}} {} + message ListBooksResponse {} + `, test) + m := f.GetServices()[0].GetMethods()[0] + if diff := test.problems.SetDescriptor(m).Diff(requestMessageName.Lint(f)); diff != "" { t.Errorf(diff) } }) diff --git a/rules/aip0132/response_message_name_test.go b/rules/aip0132/response_message_name_test.go index ef4905354..5ff198061 100644 --- a/rules/aip0132/response_message_name_test.go +++ b/rules/aip0132/response_message_name_test.go @@ -18,15 +18,14 @@ import ( "testing" "github.com/googleapis/api-linter/rules/internal/testutils" - "github.com/jhump/protoreflect/desc/builder" ) func TestResponseMessageName(t *testing.T) { // Set up the testing permutations. tests := []struct { testName string - methodName string - respMessageName string + MethodName string + RespMessageName string problems testutils.Problems }{ {"Valid", "ListBooks", "ListBooksResponse", testutils.Problems{}}, @@ -37,21 +36,17 @@ func TestResponseMessageName(t *testing.T) { // Run each test individually. for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a minimal service with a AIP-131 Get method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage("ListBooksRequest"), false), - builder.RpcTypeMessage(builder.NewMessage(test.respMessageName), false), - )).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the lint rule, and establish that it returns the correct - // number of problems. - problems := responseMessageName.Lint(service.GetFile()) - if diff := test.problems.SetDescriptor(service.GetMethods()[0]).Diff(problems); diff != "" { - t.Errorf(diff) + file := testutils.ParseProto3Tmpl(t, ` + service Library { + rpc {{.MethodName}}({{.MethodName}}Request) returns ({{.RespMessageName}}) {} + } + message {{.MethodName}}Request {} + message {{.RespMessageName}} {} + `, test) + method := file.GetServices()[0].GetMethods()[0] + problems := responseMessageName.Lint(file) + if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" { + t.Error(diff) } }) } diff --git a/rules/aip0133/http_body_test.go b/rules/aip0133/http_body_test.go index e5416af33..2feb306f9 100644 --- a/rules/aip0133/http_body_test.go +++ b/rules/aip0133/http_body_test.go @@ -15,65 +15,48 @@ package aip0133 import ( - "strings" "testing" - dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/jhump/protoreflect/desc/builder" - "google.golang.org/genproto/googleapis/api/annotations" - "google.golang.org/protobuf/proto" + "github.com/googleapis/api-linter/rules/internal/testutils" ) func TestHttpBody(t *testing.T) { tests := []struct { testName string - resourceField string - body string - methodName string - msg string + ResourceField string + Body string + MethodName string + problems testutils.Problems }{ - {"Valid", "book", "book", "CreateBook", ""}, - {"Valid", "textbook", "textbook", "CreateBook", ""}, - {"Valid", "", "book", "CreateBook", ""}, // valid for http body rule check, but it will fail under resource fail rule check - {"Invalid_BodyMissing", "book", "", "CreateBook", "Post methods should have an HTTP body"}, - {"Invalid_BodyMismatch", "book", "abook", "CreateBook", "The content of body \"abook\" must map to the resource field \"book\" in the request message"}, - {"Irrelevant", "book", "book", "CreateBook", ""}, + {"Valid", "Book book = 1;", "book", "CreateBook", nil}, + {"Valid", "Book textbook = 1;", "textbook", "CreateBook", nil}, + {"Valid", "", "book", "CreateBook", nil}, // valid for http body rule check, but it will fail under resource fail rule check + {"Invalid_BodyMissing", "Book book = 1;", "", "CreateBook", testutils.Problems{{Message: "Post methods should have an HTTP body"}}}, + {"Invalid_BodyMismatch", "Book book = 1;", "abook", "CreateBook", testutils.Problems{{Message: `The content of body "abook" must map to the resource field "book" in the request message`}}}, + {"Irrelevant", "Book book = 1;", "book", "CreateBook", nil}, } for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a MethodOptions with the annotation set. - opts := &dpb.MethodOptions{} - httpRule := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Post{ - Post: "/v1/{parent=publishers/*/books/*}", - }, - Body: test.body, - } - proto.SetExtension(opts, annotations.E_Http, httpRule) - - // Create the book. - book, err := builder.NewMessage("Book").Build() - if err != nil { - t.Fatalf("Failed to create resource 'Book' message for test.") - } - - // Create a minimal service with a AIP-133 Create method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage("CreateBookRequest").AddField(builder.NewField(test.resourceField, builder.FieldTypeImportedMessage(book))), false), - builder.RpcTypeMessage(builder.NewMessage("Book"), false), - ).SetOptions(opts)).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the method, ensure we get what we expect. - problems := httpBody.Lint(service.GetFile()) - if test.msg == "" && len(problems) > 0 { - t.Errorf("Got %v, expected no problems.", problems) - } else if test.msg != "" && !strings.Contains(problems[0].Message, test.msg) { - t.Errorf("Got %q, expected message containing %q", problems[0].Message, test.msg) + file := testutils.ParseProto3Tmpl(t, ` + import "google/api/annotations.proto"; + service Library { + rpc {{.MethodName}}({{.MethodName}}Request) returns (Book) { + option (google.api.http) = { + post: "/v1/{parent=publishers/*}/books" + body: "{{.Body}}" + }; + } + } + message Book {} + message {{.MethodName}}Request { + {{.ResourceField}} + } + `, test) + method := file.GetServices()[0].GetMethods()[0] + problems := httpBody.Lint(file) + if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" { + t.Error(diff) } }) } diff --git a/rules/aip0133/http_method_test.go b/rules/aip0133/http_method_test.go index 8a0fbea98..e7d99e173 100644 --- a/rules/aip0133/http_method_test.go +++ b/rules/aip0133/http_method_test.go @@ -15,63 +15,43 @@ package aip0133 import ( - "strings" "testing" - dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/jhump/protoreflect/desc/builder" - "google.golang.org/genproto/googleapis/api/annotations" - "google.golang.org/protobuf/proto" + "github.com/googleapis/api-linter/rules/internal/testutils" ) func TestHttpMethod(t *testing.T) { - // Set up GET and POST HTTP annotations. - httpGet := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Get{ - Get: "/v1/{name=publishers/*/books/*}", - }, - } - httpPost := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Post{ - Post: "/v1/{=publishers/*/books/*}", - }, - } - // Set up testing permutations. tests := []struct { testName string - httpRule *annotations.HttpRule - methodName string - msg string + Method string + MethodName string + problems testutils.Problems }{ - {"Valid", httpPost, "CreateBook", ""}, - {"Invalid", httpGet, "CreateBook", "HTTP POST"}, - {"Irrelevant", httpPost, "AcquireBook", ""}, + {"Valid", "post", "CreateBook", nil}, + {"Invalid", "get", "CreateBook", testutils.Problems{{Message: "HTTP POST"}}}, + {"Irrelevant", "get", "AcquireBook", nil}, } // Run each test. for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a MethodOptions with the annotation set. - opts := &dpb.MethodOptions{} - proto.SetExtension(opts, annotations.E_Http, test.httpRule) - - // Create a minimal service with a AIP-133 Create method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage("CreateBookRequest"), false), - builder.RpcTypeMessage(builder.NewMessage("Book"), false), - ).SetOptions(opts)).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the method, ensure we get what we expect. - problems := httpMethod.Lint(service.GetFile()) - if test.msg == "" && len(problems) > 0 { - t.Errorf("Got %v, expected no problems.", problems) - } else if test.msg != "" && !strings.Contains(problems[0].Message, test.msg) { - t.Errorf("Got %q, expected message containing %q", problems[0].Message, test.msg) + file := testutils.ParseProto3Tmpl(t, ` + import "google/api/annotations.proto"; + service Library { + rpc {{.MethodName}}({{.MethodName}}Request) returns (Book) { + option (google.api.http) = { + {{.Method}}: "/v1/{name=publishers/*/books/*}" + }; + } + } + message Book {} + message {{.MethodName}}Request {} + `, test) + method := file.GetServices()[0].GetMethods()[0] + problems := httpMethod.Lint(file) + if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" { + t.Error(diff) } }) } diff --git a/rules/aip0133/request_message_name_test.go b/rules/aip0133/request_message_name_test.go index 008db0a6b..cc5bf67f4 100644 --- a/rules/aip0133/request_message_name_test.go +++ b/rules/aip0133/request_message_name_test.go @@ -15,47 +15,44 @@ package aip0133 import ( - "strings" "testing" - "github.com/jhump/protoreflect/desc/builder" + "github.com/googleapis/api-linter/rules/internal/testutils" ) func TestInputName(t *testing.T) { // Set up the testing permutations. tests := []struct { testName string - methodName string - inputName string - msg string + MethodName string + InputName string + problems testutils.Problems }{ - {"Valid", "CreateBook", "CreateBookRequest", ""}, + {"Valid", "CreateBook", "CreateBookRequest", nil}, {"Invalid", "CreateBook", "Book", - "Post RPCs should have a properly named request message \"CreateBookRequest\", but not \"Book\""}, - {"Irrelevant_OutputWrong", "CreateIamPolicy", "CreateIamPolicyRequest", ""}, - {"Irrelevant_NotCreate", "BuildBook", "Book", ""}, + testutils.Problems{{ + Message: `Post RPCs should have a properly named request message "CreateBookRequest", but not "Book"`, + Suggestion: "CreateBookRequest", + }}}, + {"Irrelevant_OutputWrong", "CreateIamPolicy", "CreateIamPolicyRequest", nil}, + {"Irrelevant_NotCreate", "BuildBook", "Book", nil}, } // Run each test individually. for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - - // Create a minimal service with a AIP-133 Get method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage(test.inputName), false), - builder.RpcTypeMessage(builder.NewMessage("Book"), false), - )).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the lint rule, and establish that it returns the expected problems. - problems := inputName.Lint(service.GetFile()) - if test.msg == "" && len(problems) > 0 { - t.Errorf("Got %v, expected no problems.", problems) - } else if test.msg != "" && !strings.Contains(problems[0].Message, test.msg) { - t.Errorf("Got %q, expected message containing %q", problems[0].Message, test.msg) + f := testutils.ParseProto3Tmpl(t, ` + service Library { + rpc {{.MethodName}}({{.InputName}}) returns (Book) {} + } + message {{.InputName}} {} + {{if ne .InputName "Book"}} + message Book {} + {{end}} + `, test) + m := f.GetServices()[0].GetMethods()[0] + if diff := test.problems.SetDescriptor(m).Diff(inputName.Lint(f)); diff != "" { + t.Errorf(diff) } }) } diff --git a/rules/aip0134/http_body_test.go b/rules/aip0134/http_body_test.go index 2d23d1d5d..3b58d947f 100644 --- a/rules/aip0134/http_body_test.go +++ b/rules/aip0134/http_body_test.go @@ -15,57 +15,46 @@ package aip0134 import ( - "strings" "testing" - dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/jhump/protoreflect/desc/builder" - "google.golang.org/genproto/googleapis/api/annotations" - "google.golang.org/protobuf/proto" + "github.com/googleapis/api-linter/rules/internal/testutils" ) func TestHttpBody(t *testing.T) { tests := []struct { testName string - body string - methodName string - msg string + Body string + MethodName string + problems testutils.Problems }{ - {"Valid", "book", "UpdateBook", ""}, - {"InvalidFoo", "foo", "UpdateBook", "HTTP body"}, - {"InvalidStar", "*", "UpdateBook", "HTTP body"}, - {"InvalidEmpty", "", "UpdateBook", "HTTP body"}, - {"Irrelevant", "*", "AcquireBook", ""}, + {"Valid", "book", "UpdateBook", nil}, + {"InvalidFoo", "foo", "UpdateBook", testutils.Problems{{Message: "HTTP body"}}}, + {"InvalidStar", "*", "UpdateBook", testutils.Problems{{Message: "HTTP body"}}}, + {"InvalidEmpty", "", "UpdateBook", testutils.Problems{{Message: "HTTP body"}}}, + {"Irrelevant", "*", "AcquireBook", nil}, } for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a MethodOptions with the annotation set. - opts := &dpb.MethodOptions{} - httpRule := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Patch{ - Patch: "/v1/{book.name=publishers/*/books/*}", - }, - Body: test.body, - } - proto.SetExtension(opts, annotations.E_Http, httpRule) - - // Create a minimal service with a AIP-134 Update method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage("UpdateBookRequest"), false), - builder.RpcTypeMessage(builder.NewMessage("Book"), false), - ).SetOptions(opts)).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the method, ensure we get what we expect. - problems := httpBody.Lint(service.GetFile()) - if test.msg == "" && len(problems) > 0 { - t.Errorf("Got %v, expected no problems.", problems) - } else if test.msg != "" && !strings.Contains(problems[0].Message, test.msg) { - t.Errorf("Got %q, expected message containing %q", problems[0].Message, test.msg) + file := testutils.ParseProto3Tmpl(t, ` + import "google/api/annotations.proto"; + service Library { + rpc {{.MethodName}}({{.MethodName}}Request) returns (Book) { + option (google.api.http) = { + patch: "/v1/{book.name=publishers/*/books/*}" + body: "{{.Body}}" + }; + } + } + message Book { + string name = 1; + } + message {{.MethodName}}Request {} + `, test) + method := file.GetServices()[0].GetMethods()[0] + problems := httpBody.Lint(file) + if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" { + t.Error(diff) } }) } diff --git a/rules/aip0134/http_method_test.go b/rules/aip0134/http_method_test.go index 48b0301fe..6e38aaefe 100644 --- a/rules/aip0134/http_method_test.go +++ b/rules/aip0134/http_method_test.go @@ -15,63 +15,47 @@ package aip0134 import ( - "strings" "testing" - dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/jhump/protoreflect/desc/builder" - "google.golang.org/genproto/googleapis/api/annotations" - "google.golang.org/protobuf/proto" + "github.com/googleapis/api-linter/rules/internal/testutils" ) func TestHttpMethod(t *testing.T) { - // Set up POST and PATCH HTTP annotations. - httpPost := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Post{ - Post: "/v1/{book.name=publishers/*/books/*}", - }, - } - httpPatch := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Patch{ - Patch: "/v1/{book.name=publishers/*/books/*}", - }, - } - // Set up testing permutations. tests := []struct { testName string - httpRule *annotations.HttpRule - methodName string - msg string + Method string + MethodName string + problems testutils.Problems }{ - {"Valid", httpPatch, "UpdateBook", ""}, - {"Invalid", httpPost, "UpdateBook", "HTTP PATCH"}, - {"Irrelevant", httpPost, "AcquireBook", ""}, + {"Valid", "patch", "UpdateBook", nil}, + {"Invalid", "post", "UpdateBook", testutils.Problems{{Message: "HTTP PATCH"}}}, + {"Irrelevant", "post", "AcquireBook", nil}, } // Run each test. for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a MethodOptions with the annotation set. - opts := &dpb.MethodOptions{} - proto.SetExtension(opts, annotations.E_Http, test.httpRule) - - // Create a minimal service with a AIP-134 Update method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage("UpdateBookRequest"), false), - builder.RpcTypeMessage(builder.NewMessage("Book"), false), - ).SetOptions(opts)).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the method, ensure we get what we expect. - problems := httpMethod.Lint(service.GetFile()) - if test.msg == "" && len(problems) > 0 { - t.Errorf("Got %v, expected no problems.", problems) - } else if test.msg != "" && !strings.Contains(problems[0].Message, test.msg) { - t.Errorf("Got %q, expected message containing %q", problems[0].Message, test.msg) + file := testutils.ParseProto3Tmpl(t, ` + import "google/api/annotations.proto"; + service Library { + rpc {{.MethodName}}({{.MethodName}}Request) returns (Book) { + option (google.api.http) = { + {{.Method}}: "/v1/{book.name=publishers/*/books/*}" + }; + } + } + message Book { + string name = 1; + } + message {{.MethodName}}Request { + Book book = 1; + } + `, test) + method := file.GetServices()[0].GetMethods()[0] + problems := httpMethod.Lint(file) + if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" { + t.Error(diff) } }) } diff --git a/rules/aip0134/http_uri_name_test.go b/rules/aip0134/http_uri_name_test.go index 959355970..0318e6be5 100644 --- a/rules/aip0134/http_uri_name_test.go +++ b/rules/aip0134/http_uri_name_test.go @@ -15,67 +15,51 @@ package aip0134 import ( - "strings" "testing" - dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/jhump/protoreflect/desc/builder" - "google.golang.org/genproto/googleapis/api/annotations" - "google.golang.org/protobuf/proto" + "github.com/googleapis/api-linter/rules/internal/testutils" ) func TestHttpNameField(t *testing.T) { tests := []struct { testName string - uri string - methodName string - msg string + URI string + MethodName string + problems testutils.Problems }{ {"Valid", "/v1/{big_book.name=publishers/*/books/*}", - "UpdateBigBook", ""}, + "UpdateBigBook", nil}, {"InvalidNoUnderscore", "/v1/{bigbook.name=publishers/*/books/*}", - "UpdateBigBook", "`big_book.name` field"}, + "UpdateBigBook", testutils.Problems{{Message: "`big_book.name` field"}}}, {"InvalidVarNameBook", "/v1/{big_book=publishers/*/books/*}", - "UpdateBigBook", "`big_book.name` field"}, + "UpdateBigBook", testutils.Problems{{Message: "`big_book.name` field"}}}, {"InvalidVarNameName", "/v1/{name=publishers/*/books/*}", - "UpdateBigBook", "`big_book.name` field"}, + "UpdateBigBook", testutils.Problems{{Message: "`big_book.name` field"}}}, {"InvalidVarNameReversed", "/v1/{name.big_book=publishers/*/books/*}", - "UpdateBigBook", "`big_book.name` field"}, + "UpdateBigBook", testutils.Problems{{Message: "`big_book.name` field"}}}, {"NoVarName", "/v1/publishers/*/books/*", - "UpdateBigBook", "`big_book.name` field"}, + "UpdateBigBook", testutils.Problems{{Message: "`big_book.name` field"}}}, {"Irrelevant", "/v1/{book=publishers/*/books/*}", - "AcquireBigBook", ""}, + "AcquireBigBook", nil}, } for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a MethodOptions with the annotation set. - opts := &dpb.MethodOptions{} - httpRule := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Patch{ - Patch: test.uri, - }, - } - proto.SetExtension(opts, annotations.E_Http, httpRule) - - // Create a minimal service with a AIP-134 Update method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage("UpdateBigBookRequest"), false), - builder.RpcTypeMessage(builder.NewMessage("BigBook"), false), - ).SetOptions(opts)).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the method, ensure we get what we expect. - problems := httpNameField.Lint(service.GetFile()) - if test.msg == "" && len(problems) > 0 { - t.Errorf("Got %v, expected no problems.", problems) - } else if test.msg != "" && len(problems) == 0 { - t.Errorf("Got no problems, expected 1.") - } else if test.msg != "" && !strings.Contains(problems[0].Message, test.msg) { - t.Errorf("Got %q, expected message containing %q", problems[0].Message, test.msg) + f := testutils.ParseProto3Tmpl(t, ` + import "google/api/annotations.proto"; + service Library { + rpc {{.MethodName}}({{.MethodName}}Request) returns (BigBook) { + option (google.api.http) = { + patch: "{{.URI}}" + }; + } + } + message BigBook {} + message {{.MethodName}}Request {} + `, test) + method := f.GetServices()[0].GetMethods()[0] + if diff := test.problems.SetDescriptor(method).Diff(httpNameField.Lint(f)); diff != "" { + t.Errorf(diff) } }) } diff --git a/rules/aip0134/request_message_name_test.go b/rules/aip0134/request_message_name_test.go index a5432ab9b..640270926 100644 --- a/rules/aip0134/request_message_name_test.go +++ b/rules/aip0134/request_message_name_test.go @@ -18,15 +18,14 @@ import ( "testing" "github.com/googleapis/api-linter/rules/internal/testutils" - "github.com/jhump/protoreflect/desc/builder" ) func TestRequestMessageName(t *testing.T) { // Set up the testing permutations. tests := []struct { testName string - methodName string - reqMessageName string + MethodName string + ReqMessageName string problems testutils.Problems }{ {"Valid", "UpdateBook", "UpdateBookRequest", testutils.Problems{}}, @@ -37,19 +36,17 @@ func TestRequestMessageName(t *testing.T) { // Run each test individually. for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a minimal service with a AIP-134 Update method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage(test.reqMessageName), false), - builder.RpcTypeMessage(builder.NewMessage("Book"), false), - )).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the lint rule, and establish that it returns the expected problems. - problems := requestMessageName.Lint(service.GetFile()) - if diff := test.problems.SetDescriptor(service.GetMethods()[0]).Diff(problems); diff != "" { + f := testutils.ParseProto3Tmpl(t, ` + service Library { + rpc {{.MethodName}}({{.ReqMessageName}}) returns (Book) {} + } + message {{.ReqMessageName}} {} + {{if ne .ReqMessageName "Book"}} + message Book {} + {{end}} + `, test) + m := f.GetServices()[0].GetMethods()[0] + if diff := test.problems.SetDescriptor(m).Diff(requestMessageName.Lint(f)); diff != "" { t.Errorf(diff) } }) diff --git a/rules/aip0135/http_body_test.go b/rules/aip0135/http_body_test.go index 53314000e..432ccc1d6 100644 --- a/rules/aip0135/http_body_test.go +++ b/rules/aip0135/http_body_test.go @@ -15,55 +15,42 @@ package aip0135 import ( - "strings" "testing" - dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/jhump/protoreflect/desc/builder" - "google.golang.org/genproto/googleapis/api/annotations" - "google.golang.org/protobuf/proto" + "github.com/googleapis/api-linter/rules/internal/testutils" ) func TestHttpBody(t *testing.T) { tests := []struct { testName string - body string - methodName string - msg string + Body string + MethodName string + problems testutils.Problems }{ - {"Valid", "", "DeleteBook", ""}, - {"Invalid", "*", "DeleteBook", "HTTP body"}, - {"Irrelevant", "*", "AcquireBook", ""}, + {"Valid", "", "DeleteBook", nil}, + {"Invalid", "*", "DeleteBook", testutils.Problems{{Message: "HTTP body"}}}, + {"Irrelevant", "*", "AcquireBook", nil}, } for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a MethodOptions with the annotation set. - opts := &dpb.MethodOptions{} - httpRule := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Delete{ - Delete: "/v1/{name=publishers/*/books/*}", - }, - Body: test.body, - } - proto.SetExtension(opts, annotations.E_Http, httpRule) - - // Create a minimal service with a AIP-135 Delete method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage("DeleteBookRequest"), false), - builder.RpcTypeMessage(builder.NewMessage("Book"), false), - ).SetOptions(opts)).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the method, ensure we get what we expect. - problems := httpBody.Lint(service.GetFile()) - if test.msg == "" && len(problems) > 0 { - t.Errorf("Got %v, expected no problems.", problems) - } else if test.msg != "" && !strings.Contains(problems[0].Message, test.msg) { - t.Errorf("Got %q, expected message containing %q", problems[0].Message, test.msg) + file := testutils.ParseProto3Tmpl(t, ` + import "google/api/annotations.proto"; + service Library { + rpc {{.MethodName}}({{.MethodName}}Request) returns (Book) { + option (google.api.http) = { + delete: "/v1/{name=publishers/*/books/*}" + body: "{{.Body}}" + }; + } + } + message Book {} + message {{.MethodName}}Request {} + `, test) + method := file.GetServices()[0].GetMethods()[0] + problems := httpBody.Lint(file) + if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" { + t.Error(diff) } }) } diff --git a/rules/aip0135/http_method_test.go b/rules/aip0135/http_method_test.go index 73e9ec356..0ec054a6a 100644 --- a/rules/aip0135/http_method_test.go +++ b/rules/aip0135/http_method_test.go @@ -15,63 +15,43 @@ package aip0135 import ( - "strings" "testing" - dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/jhump/protoreflect/desc/builder" - "google.golang.org/genproto/googleapis/api/annotations" - "google.golang.org/protobuf/proto" + "github.com/googleapis/api-linter/rules/internal/testutils" ) func TestHttpMethod(t *testing.T) { - // Set up GET and DELETE HTTP annotations. - httpGet := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Get{ - Get: "/v1/{name=publishers/*/books/*}", - }, - } - httpDelete := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Delete{ - Delete: "/v1/{name=publishers/*/books/*}", - }, - } - // Set up testing permutations. tests := []struct { testName string - httpRule *annotations.HttpRule - methodName string - msg string + Method string + MethodName string + problems testutils.Problems }{ - {"Valid", httpDelete, "DeleteBook", ""}, - {"Invalid", httpGet, "DeleteBook", "HTTP DELETE"}, - {"Irrelevant", httpGet, "AcquireBook", ""}, + {"Valid", "delete", "DeleteBook", nil}, + {"Invalid", "get", "DeleteBook", testutils.Problems{{Message: "HTTP DELETE"}}}, + {"Irrelevant", "get", "AcquireBook", nil}, } // Run each test. for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a MethodOptions with the annotation set. - opts := &dpb.MethodOptions{} - proto.SetExtension(opts, annotations.E_Http, test.httpRule) - - // Create a minimal service with a AIP-135 Delete method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage("DeleteBookRequest"), false), - builder.RpcTypeMessage(builder.NewMessage("Book"), false), - ).SetOptions(opts)).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the method, ensure we get what we expect. - problems := httpMethod.Lint(service.GetFile()) - if test.msg == "" && len(problems) > 0 { - t.Errorf("Got %v, expected no problems.", problems) - } else if test.msg != "" && !strings.Contains(problems[0].Message, test.msg) { - t.Errorf("Got %q, expected message containing %q", problems[0].Message, test.msg) + file := testutils.ParseProto3Tmpl(t, ` + import "google/api/annotations.proto"; + service Library { + rpc {{.MethodName}}({{.MethodName}}Request) returns (Book) { + option (google.api.http) = { + {{.Method}}: "/v1/{name=publishers/*/books/*}" + }; + } + } + message Book {} + message {{.MethodName}}Request {} + `, test) + method := file.GetServices()[0].GetMethods()[0] + problems := httpMethod.Lint(file) + if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" { + t.Error(diff) } }) } diff --git a/rules/aip0135/http_uri_name_test.go b/rules/aip0135/http_uri_name_test.go index 8c8e91cef..bf4458aa6 100644 --- a/rules/aip0135/http_uri_name_test.go +++ b/rules/aip0135/http_uri_name_test.go @@ -15,57 +15,41 @@ package aip0135 import ( - "strings" "testing" - dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/jhump/protoreflect/desc/builder" - "google.golang.org/genproto/googleapis/api/annotations" - "google.golang.org/protobuf/proto" + "github.com/googleapis/api-linter/rules/internal/testutils" ) func TestHttpNameField(t *testing.T) { tests := []struct { testName string - uri string - methodName string - msg string + URI string + MethodName string + problems testutils.Problems }{ - {"Valid", "/v1/{name=publishers/*/books/*}", "DeleteBook", ""}, - {"InvalidVarName", "/v1/{book=publishers/*/books/*}", "DeleteBook", "`name` field"}, - {"NoVarName", "/v1/publishers/*/books/*", "DeleteBook", "`name` field"}, - {"Irrelevant", "/v1/{book=publishers/*/books/*}", "AcquireBook", ""}, + {"Valid", "/v1/{name=publishers/*/books/*}", "DeleteBook", nil}, + {"InvalidVarName", "/v1/{book=publishers/*/books/*}", "DeleteBook", testutils.Problems{{Message: "`name` field"}}}, + {"NoVarName", "/v1/publishers/*/books/*", "DeleteBook", testutils.Problems{{Message: "`name` field"}}}, + {"Irrelevant", "/v1/{book=publishers/*/books/*}", "AcquireBook", nil}, } for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a MethodOptions with the annotation set. - opts := &dpb.MethodOptions{} - httpRule := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Delete{ - Delete: test.uri, - }, - } - proto.SetExtension(opts, annotations.E_Http, httpRule) - - // Create a minimal service with a AIP-135 Delete method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage("DeleteBookRequest"), false), - builder.RpcTypeMessage(builder.NewMessage("Book"), false), - ).SetOptions(opts)).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the method, ensure we get what we expect. - problems := httpNameField.Lint(service.GetFile()) - if test.msg == "" && len(problems) > 0 { - t.Errorf("Got %v, expected no problems.", problems) - } else if test.msg != "" && len(problems) == 0 { - t.Errorf("Got no problems, expected 1.") - } else if test.msg != "" && !strings.Contains(problems[0].Message, test.msg) { - t.Errorf("Got %q, expected message containing %q", problems[0].Message, test.msg) + f := testutils.ParseProto3Tmpl(t, ` + import "google/api/annotations.proto"; + service Library { + rpc {{.MethodName}}({{.MethodName}}Request) returns (Book) { + option (google.api.http) = { + delete: "{{.URI}}" + }; + } + } + message Book {} + message {{.MethodName}}Request {} + `, test) + method := f.GetServices()[0].GetMethods()[0] + if diff := test.problems.SetDescriptor(method).Diff(httpNameField.Lint(f)); diff != "" { + t.Errorf(diff) } }) } diff --git a/rules/aip0135/request_message_name_test.go b/rules/aip0135/request_message_name_test.go index 8c2fb2851..e72990d47 100644 --- a/rules/aip0135/request_message_name_test.go +++ b/rules/aip0135/request_message_name_test.go @@ -18,15 +18,14 @@ import ( "testing" "github.com/googleapis/api-linter/rules/internal/testutils" - "github.com/jhump/protoreflect/desc/builder" ) func TestRequestMessageName(t *testing.T) { // Set up the testing permutations. tests := []struct { testName string - methodName string - reqMessageName string + MethodName string + ReqMessageName string problems testutils.Problems }{ {"Valid", "DeleteBook", "DeleteBookRequest", testutils.Problems{}}, @@ -37,19 +36,17 @@ func TestRequestMessageName(t *testing.T) { // Run each test individually. for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a minimal service with a AIP-135 Delete method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage(test.reqMessageName), false), - builder.RpcTypeMessage(builder.NewMessage("Book"), false), - )).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the lint rule, and establish that it returns the expected problems. - problems := requestMessageName.Lint(service.GetFile()) - if diff := test.problems.SetDescriptor(service.GetMethods()[0]).Diff(problems); diff != "" { + f := testutils.ParseProto3Tmpl(t, ` + service Library { + rpc {{.MethodName}}({{.ReqMessageName}}) returns (Book) {} + } + message {{.ReqMessageName}} {} + {{if ne .ReqMessageName "Book"}} + message Book {} + {{end}} + `, test) + m := f.GetServices()[0].GetMethods()[0] + if diff := test.problems.SetDescriptor(m).Diff(requestMessageName.Lint(f)); diff != "" { t.Errorf(diff) } }) diff --git a/rules/aip0164/http_body_test.go b/rules/aip0164/http_body_test.go index 93b27cb48..6b53ae6f0 100644 --- a/rules/aip0164/http_body_test.go +++ b/rules/aip0164/http_body_test.go @@ -15,55 +15,42 @@ package aip0164 import ( - "strings" "testing" - dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/jhump/protoreflect/desc/builder" - "google.golang.org/genproto/googleapis/api/annotations" - "google.golang.org/protobuf/proto" + "github.com/googleapis/api-linter/rules/internal/testutils" ) func TestHttpBody(t *testing.T) { tests := []struct { testName string - body string - methodName string - msg string + Body string + MethodName string + problems testutils.Problems }{ - {"Valid", "*", "UndeleteBook", ""}, - {"Invalid", "", "UndeleteBook", "HTTP body"}, - {"Irrelevant", "", "AcquireBook", ""}, + {"Valid", "*", "UndeleteBook", nil}, + {"Invalid", "", "UndeleteBook", testutils.Problems{{Message: "HTTP body"}}}, + {"Irrelevant", "", "AcquireBook", nil}, } for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a MethodOptions with the annotation set. - opts := &dpb.MethodOptions{} - httpRule := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Post{ - Post: "/v1/{name=publishers/*/books/*}:undelete", - }, - Body: test.body, - } - proto.SetExtension(opts, annotations.E_Http, httpRule) - - // Create a minimal service with a AIP-164 Undelete method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage("UndeleteBookRequest"), false), - builder.RpcTypeMessage(builder.NewMessage("Book"), false), - ).SetOptions(opts)).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the method, ensure we get what we expect. - problems := httpBody.Lint(service.GetFile()) - if test.msg == "" && len(problems) > 0 { - t.Errorf("Got %v, expected no problems.", problems) - } else if test.msg != "" && !strings.Contains(problems[0].Message, test.msg) { - t.Errorf("Got %q, expected message containing %q", problems[0].Message, test.msg) + file := testutils.ParseProto3Tmpl(t, ` + import "google/api/annotations.proto"; + service Library { + rpc {{.MethodName}}({{.MethodName}}Request) returns (Book) { + option (google.api.http) = { + post: "/v1/{name=publishers/*/books/*}:undelete" + body: "{{.Body}}" + }; + } + } + message Book {} + message {{.MethodName}}Request {} + `, test) + method := file.GetServices()[0].GetMethods()[0] + problems := httpBody.Lint(file) + if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" { + t.Error(diff) } }) } diff --git a/rules/aip0164/http_method_test.go b/rules/aip0164/http_method_test.go index 9b2323286..72d6ad374 100644 --- a/rules/aip0164/http_method_test.go +++ b/rules/aip0164/http_method_test.go @@ -15,63 +15,43 @@ package aip0164 import ( - "strings" "testing" - dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/jhump/protoreflect/desc/builder" - "google.golang.org/genproto/googleapis/api/annotations" - "google.golang.org/protobuf/proto" + "github.com/googleapis/api-linter/rules/internal/testutils" ) func TestHttpMethod(t *testing.T) { - // Set up POST and DELETE HTTP annotations. - httpPost := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Post{ - Post: "/v1/{name=publishers/*/books/*}", - }, - } - httpDelete := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Delete{ - Delete: "/v1/{name=publishers/*/books/*}", - }, - } - // Set up testing permutations. tests := []struct { testName string - httpRule *annotations.HttpRule - methodName string - msg string + Method string + MethodName string + problems testutils.Problems }{ - {"Valid", httpPost, "UndeleteBook", ""}, - {"Invalid", httpDelete, "UndeleteBook", "HTTP POST"}, - {"Irrelevant", httpDelete, "AcquireBook", ""}, + {"Valid", "post", "UndeleteBook", nil}, + {"Invalid", "delete", "UndeleteBook", testutils.Problems{{Message: "HTTP POST"}}}, + {"Irrelevant", "delete", "AcquireBook", nil}, } // Run each test. for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a MethodOptions with the annotation set. - opts := &dpb.MethodOptions{} - proto.SetExtension(opts, annotations.E_Http, test.httpRule) - - // Create a minimal service with a AIP-164 Undelete method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage("UndeleteBookRequest"), false), - builder.RpcTypeMessage(builder.NewMessage("Book"), false), - ).SetOptions(opts)).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the method, ensure we get what we expect. - problems := httpMethod.Lint(service.GetFile()) - if test.msg == "" && len(problems) > 0 { - t.Errorf("Got %v, expected no problems.", problems) - } else if test.msg != "" && !strings.Contains(problems[0].Message, test.msg) { - t.Errorf("Got %q, expected message containing %q", problems[0].Message, test.msg) + file := testutils.ParseProto3Tmpl(t, ` + import "google/api/annotations.proto"; + service Library { + rpc {{.MethodName}}({{.MethodName}}Request) returns (Book) { + option (google.api.http) = { + {{.Method}}: "/v1/{name=publishers/*/books/*}:undelete" + }; + } + } + message Book {} + message {{.MethodName}}Request {} + `, test) + method := file.GetServices()[0].GetMethods()[0] + problems := httpMethod.Lint(file) + if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" { + t.Error(diff) } }) } diff --git a/rules/aip0164/request_message_name_test.go b/rules/aip0164/request_message_name_test.go index 910cdc399..c587b99e5 100644 --- a/rules/aip0164/request_message_name_test.go +++ b/rules/aip0164/request_message_name_test.go @@ -18,15 +18,14 @@ import ( "testing" "github.com/googleapis/api-linter/rules/internal/testutils" - "github.com/jhump/protoreflect/desc/builder" ) func TestRequestMessageName(t *testing.T) { // Set up the testing permutations. tests := []struct { testName string - methodName string - reqMessageName string + MethodName string + ReqMessageName string problems testutils.Problems }{ {"Valid", "UndeleteBook", "UndeleteBookRequest", testutils.Problems{}}, @@ -37,19 +36,17 @@ func TestRequestMessageName(t *testing.T) { // Run each test individually. for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a minimal service with a AIP-164 Undelete method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage(test.reqMessageName), false), - builder.RpcTypeMessage(builder.NewMessage("Book"), false), - )).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the lint rule, and establish that it returns the expected problems. - problems := requestMessageName.Lint(service.GetFile()) - if diff := test.problems.SetDescriptor(service.GetMethods()[0]).Diff(problems); diff != "" { + f := testutils.ParseProto3Tmpl(t, ` + service Library { + rpc {{.MethodName}}({{.ReqMessageName}}) returns (Book) {} + } + message {{.ReqMessageName}} {} + {{if ne .ReqMessageName "Book"}} + message Book {} + {{end}} + `, test) + m := f.GetServices()[0].GetMethods()[0] + if diff := test.problems.SetDescriptor(m).Diff(requestMessageName.Lint(f)); diff != "" { t.Errorf(diff) } }) diff --git a/rules/aip0231/http_body_test.go b/rules/aip0231/http_body_test.go index d02a3d04a..deaa56019 100644 --- a/rules/aip0231/http_body_test.go +++ b/rules/aip0231/http_body_test.go @@ -17,18 +17,14 @@ package aip0231 import ( "testing" - dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" "github.com/googleapis/api-linter/rules/internal/testutils" - "github.com/jhump/protoreflect/desc/builder" - "google.golang.org/genproto/googleapis/api/annotations" - "google.golang.org/protobuf/proto" ) func TestHttpBody(t *testing.T) { tests := []struct { testName string - body string - methodName string + Body string + MethodName string problems testutils.Problems }{ {"Valid", "", "BatchGetBooks", nil}, @@ -38,30 +34,23 @@ func TestHttpBody(t *testing.T) { for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a MethodOptions with the annotation set. - opts := &dpb.MethodOptions{} - httpRule := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Get{ - Get: "/v1/{name=publishers/*/books/*}", - }, - Body: test.body, - } - proto.SetExtension(opts, annotations.E_Http, httpRule) - - // Create a minimal service with a AIP-231 Get method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("BookService").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage("BatchGetBooksRequest"), false), - builder.RpcTypeMessage(builder.NewMessage("BatchGetBooksResponse"), false), - ).SetOptions(opts)).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the method, ensure we get what we expect. - problems := httpBody.Lint(service.GetFile()) - if diff := test.problems.SetDescriptor(service.GetMethods()[0]).Diff(problems); diff != "" { - t.Errorf(diff) + file := testutils.ParseProto3Tmpl(t, ` + import "google/api/annotations.proto"; + service Library { + rpc {{.MethodName}}({{.MethodName}}Request) returns ({{.MethodName}}Response) { + option (google.api.http) = { + get: "/v1/{parent=publishers/*}/books:batchGet" + body: "{{.Body}}" + }; + } + } + message {{.MethodName}}Request {} + message {{.MethodName}}Response {} + `, test) + method := file.GetServices()[0].GetMethods()[0] + problems := httpBody.Lint(file) + if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" { + t.Error(diff) } }) } diff --git a/rules/aip0231/http_method_test.go b/rules/aip0231/http_method_test.go index ec6221132..0aec9e99f 100644 --- a/rules/aip0231/http_method_test.go +++ b/rules/aip0231/http_method_test.go @@ -17,59 +17,41 @@ package aip0231 import ( "testing" - dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" "github.com/googleapis/api-linter/rules/internal/testutils" - "github.com/jhump/protoreflect/desc/builder" - "google.golang.org/genproto/googleapis/api/annotations" - "google.golang.org/protobuf/proto" ) func TestHttpVerb(t *testing.T) { - // Set up GET and POST HTTP annotations. - httpGet := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Get{ - Get: "/v1/{parent=publishers/*}/books:batchGet", - }, - } - httpPost := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Post{ - Post: "/v1/{name=publishers/*/books/*}", - }, - } - // Set up testing permutations. tests := []struct { testName string - httpRule *annotations.HttpRule - methodName string + Method string + MethodName string problems testutils.Problems }{ - {"Valid", httpGet, "BatchGetBooks", nil}, - {"Invalid", httpPost, "BatchGetBooks", testutils.Problems{{Message: "Batch Get methods must use the HTTP GET verb."}}}, - {"Irrelevant", httpPost, "AcquireBook", nil}, + {"Valid", "get", "BatchGetBooks", nil}, + {"Invalid", "post", "BatchGetBooks", testutils.Problems{{Message: "Batch Get methods must use the HTTP GET verb."}}}, + {"Irrelevant", "post", "AcquireBook", nil}, } // Run each test. for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a MethodOptions with the annotation set. - opts := &dpb.MethodOptions{} - proto.SetExtension(opts, annotations.E_Http, test.httpRule) - - // Create a minimal service with a AIP-231 Get method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("BookService").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage("BatchGetBooksRequest"), false), - builder.RpcTypeMessage(builder.NewMessage("BatchGetBooksResponse"), false), - ).SetOptions(opts)).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the method, ensure we get what we expect. - problems := httpVerb.Lint(service.GetFile()) - if diff := test.problems.SetDescriptor(service.GetMethods()[0]).Diff(problems); diff != "" { - t.Errorf(diff) + file := testutils.ParseProto3Tmpl(t, ` + import "google/api/annotations.proto"; + service Library { + rpc {{.MethodName}}({{.MethodName}}Request) returns ({{.MethodName}}Response) { + option (google.api.http) = { + {{.Method}}: "/v1/{name=publishers/*/books/*}:batchGet" + }; + } + } + message {{.MethodName}}Request {} + message {{.MethodName}}Response {} + `, test) + method := file.GetServices()[0].GetMethods()[0] + problems := httpVerb.Lint(file) + if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" { + t.Error(diff) } }) } diff --git a/rules/aip0231/http_uri_suffix_test.go b/rules/aip0231/http_uri_suffix_test.go index 8e1c4a4fe..ed49ef9ec 100644 --- a/rules/aip0231/http_uri_suffix_test.go +++ b/rules/aip0231/http_uri_suffix_test.go @@ -17,18 +17,14 @@ package aip0231 import ( "testing" - dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" "github.com/googleapis/api-linter/rules/internal/testutils" - "github.com/jhump/protoreflect/desc/builder" - "google.golang.org/genproto/googleapis/api/annotations" - "google.golang.org/protobuf/proto" ) func TestHttpUrl(t *testing.T) { tests := []struct { testName string - uri string - methodName string + URI string + MethodName string problems testutils.Problems }{ {"Valid", "/v1/{parent=publishers/*}/books:batchGet", "BatchGetBooks", nil}, @@ -39,28 +35,20 @@ func TestHttpUrl(t *testing.T) { for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a MethodOptions with the annotation set. - opts := &dpb.MethodOptions{} - httpRule := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Get{ - Get: test.uri, - }, - } - proto.SetExtension(opts, annotations.E_Http, httpRule) - - // Create a minimal service with a AIP-231 Get method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("BookService").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage("BatchGetBooksRequest"), false), - builder.RpcTypeMessage(builder.NewMessage("BatchGetBooksResponse"), false), - ).SetOptions(opts)).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the method, ensure we get what we expect. - problems := uriSuffix.Lint(service.GetFile()) - if diff := test.problems.SetDescriptor(service.GetMethods()[0]).Diff(problems); diff != "" { + f := testutils.ParseProto3Tmpl(t, ` + import "google/api/annotations.proto"; + service Library { + rpc {{.MethodName}}({{.MethodName}}Request) returns ({{.MethodName}}Response) { + option (google.api.http) = { + get: "{{.URI}}" + }; + } + } + message {{.MethodName}}Request {} + message {{.MethodName}}Response {} + `, test) + method := f.GetServices()[0].GetMethods()[0] + if diff := test.problems.SetDescriptor(method).Diff(uriSuffix.Lint(f)); diff != "" { t.Errorf(diff) } }) diff --git a/rules/aip0235/http_body_test.go b/rules/aip0235/http_body_test.go index 6ffad74ae..c0c295a9e 100644 --- a/rules/aip0235/http_body_test.go +++ b/rules/aip0235/http_body_test.go @@ -15,55 +15,42 @@ package aip0235 import ( - "strings" "testing" - dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/jhump/protoreflect/desc/builder" - "google.golang.org/genproto/googleapis/api/annotations" - "google.golang.org/protobuf/proto" + "github.com/googleapis/api-linter/rules/internal/testutils" ) func TestHttpBody(t *testing.T) { tests := []struct { testName string - body string - methodName string - msg string + Body string + MethodName string + problems testutils.Problems }{ - {"Valid", "*", "BatchDeleteBooks", ""}, - {"Invalid", "", "BatchDeleteBooks", "HTTP body"}, - {"Irrelevant", "*", "AcquireBook", ""}, + {"Valid", "*", "BatchDeleteBooks", nil}, + {"Invalid", "", "BatchDeleteBooks", testutils.Problems{{Message: "HTTP body"}}}, + {"Irrelevant", "*", "AcquireBook", nil}, } for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a MethodOptions with the annotation set. - opts := &dpb.MethodOptions{} - httpRule := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Post{ - Post: "/v1/{name=publishers/*/books/*}", - }, - Body: test.body, - } - proto.SetExtension(opts, annotations.E_Http, httpRule) - - // Create a minimal service with a AIP-235 Batch Delete method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage("BatchDeleteBooksRequest"), false), - builder.RpcTypeMessage(builder.NewMessage("Book"), false), - ).SetOptions(opts)).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the method, ensure we get what we expect. - problems := httpBody.Lint(service.GetFile()) - if test.msg == "" && len(problems) > 0 { - t.Errorf("Got %v, expected no problems.", problems) - } else if test.msg != "" && !strings.Contains(problems[0].Message, test.msg) { - t.Errorf("Got %q, expected message containing %q", problems[0].Message, test.msg) + file := testutils.ParseProto3Tmpl(t, ` + import "google/api/annotations.proto"; + service Library { + rpc {{.MethodName}}({{.MethodName}}Request) returns ({{.MethodName}}Response) { + option (google.api.http) = { + post: "/v1/{parent=publishers/*}/books:batchDelete" + body: "{{.Body}}" + }; + } + } + message {{.MethodName}}Request {} + message {{.MethodName}}Response {} + `, test) + method := file.GetServices()[0].GetMethods()[0] + problems := httpBody.Lint(file) + if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" { + t.Error(diff) } }) } diff --git a/rules/aip0235/http_method_test.go b/rules/aip0235/http_method_test.go index 3d5549410..88ec8d6f4 100644 --- a/rules/aip0235/http_method_test.go +++ b/rules/aip0235/http_method_test.go @@ -15,63 +15,43 @@ package aip0235 import ( - "strings" "testing" - dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/jhump/protoreflect/desc/builder" - "google.golang.org/genproto/googleapis/api/annotations" - "google.golang.org/protobuf/proto" + "github.com/googleapis/api-linter/rules/internal/testutils" ) func TestHttpMethod(t *testing.T) { - // Set up POST and DELETE HTTP annotations. - httpPost := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Post{ - Post: "/v1/{name=publishers/*/books/*}", - }, - } - httpDelete := &annotations.HttpRule{ - Pattern: &annotations.HttpRule_Delete{ - Delete: "/v1/{name=publishers/*/books/*}", - }, - } - // Set up testing permutations. tests := []struct { testName string - httpRule *annotations.HttpRule - methodName string - msg string + Method string + MethodName string + problems testutils.Problems }{ - {"Valid", httpPost, "BatchDeleteBooks", ""}, - {"Invalid", httpDelete, "BatchDeleteBooks", "HTTP POST"}, - {"Irrelevant", httpPost, "AcquireBook", ""}, + {"Valid", "post", "BatchDeleteBooks", nil}, + {"Invalid", "delete", "BatchDeleteBooks", testutils.Problems{{Message: "HTTP POST"}}}, + {"Irrelevant", "post", "AcquireBook", nil}, } // Run each test. for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a MethodOptions with the annotation set. - opts := &dpb.MethodOptions{} - proto.SetExtension(opts, annotations.E_Http, test.httpRule) - - // Create a minimal service with a AIP-235 Batch Delete method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage("BatchDeleteBooksRequests"), false), - builder.RpcTypeMessage(builder.NewMessage("Book"), false), - ).SetOptions(opts)).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the method, ensure we get what we expect. - problems := httpMethod.Lint(service.GetFile()) - if test.msg == "" && len(problems) > 0 { - t.Errorf("Got %v, expected no problems.", problems) - } else if test.msg != "" && !strings.Contains(problems[0].Message, test.msg) { - t.Errorf("Got %q, expected message containing %q", problems[0].Message, test.msg) + file := testutils.ParseProto3Tmpl(t, ` + import "google/api/annotations.proto"; + service Library { + rpc {{.MethodName}}({{.MethodName}}Request) returns ({{.MethodName}}Response) { + option (google.api.http) = { + {{.Method}}: "/v1/{name=publishers/*/books/*}:batchDelete" + }; + } + } + message {{.MethodName}}Request {} + message {{.MethodName}}Response {} + `, test) + method := file.GetServices()[0].GetMethods()[0] + problems := httpMethod.Lint(file) + if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" { + t.Error(diff) } }) } diff --git a/rules/aip0235/request_message_name_test.go b/rules/aip0235/request_message_name_test.go index cfb963d03..ab1f0bccd 100644 --- a/rules/aip0235/request_message_name_test.go +++ b/rules/aip0235/request_message_name_test.go @@ -18,15 +18,14 @@ import ( "testing" "github.com/googleapis/api-linter/rules/internal/testutils" - "github.com/jhump/protoreflect/desc/builder" ) func TestRequestMessageName(t *testing.T) { // Set up the testing permutations. tests := []struct { testName string - methodName string - reqMessageName string + MethodName string + ReqMessageName string problems testutils.Problems }{ {"Valid", "BatchDeleteBooks", "BatchDeleteBooksRequest", testutils.Problems{}}, @@ -37,19 +36,15 @@ func TestRequestMessageName(t *testing.T) { // Run each test individually. for _, test := range tests { t.Run(test.testName, func(t *testing.T) { - // Create a minimal service with a AIP-235 Batch Delete method - // (or with a different method, in the "Irrelevant" case). - service, err := builder.NewService("Library").AddMethod(builder.NewMethod(test.methodName, - builder.RpcTypeMessage(builder.NewMessage(test.reqMessageName), false), - builder.RpcTypeMessage(builder.NewMessage("Book"), false), - )).Build() - if err != nil { - t.Fatalf("Could not build %s method.", test.methodName) - } - - // Run the lint rule, and establish that it returns the expected problems. - problems := requestMessageName.Lint(service.GetFile()) - if diff := test.problems.SetDescriptor(service.GetMethods()[0]).Diff(problems); diff != "" { + f := testutils.ParseProto3Tmpl(t, ` + service Library { + rpc {{.MethodName}}({{.ReqMessageName}}) returns (BatchDeleteBooksResponse) {} + } + message {{.ReqMessageName}} {} + message BatchDeleteBooksResponse {} + `, test) + m := f.GetServices()[0].GetMethods()[0] + if diff := test.problems.SetDescriptor(m).Diff(requestMessageName.Lint(f)); diff != "" { t.Errorf(diff) } })