github.com/googleapis/api-linter@v1.65.2/rules/aip0148/declarative_friendly_fields_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 aip0148
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  	"testing"
    21  
    22  	"bitbucket.org/creachadair/stringset"
    23  	"github.com/googleapis/api-linter/rules/internal/testutils"
    24  )
    25  
    26  func TestDeclarativeFriendlyFields(t *testing.T) {
    27  	for _, test := range []struct {
    28  		name    string
    29  		skipped stringset.Set
    30  	}{
    31  		{"Valid", stringset.New()},
    32  		{"Name", stringset.New("name")},
    33  		{"UID", stringset.New("uid")},
    34  		{"DisplayName", stringset.New("display_name")},
    35  		{"CreateTime", stringset.New("create_time")},
    36  		{"UpdateTime", stringset.New("update_time")},
    37  		{"DeleteTime", stringset.New("delete_time")},
    38  		{"AllTimes", stringset.New("create_time", "update_time", "delete_time")},
    39  		{"Randos", stringset.New("uid", "display_name")},
    40  	} {
    41  		t.Run(test.name, func(t *testing.T) {
    42  			// Set up the string with the fields we will include.
    43  			fields := ""
    44  			cursor := 1
    45  			for fieldName, fieldType := range reqFields {
    46  				if !test.skipped.Contains(fieldName) {
    47  					fields += fmt.Sprintf("  %s %s = %d;\n", fieldType, fieldName, cursor)
    48  					cursor++
    49  				}
    50  			}
    51  
    52  			// Create the potential problem object for the missing fields.
    53  			var problems testutils.Problems
    54  			if test.skipped.Len() == 1 {
    55  				f := test.skipped.Unordered()[0]
    56  				problems = testutils.Problems{{
    57  					Message: fmt.Sprintf("must include the `%s %s` field", reqFields[f], f),
    58  				}}
    59  			} else if test.skipped.Len() > 1 {
    60  				missingFields := stringset.New()
    61  				for _, f := range test.skipped.Unordered() {
    62  					missingFields.Add(fmt.Sprintf("%s %s", reqFields[f], f))
    63  				}
    64  				msg := ""
    65  				for _, f := range missingFields.Elements() {
    66  					msg += fmt.Sprintf("  - `%s`\n", f)
    67  				}
    68  				problems = testutils.Problems{{Message: strings.TrimSuffix(msg, "\n")}}
    69  			}
    70  
    71  			// Test against declarative-friendly and standard styles.
    72  			for _, subtest := range []struct {
    73  				name     string
    74  				style    string
    75  				problems testutils.Problems
    76  			}{
    77  				{"DeclFriendly", "style: DECLARATIVE_FRIENDLY", problems},
    78  				{"NotDeclFriendly", "", nil},
    79  			} {
    80  				t.Run(subtest.name, func(t *testing.T) {
    81  					f := testutils.ParseProto3Tmpl(t, `
    82  						import "google/api/resource.proto";
    83  						import "google/protobuf/timestamp.proto";
    84  						message Book {
    85  							option (google.api.resource) = {
    86  								type: "library.googleapis.com/Book"
    87  								pattern: "publishers/{publisher}/books/{book}"
    88  								{{.Style}}
    89  							};
    90  							{{.Fields}}
    91  						}
    92  					`, struct {
    93  						Fields string
    94  						Style  string
    95  					}{Fields: fields, Style: subtest.style})
    96  					m := f.GetMessageTypes()[0]
    97  					got := declarativeFriendlyRequired.Lint(f)
    98  					if diff := subtest.problems.SetDescriptor(m).Diff(got); diff != "" {
    99  						t.Error(diff)
   100  					}
   101  				})
   102  			}
   103  		})
   104  	}
   105  }
   106  
   107  func TestDeclarativeFriendlyFieldsSingleton(t *testing.T) {
   108  	for _, test := range []struct {
   109  		name   string
   110  		Fields string
   111  		want   testutils.Problems
   112  	}{
   113  		{
   114  			"InvalidNoCreateTime", `string name = 1; string display_name = 2; google.protobuf.Timestamp update_time = 3;`,
   115  			testutils.Problems{{Message: "create_time"}},
   116  		},
   117  		{
   118  			"ValidNoDeleteTimeNoUid", `string name = 1; string display_name = 2; ` +
   119  				`google.protobuf.Timestamp create_time = 3; google.protobuf.Timestamp update_time = 4;`,
   120  			nil,
   121  		},
   122  	} {
   123  		t.Run(test.name, func(t *testing.T) {
   124  			f := testutils.ParseProto3Tmpl(t, `
   125  				import "google/api/resource.proto";
   126  				import "google/protobuf/timestamp.proto";
   127  				message Book {
   128  					option (google.api.resource) = {
   129  						type: "library.googleapis.com/Settings"
   130  						pattern: "publishers/{publisher}/settings"
   131  						style: DECLARATIVE_FRIENDLY
   132  					};
   133  					{{.Fields}}
   134  				}
   135  			`, test)
   136  			m := f.GetMessageTypes()[0]
   137  			got := declarativeFriendlyRequired.Lint(f)
   138  			if diff := test.want.SetDescriptor(m).Diff(got); diff != "" {
   139  				t.Error(diff)
   140  			}
   141  		})
   142  	}
   143  }