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 }