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

     1  // Copyright 2020 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package utils
    16  
    17  import (
    18  	"fmt"
    19  	"testing"
    20  
    21  	"github.com/googleapis/api-linter/rules/internal/testutils"
    22  )
    23  
    24  func TestDeclarativeFriendlyMessage(t *testing.T) {
    25  	// Test the cases where a google.api.resource annotation is present.
    26  	for _, test := range []struct {
    27  		name  string
    28  		Style string
    29  		want  bool
    30  	}{
    31  		{"True", "style: DECLARATIVE_FRIENDLY", true},
    32  		{"FalseNoStyle", "", false},
    33  		{"FalseOtherStyle", "style: STYLE_UNSPECIFIED", false},
    34  	} {
    35  		t.Run(test.name, func(t *testing.T) {
    36  			f := testutils.ParseProto3Tmpl(t, `
    37  				import "google/api/resource.proto";
    38  
    39  				message Book {
    40  					option (google.api.resource) = {
    41  						type: "library.googleapis.com/Book"
    42  						{{.Style}}
    43  					};
    44  				}
    45  
    46  				message CreateBookRequest {
    47  					Book book = 1;
    48  				}
    49  
    50  				service Library {
    51  					rpc CreateBook(CreateBookRequest) returns (Book);
    52  				}
    53  			`, test)
    54  			for _, m := range f.GetMessageTypes() {
    55  				t.Run(m.GetName(), func(t *testing.T) {
    56  					if got := IsDeclarativeFriendlyMessage(m); got != test.want {
    57  						t.Errorf("Got %v, expected %v.", got, test.want)
    58  					}
    59  				})
    60  			}
    61  		})
    62  	}
    63  
    64  	// Test the case where the google.api.resource annotation is not present.
    65  	t.Run("NotResource", func(t *testing.T) {
    66  		m := testutils.ParseProto3String(t, "message Book {}").GetMessageTypes()[0]
    67  		if IsDeclarativeFriendlyMessage(m) {
    68  			t.Errorf("Got true, expected false.")
    69  		}
    70  	})
    71  }
    72  
    73  func TestDeclarativeFriendlyMethod(t *testing.T) {
    74  	// We need different templates for different situations.
    75  	//
    76  	// Note: The Book resource itself is always present and omitted here to
    77  	// avoid excess repetition; it is appended to the templates in the body of
    78  	// the test.
    79  	tmpls := map[string]string{
    80  		// The basic template just returns the resource with no frills.
    81  		"basic": `
    82  			service Library {
    83  				rpc GetBook(GetBookRequest) returns (Book);
    84  			}
    85  
    86  			message GetBookRequest {}
    87  		`,
    88  
    89  		// The LRO template returns the resource, but as the result of an LRO
    90  		// that has to be resolved first.
    91  		"lro": `
    92  			import "google/longrunning/operations.proto";
    93  
    94  			service Library {
    95  				rpc GetBook(GetBookRequest) returns (google.longrunning.Operation) {
    96  					option (google.longrunning.operation_info) = {
    97  						response_type: "Book"
    98  					};
    99  				}
   100  			}
   101  
   102  			message GetBookRequest {}
   103  		`,
   104  
   105  		// The List template returns a normal list response.
   106  		"list": `
   107  			service Library {
   108  				rpc ListBooks(ListBooksRequest) returns (ListBooksResponse);
   109  			}
   110  
   111  			message ListBooksRequest {}
   112  			message ListBooksResponse {
   113  				repeated Book books = 1;
   114  			}
   115  		`,
   116  
   117  		// The Delete template returns a normal delete response.
   118  		"delete": `
   119  			import "google/protobuf/empty.proto";
   120  			service Library {
   121  				rpc DeleteBook(DeleteBookRequest) returns (google.protobuf.Empty);
   122  			}
   123  			message DeleteBookRequest {}
   124  		`,
   125  
   126  		// The custom method template is a straightforward custom method
   127  		// with no direct reference to Book.
   128  		"custom": `
   129  			service Library {
   130  				rpc ArchiveBook(ArchiveBookRequest) returns (ArchiveBookResponse);
   131  			}
   132  
   133  			message ArchiveBookRequest {}
   134  			message ArchiveBookResponse {}
   135  		`,
   136  	}
   137  
   138  	for key, tmpl := range tmpls {
   139  		t.Run(key, func(t *testing.T) {
   140  			for _, test := range []struct {
   141  				name string
   142  				want bool
   143  			}{
   144  				{"true", true},
   145  				{"false", false},
   146  			} {
   147  				t.Run(test.name, func(t *testing.T) {
   148  					// Set the style of the resource to DECLARATIVE_FRIENDLY if that
   149  					// is the expected result.
   150  					s := struct{ Style string }{Style: ""}
   151  					if test.want == true {
   152  						s.Style = "style: DECLARATIVE_FRIENDLY"
   153  					}
   154  
   155  					// Parse the template and test the method.
   156  					f := testutils.ParseProto3Tmpl(t, fmt.Sprintf(`
   157  						import "google/api/resource.proto";
   158  
   159  						%s
   160  
   161  						message Book {
   162  							option (google.api.resource) = {
   163  								type: "library.googleapis.com/Book"
   164  								{{.Style}}
   165  							};
   166  						}
   167  					`, tmpl), s)
   168  					m := f.GetServices()[0].GetMethods()[0]
   169  					if got := IsDeclarativeFriendlyMethod(m); got != test.want {
   170  						t.Errorf("Got %v, expected %v.", got, test.want)
   171  					}
   172  				})
   173  			}
   174  		})
   175  	}
   176  
   177  	// Test an edge case where the LRO response is not found.
   178  	t.Run("lro/not-found", func(t *testing.T) {
   179  		f := testutils.ParseProto3String(t, `
   180  			import "google/longrunning/operations.proto";
   181  			service Library {
   182  				rpc CreateBook(CreateBookRequest) returns (google.longrunning.Operation) {
   183  					option (google.longrunning.operation_info) = {
   184  						response_type: "Shrug"
   185  					};
   186  				}
   187  			}
   188  			message CreateBookRequest {}
   189  		`)
   190  		m := f.GetServices()[0].GetMethods()[0]
   191  		want := false
   192  		if got := IsDeclarativeFriendlyMethod(m); got != want {
   193  			t.Errorf("Got %v, expected %v.", got, want)
   194  		}
   195  	})
   196  }