github.com/googleapis/api-linter@v1.65.2/rules/internal/utils/common_lints_test.go (about) 1 package utils 2 3 import ( 4 "testing" 5 6 "github.com/googleapis/api-linter/rules/internal/testutils" 7 "github.com/jhump/protoreflect/desc/builder" 8 ) 9 10 func TestLintSingularStringField(t *testing.T) { 11 for _, test := range []struct { 12 testName string 13 FieldType string 14 problems testutils.Problems 15 }{ 16 {"Valid", `string`, nil}, 17 {"Invalid", `int32`, testutils.Problems{{Suggestion: "string"}}}, 18 {"InvalidRepeated", `repeated string`, testutils.Problems{{Suggestion: "string"}}}, 19 } { 20 t.Run(test.testName, func(t *testing.T) { 21 f := testutils.ParseProto3Tmpl(t, ` 22 message Message { 23 {{.FieldType}} foo = 1; 24 } 25 `, test) 26 field := f.GetMessageTypes()[0].GetFields()[0] 27 problems := LintSingularStringField(field) 28 if diff := test.problems.SetDescriptor(field).Diff(problems); diff != "" { 29 t.Error(diff) 30 } 31 }) 32 } 33 } 34 35 func TestLintRequiredField(t *testing.T) { 36 for _, test := range []struct { 37 testName string 38 Annotation string 39 problems testutils.Problems 40 }{ 41 {"Valid", `[(google.api.field_behavior) = REQUIRED]`, nil}, 42 {"Invalid", ``, testutils.Problems{{Message: "REQUIRED"}}}, 43 } { 44 t.Run(test.testName, func(t *testing.T) { 45 f := testutils.ParseProto3Tmpl(t, ` 46 import "google/api/field_behavior.proto"; 47 message Message { 48 string foo = 1 {{.Annotation}}; 49 } 50 `, test) 51 field := f.GetMessageTypes()[0].GetFields()[0] 52 problems := LintRequiredField(field) 53 if diff := test.problems.SetDescriptor(field).Diff(problems); diff != "" { 54 t.Error(diff) 55 } 56 }) 57 } 58 } 59 60 func TestLintFieldResourceReference(t *testing.T) { 61 for _, test := range []struct { 62 testName string 63 Annotation string 64 problems testutils.Problems 65 }{ 66 {"Valid", `[(google.api.resource_reference).type = "bar"]`, nil}, 67 {"Invalid", ``, testutils.Problems{{Message: "resource_reference"}}}, 68 } { 69 t.Run(test.testName, func(t *testing.T) { 70 f := testutils.ParseProto3Tmpl(t, ` 71 import "google/api/resource.proto"; 72 message Message { 73 string foo = 1 {{.Annotation}}; 74 } 75 `, test) 76 field := f.GetMessageTypes()[0].GetFields()[0] 77 problems := LintFieldResourceReference(field) 78 if diff := test.problems.SetDescriptor(field).Diff(problems); diff != "" { 79 t.Error(diff) 80 } 81 }) 82 } 83 } 84 85 func TestLintNoHTTPBody(t *testing.T) { 86 for _, test := range []struct { 87 testName string 88 Body string 89 problems testutils.Problems 90 }{ 91 {"Valid", ``, nil}, 92 {"Invalid", `*`, testutils.Problems{{Message: "not have an HTTP body"}}}, 93 } { 94 t.Run(test.testName, func(t *testing.T) { 95 f := testutils.ParseProto3Tmpl(t, ` 96 import "google/api/annotations.proto"; 97 service Library { 98 rpc GetBook(GetBookRequest) returns (Book) { 99 option (google.api.http) = { 100 get: "/v1/{name=publishers/*/books/*}" 101 body: "{{.Body}}" 102 }; 103 } 104 } 105 message Book {} 106 message GetBookRequest {} 107 `, test) 108 method := f.GetServices()[0].GetMethods()[0] 109 problems := LintNoHTTPBody(method) 110 if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" { 111 t.Error(diff) 112 } 113 }) 114 } 115 } 116 117 func TestLintWildcardHTTPBody(t *testing.T) { 118 for _, test := range []struct { 119 testName string 120 Body string 121 problems testutils.Problems 122 }{ 123 {"Valid", `*`, nil}, 124 {"Invalid", ``, testutils.Problems{{Message: `use "*" as the HTTP body`}}}, 125 } { 126 t.Run(test.testName, func(t *testing.T) { 127 f := testutils.ParseProto3Tmpl(t, ` 128 import "google/api/annotations.proto"; 129 service Library { 130 rpc ArchiveBook(ArchiveBookRequest) returns (Book) { 131 option (google.api.http) = { 132 post: "/v1/{name=publishers/*/books/*}:archive" 133 body: "{{.Body}}" 134 }; 135 } 136 } 137 message Book {} 138 message ArchiveBookRequest {} 139 `, test) 140 method := f.GetServices()[0].GetMethods()[0] 141 problems := LintWildcardHTTPBody(method) 142 if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" { 143 t.Error(diff) 144 } 145 }) 146 } 147 } 148 149 func TestLintHTTPMethod(t *testing.T) { 150 for _, test := range []struct { 151 testName string 152 Method string 153 problems testutils.Problems 154 }{ 155 {"Valid", `get`, nil}, 156 {"Invalid", `delete`, testutils.Problems{{Message: `HTTP GET`}}}, 157 } { 158 t.Run(test.testName, func(t *testing.T) { 159 f := testutils.ParseProto3Tmpl(t, ` 160 import "google/api/annotations.proto"; 161 service Library { 162 rpc GetBook(GetBookRequest) returns (Book) { 163 option (google.api.http) = { 164 {{.Method}}: "/v1/{name=publishers/*/books/*}" 165 }; 166 } 167 } 168 message Book {} 169 message GetBookRequest {} 170 `, test) 171 method := f.GetServices()[0].GetMethods()[0] 172 problems := LintHTTPMethod("GET")(method) 173 if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" { 174 t.Error(diff) 175 } 176 }) 177 } 178 } 179 180 func TestLintMethodHasMatchingRequestName(t *testing.T) { 181 for _, test := range []struct { 182 testName string 183 MessageName string 184 problems testutils.Problems 185 }{ 186 {"Valid", "GetBookRequest", nil}, 187 {"Invalid", "AcquireBookRequest", testutils.Problems{{Suggestion: "GetBookRequest"}}}, 188 } { 189 t.Run(test.testName, func(t *testing.T) { 190 f := testutils.ParseProto3Tmpl(t, ` 191 service Library { 192 rpc GetBook({{.MessageName}}) returns (Book); 193 } 194 message Book {} 195 message {{.MessageName}} {} 196 `, test) 197 method := f.GetServices()[0].GetMethods()[0] 198 problems := LintMethodHasMatchingRequestName(method) 199 if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" { 200 t.Error(diff) 201 } 202 }) 203 } 204 } 205 206 func TestLintMethodHasMatchingResponseName(t *testing.T) { 207 for _, test := range []struct { 208 testName string 209 MessageName string 210 problems testutils.Problems 211 }{ 212 {"Valid", "GetBookResponse", nil}, 213 {"Invalid", "AcquireBookResponse", testutils.Problems{{Suggestion: "GetBookResponse"}}}, 214 } { 215 t.Run(test.testName, func(t *testing.T) { 216 f := testutils.ParseProto3Tmpl(t, ` 217 service Library { 218 rpc GetBook(GetBookRequest) returns ({{.MessageName}}); 219 } 220 message GetBookRequest {} 221 message {{.MessageName}} {} 222 `, test) 223 method := f.GetServices()[0].GetMethods()[0] 224 problems := LintMethodHasMatchingResponseName(method) 225 if diff := test.problems.SetDescriptor(method).Diff(problems); diff != "" { 226 t.Error(diff) 227 } 228 }) 229 } 230 } 231 232 func TestLintSingularField(t *testing.T) { 233 for _, test := range []struct { 234 testName string 235 Label string 236 problems testutils.Problems 237 }{ 238 {"Valid", "", nil}, 239 {"Invalid", "repeated", testutils.Problems{{Suggestion: "string"}}}, 240 } { 241 t.Run(test.testName, func(t *testing.T) { 242 f := testutils.ParseProto3Tmpl(t, ` 243 message Message { 244 {{.Label}} string foo = 1; 245 } 246 `, test) 247 field := f.GetMessageTypes()[0].GetFields()[0] 248 problems := LintSingularField(field, builder.FieldTypeString(), "string") 249 if diff := test.problems.SetDescriptor(field).Diff(problems); diff != "" { 250 t.Error(diff) 251 } 252 }) 253 } 254 } 255 256 func TestLintNotOneof(t *testing.T) { 257 for _, test := range []struct { 258 testName string 259 Field string 260 problems testutils.Problems 261 }{ 262 {"Valid", `string foo = 1;`, nil}, 263 {"ValidProto3Optional", `optional string foo = 1;`, nil}, 264 {"Invalid", `oneof foo_oneof { string foo = 1; }`, testutils.Problems{{Message: "should not be a oneof"}}}, 265 } { 266 t.Run(test.testName, func(t *testing.T) { 267 f := testutils.ParseProto3Tmpl(t, ` 268 message Message { 269 {{.Field}} 270 } 271 `, test) 272 field := f.GetMessageTypes()[0].GetFields()[0] 273 problems := LintNotOneof(field) 274 if diff := test.problems.SetDescriptor(field).Diff(problems); diff != "" { 275 t.Error(diff) 276 } 277 }) 278 } 279 }