github.com/googleapis/api-linter@v1.65.2/rules/aip0121/no_mutable_cycles_test.go (about)

     1  // Copyright 2023 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 aip0121
    16  
    17  import (
    18  	"testing"
    19  
    20  	"github.com/googleapis/api-linter/rules/internal/testutils"
    21  )
    22  
    23  func TestNoMutableCycles(t *testing.T) {
    24  
    25  	for _, test := range []struct {
    26  		name                                                                             string
    27  		BookExtensions, PublisherExtensions, LibraryExtensions, OtherPublisherExtensions string
    28  		problems                                                                         testutils.Problems
    29  	}{
    30  		{
    31  			"ValidNoCycle",
    32  			`[(google.api.resource_reference).type = "library.googleapis.com/Library"]`,
    33  			`[(google.api.resource_reference).type = "library.googleapis.com/Library"]`,
    34  			"",
    35  			"",
    36  			nil,
    37  		},
    38  		{
    39  			"InvalidCycle",
    40  			`[(google.api.resource_reference).type = "library.googleapis.com/Publisher"]`,
    41  			`[(google.api.resource_reference).type = "library.googleapis.com/Book"]`,
    42  			"",
    43  			"",
    44  			testutils.Problems{{
    45  				Message: "cycle",
    46  			}},
    47  		},
    48  		{
    49  			"InvalidSelfReferenceCycle",
    50  			"",
    51  			`[(google.api.resource_reference).type = "library.googleapis.com/Publisher"]`,
    52  			"",
    53  			"",
    54  			testutils.Problems{{
    55  				Message: "cycle",
    56  			}},
    57  		},
    58  		{
    59  			"InvalidDeepCycle",
    60  			`[(google.api.resource_reference).type = "library.googleapis.com/Publisher"]`,
    61  			`[(google.api.resource_reference).type = "library.googleapis.com/Library"]`,
    62  			`[(google.api.resource_reference).type = "library.googleapis.com/Book"]`,
    63  			"",
    64  			testutils.Problems{{
    65  				Message: "cycle",
    66  			}},
    67  		},
    68  		{
    69  			"InvalidDeepAndShallowCycles",
    70  			`[(google.api.resource_reference).type = "library.googleapis.com/Publisher"]`,
    71  			`[(google.api.resource_reference).type = "library.googleapis.com/Library"]`,
    72  			`[(google.api.resource_reference).type = "library.googleapis.com/Book"]`,
    73  			`[(google.api.resource_reference).type = "library.googleapis.com/Book"]`,
    74  			testutils.Problems{
    75  				{
    76  					Message: "cycle",
    77  				},
    78  				{
    79  					Message: "cycle",
    80  				},
    81  			},
    82  		},
    83  		{
    84  			"ValidOutputOnlyCyclicReference",
    85  			`[(google.api.resource_reference).type = "library.googleapis.com/Publisher"]`,
    86  			`[
    87  				(google.api.resource_reference).type = "library.googleapis.com/Book",
    88  				(google.api.field_behavior) = OUTPUT_ONLY
    89  			]`,
    90  			"",
    91  			"",
    92  			nil,
    93  		},
    94  		{
    95  			"ValidOutputOnlyDeepCyclicReference",
    96  			`[(google.api.resource_reference).type = "library.googleapis.com/Publisher"]`,
    97  			`[(google.api.resource_reference).type = "library.googleapis.com/Library"]`,
    98  			`[
    99  				(google.api.resource_reference).type = "library.googleapis.com/Book",
   100  				(google.api.field_behavior) = OUTPUT_ONLY
   101  			]`,
   102  			"",
   103  			nil,
   104  		},
   105  	} {
   106  		t.Run(test.name, func(t *testing.T) {
   107  			f := testutils.ParseProto3Tmpl(t, `
   108  			import "google/api/resource.proto";
   109  			import "google/api/field_behavior.proto";
   110  			message Book {
   111  				option (google.api.resource) = {
   112  					type: "library.googleapis.com/Book"
   113  					pattern: "publishers/{publisher}/books/{book}"
   114  				};
   115  				string name = 1;
   116  
   117  				string resource = 2 {{.BookExtensions}};
   118  			}
   119  
   120  			message Publisher {
   121  				option (google.api.resource) = {
   122  					type: "library.googleapis.com/Publisher"
   123  					pattern: "publishers/{publisher}"
   124  				};
   125  				string name = 1;
   126  
   127  				string resource = 2 {{.PublisherExtensions}};
   128  
   129  				string other_resource = 3 {{.OtherPublisherExtensions}};
   130  			}
   131  
   132  			message Library {
   133  				option (google.api.resource) = {
   134  					type: "library.googleapis.com/Library"
   135  					pattern: "libraries/{library}"
   136  				};
   137  				string name = 1;
   138  
   139  				string resource = 3 {{.LibraryExtensions}};
   140  			}
   141  			`, test)
   142  
   143  			msg := f.FindMessage("Publisher")
   144  			want := test.problems
   145  			if len(want) >= 1 {
   146  				want[0].Descriptor = msg.FindFieldByName("resource")
   147  			}
   148  			if len(want) == 2 {
   149  				want[1].Descriptor = msg.FindFieldByName("other_resource")
   150  			}
   151  			// If this rule was run on the entire test file, there would be two
   152  			// findings, one for each resource in the cycle. To simplify that,
   153  			// we just lint one of the offending messages.
   154  			if diff := want.Diff(noMutableCycles.LintMessage(msg)); diff != "" {
   155  				t.Error(diff)
   156  			}
   157  		})
   158  	}
   159  }