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  }