github.com/googleapis/api-linter@v1.65.2/rules/internal/utils/method_test.go (about)

     1  // Copyright 2023 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //	https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  package utils
    15  
    16  import (
    17  	"testing"
    18  
    19  	"github.com/googleapis/api-linter/rules/internal/testutils"
    20  )
    21  
    22  func TestIsCreateMethod(t *testing.T) {
    23  	for _, test := range []struct {
    24  		name string
    25  		RPCs string
    26  		want bool
    27  	}{
    28  		{"ValidBook", `
    29  			rpc CreateBook(CreateBookRequest) returns (Book) {};
    30  		`, true},
    31  		{"InvalidNonCreate", `
    32  			rpc GenerateBook(CreateBookRequest) returns (Book) {};
    33  		`, false},
    34  	} {
    35  		t.Run(test.name, func(t *testing.T) {
    36  			file := testutils.ParseProto3Tmpl(t, `
    37  				import "google/api/resource.proto";
    38  				import "google/protobuf/field_mask.proto";
    39  				service Foo {
    40  					{{.RPCs}}
    41  				}
    42  
    43  				// This is at the top to make it retrievable
    44  				// by the test code.
    45  				message Book {
    46  					option (google.api.resource) = {
    47  						type: "library.googleapis.com/Book"
    48  						pattern: "books/{book}"
    49  						singular: "book"
    50  						plural: "books"
    51  					};
    52  				}
    53  
    54  				message CreateBookRequest {
    55  					// The parent resource where this book will be created.
    56  					// Format: publishers/{publisher}
    57  					string parent = 1;
    58  
    59  					// The book to create.
    60  					Book book = 2;
    61  				}
    62  			`, test)
    63  			method := file.GetServices()[0].GetMethods()[0]
    64  			got := IsCreateMethod(method)
    65  			if got != test.want {
    66  				t.Errorf("IsCreateMethod got %v, want %v", got, test.want)
    67  			}
    68  		})
    69  	}
    70  }
    71  
    72  func TestIsUpdateMethod(t *testing.T) {
    73  	for _, test := range []struct {
    74  		name string
    75  		RPCs string
    76  		want bool
    77  	}{
    78  		{"ValidBook", `
    79  			rpc UpdateBook(UpdateBookRequest) returns (Book) {};
    80  		`, true},
    81  		{"InvalidNonUpdate", `
    82  			rpc UpsertBook(UpdateBookRequest) returns (Book) {};
    83  		`, false},
    84  	} {
    85  		t.Run(test.name, func(t *testing.T) {
    86  			file := testutils.ParseProto3Tmpl(t, `
    87  				import "google/api/resource.proto";
    88  				import "google/protobuf/field_mask.proto";
    89  				service Foo {
    90  					{{.RPCs}}
    91  				}
    92  
    93  				// This is at the top to make it retrievable
    94  				// by the test code.
    95  				message Book {
    96  					option (google.api.resource) = {
    97  						type: "library.googleapis.com/Book"
    98  						pattern: "books/{book}"
    99  						singular: "book"
   100  						plural: "books"
   101  					};
   102  				}
   103  
   104  				message UpdateBookRequest {
   105  					Book book = 1;
   106  					google.protobuf.FieldMask update_mask = 2;
   107  				}
   108  			`, test)
   109  			method := file.GetServices()[0].GetMethods()[0]
   110  			got := IsUpdateMethod(method)
   111  			if got != test.want {
   112  				t.Errorf("IsUpdateMethod got %v, want %v", got, test.want)
   113  			}
   114  		})
   115  	}
   116  }
   117  
   118  func TestIsListMethod(t *testing.T) {
   119  	for _, test := range []struct {
   120  		name string
   121  		RPCs string
   122  		want bool
   123  	}{
   124  		{"ValidList", `
   125  			rpc ListBooks(ListBooksRequest) returns (ListBooksResponse) {};
   126  		`, true},
   127  		{"ValidListRevisionsMethod", `
   128  			rpc ListBookRevisions(ListBookRevisionsRequest) returns (ListBookRevisionsResponse) {};
   129  		`, true},
   130  		{"InvalidNonList", `
   131  			rpc EnumerateBooks(ListBooksRequest) returns (ListBooksResponse) {};
   132  		`, false},
   133  	} {
   134  		t.Run(test.name, func(t *testing.T) {
   135  			file := testutils.ParseProto3Tmpl(t, `
   136  				import "google/api/resource.proto";
   137  				import "google/protobuf/field_mask.proto";
   138  				service Foo {
   139  					{{.RPCs}}
   140  				}
   141  
   142  				// This is at the top to make it retrievable
   143  				// by the test code.
   144  				message Book {
   145  					option (google.api.resource) = {
   146  						type: "library.googleapis.com/Book"
   147  						pattern: "books/{book}"
   148  						singular: "book"
   149  						plural: "books"
   150  					};
   151  				}
   152  
   153  				// This is at the top to make it retrievable
   154  				// by the test code.
   155  				message BookRevision {
   156  					option (google.api.resource) = {
   157  						type: "library.googleapis.com/BookRevision"
   158  						pattern: "books/{book}/revisions/{revision}"
   159  						singular: "bookRevision"
   160  						plural: "bookRevisions"
   161  					};
   162  				}
   163  
   164  				message ListBooksRequest {
   165  					string parent = 1;
   166  					int32 page_size = 2;
   167  					string page_token = 3;
   168  				}
   169  
   170  				message ListBooksResponse {
   171  					repeated Book books = 1;
   172  					string next_page_token = 2;
   173  				}
   174  
   175  				message ListBookRevisionsRequest {
   176  					string parent = 1;
   177  					int32 page_size = 2;
   178  					string page_token = 3;
   179  				}
   180  
   181  				message ListBookRevisionsResponse {
   182  					repeated BookRevision book_revisions = 1;
   183  					string next_page_token = 2;
   184  				}
   185  			`, test)
   186  			method := file.GetServices()[0].GetMethods()[0]
   187  			got := IsListMethod(method)
   188  			if got != test.want {
   189  				t.Errorf("IsListMethod got %v, want %v", got, test.want)
   190  			}
   191  		})
   192  	}
   193  }
   194  
   195  func TestIsLegacyListRevisionsMethod(t *testing.T) {
   196  	for _, test := range []struct {
   197  		name string
   198  		RPCs string
   199  		want bool
   200  	}{
   201  		{"ValidLegacyListRevisionsMethod", `
   202  			rpc ListBookRevisions(ListBookRevisionsRequest) returns (ListBookRevisionsResponse) {
   203  				option (google.api.http) = {
   204  					get: "/v1/{name=books/*}:listRevisions"
   205  				};
   206  			};
   207  		`, true},
   208  		{"ValidLegacyListRevisionsMethodWithoutHTTP", `
   209  			rpc ListBookRevisions(ListBookRevisionsRequest) returns (ListBookRevisionsResponse) {};
   210  		`, true},
   211  		{"InvalidLegacyListRevisionsMethod", `
   212  			rpc ListBooks(ListBooksRequest) returns (ListBooksResponse) {};
   213  		`, false},
   214  	} {
   215  		t.Run(test.name, func(t *testing.T) {
   216  			file := testutils.ParseProto3Tmpl(t, `
   217  				import "google/api/annotations.proto";
   218  				import "google/api/resource.proto";
   219  				import "google/protobuf/field_mask.proto";
   220  				service Foo {
   221  					{{.RPCs}}
   222  				}
   223  
   224  				// This is at the top to make it retrievable
   225  				// by the test code.
   226  				message Book {
   227  					option (google.api.resource) = {
   228  						type: "library.googleapis.com/Book"
   229  						pattern: "books/{book}"
   230  						singular: "book"
   231  						plural: "books"
   232  					};
   233  				}
   234  
   235  				message ListBooksRequest {
   236  					string parent = 1;
   237  					int32 page_size = 2;
   238  					string page_token = 3;
   239  				}
   240  
   241  				message ListBooksResponse {
   242  					repeated Book books = 1;
   243  					string next_page_token = 2;
   244  				}
   245  
   246  				message ListBookRevisionsRequest {
   247  					string name = 1;
   248  					int32 page_size = 2;
   249  					string page_token = 3;
   250  				}
   251  
   252  				message ListBookRevisionsResponse {
   253  					repeated Book books = 1;
   254  					string next_page_token = 2;
   255  				}
   256  			`, test)
   257  			method := file.GetServices()[0].GetMethods()[0]
   258  			got := IsLegacyListRevisionsMethod(method)
   259  			if got != test.want {
   260  				t.Errorf("IsLegacyListRevisionsMethod got %v, want %v", got, test.want)
   261  			}
   262  		})
   263  	}
   264  }
   265  
   266  func TestGetListResourceMessage(t *testing.T) {
   267  	for _, test := range []struct {
   268  		name string
   269  		RPCs string
   270  		want string
   271  	}{
   272  		{"ValidBooks", `
   273  			rpc ListBooks(ListBooksRequest) returns (ListBooksResponse) {};
   274  		`, "Book"},
   275  		{"InvalidNotListMethod", `
   276  			rpc GetBook(ListBooksRequest) returns (Book) {};
   277  		`, ""},
   278  	} {
   279  		t.Run(test.name, func(t *testing.T) {
   280  			file := testutils.ParseProto3Tmpl(t, `
   281  				import "google/api/resource.proto";
   282  				import "google/protobuf/field_mask.proto";
   283  				service Foo {
   284  					{{.RPCs}}
   285  				}
   286  
   287  				// This is at the top to make it retrievable
   288  				// by the test code.
   289  				message Book {
   290  					option (google.api.resource) = {
   291  						type: "library.googleapis.com/Book"
   292  						pattern: "books/{book}"
   293  						singular: "book"
   294  						plural: "books"
   295  					};
   296  				}
   297  
   298  				message ListBooksRequest {
   299  					string parent = 1;
   300  					int32 page_size = 2;
   301  					string page_token = 3;
   302  				}
   303  
   304  				message ListBooksResponse {
   305  					repeated Book books = 1;
   306  					string next_page_token = 2;
   307  				}
   308  			`, test)
   309  			method := file.GetServices()[0].GetMethods()[0]
   310  			message := GetListResourceMessage(method)
   311  			got := ""
   312  			if message != nil {
   313  				got = message.GetName()
   314  			}
   315  			if got != test.want {
   316  				t.Errorf("GetListResourceMessage got %q, want %q", got, test.want)
   317  			}
   318  		})
   319  	}
   320  }